This commit is contained in:
John Alanbrook 2024-01-14 16:24:31 +00:00
parent eb4abb9f8d
commit ffb7631a6b
29 changed files with 549 additions and 462 deletions

View file

@ -137,9 +137,9 @@ else
ifeq ($(UNAME), Darwin) ifeq ($(UNAME), Darwin)
OS := macos OS := macos
CPPFLAGS += -arch $(ARCH) CPPFLAGS += -arch $(ARCH)
CFLAGS += -x objective-c# -fopenmp=libomp CFLAGS += -x objective-c
CXXFLAGS += -std=c++11 CXXFLAGS += -std=c++11
LDFLAGS += -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit# -fopenmp=libomp LDFLAGS += -framework Cocoa -framework QuartzCore -framework AudioToolbox -framework Metal -framework MetalKit
endif endif
endif endif

View file

@ -2,4 +2,10 @@
The particle system works in conjunction with script. The particle system works in conjunction with script.
They are CPU-based particles, which enable a greater amount of particle interactivity thando GPU-based particles. They are CPU-based particles, which enable a greater amount of particle interactivity thando GPU-based particles.
The particles are based on a node system. Particles can have a new set of parameters at different stages of its life.
Each particle can be individually queried for position, angle, life, and so on.
Emitters and even individual particles can have specific callback functions applied for death, birth, and more.

View file

@ -3,7 +3,12 @@
The core tenents of Primum are as follows. When choosing features for Primum, these are the guidelines we follow. The core tenents of Primum are as follows. When choosing features for Primum, these are the guidelines we follow.
- Gameplay first. Visuals are important but should be chosen to be more simple if it makes implementing gameplay more difficult. - Gameplay first. Visuals are important but should be chosen to be more simple if it makes implementing gameplay more difficult.
- Dynamic first. Video games are dynamic, so as much as possible should be dynamically generated. For example, signed distance fields for particle system collision will not be used, as it requires baking sometimes false geometry. We include midi playback, which can be changed in real time far easier than wavs or mp3s. - Dynamic first. Video games are dynamic, so as much as possible should be dynamically generated. For example, signed distance fields for particle system collision will not be used, as it requires baking sometimes false geometry. We include midi playback, which can be changed in real time far easier than wavs or mp3s.
- Marriage of code and editor. Neither completely replaces the other. What is easier to do in code should be done in code, and what should be done in editor should be done in editor. No solutions try to step on the toes of the other solution. - Code first, and marriage of code and editor. Neither completely replaces the other. What is easier to do in code should be done in code, and what should be done in editor should be done in editor. No solutions try to step on the toes of the other solution.
- Uniformity. There should not be a specific wind system that interacts with a specific tree system, for example. If there is a "wind" system, it will affect trees, grass, particles, potentially objects, the clouds in the sky, and everything else. - Uniformity. There should not be a specific wind system that interacts with a specific tree system, for example. If there is a "wind" system, it will affect trees, grass, particles, potentially objects, the clouds in the sky, and everything else.
- Mathematics. - Mathematics.
- Fast. 240 frames per second on modest hardware. - Fast. 240 frames per second on modest hardware.
- "Does it for you". Advanced baking techniques so you can focus on making the game, and know you will not ship any unneeded files. Bake used textures smartly into sprite sheets for a given platform (ie if the platform supports 1k textures make 1k sheets, etc)
- Source control friendly. No binary data created by the engine, until shipping. Data references are handled with relative file paths.
- Heavily emphasize composition over inheritence. Allow for numerous ways of data sharing (like unity's scriptable objects).
- Crashing encouraged (during development). Very open data access, so that all systems can easily interact with and query all other systems. Robust debugging tools to help sort out the complexities that will surely arise from that.
- Actor model built in.

View file

@ -4,7 +4,9 @@ Primum programs are organized into two different types of source files: scripts
Actors can be created with jso files. Make one by calling 'actor.spawn(file)'. Actors can be created with jso files. Make one by calling 'actor.spawn(file)'.
Entities are specialized actors, that are in the world of the computer game. While calling delay on actor causes a delay relative to real-life time, delay on an entity causes a delay relative to the game world time. Entities are specialized actors, that are in the world of the computer game. While calling delay on actor causes a delay relative to real-life time, delay on an entity causes a delay relative to the game world time. Entities can be thought of as running in parallel, but on a single thread on a local computer. In addition, their timeframe can be thought of as in sync with the computer game world.
The game itself has its own time and memory space.
## Scripts ## Scripts

View file

@ -270,6 +270,18 @@ json.decode = function(text, reviver)
return JSON.parse(text,reviver); return JSON.parse(text,reviver);
} }
json.readout = function(obj)
{
var j = {};
for (var k in obj)
if (typeof obj[k] === 'function')
j[k] = 'function ' + obj[k].toString();
else
j[k] = obj[k];
return json.encode(j);
}
Object.methods = function(o) Object.methods = function(o)
{ {
var m = []; var m = [];

View file

@ -79,6 +79,11 @@ component.sprite = Object.copy(component, {
_enghook: make_sprite, _enghook: make_sprite,
}); });
component.sprite.mode = {
simple: 0,
tile: 1
};
component.sprite.impl = { component.sprite.impl = {
toJSON() { toJSON() {
var j = {}; var j = {};
@ -99,6 +104,7 @@ component.sprite.impl = {
} }
else { else {
this.anims = SpriteAnim.make(x); this.anims = SpriteAnim.make(x);
Object.hide(this, 'anims');
var anim = this.anims[0]; var anim = this.anims[0];
this.rect = anim.frames[0].rect; this.rect = anim.frames[0].rect;
cmd(12,this.id,anim.path,this.rect); cmd(12,this.id,anim.path,this.rect);
@ -866,48 +872,3 @@ component.circle2d.impl = Object.mix({
set pos(x) { this.offset = x; }, set pos(x) { this.offset = x; },
}, collider2d.impl); }, collider2d.impl);
component.particle = Object.copy(component, {
make() {
var p = Object.create(this);
p.id = cmd(234);
return p;
},
get pos() {},
set pos(x) {cmd(238,this.id,x);},
get angle() {},
set angle(x) {cmd(239,this.id,Math.turn2rad(x));},
get life() {},
set life(x) { cmd(235,this.id,x); },
get explosiveness() {},
set explosiveness(x) {cmd(237,this.id,x);},
set speed(x) { cmd(240,this.id,x); },
set speed_var(x) { cmd(241,this.id,x); },
set divergence(x) { cmd(242,this.id,x); },
set scale(x) { cmd(243,this.id,x); },
set scale_var(x) { cmd(244,this.id,x); },
set grow_for(x) { cmd(245,this.id,x); },
set shrink_for(x) {cmd(246,this.id,x); },
get max() {},
set max(x) {},
emit(n) {
cmd(236,this.id,n);
},
play() {
},
pause() {
},
});
/* ASSETS */
var Texture = {
mipmaps(path, x) { cmd(94, path, x); },
sprite(path, x) { cmd(95, path, x); },
};
Texture.mipmaps.doc = "Return true if the texture has mipmaps.";
Texture.sprite.do = "Return true if the texture is treated as a sprite.";

View file

@ -217,8 +217,11 @@ Profile.test = {
pack_string() { profile(6); }, pack_string() { profile(6); },
unpack_string(s) { profile(4,s); }, unpack_string(s) { profile(4,s); },
unpack_32farr(a) { profile(5,a); }, unpack_32farr(a) { profile(5,a); },
call_fn_n(fn1, n) { profile(7,fn1,n,fn2); },
}; };
Profile.test.call_fn_n.doc = "Calls fn1 n times, and then fn2.";
Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`; Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
/* These controls are available during editing, and during play of debug builds */ /* These controls are available during editing, and during play of debug builds */

View file

@ -876,7 +876,7 @@ editor.inputs['M-t'] = function() { editor.edit_level.objects.forEach(function(x
editor.inputs['M-t'].doc = "Unlock all objects in current level."; editor.inputs['M-t'].doc = "Unlock all objects in current level.";
editor.inputs['C-n'] = function() { editor.inputs['C-n'] = function() {
// editor.edit_level.spawn(); editor.edit_level.spawn();
return; return;
}; };
editor.inputs['C-n'].doc = "Create an empty object."; editor.inputs['C-n'].doc = "Create an empty object.";

View file

@ -119,12 +119,14 @@ var gameobject_impl = {
get draw_layer() { return cmd(171, this.body); }, get draw_layer() { return cmd(171, this.body); },
set draw_layer(x) { cmd(172, this.body, x); }, set draw_layer(x) { cmd(172, this.body, x); },
set layer(x) { cmd(75,this.body,x); }, set layer(x) { cmd(75,this.body,x); },
get layer() { cmd(77,this.body); }, get layer() { return cmd(77,this.body); },
set warp_layer(x) { cmd(251, this.body,x); },
get warp_layer() { return cmd(252, this.body); },
set mass(x) { set_body(7,this.body,x); }, set mass(x) { set_body(7,this.body,x); },
get mass() { get mass() {
if (!(this.phys === Physics.dynamic)) if (!(this.phys === Physics.dynamic))
return this.__proto__.mass; return undefined;
return q_body(5, this.body); return q_body(5, this.body);
}, },
@ -132,8 +134,6 @@ var gameobject_impl = {
set elasticity(x) { cmd(106,this.body,x); }, set elasticity(x) { cmd(106,this.body,x); },
get friction() { return cmd(109,this.body); }, get friction() { return cmd(109,this.body); },
set friction(x) { cmd(108,this.body,x); }, set friction(x) { cmd(108,this.body,x); },
set gravity(x) { cmd(167,this.body, x); },
get gravity() { return cmd(159,this.body); },
set timescale(x) { cmd(168,this.body,x); }, set timescale(x) { cmd(168,this.body,x); },
get timescale() { return cmd(169,this.body); }, get timescale() { return cmd(169,this.body); },
set phys(x) { set_body(1, this.body, x); }, set phys(x) { set_body(1, this.body, x); },
@ -203,6 +203,8 @@ var gameobject = {
min ??= 0; min ??= 0;
max ??= 50; max ??= 50;
var p = cmd(229,this.body,to.body,a,b,min,max); var p = cmd(229,this.body,to.body,a,b,min,max);
p.max_force = 500;
p.break();
}, },
pivot(to, piv) { pivot(to, piv) {
piv ??= this.worldpos(); piv ??= this.worldpos();
@ -281,10 +283,9 @@ var gameobject = {
}, },
cry(file) { cry(file) {
var p = Sound.play(file, Sound.bus.sfx); this.crying = Sound.play(file, Sound.bus.sfx);
var killfn = p.kill.bind(p); var killfn = () => {this.crying = undefined; console.warn("killed"); }
p.end = killfn; this.crying.hook = killfn;
this.timers.push(killfn);
return killfn; return killfn;
}, },
@ -527,8 +528,8 @@ var gameobject = {
/* The object needed to store an object as an instance of a level */ /* The object needed to store an object as an instance of a level */
instance_obj() { instance_obj() {
var t = this.transform(); var t = this.transform();
var j = this.json_obj(); // var j = this.json_obj();
Object.assign(t,j); // Object.assign(t,j);
t.ur = this.ur; t.ur = this.ur;
return t; return t;
}, },
@ -715,7 +716,6 @@ gameobject.doc = {
flipy: "Check if the object is flipped on its y axis.", flipy: "Check if the object is flipped on its y axis.",
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`, elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`, friction: `When one object touches another, friction slows them down.`,
gravity: 'True if this object should be affected by gravity.',
mass: `The higher the mass of the object, the less forces will affect it.`, mass: `The higher the mass of the object, the less forces will affect it.`,
phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`, phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`,
worldpos: `Function returns the world position of the object.`, worldpos: `Function returns the world position of the object.`,
@ -798,7 +798,7 @@ prototypes.from_file = function(file)
try { try {
if (jsonfile) json = JSON.parse(IO.slurp(jsonfile)); if (jsonfile) json = JSON.parse(IO.slurp(jsonfile));
} catch(e) { } catch(e) {
Log.warn(`Unable to create json from ${jsonfile}`); Log.warn(`Unable to create json from ${jsonfile}. ${e}`);
} }
if (!json && !script) { if (!json && !script) {

View file

@ -15,10 +15,6 @@ var Physics = {
}; };
var physics = { var physics = {
set gravity(x) { cmd(8, x); },
get gravity() { return cmd(72); },
set damping(x) { cmd(73,Math.clamp(x,0,1)); },
get damping() { return cmd(74); },
pos_query(pos, give) { pos_query(pos, give) {
give ??= 25; give ??= 25;
return cmd(44, pos, give); return cmd(44, pos, give);
@ -43,8 +39,6 @@ var physics = {
}; };
physics.doc = {}; physics.doc = {};
physics.doc.gravity = "Gravity expressed in units per second.";
physics.doc.damping = "Damping applied to all physics bodies. Bound between 0 and 1.";
physics.doc.pos_query = "Returns any object colliding with the given point."; physics.doc.pos_query = "Returns any object colliding with the given point.";
physics.doc.box_query = "Returns an array of body ids that collide with a given box."; physics.doc.box_query = "Returns an array of body ids that collide with a given box.";
physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box."; physics.doc.box_point_query = "Returns the subset of points from a given list that are inside a given box.";
@ -71,3 +65,9 @@ for (var i = 0; i < Collision.num; i++) {
Collision.sync(); Collision.sync();
var Warp = {};
Warp.gravity = function() { return cmd(253); }
Warp.damp = function() { return cmd(254); }
physics.gravity = Warp.gravity();
physics.damp = Warp.damp();

View file

@ -1,42 +1,3 @@
var dsp_node = {
make(id) {
var n = Object.create(this);
n.id = id;
return n;
},
id: undefined,
get volume() { return cmd(175, this.id); },
set volume(x) { x = Math.clamp(x,0,1); cmd(176, this.id,x); },
get db() { return 20*Math.log10(Math.abs(this.volume)); },
set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); },
get pan() { return cmd(178,this.id); },
set pan(x) { x = Math.clamp(x,-1,1); cmd(179,this.id,x); },
off(t) { cmd(183, this.id, t); }, /* True to turn off */
bypass(t) { cmd(184, this.id, t); }, /* True to bypass filter effect */
unplug() { cmd(164, this.id); },
plugin(to) { cmd(177, this.id, to.id); },
kill() {
if (this._dead) return;
this._dead = true;
cmd(193, this.id);
},
end() {},
};
var dsp_source = Object.copy(dsp_node,{
end(){},
get loop() { return cmd(194,this.id); },
set loop(x) { cmd(195,this.id, x);},
get timescale() { return cmd(201,this.id); },
set timescale(x) { cmd(202,this.id,x); },
get frame() { return cmd(196,this.id); },
set frame(x) { cmd(199, this.id, x); },
frames() { return cmd(197,this.id); },
length() { return this.frames()/Sound.samplerate(); },
time() { return this.frame/Sound.samplerate(); },
pct() { return this.time()/this.length(); },
});
var Sound = { var Sound = {
bus: {}, bus: {},
@ -52,28 +13,22 @@ var Sound = {
src.plugin(bus); src.plugin(bus);
return src; return src;
}, },
music(midi, sf) {
cmd(13, midi, sf);
},
}; };
var DSP = { var DSP = {
mix(to) { mix(to) {
var n = dsp_node.make(cmd(181)); var n = cmd(181);
if (to) n.plugin(to); if (to) n.plugin(to);
return n; return n;
}, },
source(path) { source(path) {
var src = Object.create(dsp_source); return cmd(182,path);
src.id = cmd(182,path,src);
return src;
}, },
delay(secs,decay) { delay(secs,decay) {
return dsp_node.make(cmd(185, secs, decay)); return cmd(185, secs, decay);
}, },
fwd_delay(secs, decay) { fwd_delay(secs, decay) {
return dsp_node.make(cmd(207,secs,decay)); return cmd(207,secs,decay);
}, },
allpass(secs, decay) { allpass(secs, decay) {
var composite = {}; var composite = {};
@ -86,46 +41,76 @@ var DSP = {
return composite; return composite;
}, },
lpf(f) { lpf(f) {
return dsp_node.make(cmd(186,f)); return cmd(186,f);
}, },
hpf(f) { hpf(f) {
return dsp_node.make(cmd(187,f)); return cmd(187,f);
}, },
mod(path) { mod(path) {
return dsp_node.make(cmd(188,path)); return cmd(188,path);
}, },
midi(midi,sf) { midi(midi,sf) {
return dsp_node.make(cmd(206,midi,sf)); return cmd(206,midi,sf);
}, },
crush(rate, depth) { crush(rate, depth) {
return dsp_node.make(cmd(189,rate,depth)); return cmd(189,rate,depth);
}, },
compressor() { compressor() {
return dsp_node.make(cmd(190)); return cmd(190);
}, },
limiter(ceil) { limiter(ceil) {
return dsp_node.make(cmd(191,ceil)); return cmd(191,ceil);
}, },
noise_gate(floor) { noise_gate(floor) {
return dsp_node.make(cmd(192,floor)); return cmd(192,floor);
}, },
pitchshift(octaves) { pitchshift(octaves) {
return dsp_node.make(cmd(200,octaves)); return cmd(200,octaves);
}, },
noise() { noise() {
return dsp_node.make(cmd(203)); return cmd(203);
}, },
pink() { pink() {
return dsp_node.make(cmd(204)); return cmd(204);
}, },
red() { red() {
return dsp_node.make(cmd(205)); return cmd(205);
}, },
}; };
Sound.bus.master = dsp_node.make(cmd(180)); Sound.bus.master = cmd(180);
Sound.master = Sound.bus.master; Sound.master = Sound.bus.master;
Sound.play.doc = "Play the given file once."; Sound.play.doc = "Play the given file once.";
Sound.doc = {}; Sound.doc = {};
Sound.doc.volume = "Set the master volume. 0 is no sound and 100 is loudest."; Sound.doc.volume = "Set the master volume. 0 is no sound and 100 is loudest.";
DSP.doc = {
delay: "Delays the input by secs, multiplied by decay",
fwd_delay: "Forward feedback delays the input by secs, multiplied by decay",
allpass: "Composite node of a delay and fwd_delay",
lpf: "Low pass filter at a given frequency",
hpf: "High pass filter at a given frequency",
midi: "A source node for a midi file with a given soundfont file",
crush: "Bitcrush the input to a given rate and bit depth",
limiter: "Limit audio to ceil with pleasent rolloff",
noise_gate: "Do not pass audio below the given floor",
pitchshift: "Shift sound by octaves",
noise: "Plain randon noise",
pink: "Pink noise",
red: "Red noise"
};
Object.mixin(cmd(180).__proto__, {
get db() { return 20*Math.log10(Math.abs(this.volume)); },
set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); },
get volume() { return this.gain; },
set volume(x) { this.gain = x; },
});
Object.mixin(DSP.source().__proto__, {
frames() { return cmd(197,this); },
length() { return this.frames()/Sound.samplerate(); },
time() { return this.frame/Sound.samplerate(); },
pct() { return this.time()/this.length(); },
});

View file

@ -198,7 +198,6 @@ void phys2d_init()
cpSpaceSetCollisionSlop(space, 0.01); cpSpaceSetCollisionSlop(space, 0.01);
cpSpaceSetCollisionBias(space, cpfpow(1.0-0.5, 165.f)); cpSpaceSetCollisionBias(space, cpfpow(1.0-0.5, 165.f));
space_gravity = warp_gravity_make(); space_gravity = warp_gravity_make();
YughWarn("Made gravity with %d spherical", space_gravity->spherical);
} }
void phys2d_set_gravity(HMM_Vec2 v) void phys2d_set_gravity(HMM_Vec2 v)
@ -210,7 +209,45 @@ void phys2d_set_gravity(HMM_Vec2 v)
space_gravity->planar_force = (HMM_Vec3){v.x,v.y,0}; space_gravity->planar_force = (HMM_Vec3){v.x,v.y,0};
} }
void phys2d_update(float deltaT) { cpSpaceStep(space, deltaT); } constraint *constraint_make(cpConstraint *c)
{
constraint *cp = malloc(sizeof(*cp));
cp->c = c;
cp->break_cb = JS_UNDEFINED;
cp->remove_cb = JS_UNDEFINED;
cpSpaceAddConstraint(space,c);
cpConstraintSetUserData(c, cp);
return cp;
}
void constraint_break(constraint *constraint)
{
if (!constraint->c) return;
cpSpaceRemoveConstraint(space, constraint->c);
cpConstraintFree(constraint->c);
constraint->c = NULL;
script_call_sym(constraint->break_cb);
}
void constraint_free(constraint *constraint)
{
constraint_break(constraint);
free(constraint);
}
void constraint_test(cpConstraint *constraint, float *dt)
{
float max = cpConstraintGetMaxForce(constraint);
if (!isfinite(max)) return;
float force = cpConstraintGetImpulse(constraint)/ *dt;
if (force > max)
constraint_break(cpConstraintGetUserData(constraint));
}
void phys2d_update(float deltaT) {
cpSpaceStep(space, deltaT);
cpSpaceEachConstraint(space, constraint_test, &deltaT);
}
void init_phys2dshape(struct phys2d_shape *shape, gameobject *go, void *data) { void init_phys2dshape(struct phys2d_shape *shape, gameobject *go, void *data) {
shape->go = go; shape->go = go;

View file

@ -17,6 +17,16 @@ extern struct rgba kinematic_color;
extern struct rgba static_color; extern struct rgba static_color;
extern struct rgba sleep_color; extern struct rgba sleep_color;
typedef struct constraint {
cpConstraint *c;
JSValue break_cb; /* function called when it is forcibly broken */
JSValue remove_cb; /* called when it is removed at all */
} constraint;
constraint *constraint_make(cpConstraint *c);
void constraint_break(constraint *constraint);
void constraint_free(constraint *constraint);
struct phys2d_shape { struct phys2d_shape {
cpShape *shape; /* user data is this phys2d_shape */ cpShape *shape; /* user data is this phys2d_shape */
transform2d t; transform2d t;

View file

@ -142,9 +142,9 @@ void mesh_add_material(mesh *mesh, cgltf_material *mat)
cgltf_buffer_view *buf = img->buffer_view; cgltf_buffer_view *buf = img->buffer_view;
mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id; mesh->bind.fs.images[0] = texture_fromdata(buf->buffer->data, buf->size)->id;
} else { } else {
char *imp = seprint("%s/%s", dirname(mesh->model->path), img->uri); // char *imp = seprint("%s/%s", dirname(mesh->model->path), img->uri);
mesh->bind.fs.images[0] = texture_pullfromfile(imp)->id; // mesh->bind.fs.images[0] = texture_pullfromfile(imp)->id;
free(imp); // free(imp);
} }
} else } else
mesh->bind.fs.images[0] = texture_pullfromfile("k")->id; mesh->bind.fs.images[0] = texture_pullfromfile("k")->id;
@ -289,7 +289,6 @@ void mesh_add_primitive(mesh *mesh, cgltf_primitive *prim)
void model_add_cgltf_mesh(model *model, cgltf_mesh *gltf_mesh) void model_add_cgltf_mesh(model *model, cgltf_mesh *gltf_mesh)
{ {
mesh mesh = {0}; mesh mesh = {0};
mesh.model = model;
for (int i = 0; i < gltf_mesh->primitives_count; i++) for (int i = 0; i < gltf_mesh->primitives_count; i++)
mesh_add_primitive(&mesh, &gltf_mesh->primitives[i]); mesh_add_primitive(&mesh, &gltf_mesh->primitives[i]);

View file

@ -9,7 +9,7 @@
extern HMM_Vec3 eye; extern HMM_Vec3 eye;
typedef struct material { typedef struct material {
} material; } material;
struct model; struct model;
@ -18,7 +18,6 @@ struct model;
typedef struct mesh { typedef struct mesh {
sg_bindings bind; /* Encapsulates material, norms, etc */ sg_bindings bind; /* Encapsulates material, norms, etc */
uint32_t idx_count; uint32_t idx_count;
struct model *model;
} mesh; } mesh;
/* A collection of meshes which create a full figure */ /* A collection of meshes which create a full figure */
@ -56,4 +55,10 @@ void draw_drawmodel(struct drawmodel *dm);
void model_draw_all(); void model_draw_all();
void free_drawmodel(struct drawmodel *dm); void free_drawmodel(struct drawmodel *dm);
material *material_make();
void material_free(material *mat);
mesh *mesh_make();
void mesh_free(mesh *m);
#endif #endif

View file

@ -125,16 +125,13 @@ static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt
{ {
gameobject *go = body2go(body); gameobject *go = body2go(body);
cpVect pos = cpBodyGetPosition(body); cpVect pos = cpBodyGetPosition(body);
HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, gravmask).xy; HMM_Vec2 g = warp_force((HMM_Vec3){pos.x, pos.y, 0}, go->warp_filter).xy;
if (!go) { if (!go) {
cpBodyUpdateVelocity(body,g.cp,damping,dt); cpBodyUpdateVelocity(body,g.cp,damping,dt);
return; return;
} }
cpFloat d = isfinite(go->damping) ? go->damping : damping; cpFloat d = isfinite(go->damping) ? go->damping : damping;
if (isfinite(go->gravity.x) && isfinite(go->gravity.y))
g = go->gravity;
cpBodyUpdateVelocity(body,g.cp,d,dt*go->timescale); cpBodyUpdateVelocity(body,g.cp,d,dt*go->timescale);
@ -159,7 +156,6 @@ gameobject *MakeGameobject() {
.next = -1, .next = -1,
.drawlayer = 0, .drawlayer = 0,
.shape_cbs = NULL, .shape_cbs = NULL,
.gravity = (HMM_Vec2){INFINITY,INFINITY},
.damping = INFINITY, .damping = INFINITY,
.timescale = 1.0, .timescale = 1.0,
.ref = JS_UNDEFINED, .ref = JS_UNDEFINED,
@ -193,8 +189,7 @@ void rm_body_shapes(cpBody *body, cpShape *shape, void *data) {
void rm_body_constraints(cpBody *body, cpConstraint *constraint, void *data) void rm_body_constraints(cpBody *body, cpConstraint *constraint, void *data)
{ {
cpSpaceRemoveConstraint(space, constraint); constraint_break(cpConstraintGetUserData(constraint));
cpConstraintFree(constraint);
} }
void gameobject_free(gameobject *go) { void gameobject_free(gameobject *go) {

View file

@ -34,13 +34,13 @@ struct gameobject {
float mass; float mass;
float f; /* friction */ float f; /* friction */
float e; /* elasticity */ float e; /* elasticity */
float damping;
float timescale; float timescale;
float maxvelocity; float maxvelocity;
float maxangularvelocity; float maxangularvelocity;
HMM_Vec2 gravity; /* its own gravity */
float damping;
unsigned int layer; unsigned int layer;
cpShapeFilter filter; cpShapeFilter filter;
unsigned int warp_filter;
// warpmask warpmask; // warpmask warpmask;
struct phys_cbs cbs; struct phys_cbs cbs;
struct shape_cb *shape_cbs; struct shape_cb *shape_cbs;
@ -49,6 +49,21 @@ struct gameobject {
float drawlayer; float drawlayer;
}; };
/*
Friction uses coulomb model. When shapes collide, their friction is multiplied. Some example values:
Steel on steel: 0.0005
Wood on steel: 0.0012
Wood on wood: 0.0015
=> steel = 0.025
=> wood = 0.04
=> hardrubber = 0.31
=> concrete = 0.05
=> rubber = 0.5
Hardrubber on steel: 0.0077
Hardrubber on concrete: 0.015
Rubber on concrete: 0.025
*/
typedef struct gameobject gameobject; typedef struct gameobject gameobject;
gameobject *MakeGameobject(); gameobject *MakeGameobject();

View file

@ -32,6 +32,8 @@
#include "HandmadeMath.h" #include "HandmadeMath.h"
#define countof(x) (sizeof(x)/sizeof((x)[0]))
static JSValue globalThis; static JSValue globalThis;
static JSClassID js_ptr_id; static JSClassID js_ptr_id;
@ -53,16 +55,49 @@ static JSValue TYPE##2js(TYPE *n) { \
return j; }\ return j; }\
QJSCLASS(dsp_node)
QJSCLASS(gameobject) QJSCLASS(gameobject)
QJSCLASS(emitter)
QJSCLASS(dsp_node)
QJSCLASS(warp_gravity)
QJSCLASS(warp_damp)
QJSCLASS(material)
QJSCLASS(mesh)
/* qjs class colliders and constraints */
/* constraint works for all constraints - 2d or 3d */
static JSClassID js_constraint_id;
static void js_constraint_finalizer(JSRuntime *rt, JSValue val) {
constraint *c = JS_GetOpaque(val, js_constraint_id);
constraint_free(c);
}
static JSClassDef js_constraint_class = {
"constraint",
.finalizer = js_constraint_finalizer
};
static constraint *js2constraint(JSValue val) { return JS_GetOpaque(val, js_constraint_id); }
static JSValue constraint2js(constraint *c)
{
JSValue j = JS_NewObjectClass(js, js_constraint_id);
JS_SetOpaque(j, c);
return j;
}
static JSValue sound_proto;
sound *js2sound(JSValue v) { return js2dsp_node(v)->data; }
#define QJSCLASSPREP(TYPE) \ #define QJSCLASSPREP(TYPE) \
JS_NewClassID(&js_##TYPE##_id);\ JS_NewClassID(&js_##TYPE##_id);\
JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\ JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
#define QJSCLASSPREP_FUNCS(TYPE) \
QJSCLASSPREP(TYPE); \
JSValue TYPE##_proto = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \
JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \ #define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \ (byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \ (byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \ (byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \ (byte & 0x10 ? '1' : '0'), \
@ -78,7 +113,7 @@ JSValue jstzone()
time_t t = time(NULL); time_t t = time(NULL);
time_t local_t = mktime(localtime(&t)); time_t local_t = mktime(localtime(&t));
double diff = difftime(t, local_t); double diff = difftime(t, local_t);
return num2js(diff/3600); return number2js(diff/3600);
} }
int js2bool(JSValue v) { return JS_ToBool(js, v); } int js2bool(JSValue v) { return JS_ToBool(js, v); }
@ -95,7 +130,7 @@ JSValue jscurtime()
{ {
time_t t; time_t t;
time(&t); time(&t);
JSValue jst = num2js(t); JSValue jst = number2js(t);
return jst; return jst;
} }
@ -162,11 +197,8 @@ double js2number(JSValue v) {
void *js2ptr(JSValue v) { return JS_GetOpaque(v,js_ptr_id); } void *js2ptr(JSValue v) { return JS_GetOpaque(v,js_ptr_id); }
JSValue float2js(double g) { return JS_NewFloat64(js, g);} JSValue float2js(double g) { return JS_NewFloat64(js, g);}
JSValue num2js(double g) { return float2js(g); } JSValue number2js(double g) { return float2js(g); }
struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); } struct sprite *js2sprite(JSValue v) { return id2sprite(js2int(v)); }
emitter *js2emitter(JSValue v) { return (emitter*)js2ptr(v); }
JSValue ptr2js(void *ptr) { JSValue ptr2js(void *ptr) {
JSValue obj = JS_NewObjectClass(js, js_ptr_id); JSValue obj = JS_NewObjectClass(js, js_ptr_id);
@ -246,6 +278,24 @@ HMM_Vec2 js2vec2(JSValue v)
return v2; return v2;
} }
HMM_Vec3 js2vec3(JSValue v)
{
HMM_Vec3 v3;
v3.x = js2number(js_getpropidx(v,0));
v3.y = js2number(js_getpropidx(v,1));
v3.z = js2number(js_getpropidx(v,2));
return v3;
}
JSValue vec32js(HMM_Vec3 v)
{
JSValue array = JS_NewArray(js);
js_setprop_num(array,0,number2js(v.x));
js_setprop_num(array,1,number2js(v.y));
js_setprop_num(array,2,number2js(v.z));
return array;
}
cpBitmask js2bitmask(JSValue v) { cpBitmask js2bitmask(JSValue v) {
cpBitmask mask = 0; cpBitmask mask = 0;
int len = js_arrlen(v); int len = js_arrlen(v);
@ -299,8 +349,8 @@ void vec2float(HMM_Vec2 v, float *f) {
JSValue vec2js(HMM_Vec2 v) { JSValue vec2js(HMM_Vec2 v) {
JSValue array = JS_NewArray(js); JSValue array = JS_NewArray(js);
js_setprop_num(array,0,num2js(v.x)); js_setprop_num(array,0,number2js(v.x));
js_setprop_num(array,1,num2js(v.y)); js_setprop_num(array,1,number2js(v.y));
return array; return array;
} }
@ -523,10 +573,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
// set_pawn(js2ptrduk_get_heapptr(duk, 1)); // set_pawn(js2ptrduk_get_heapptr(duk, 1));
break; break;
case 2:
// gameobject_free(js2gameobject(argv[1]));
break;
case 3: case 3:
set_timescale(js2number(argv[1])); set_timescale(js2number(argv[1]));
break; break;
@ -535,10 +581,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
debug_draw_phys(JS_ToBool(js, argv[1])); debug_draw_phys(JS_ToBool(js, argv[1]));
break; break;
case 5:
// renderMS = js2number(argv[1]);
break;
case 6: case 6:
updateMS = js2number(argv[1]); updateMS = js2number(argv[1]);
break; break;
@ -574,33 +616,14 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
str2 = JS_ToCString(js, argv[2]); str2 = JS_ToCString(js, argv[2]);
play_song(str, str2); play_song(str, str2);
break; break;
case 14:
str = JS_ToCString(js, argv[1]);
ret = ptr2js(dsp_source(str));
((sound*)((dsp_node*)js2ptr(ret))->data)->hook = argv[2];
break;
case 15: case 15:
gameobject_draw_debug(js2gameobject(argv[1])); gameobject_draw_debug(js2gameobject(argv[1]));
break; break;
case 16:
// dbg_color = js2color(argv[1]);
break;
case 17:
// trigger_color = js2color(argv[1]);
break;
case 18: case 18:
shape_set_sensor(js2ptr(argv[1]), JS_ToBool(js, argv[2])); shape_set_sensor(js2ptr(argv[1]), JS_ToBool(js, argv[2]));
break; break;
case 19:
// mini_master(js2number(argv[1]));
break;
case 20: case 20:
sprite_enabled(js2int(argv[1]), JS_ToBool(js, argv[2])); sprite_enabled(js2int(argv[1]), JS_ToBool(js, argv[2]));
break; break;
@ -641,9 +664,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = JS_NewFloat64(js, js2timer(argv[1])->interval); ret = JS_NewFloat64(js, js2timer(argv[1])->interval);
break; break;
case 30:
break;
case 31: case 31:
free(js2ptr(argv[1])); free(js2ptr(argv[1]));
break; break;
@ -752,22 +772,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
gameobject_apply(js2gameobject(argv[1])); gameobject_apply(js2gameobject(argv[1]));
break; break;
case 55:
break;
// js2gameobject(argv[1])->flipx = JS_ToBool(js, argv[2]) ? -1 : 1;
case 56:
// js2gameobject(argv[1])->flipy = JS_ToBool(js, argv[2]) ? -1 : 1;
break;
case 57:
// ret = JS_NewBool(js, js2gameobject(argv[1])->flipx == -1 ? 1 : 0);
break;
case 58:
// ret = JS_NewBool(js, js2gameobject(argv[1])->flipy == -1 ? 1 : 0);
break;
case 59: case 59:
v1 = js2cpvec2arr(argv[2]); v1 = js2cpvec2arr(argv[2]);
ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), v1, js2number(argv[3]))); ret = JS_NewInt64(js, point2segindex(js2vec2(argv[1]), v1, js2number(argv[3])));
@ -810,9 +814,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
opengl_rendermode(WIREFRAME); opengl_rendermode(WIREFRAME);
break; break;
case 69:
break;
case 70: case 70:
ret = vec2js(world2go(js2gameobject(argv[1]), js2vec2(argv[2]))); ret = vec2js(world2go(js2gameobject(argv[1]), js2vec2(argv[2])));
break; break;
@ -845,9 +846,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = int2js(js2gameobject(argv[1])->layer); ret = int2js(js2gameobject(argv[1])->layer);
break; break;
case 78:
break;
case 79: case 79:
ret = JS_NewBool(js, phys_stepping()); ret = JS_NewBool(js, phys_stepping());
break; break;
@ -858,9 +856,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
arrfree(ids); arrfree(ids);
break; break;
case 81:
break;
case 82: case 82:
gameobject_draw_debug(js2gameobject(argv[1])); gameobject_draw_debug(js2gameobject(argv[1]));
break; break;
@ -885,17 +880,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
arrfree(intids); arrfree(intids);
break; break;
case 87:
// str = JS_ToCString(js, argv[1]);
// mini_music_play(str);
break;
case 88: case 88:
ret = num2js(HMM_DotV2(js2vec2(argv[1]), js2vec2(argv[2]))); ret = number2js(HMM_DotV2(js2vec2(argv[1]), js2vec2(argv[2])));
break;
case 89:
// mini_music_stop();
break; break;
case 90: case 90:
@ -916,76 +902,26 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = int2js(logLevel); ret = int2js(logLevel);
break; break;
case 94:
str = JS_ToCString(js, argv[1]);
texture_pullfromfile(str)->opts.mips = js2bool(argv[2]);
texture_sync(str);
break;
case 95:
str = JS_ToCString(js, argv[1]);
texture_pullfromfile(str)->opts.sprite = js2bool(argv[2]);
texture_sync(str);
break;
case 96: case 96:
id2sprite(js2int(argv[1]))->color = js2color(argv[2]); id2sprite(js2int(argv[1]))->color = js2color(argv[2]);
break; break;
case 97:
eye = HMM_AddV3(eye, (HMM_Vec3){0, 0.01, 0});
break;
case 98:
eye = HMM_AddV3(eye, (HMM_Vec3){0, -0.01, 0});
break;
case 99:
eye = HMM_AddV3(eye, (HMM_Vec3){-0.01, 0, 0});
break;
case 100:
eye = HMM_AddV3(eye, (HMM_Vec3){0.01, 0,0});
break;
case 101:
eye = HMM_AddV3(eye, (HMM_Vec3){0,0,-0.01});
break;
case 102:
eye = HMM_AddV3(eye,(HMM_Vec3){0,0,0.01});
break;
case 103: case 103:
ret = vec2js(js2gameobject(argv[1])->scale.XY); ret = vec2js(js2gameobject(argv[1])->scale.XY);
break; break;
case 104:
// ret = bool2js(js2gameobject(argv[1])->flipx == -1 ? 1 : 0);
break;
case 105:
// ret = bool2js(js2gameobject(argv[1])->flipy == -1 ? 1 : 0);
break;
case 106: case 106:
js2gameobject(argv[1])->e = js2number(argv[2]); js2gameobject(argv[1])->e = js2number(argv[2]);
break; break;
case 107: case 107:
ret = num2js(js2gameobject(argv[1])->e); ret = number2js(js2gameobject(argv[1])->e);
break; break;
case 108: case 108:
js2gameobject(argv[1])->f = js2number(argv[2]); js2gameobject(argv[1])->f = js2number(argv[2]);
break; break;
case 109: case 109:
ret = num2js(js2gameobject(argv[1])->f); ret = number2js(js2gameobject(argv[1])->f);
break; break;
case 110: case 110:
ret = num2js(js2gameobject(argv[1])->e); ret = number2js(js2gameobject(argv[1])->e);
break; break;
case 111: case 111:
@ -993,7 +929,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 112: case 112:
ret = num2js(((struct phys2d_edge*)js2ptr(argv[1]))->thickness); ret = number2js(((struct phys2d_edge*)js2ptr(argv[1]))->thickness);
break; break;
case 113: case 113:
@ -1031,7 +967,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
ret = str2js(engine_info()); ret = str2js(engine_info());
break; break;
case 121: case 121:
ret = num2js(get_timescale()); ret = number2js(get_timescale());
break; break;
case 122: case 122:
break; break;
@ -1158,7 +1094,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2gameobject(argv[1])->maxvelocity = js2number(argv[2]); js2gameobject(argv[1])->maxvelocity = js2number(argv[2]);
break; break;
case 152: case 152:
ret = num2js(js2gameobject(argv[1])->maxvelocity); ret = number2js(js2gameobject(argv[1])->maxvelocity);
break; break;
case 153: case 153:
cpBodySetTorque(js2gameobject(argv[1])->body, js2number(argv[2])); cpBodySetTorque(js2gameobject(argv[1])->body, js2number(argv[2]));
@ -1167,19 +1103,14 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2gameobject(argv[1])->maxangularvelocity = js2number(argv[2]); js2gameobject(argv[1])->maxangularvelocity = js2number(argv[2]);
break; break;
case 155: case 155:
ret = num2js(js2gameobject(argv[1])->maxangularvelocity); ret = number2js(js2gameobject(argv[1])->maxangularvelocity);
break; break;
case 156: case 156:
js2gameobject(argv[1])->damping = js2number(argv[2]); js2gameobject(argv[1])->damping = js2number(argv[2]);
break; break;
case 157: case 157:
ret = num2js(js2gameobject(argv[1])->damping); ret = number2js(js2gameobject(argv[1])->damping);
break;
case 158:
break;
case 159:
ret = vec2js(js2gameobject(argv[1])->gravity);
break; break;
case 160: case 160:
ret = vec2js(mat_t_dir(t_world2go(js2gameobject(argv[1])), js2vec2(argv[2]))); ret = vec2js(mat_t_dir(t_world2go(js2gameobject(argv[1])), js2vec2(argv[2])));
@ -1200,27 +1131,23 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
unplug_node(js2ptr(argv[1])); unplug_node(js2ptr(argv[1]));
break; break;
case 165: case 165:
// ret = bool2js(sound_paused(js2ptr(argv[1])));
break; break;
case 166: case 166:
str = js2str(argv[1]); str = js2str(argv[1]);
str2 = js2str(argv[2]); str2 = js2str(argv[2]);
ret = int2js(cp(str, str2)); ret = int2js(cp(str, str2));
break; break;
case 167:
js2gameobject(argv[1])->gravity = js2vec2(argv[2]);
break;
case 168: case 168:
js2gameobject(argv[1])->timescale = js2number(argv[2]); js2gameobject(argv[1])->timescale = js2number(argv[2]);
break; break;
case 169: case 169:
ret = num2js(js2gameobject(argv[1])->timescale); ret = number2js(js2gameobject(argv[1])->timescale);
break; break;
case 170: case 170:
id2sprite(js2int(argv[1]))->emissive = js2color(argv[2]); id2sprite(js2int(argv[1]))->emissive = js2color(argv[2]);
break; break;
case 171: case 171:
ret = num2js(js2gameobject(argv[1])->drawlayer); ret = number2js(js2gameobject(argv[1])->drawlayer);
break; break;
case 172: case 172:
js2gameobject(argv[1])->drawlayer = js2number(argv[2]); js2gameobject(argv[1])->drawlayer = js2number(argv[2]);
@ -1233,21 +1160,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
str = js2str(argv[1]); str = js2str(argv[1]);
ds_openvideo(str); ds_openvideo(str);
break; break;
case 175:
ret = num2js((js2dsp_node(argv[1]))->gain);
break;
case 176:
js2dsp_node(argv[1])->gain = js2number(argv[2]);
break;
case 177: case 177:
plugin_node(js2dsp_node(argv[1]), js2dsp_node(argv[2])); plugin_node(js2dsp_node(argv[1]), js2dsp_node(argv[2]));
break; break;
case 178:
ret = num2js(js2dsp_node(argv[1])->pan);
break;
case 179:
js2dsp_node(argv[1])->pan=js2number(argv[2]);
break;
case 180: case 180:
ret = dsp_node2js(masterbus); ret = dsp_node2js(masterbus);
break; break;
@ -1257,13 +1172,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 182: case 182:
str = js2str(argv[1]); str = js2str(argv[1]);
ret = dsp_node2js(dsp_source(str)); ret = dsp_node2js(dsp_source(str));
((sound*)js2dsp_node(ret)->data)->hook = JS_DupValue(js,argv[2]); JS_SetPrototype(js, ret, sound_proto);
break;
case 183:
js2dsp_node(argv[1])->off = js2bool(argv[2]);
break;
case 184:
js2dsp_node(argv[1])->pass = js2bool(argv[2]);
break; break;
case 185: case 185:
ret = dsp_node2js(dsp_delay(js2number(argv[1]), js2number(argv[2]))); ret = dsp_node2js(dsp_delay(js2number(argv[1]), js2number(argv[2])));
@ -1290,37 +1199,15 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 192: case 192:
ret = dsp_node2js(dsp_noise_gate(js2number(argv[1]))); ret = dsp_node2js(dsp_noise_gate(js2number(argv[1])));
break; break;
case 193:
// node_free(js2ptr(argv[1]));
break;
case 194:
ret = bool2js(((sound*)js2dsp_node(argv[1])->data)->loop);
break;
case 195:
((sound*)js2dsp_node(argv[1])->data)->loop = js2bool(argv[2]);
break;
case 196:
ret = num2js(((sound*)js2dsp_node(argv[1])->data)->frame);
break;
case 197: case 197:
ret = num2js(((sound*)js2dsp_node(argv[1])->data)->data->frames); ret = number2js(js2sound(argv[1])->data->frames);
break; break;
case 198: case 198:
ret = num2js(SAMPLERATE); ret = number2js(SAMPLERATE);
break;
case 199:
((sound*)js2dsp_node(argv[1])->data)->frame = js2number(argv[2]);
break; break;
case 200: case 200:
ret = dsp_node2js(dsp_pitchshift(js2number(argv[1]))); ret = dsp_node2js(dsp_pitchshift(js2number(argv[1])));
break; break;
case 201:
ret = num2js(((sound*)js2dsp_node(argv[1])->data)->timescale);
break;
case 202:
YughWarn("%g", js2number(argv[2]));
((sound*)js2dsp_node(argv[1])->data)->timescale = js2number(argv[2]);
break;
case 203: case 203:
ret = dsp_node2js(dsp_whitenoise()); ret = dsp_node2js(dsp_whitenoise());
break; break;
@ -1338,11 +1225,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 207: case 207:
ret = dsp_node2js(dsp_fwd_delay(js2number(argv[1]), js2number(argv[2]))); ret = dsp_node2js(dsp_fwd_delay(js2number(argv[1]), js2number(argv[2])));
break; break;
case 208:
// dag_set(js2gameobject(argv[1]), js2gameobject(argv[2]));
break;
case 209:
break;
case 210: case 210:
ret = jscurtime(); ret = jscurtime();
break; break;
@ -1365,7 +1247,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2sprite(argv[1])->t.scale = js2vec2(argv[2]); js2sprite(argv[1])->t.scale = js2vec2(argv[2]);
break; break;
case 217: case 217:
ret = num2js(js2sprite(argv[1])->t.angle); ret = number2js(js2sprite(argv[1])->t.angle);
break; break;
case 218: case 218:
js2sprite(argv[1])->t.angle = js2number(argv[2]); js2sprite(argv[1])->t.angle = js2number(argv[2]);
@ -1374,94 +1256,66 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
js2sprite(argv[1])->drawmode = js2number(argv[2]); js2sprite(argv[1])->drawmode = js2number(argv[2]);
break; break;
case 220: case 220:
ret = num2js(js2sprite(argv[1])->drawmode); ret = number2js(js2sprite(argv[1])->drawmode);
break; break;
case 221: case 221:
ret = ptr2js(cpPivotJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body,js2vec2(argv[3]).cp)); ret = constraint2js(constraint_make(cpPivotJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body,js2vec2(argv[3]).cp)));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 222: case 222:
ret = ptr2js(cpPinJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, cpvzero,cpvzero)); ret = constraint2js(constraint_make(cpPinJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, cpvzero,cpvzero)));
cpSpaceAddConstraint(space, js2ptr(ret));
break; break;
case 223: case 223:
ret = ptr2js(cpGearJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]))); ret = constraint2js(constraint_make(cpGearJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]))));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 224: case 224:
str = js2str(argv[1]); str = js2str(argv[1]);
ret = ints2js(gif_delays(str)); ret = ints2js(gif_delays(str));
break; break;
case 225: case 225:
ret = ptr2js(cpRotaryLimitJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]))); ret = constraint2js(constraint_make(cpRotaryLimitJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]))));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 226: case 226:
ret = ptr2js(cpDampedRotarySpringNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]), js2number(argv[5]))); ret = constraint2js(constraint_make(cpDampedRotarySpringNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2number(argv[3]), js2number(argv[4]), js2number(argv[5]))));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 227: case 227:
ret = ptr2js(cpDampedSpringNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2number(argv[5]), js2number(argv[6]), js2number(argv[7]))); ret = constraint2js(constraint_make(cpDampedSpringNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2number(argv[5]), js2number(argv[6]), js2number(argv[7]))));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 228: case 228:
ret = ptr2js(cpGrooveJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2vec2(argv[5]).cp)); ret = constraint2js(constraint_make(cpGrooveJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2vec2(argv[5]).cp)));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 229: case 229:
ret = ptr2js(cpSlideJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2number(argv[5]), js2number(argv[6]))); ret = constraint2js(constraint_make(cpSlideJointNew(js2gameobject(argv[1])->body, js2gameobject(argv[2])->body, js2vec2(argv[3]).cp, js2vec2(argv[4]).cp, js2number(argv[5]), js2number(argv[6]))));
cpSpaceAddConstraint(space,js2ptr(ret));
break; break;
case 230: case 230:
ret = ptr2js(cpSpaceAddConstraint(space, cpRatchetJointNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3]), js2number(argv[4])))); ret = constraint2js(constraint_make(cpRatchetJointNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3]), js2number(argv[4]))));
break; break;
case 231: case 231:
ret = ptr2js(cpSpaceAddConstraint(space, cpSimpleMotorNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3])))); ret = constraint2js(constraint_make(cpSimpleMotorNew(js2body(argv[1]), js2body(argv[2]), js2number(argv[3]))));
break; break;
case 232: case 232:
ret = num2js(js2sprite(argv[1])->parallax); ret = number2js(js2sprite(argv[1])->parallax);
break; break;
case 233: case 233:
js2sprite(argv[1])->parallax = js2number(argv[2]); js2sprite(argv[1])->parallax = js2number(argv[2]);
break; break;
case 234: case 234:
ret = ptr2js(make_emitter()); ret = emitter2js(make_emitter());
break; break;
case 235: case 249:
js2emitter(argv[1])->life = js2number(argv[2]); str = JS_ToCString(js,argv[2]);
js2emitter(argv[1])->texture = texture_pullfromfile(str);
break; break;
case 236: case 251:
emitter_emit(js2emitter(argv[1]), js2number(argv[2])); js2gameobject(argv[1])->warp_filter = js2bitmask(argv[2]);
break; break;
case 237: case 252:
js2emitter(argv[1])->explosiveness = js2number(argv[2]); ret = bitmask2js(js2gameobject(argv[1])->warp_filter);
break; break;
case 238: case 253:
js2emitter(argv[1])->t.pos.xy = js2vec2(argv[2]); ret = warp_gravity2js(warp_gravity_make());
break; break;
case 239: case 254:
js2emitter(argv[1])->t.rotation.Elements[0] = js2number(argv[2]); ret = warp_damp2js(warp_damp_make());
break;
case 240:
js2emitter(argv[1])->speed = js2number(argv[2]);
break;
case 241:
js2emitter(argv[1])->variation = js2number(argv[2]);
break;
case 242:
js2emitter(argv[1])->divergence = js2number(argv[2]);
break;
case 243:
js2emitter(argv[1])->scale = js2number(argv[2]);
break;
case 244:
js2emitter(argv[1])->scale_var = js2number(argv[2]);
break;
case 245:
js2emitter(argv[1])->grow_for = js2number(argv[2]);
break;
case 246:
js2emitter(argv[1])->shrink_for = js2number(argv[2]);
break; break;
} }
@ -1506,13 +1360,6 @@ JSValue duk_register(JSContext *js, JSValueConst this, int argc, JSValueConst *a
case 3: case 3:
register_nk_gui(c); register_nk_gui(c);
break; break;
case 4:
break;
case 5:
break;
case 6: case 6:
register_debug(c); register_debug(c);
break; break;
@ -1600,15 +1447,9 @@ JSValue duk_sys_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
case 6: case 6:
return JS_NewBool(js, sim_paused()); return JS_NewBool(js, sim_paused());
case 7:
return gameobject2js(MakeGameobject());
case 8: case 8:
return JS_NewInt64(js, frame_fps()); return JS_NewInt64(js, frame_fps());
case 9: /* Clear the level out */
break;
case 10: case 10:
editor_mode = js2bool(argv[1]); editor_mode = js2bool(argv[1]);
break; break;
@ -1657,19 +1498,10 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a
cpBodySetPosition(go->body, js2vec2(argv[2]).cp); cpBodySetPosition(go->body, js2vec2(argv[2]).cp);
break; break;
case 3:
break;
case 4: case 4:
cpBodyApplyImpulseAtWorldPoint(go->body, js2vec2(argv[2]).cp, cpBodyGetPosition(go->body)); cpBodyApplyImpulseAtWorldPoint(go->body, js2vec2(argv[2]).cp, cpBodyGetPosition(go->body));
return JS_UNDEFINED; return JS_UNDEFINED;
case 5:
break;
case 6:
break;
case 7: case 7:
go->mass = js2number(argv[2]); go->mass = js2number(argv[2]);
if (go->bodytype == CP_BODY_TYPE_DYNAMIC) if (go->bodytype == CP_BODY_TYPE_DYNAMIC)
@ -1790,7 +1622,7 @@ JSValue duk_cmd_circle2d(JSContext *js, JSValueConst this, int argc, JSValueCons
break; break;
case 2: case 2:
return num2js(circle->radius); return number2js(circle->radius);
case 3: case 3:
return vec2js(circle->offset); return vec2js(circle->offset);
@ -1841,6 +1673,174 @@ JSValue duk_make_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst
return edgeval; return edgeval;
} }
#define GETSET_PAIR(ID, ENTRY, TYPE) \
JSValue ID##_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
js2##ID (this)->ENTRY = js2##TYPE (val); \
return JS_UNDEFINED; \
} \
\
JSValue ID##_get_##ENTRY (JSContext *js, JSValue this) { \
return TYPE##2js(js2##ID (this)->ENTRY); \
} \
#define GETSET_PAIR_HOOK(ID, ENTRY) \
JSValue ID##_set_##ENTRY (JSContext *js, JSValue this, JSValue val) { \
ID *n = js2##ID (this); \
n->ENTRY = val; \
return JS_UNDEFINED; \
} \
\
JSValue ID##_get_##ENTRY (JSContext *js, JSValue this) { \
ID *n = js2##ID (this); \
return n->ENTRY; \
} \
GETSET_PAIR(warp_gravity, strength, number)
GETSET_PAIR(warp_gravity, decay, number)
GETSET_PAIR(warp_gravity, spherical, bool)
GETSET_PAIR(warp_gravity, mask, bitmask)
GETSET_PAIR(warp_gravity, planar_force, vec3)
#define CGETSET_ADD(ID, ENTRY) JS_CGETSET_DEF(#ENTRY, ID##_get_##ENTRY, ID##_set_##ENTRY)
static const JSCFunctionListEntry js_warp_gravity_funcs [] = {
CGETSET_ADD(warp_gravity, strength),
CGETSET_ADD(warp_gravity, decay),
CGETSET_ADD(warp_gravity, spherical),
CGETSET_ADD(warp_gravity, mask),
CGETSET_ADD(warp_gravity, planar_force),
};
GETSET_PAIR(warp_damp, damp, vec3)
static const JSCFunctionListEntry js_warp_damp_funcs [] = {
CGETSET_ADD(warp_damp, damp)
};
GETSET_PAIR(emitter, life, number)
GETSET_PAIR(emitter, life_var, number)
GETSET_PAIR(emitter, speed, number)
GETSET_PAIR(emitter, variation, number)
GETSET_PAIR(emitter, divergence, number)
GETSET_PAIR(emitter, scale, number)
GETSET_PAIR(emitter, scale_var, number)
GETSET_PAIR(emitter, grow_for, number)
GETSET_PAIR(emitter, shrink_for, number)
GETSET_PAIR(emitter, max, number)
GETSET_PAIR(emitter, explosiveness, number)
GETSET_PAIR(emitter, go, gameobject)
GETSET_PAIR(emitter, bounce, number)
GETSET_PAIR(emitter, collision_mask, bitmask)
GETSET_PAIR(emitter, die_after_collision, bool)
GETSET_PAIR(emitter, persist, number)
GETSET_PAIR(emitter, persist_var, number)
GETSET_PAIR(emitter, warp_mask, bitmask)
JSValue js_emitter_start (JSContext *js, JSValue this)
{
emitter *n = js2emitter(this);
start_emitter(n);
return JS_UNDEFINED;
}
JSValue js_emitter_stop(JSContext *js, JSValue this)
{
emitter *n = js2emitter(this);
stop_emitter(n);
return JS_UNDEFINED;
}
JSValue js_emitter_emit(JSContext *js, JSValueConst this, int argc, JSValue *argv)
{
emitter *n = js2emitter(this);
emitter_emit(n, js2number(argv[0]));
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_emitter_funcs[] = {
CGETSET_ADD(emitter, life),
CGETSET_ADD(emitter, life_var),
CGETSET_ADD(emitter, speed),
CGETSET_ADD(emitter, variation),
CGETSET_ADD(emitter, divergence),
CGETSET_ADD(emitter, scale),
CGETSET_ADD(emitter, scale_var),
CGETSET_ADD(emitter, grow_for),
CGETSET_ADD(emitter, shrink_for),
CGETSET_ADD(emitter, max),
CGETSET_ADD(emitter, explosiveness),
CGETSET_ADD(emitter, go),
CGETSET_ADD(emitter, bounce),
CGETSET_ADD(emitter, collision_mask),
CGETSET_ADD(emitter, die_after_collision),
CGETSET_ADD(emitter, persist),
CGETSET_ADD(emitter, persist_var),
CGETSET_ADD(emitter, warp_mask),
JS_CFUNC_DEF("start", 0, js_emitter_start),
JS_CFUNC_DEF("stop", 0, js_emitter_stop),
JS_CFUNC_DEF("emit", 1, js_emitter_emit)
};
GETSET_PAIR(dsp_node, pass, bool)
GETSET_PAIR(dsp_node, off, bool)
GETSET_PAIR(dsp_node, gain, number)
GETSET_PAIR(dsp_node, pan, number)
JSValue js_dsp_node_plugin(JSContext *js, JSValueConst this, int argc, JSValue *argv)
{
plugin_node(js2dsp_node(this), js2dsp_node(argv[0]));
return JS_UNDEFINED;
}
JSValue js_dsp_node_unplug(JSContext *js, JSValueConst this)
{
unplug_node(js2dsp_node(this));
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_dsp_node_funcs[] = {
CGETSET_ADD(dsp_node, pass),
CGETSET_ADD(dsp_node, off),
CGETSET_ADD(dsp_node, gain),
CGETSET_ADD(dsp_node, pan),
JS_CFUNC_DEF("plugin", 1, js_dsp_node_plugin),
JS_CFUNC_DEF("unplug", 0, js_dsp_node_unplug)
};
GETSET_PAIR(sound, loop, bool)
GETSET_PAIR(sound, timescale, number)
GETSET_PAIR(sound, frame, number)
GETSET_PAIR_HOOK(sound, hook)
static const JSCFunctionListEntry js_sound_funcs[] = {
CGETSET_ADD(sound, loop),
CGETSET_ADD(sound, timescale),
CGETSET_ADD(sound, frame),
CGETSET_ADD(sound, hook)
};
JSValue constraint_set_max_force (JSContext *js, JSValue this, JSValue val) {
cpConstraintSetMaxForce(js2constraint(this)->c, js2number(val));
return JS_UNDEFINED;
}
JSValue constraint_get_max_force(JSContext *js, JSValue this) { return number2js(cpConstraintGetMaxForce(js2constraint(this)->c));
}
JSValue constraint_set_collide (JSContext *js, JSValue this, JSValue val) {
cpConstraintSetCollideBodies(js2constraint(this)->c, js2bool(val));
return JS_UNDEFINED;
}
JSValue constraint_get_collide(JSContext *js, JSValue this) { return bool2js(cpConstraintGetCollideBodies(js2constraint(this)->c));
}
static const JSCFunctionListEntry js_constraint_funcs[] = {
CGETSET_ADD(constraint, max_force),
CGETSET_ADD(constraint, collide),
};
JSValue duk_cmd_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { JSValue duk_cmd_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) {
int cmd = js2int(argv[0]); int cmd = js2int(argv[0]);
struct phys2d_edge *edge = js2ptr(argv[1]); struct phys2d_edge *edge = js2ptr(argv[1]);
@ -1913,7 +1913,7 @@ JSValue duk_profile(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
js2cpvec2arr(argv[1]); js2cpvec2arr(argv[1]);
break; break;
case 3: case 3:
return num2js(1.0); return number2js(1.0);
case 4: case 4:
js2str(argv[1]); js2str(argv[1]);
break; break;
@ -1922,6 +1922,11 @@ JSValue duk_profile(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
break; break;
case 6: case 6:
return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST)); return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST));
case 7:
for (int i = 0; i < js2number(argv[2]); i++)
script_call_sym(argv[1]);
script_call_sym(argv[3]);
break;
} }
return JS_UNDEFINED; return JS_UNDEFINED;
} }
@ -1964,10 +1969,18 @@ void ffi_load() {
DUK_FUNC(profile, 2) DUK_FUNC(profile, 2)
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
JS_NewClassID(&js_ptr_id); QJSCLASSPREP(ptr);
JS_NewClass(JS_GetRuntime(js), js_ptr_id, &js_ptr_class);
QJSCLASSPREP(dsp_node);
QJSCLASSPREP(gameobject); QJSCLASSPREP(gameobject);
QJSCLASSPREP_FUNCS(dsp_node);
sound_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, sound_proto, js_sound_funcs, countof(js_sound_funcs));
JS_SetPrototype(js, sound_proto, dsp_node_proto);
QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity);
QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(constraint);
} }

View file

@ -3,6 +3,7 @@
#include "quickjs/quickjs.h" #include "quickjs/quickjs.h"
#include "HandmadeMath.h" #include "HandmadeMath.h"
#include "dsp.h"
void ffi_load(); void ffi_load();
@ -15,7 +16,7 @@ int js_print_exception(JSValue v);
struct rgba js2color(JSValue v); struct rgba js2color(JSValue v);
double js2number(JSValue v); double js2number(JSValue v);
JSValue num2js(double g); JSValue number2js(double g);
JSValue int2js(int i); JSValue int2js(int i);
JSValue str2js(const char *c); JSValue str2js(const char *c);

View file

@ -105,8 +105,9 @@ emitter *make_emitter() {
return e; return e;
} }
void free_emitter(emitter *e) void emitter_free(emitter *e)
{ {
YughWarn("kill emitter");
arrfree(e->particles); arrfree(e->particles);
for (int i = arrlen(emitters)-1; i >= 0; i--) for (int i = arrlen(emitters)-1; i >= 0; i--)
if (emitters[i] == e) { if (emitters[i] == e) {
@ -131,7 +132,6 @@ int emitter_spawn(emitter *e)
p.life = e->life; p.life = e->life;
p.pos = (HMM_Vec4){e->t.pos.x,e->t.pos.y,0,0}; p.pos = (HMM_Vec4){e->t.pos.x,e->t.pos.y,0,0};
float newan = e->t.rotation.Elements[0]+(2*HMM_PI*(frand(e->divergence)-(e->divergence/2))); float newan = e->t.rotation.Elements[0]+(2*HMM_PI*(frand(e->divergence)-(e->divergence/2)));
YughWarn("angle %g", newan);
HMM_Vec2 norm = HMM_V2Rotate((HMM_Vec2){0,1}, newan); HMM_Vec2 norm = HMM_V2Rotate((HMM_Vec2){0,1}, newan);
p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,0,0}, variate(e->speed, e->variation)); p.v = HMM_MulV4F((HMM_Vec4){norm.x,norm.y,0,0}, variate(e->speed, e->variation));
p.angle = 0; p.angle = 0;

View file

@ -6,6 +6,7 @@
#include "transform.h" #include "transform.h"
#include "texture.h" #include "texture.h"
#include "anim.h" #include "anim.h"
#include "gameobject.h"
typedef struct particle { typedef struct particle {
HMM_Vec4 pos; HMM_Vec4 pos;
@ -18,17 +19,27 @@ typedef struct particle {
HMM_Vec4 color; HMM_Vec4 color;
} particle; } particle;
#define SPRAY 0
#define CLOUD 1
#define MESH 2
typedef struct emitter { typedef struct emitter {
struct particle *particles; struct particle *particles;
transform3d t; transform3d t;
gameobject *go;
HMM_Vec3 *mesh; /* list of points to optionally spawn from */
HMM_Vec3 *norm; /* norm at each point */
int type; /* spray, cloud, or mesh */
float explosiveness; /* 0 for a stream, 1 for all at once. Range of values allowed. */ float explosiveness; /* 0 for a stream, 1 for all at once. Range of values allowed. */
int max; /* number of particles */ int max; /* number of particles */
double life; /* how long a particle lasts */ double life; /* how long a particle lasts */
double life_var; double life_var;
/* PARTICLE GEN */ /* SPRAY PARTICLE GEN */
float speed; /* initial speed of particle */ float speed; /* initial speed of particle */
float variation; /* variation on speed */ float variation; /* variation on speed */
float divergence; /* angular degree of variation from emitter normal, up to 1 */ float divergence; /* angular degree of variation from emitter normal, up to 1 */
float tumble; /* amount of random rotation of particles */
float tumble_rate; /* tumble rotation */
sampler color; /* color over particle lifetime */ sampler color; /* color over particle lifetime */
float scale; float scale;
float scale_var; float scale_var;
@ -52,10 +63,9 @@ typedef struct emitter {
void particle_init(); void particle_init();
emitter *make_emitter(); emitter *make_emitter();
void free_emitter(emitter *e); void emitter_free(emitter *e);
void start_emitter(emitter *e); void start_emitter(emitter *e);
void pause_emitter(emitter *e);
void stop_emitter(emitter *e); void stop_emitter(emitter *e);
void emitter_emit(emitter *e, int count); void emitter_emit(emitter *e, int count);

View file

@ -287,7 +287,7 @@ void call_callee(struct callee *c) {
} }
void callee_dbl(struct callee c, double d) { void callee_dbl(struct callee c, double d) {
JSValue v = num2js(d); JSValue v = number2js(d);
js_callee_exec(&c, 1, &v); js_callee_exec(&c, 1, &v);
JS_FreeValue(js, v); JS_FreeValue(js, v);
} }

View file

@ -10,6 +10,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "pthread.h" #include "pthread.h"
#include "debug.h" #include "debug.h"
#include "jsffi.h"
pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
@ -264,7 +265,8 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
if(end) { if(end) {
if (s->loop) if (s->loop)
s->frame = 0; s->frame = 0;
call_env(s->hook, "this.end();");
script_call_sym(s->hook);
} }
} }

View file

@ -100,11 +100,7 @@ struct Texture *texture_pullfromfile(const char *path) {
struct Texture *tex = calloc(1, sizeof(*tex)); struct Texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1; tex->opts.sprite = 1;
tex->opts.mips = 0;
tex->opts.gamma = 0;
tex->opts.wrapx = 1;
tex->opts.wrapy = 1;
int n; int n;
char *ext = strrchr(path, '.'); char *ext = strrchr(path, '.');
@ -224,10 +220,6 @@ struct Texture *texture_fromdata(void *raw, long size)
{ {
struct Texture *tex = calloc(1, sizeof(*tex)); struct Texture *tex = calloc(1, sizeof(*tex));
tex->opts.sprite = 1; tex->opts.sprite = 1;
tex->opts.mips = 0;
tex->opts.gamma = 0;
tex->opts.wrapx = 1;
tex->opts.wrapy = 1;
int n; int n;
void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4); void *data = stbi_load_from_memory(raw, size, &tex->width, &tex->height, &n, 4);

View file

@ -10,6 +10,10 @@
#define TEX_HEIGHT 2 #define TEX_HEIGHT 2
#define TEX_DIFF 3 #define TEX_DIFF 3
#define FILTER_NEAREST SG_FILTER_NEAREST
#define FILTER_NONE SG_FILTER_NONE
#define FILTER_LINEAR SG_FILTER_LINEAR
float st_s_w(struct glrect st); float st_s_w(struct glrect st);
float st_s_h(struct glrect st); float st_s_h(struct glrect st);
@ -17,29 +21,29 @@ extern struct glrect ST_UNIT;
struct TextureOptions { struct TextureOptions {
int sprite; int sprite;
int mips;
unsigned int gamma:1;
int wrapx;
int wrapy;
}; };
/* Represents an actual texture on the GPU */ /* Represents an actual texture on the GPU */
struct Texture { struct Texture {
sg_image id; /* ID reference for the GPU memory location of the texture */ sg_image id; /* ID reference for the GPU memory location of the texture */
int width; int width;
int height; int height;
unsigned char *data; unsigned char *data;
struct TextureOptions opts; struct TextureOptions opts;
int frames; int frames;
int *delays; int *delays;
}; };
typedef struct Texture texture; typedef struct img_sampler{
int wrap_u;
int wrap_v;
int wrap_w;
int min_filter;
int mag_filter;
int mip_filter;
} img_sampler;
struct Image { typedef struct Texture texture;
struct Texture *tex;
struct glrect frame;
};
struct Texture *texture_pullfromfile(const char *path); // Create texture from image struct Texture *texture_pullfromfile(const char *path); // Create texture from image
struct Texture *texture_fromdata(void *raw, long size); struct Texture *texture_fromdata(void *raw, long size);

View file

@ -3,7 +3,7 @@
#include "HandmadeMath.h" #include "HandmadeMath.h"
typedef struct { typedef struct transform3d {
HMM_Vec3 pos; HMM_Vec3 pos;
HMM_Vec3 scale; HMM_Vec3 scale;
HMM_Quat rotation; HMM_Quat rotation;

View file

@ -7,19 +7,29 @@ static warp_gravity **warps = NULL;
warp_gravity *warp_gravity_make() warp_gravity *warp_gravity_make()
{ {
warp_gravity *n = calloc(sizeof(*n),1); warp_gravity *n = calloc(sizeof(*n),1);
n->t.pos = (HMM_Vec3){0,0,0};
n->strength = 9.8; n->strength = 9.8;
n->t.scale = (HMM_Vec3){0,-1,0}; n->t.scale = (HMM_Vec3){0,-1,0};
n->planar_force = HMM_MulV3F(n->t.scale, n->strength); n->planar_force = HMM_MulV3F(n->t.scale, n->strength);
n->mask = gravmask;
n->spherical = 0;
arrput(warps, n); arrput(warps, n);
return n; return n;
} }
warp_damp *warp_damp_make()
{
warp_damp *d = calloc(sizeof(*d),1);
return d;
}
void warp_damp_free(warp_damp *d) { free(d); }
void warp_gravity_free(warp_gravity *n) { free(n); }
HMM_Vec3 warp_damp_force(warp_damp *d, HMM_Vec3 pos, HMM_Vec3 vel)
{
return HMM_MulV3(vel, d->damp);
}
HMM_Vec3 warp_gravity_force(warp_gravity *g, HMM_Vec3 pos) HMM_Vec3 warp_gravity_force(warp_gravity *g, HMM_Vec3 pos)
{ {
HMM_Vec3 f = (HMM_Vec3){0,0,0}; HMM_Vec3 f = (HMM_Vec3){0,0,0};
if (g->strength == 0) return f; if (g->strength == 0) return f;
if (g->spherical) { if (g->spherical) {
@ -37,7 +47,7 @@ HMM_Vec3 warp_force(HMM_Vec3 pos, warpmask mask)
{ {
HMM_Vec3 f = (HMM_Vec3){0,0,0}; HMM_Vec3 f = (HMM_Vec3){0,0,0};
for (int i = 0; i < arrlen(warps); i++) { for (int i = 0; i < arrlen(warps); i++) {
// if (!(mask & warps[i]->mask)) continue; if (!(mask & warps[i]->mask)) continue;
f = HMM_AddV3(f, warp_gravity_force(warps[i], pos)); f = HMM_AddV3(f, warp_gravity_force(warps[i], pos));
} }
return f; return f;

View file

@ -17,11 +17,32 @@ typedef struct {
warpmask mask; warpmask mask;
} warp_gravity; } warp_gravity;
typedef struct {
transform3d t;
int unlimited_range;
HMM_Vec3 range;
HMM_Vec3 falloff;
HMM_Vec3 damp;
warpmask mask;
} warp_damp;
typedef struct {
transform3d t;
float strength;
float decay;
float pulse; /* strength of random variance in the wind effect */
float frequency; /* when 0, pulse is smooth. Increase for very pulse over time */
float turbulence; /* When 0, pulsing is smooth and regular. Increase for more chaos. */
int spherical;
} warp_wind;
/* returns the total force for an object at pos */ /* returns the total force for an object at pos */
HMM_Vec3 warp_force(HMM_Vec3 pos, warpmask mask); HMM_Vec3 warp_force(HMM_Vec3 pos, warpmask mask);
warp_gravity *warp_gravity_make(); warp_gravity *warp_gravity_make();
warp_damp *warp_damp_make();
void warp_gravity_free(warp_gravity *g);
void warp_damp_free(warp_damp *d);
#endif #endif

View file

@ -19,7 +19,6 @@ void main()
cos(angle)*v.x-sin(angle)*v.y, cos(angle)*v.x-sin(angle)*v.y,
sin(angle)*v.x+cos(angle)*v.y sin(angle)*v.x+cos(angle)*v.y
); );
p += 0.5;
p *= scale; p *= scale;
p += apos; p += apos;