From 8321a991acc5df9f3dc5ca603e8b81c8c4b4f63e Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 17 Feb 2026 16:03:07 -0600 Subject: [PATCH] fix syntax --- animation.cm | 75 ++++++---- camera.cm | 28 ++-- collision.cm | 207 +++++++++++++++++----------- core.cm | 311 +++++++++++++++++++++++++----------------- examples/forest.ce | 67 +++++---- examples/modelview.ce | 19 ++- input.cm | 15 +- math.cm | 4 +- model.c | 91 ++++++------ resources.cm | 203 ++++++++++++++++----------- sdl.cm | 30 ++-- skin.cm | 49 ++++--- 12 files changed, 665 insertions(+), 434 deletions(-) diff --git a/animation.cm b/animation.cm index 0388de6..f09c2d4 100644 --- a/animation.cm +++ b/animation.cm @@ -12,24 +12,37 @@ function prepare_animations(model) { if (!buffer_blob) return [] var prepared = [] + var ai = null + var anim = null + var channels = null + var duration = null + var ci = null + var chan = null + var sampler = null + var input_acc = null + var output_acc = null + var input_view = null + var times_blob = null + var output_view = null + var values_blob = null - for (var ai = 0; ai < length(g.animations); ai++) { - var anim = g.animations[ai] - var channels = [] - var duration = 0 + for (ai = 0; ai < length(g.animations); ai++) { + anim = g.animations[ai] + channels = [] + duration = 0 - for (var ci = 0; ci < length(anim.channels); ci++) { - var chan = anim.channels[ci] - var sampler = anim.samplers[chan.sampler] + for (ci = 0; ci < length(anim.channels); ci++) { + chan = anim.channels[ci] + sampler = anim.samplers[chan.sampler] if (!sampler) continue - var input_acc = g.accessors[sampler.input] - var output_acc = g.accessors[sampler.output] + input_acc = g.accessors[sampler.input] + output_acc = g.accessors[sampler.output] if (!input_acc || !output_acc) continue // Extract times blob - var input_view = g.views[input_acc.view] - var times_blob = model_c.extract_accessor( + input_view = g.views[input_acc.view] + times_blob = model_c.extract_accessor( buffer_blob, input_view.byte_offset || 0, input_view.byte_stride || 0, @@ -40,8 +53,8 @@ function prepare_animations(model) { ) // Extract values blob - var output_view = g.views[output_acc.view] - var values_blob = model_c.extract_accessor( + output_view = g.views[output_acc.view] + values_blob = model_c.extract_accessor( buffer_blob, output_view.byte_offset || 0, output_view.byte_stride || 0, @@ -109,7 +122,8 @@ function clip_name(instance, clip_idx) { // Find clip by name function find_clip(instance, name) { if (!instance.animations) return -1 - for (var i = 0; i < length(instance.animations); i++) { + var i = null + for (i = 0; i < length(instance.animations); i++) { if (instance.animations[i].name == name) return i } return -1 @@ -163,16 +177,23 @@ function apply(instance) { var anim = instance.animations[instance.clip_index] var model = instance.model var t = instance.time + var ci = null + var chan = null + var node_idx = null + var node = null + var v = null + var q = null + var s = null - for (var ci = 0; ci < length(anim.channels); ci++) { - var chan = anim.channels[ci] - var node_idx = chan.node + for (ci = 0; ci < length(anim.channels); ci++) { + chan = anim.channels[ci] + node_idx = chan.node if (node_idx == null || node_idx >= length(model.nodes)) continue - var node = model.nodes[node_idx] + node = model.nodes[node_idx] if (chan.path == "translation") { - var v = model_c.sample_vec3(chan.times, chan.values, chan.count, t, chan.interpolation) + v = model_c.sample_vec3(chan.times, chan.values, chan.count, t, chan.interpolation) node.x = v[0] node.y = v[1] node.z = v[2] @@ -180,7 +201,7 @@ function apply(instance) { node.dirty_local = true node.dirty_world = true } else if (chan.path == "rotation") { - var q = model_c.sample_quat(chan.times, chan.values, chan.count, t, chan.interpolation) + q = model_c.sample_quat(chan.times, chan.values, chan.count, t, chan.interpolation) node.qx = q[0] node.qy = q[1] node.qz = q[2] @@ -189,7 +210,7 @@ function apply(instance) { node.dirty_local = true node.dirty_world = true } else if (chan.path == "scale") { - var s = model_c.sample_vec3(chan.times, chan.values, chan.count, t, chan.interpolation) + s = model_c.sample_vec3(chan.times, chan.values, chan.count, t, chan.interpolation) node.sx = s[0] node.sy = s[1] node.sz = s[2] @@ -200,8 +221,10 @@ function apply(instance) { } // Mark all children dirty (propagate down hierarchy) - for (var ni = 0; ni < length(model.nodes); ni++) { - var n = model.nodes[ni] + var ni = null + var n = null + for (ni = 0; ni < length(model.nodes); ni++) { + n = model.nodes[ni] if (n.dirty_world) { _mark_children_dirty(n) } @@ -209,8 +232,10 @@ function apply(instance) { } function _mark_children_dirty(node) { - for (var i = 0; i < length(node.children); i++) { - var child = node.children[i] + var i = null + var child = null + for (i = 0; i < length(node.children); i++) { + child = node.children[i] child.dirty_world = true _mark_children_dirty(child) } diff --git a/camera.cm b/camera.cm index 98befea..eb5fc05 100644 --- a/camera.cm +++ b/camera.cm @@ -17,16 +17,14 @@ function set_aspect(aspect) { _aspect = aspect } -function look_at(ex, ey, ez, tx, ty, tz, upx, upy, upz) { - upx = upx != null ? upx : 0 - upy = upy != null ? upy : 1 - upz = upz != null ? upz : 0 - - _eye = {x: ex, y: ey, z: ez} - _target = {x: tx, y: ty, z: tz} - _up = {x: upx, y: upy, z: upz} - - _view_matrix = model_c.compute_view_matrix(ex, ey, ez, tx, ty, tz, upx, upy, upz) +function look_at(eye, target, up) { + var up_val = up || {x: 0, y: 1, z: 0} + + _eye = {x: eye.x, y: eye.y, z: eye.z} + _target = {x: target.x, y: target.y, z: target.z} + _up = {x: up_val.x, y: up_val.y, z: up_val.z} + + _view_matrix = model_c.compute_view_matrix(eye.x, eye.y, eye.z, target.x, target.y, target.z, up_val.x, up_val.y, up_val.z) } function perspective(fov_deg, near, far) { @@ -38,12 +36,12 @@ function perspective(fov_deg, near, far) { _proj_matrix = model_c.compute_perspective(_fov, _aspect, _near, _far) } -function ortho(left, right, bottom, top, near, far) { - near = near != null ? near : -1 - far = far != null ? far : 1 +function ortho(bounds, depth) { + var near = depth && depth.near != null ? depth.near : -1 + var far = depth && depth.far != null ? depth.far : 1 _projection_type = "ortho" - - _proj_matrix = model_c.compute_ortho(left, right, bottom, top, near, far) + + _proj_matrix = model_c.compute_ortho(bounds.left, bounds.right, bounds.bottom, bounds.top, near, far) } function get_view_matrix() { diff --git a/collision.cm b/collision.cm index 25bbdba..2a9f2aa 100644 --- a/collision.cm +++ b/collision.cm @@ -11,35 +11,36 @@ function clear() { } function add_sphere(transform, radius, opts) { - opts = opts || {} + var _opts = opts || {} var c = { id: _collider_id++, type: "sphere", transform: transform, radius: radius, - layer_mask: opts.layer_mask || 1, - user: opts.user + layer_mask: _opts.layer_mask || 1, + user: _opts.user } _colliders.push(c) return c } -function add_box(transform, sx, sy, sz, opts) { - opts = opts || {} +function add_box(transform, size, opts) { + var _opts = opts || {} var c = { id: _collider_id++, type: "box", transform: transform, - sx: sx, sy: sy, sz: sz, - layer_mask: opts.layer_mask || 1, - user: opts.user + sx: size.x, sy: size.y, sz: size.z, + layer_mask: _opts.layer_mask || 1, + user: _opts.user } _colliders.push(c) return c } function remove(collider) { - for (var i = 0; i < length(_colliders); i++) { + var i = null + for (i = 0; i < length(_colliders); i++) { if (_colliders[i].id == collider.id) { _colliders.splice(i, 1) return true @@ -50,14 +51,18 @@ function remove(collider) { function overlaps(layer_mask_a, layer_mask_b) { var results = [] - for (var i = 0; i < length(_colliders); i++) { - for (var j = i + 1; j < length(_colliders); j++) { - var a = _colliders[i] - var b = _colliders[j] - + var i = null + var j = null + var a = null + var b = null + for (i = 0; i < length(_colliders); i++) { + for (j = i + 1; j < length(_colliders); j++) { + a = _colliders[i] + b = _colliders[j] + if (layer_mask_a != null && !(a.layer_mask & layer_mask_a)) continue if (layer_mask_b != null && !(b.layer_mask & layer_mask_b)) continue - + if (_check_collision(a, b)) { results.push({a: a, b: b}) } @@ -66,38 +71,54 @@ function overlaps(layer_mask_a, layer_mask_b) { return results } -function raycast(ox, oy, oz, dx, dy, dz, opts) { - opts = opts || {} - var max_dist = opts.max_dist || 1000000 - var layer_mask = opts.layer_mask || 0xFFFFFFFF - +function raycast(origin, direction, opts) { + var _opts = opts || {} + var max_dist = _opts.max_dist || 1000000 + var layer_mask = _opts.layer_mask || 0xFFFFFFFF + var ox = origin.x + var oy = origin.y + var oz = origin.z + var dx = direction.x + var dy = direction.y + var dz = direction.z + var closest = null + var closest_dist = null + var i = null + var c = null + var hit = null + var len = null + var org = null + var dir = null + // Normalize direction - var len = math.sqrt(dx*dx + dy*dy + dz*dz) + len = math.sqrt(dx*dx + dy*dy + dz*dz) if (len < 0.0001) return null dx /= len dy /= len dz /= len - - var closest = null - var closest_dist = max_dist - - for (var i = 0; i < length(_colliders); i++) { - var c = _colliders[i] + + org = {x: ox, y: oy, z: oz} + dir = {x: dx, y: dy, z: dz} + + closest_dist = max_dist + + for (i = 0; i < length(_colliders); i++) { + c = _colliders[i] if (!(c.layer_mask & layer_mask)) continue - - var hit = null + + hit = null if (c.type == "sphere") { - hit = _ray_sphere(ox, oy, oz, dx, dy, dz, c) + hit = _ray_sphere(org, dir, c) } else if (c.type == "box") { - hit = _ray_box(ox, oy, oz, dx, dy, dz, c) + hit = _ray_box(org, dir, c) } - + if (hit && hit.distance < closest_dist) { closest = hit closest_dist = hit.distance } } - + return closest } @@ -117,49 +138,65 @@ function _get_position(transform) { function _check_collision(a, b) { var pa = _get_position(a.transform) var pb = _get_position(b.transform) - + var dx = pb.x - pa.x var dy = pb.y - pa.y var dz = pb.z - pa.z var dist = math.sqrt(dx*dx + dy*dy + dz*dz) - + // Simple sphere-sphere approximation var ra = a.radius || max(a.sx || 0, a.sy || 0, a.sz || 0) var rb = b.radius || max(b.sx || 0, b.sy || 0, b.sz || 0) return dist < ra + rb } -function _ray_sphere(ox, oy, oz, dx, dy, dz, sphere) { +function _ray_sphere(origin, dir, sphere) { var pos = _get_position(sphere.transform) var r = sphere.radius - + var ox = origin.x + var oy = origin.y + var oz = origin.z + var dx = dir.x + var dy = dir.y + var dz = dir.z + // Vector from ray origin to sphere center var lx = pos.x - ox var ly = pos.y - oy var lz = pos.z - oz - + // Project onto ray direction var tca = lx*dx + ly*dy + lz*dz + var d2 = null + var r2 = null + var thc = null + var t = null + var hx = null + var hy = null + var hz = null + var nx = null + var ny = null + var nz = null if (tca < 0) return null - - var d2 = lx*lx + ly*ly + lz*lz - tca*tca - var r2 = r*r + + d2 = lx*lx + ly*ly + lz*lz - tca*tca + r2 = r*r if (d2 > r2) return null - - var thc = math.sqrt(r2 - d2) - var t = tca - thc + + thc = math.sqrt(r2 - d2) + t = tca - thc if (t < 0) t = tca + thc if (t < 0) return null - - var hx = ox + dx*t - var hy = oy + dy*t - var hz = oz + dz*t - + + hx = ox + dx*t + hy = oy + dy*t + hz = oz + dz*t + // Normal at hit point - var nx = (hx - pos.x) / r - var ny = (hy - pos.y) / r - var nz = (hz - pos.z) / r - + nx = (hx - pos.x) / r + ny = (hy - pos.y) / r + nz = (hz - pos.z) / r + return { x: hx, y: hy, z: hz, nx: nx, ny: ny, nz: nz, @@ -168,57 +205,73 @@ function _ray_sphere(ox, oy, oz, dx, dy, dz, sphere) { } } -function _ray_box(ox, oy, oz, dx, dy, dz, box) { +function _ray_box(origin, dir, box) { var pos = _get_position(box.transform) + var ox = origin.x + var oy = origin.y + var oz = origin.z + var dx = dir.x + var dy = dir.y + var dz = dir.z var hx = (box.sx || 1) / 2 var hy = (box.sy || 1) / 2 var hz = (box.sz || 1) / 2 - - var minx = pos.x - hx, maxx = pos.x + hx - var miny = pos.y - hy, maxy = pos.y + hy - var minz = pos.z - hz, maxz = pos.z + hz - - var tmin = -1000000, tmax = 1000000 - var nx = 0, ny = 0, nz = 0 - + + var minx = pos.x - hx + var maxx = pos.x + hx + var miny = pos.y - hy + var maxy = pos.y + hy + var minz = pos.z - hz + var maxz = pos.z + hz + + var tmin = -1000000 + var tmax = 1000000 + var nx = 0 + var ny = 0 + var nz = 0 + var t1 = null + var t2 = null + var tmp = null + var t = null + // X slab if (abs(dx) > 0.0001) { - var t1 = (minx - ox) / dx - var t2 = (maxx - ox) / dx - if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp } + t1 = (minx - ox) / dx + t2 = (maxx - ox) / dx + if (t1 > t2) { tmp = t1; t1 = t2; t2 = tmp } if (t1 > tmin) { tmin = t1; nx = dx > 0 ? -1 : 1; ny = 0; nz = 0 } if (t2 < tmax) tmax = t2 } else if (ox < minx || ox > maxx) { return null } - + // Y slab if (abs(dy) > 0.0001) { - var t1 = (miny - oy) / dy - var t2 = (maxy - oy) / dy - if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp } + t1 = (miny - oy) / dy + t2 = (maxy - oy) / dy + if (t1 > t2) { tmp = t1; t1 = t2; t2 = tmp } if (t1 > tmin) { tmin = t1; nx = 0; ny = dy > 0 ? -1 : 1; nz = 0 } if (t2 < tmax) tmax = t2 } else if (oy < miny || oy > maxy) { return null } - + // Z slab if (abs(dz) > 0.0001) { - var t1 = (minz - oz) / dz - var t2 = (maxz - oz) / dz - if (t1 > t2) { var tmp = t1; t1 = t2; t2 = tmp } + t1 = (minz - oz) / dz + t2 = (maxz - oz) / dz + if (t1 > t2) { tmp = t1; t1 = t2; t2 = tmp } if (t1 > tmin) { tmin = t1; nx = 0; ny = 0; nz = dz > 0 ? -1 : 1 } if (t2 < tmax) tmax = t2 } else if (oz < minz || oz > maxz) { return null } - + if (tmin > tmax || tmax < 0) return null - - var t = tmin > 0 ? tmin : tmax + + t = tmin > 0 ? tmin : tmax if (t < 0) return null - + return { x: ox + dx*t, y: oy + dy*t, z: oz + dz*t, nx: nx, ny: ny, nz: nz, diff --git a/core.cm b/core.cm index ff1b14e..646a5b5 100644 --- a/core.cm +++ b/core.cm @@ -183,16 +183,19 @@ function stat(name) { function log_msg() { var args = [] - for (var i = 0; i < length(arguments); i++) { + var i = null + for (i = 0; i < length(arguments); i++) { args.push(text(arguments[i])) } log.console("[lance3d] " + text(args, " ")) } function set_lighting(opts) { + var d = null + var len = null if (opts.sun_dir) { - var d = opts.sun_dir - var len = math.sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]) + d = opts.sun_dir + len = math.sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]) if (len > 0) { _state.lighting.sun_dir = [d[0]/len, d[1]/len, d[2]/len] } @@ -213,8 +216,8 @@ function set_fog(opts) { // ============================================================================ function load_model(path, opts) { - opts = opts || {} - var tex_tier = opts.type || "normal" + var _opts = opts || {} + var tex_tier = _opts.type || "normal" var style = _styles[_state.style] return resources_mod.load_model(path, style, tex_tier) } @@ -269,35 +272,45 @@ function make_cube(w, h, d) { } function make_sphere(r, segments) { - if (!segments) segments = 12 + var _segments = segments || 12 var positions = [] var normals = [] var uvs = [] var indices = [] - - for (var y = 0; y <= segments; y++) { - var v = y / segments - var theta = v * pi - - for (var x = 0; x <= segments; x++) { - var u = x / segments - var phi = u * 2 * pi - - var nx = math.sine(theta) * math.cosine(phi) - var ny = math.cosine(theta) - var nz = math.sine(theta) * math.sine(phi) - + var y = null + var x = null + var v = null + var theta = null + var u = null + var phi = null + var nx = null + var ny = null + var nz = null + var i = null + + for (y = 0; y <= _segments; y++) { + v = y / _segments + theta = v * pi + + for (x = 0; x <= _segments; x++) { + u = x / _segments + phi = u * 2 * pi + + nx = math.sine(theta) * math.cosine(phi) + ny = math.cosine(theta) + nz = math.sine(theta) * math.sine(phi) + positions.push(nx * r, ny * r, nz * r) normals.push(nx, ny, nz) uvs.push(u, v) } } - - for (var y = 0; y < segments; y++) { - for (var x = 0; x < segments; x++) { - var i = y * (segments + 1) + x - indices.push(i, i + 1, i + segments + 1) - indices.push(i + 1, i + segments + 2, i + segments + 1) + + for (y = 0; y < _segments; y++) { + for (x = 0; x < _segments; x++) { + i = y * (_segments + 1) + x + indices.push(i, i + 1, i + _segments + 1) + indices.push(i + 1, i + _segments + 2, i + _segments + 1) } } @@ -305,30 +318,36 @@ function make_sphere(r, segments) { } function make_cylinder(r, h, segments) { - if (!segments) segments = 12 + var _segments = segments || 12 var positions = [] var normals = [] var uvs = [] var indices = [] var hh = h / 2 - - for (var i = 0; i <= segments; i++) { - var u = i / segments - var angle = u * 2 * pi - var nx = math.cosine(angle) - var nz = math.sine(angle) - + var i = null + var u = null + var angle = null + var nx = null + var nz = null + var base = null + + for (i = 0; i <= _segments; i++) { + u = i / _segments + angle = u * 2 * pi + nx = math.cosine(angle) + nz = math.sine(angle) + positions.push(nx * r, -hh, nz * r) normals.push(nx, 0, nz) uvs.push(u, 1) - + positions.push(nx * r, hh, nz * r) normals.push(nx, 0, nz) uvs.push(u, 0) } - - for (var i = 0; i < segments; i++) { - var base = i * 2 + + for (i = 0; i < _segments; i++) { + base = i * 2 indices.push(base, base + 1, base + 2) indices.push(base + 1, base + 3, base + 2) } @@ -346,8 +365,8 @@ function make_plane(w, h) { } function load_texture(path, opts) { - opts = opts || {} - var tier = opts.type || "normal" + var _opts = opts || {} + var tier = _opts.type || "normal" var style = _styles[_state.style] return resources_mod.load_texture(path, style, tier) } @@ -360,8 +379,10 @@ function anim_info(model) { if (!model || !model._internal) return [] var internal = model._internal var result = [] - for (var i = 0; i < length(internal.animations); i++) { - var anim = internal.animations[i] + var i = null + var anim = null + for (i = 0; i < length(internal.animations); i++) { + anim = internal.animations[i] result.push({ name: anim.name || ("clip_" + text(i)), duration: anim.duration || 0, @@ -374,69 +395,83 @@ function anim_info(model) { function sample_pose(model, name, time_val) { if (!model || !model._internal) return null var internal = model._internal - - // Find animation by name or index var anim_idx = -1 + var i = null + var anim = null + var duration = null + var instance = null + var pose = null + var ni = null + var si = null + var skin = null + var world_matrices = null + var j = null + var node_idx = null + var jnode = null + var palette = null + + // Find animation by name or index if (is_number(name)) { anim_idx = name } else { - for (var i = 0; i < length(internal.animations); i++) { + for (i = 0; i < length(internal.animations); i++) { if (internal.animations[i].name == name) { anim_idx = i break } } } - + if (anim_idx < 0 || anim_idx >= length(internal.animations)) { return null } - - var anim = internal.animations[anim_idx] - var duration = anim.duration || 0 - + + anim = internal.animations[anim_idx] + duration = anim.duration || 0 + // Clamp time - if (time_val < 0) time_val = 0 - if (time_val > duration) time_val = duration - + var t = time_val + if (t < 0) t = 0 + if (t > duration) t = duration + // Create a temporary animation instance and sample it - var instance = anim_mod.create_instance(internal) + instance = anim_mod.create_instance(internal) anim_mod.play(instance, anim_idx, false) - anim_mod.set_time(instance, time_val) + anim_mod.set_time(instance, t) anim_mod.apply(instance) - + // Build pose: array of node transforms - var pose = { + pose = { _internal: internal, _anim_idx: anim_idx, - _time: time_val, + _time: t, node_matrices: [] } - - for (var ni = 0; ni < length(internal.nodes); ni++) { + + for (ni = 0; ni < length(internal.nodes); ni++) { pose.node_matrices.push(resources_mod.get_transform_world_matrix(internal.nodes[ni])) } - + // Build skin palettes if model has skins if (internal.skins && length(internal.skins) > 0) { pose.skin_palettes = [] - for (var si = 0; si < length(internal.skins); si++) { - var skin = internal.skins[si] - var world_matrices = [] - for (var j = 0; j < length(skin.joints); j++) { - var node_idx = skin.joints[j] - var jnode = internal.nodes[node_idx] + for (si = 0; si < length(internal.skins); si++) { + skin = internal.skins[si] + world_matrices = [] + for (j = 0; j < length(skin.joints); j++) { + node_idx = skin.joints[j] + jnode = internal.nodes[node_idx] if (jnode) { world_matrices.push(resources_mod.get_transform_world_matrix(jnode)) } else { world_matrices.push(model_c.mat4_identity()) } } - var palette = model_c.build_joint_palette(world_matrices, skin.inv_bind, skin.joint_count) + palette = model_c.build_joint_palette(world_matrices, skin.inv_bind, skin.joint_count) pose.skin_palettes.push(palette) } } - + return pose } @@ -447,24 +482,39 @@ function sample_pose(model, name, time_val) { function draw_model(model, transform, pose) { if (!model || !model._internal) return var internal = model._internal - var view_matrix = camera_mod.get_view_matrix() var proj_matrix = camera_mod.get_proj_matrix() var extra_transform = transform || null - - // Get skin palettes from pose or build default var skin_palettes = [] + var si = null + var skin = null + var world_matrices = null + var j = null + var node_idx = null + var jnode = null + var jworld = null + var palette = null + var i = null + var entry = null + var mesh = null + var mat = null + var node_world = null + var world_matrix = null + var tex = null + var uniforms = null + + // Get skin palettes from pose or build default if (pose && pose.skin_palettes) { skin_palettes = pose.skin_palettes } else if (internal.skins && length(internal.skins) > 0) { - for (var si = 0; si < length(internal.skins); si++) { - var skin = internal.skins[si] - var world_matrices = [] - for (var j = 0; j < length(skin.joints); j++) { - var node_idx = skin.joints[j] - var jnode = internal.nodes[node_idx] + for (si = 0; si < length(internal.skins); si++) { + skin = internal.skins[si] + world_matrices = [] + for (j = 0; j < length(skin.joints); j++) { + node_idx = skin.joints[j] + jnode = internal.nodes[node_idx] if (jnode) { - var jworld = resources_mod.get_transform_world_matrix(jnode) + jworld = resources_mod.get_transform_world_matrix(jnode) if (extra_transform) { jworld = model_c.mat4_mul(extra_transform, jworld) } @@ -473,40 +523,43 @@ function draw_model(model, transform, pose) { world_matrices.push(model_c.mat4_identity()) } } - var palette = model_c.build_joint_palette(world_matrices, skin.inv_bind, skin.joint_count) + palette = model_c.build_joint_palette(world_matrices, skin.inv_bind, skin.joint_count) skin_palettes.push(palette) } } - + // Draw each mesh in the model array - for (var i = 0; i < length(model); i++) { - var entry = model[i] - var mesh = entry.mesh - var mat = entry.material - var node_idx = entry._node_index - + for (i = 0; i < length(model); i++) { + entry = model[i] + mesh = entry.mesh + mat = entry.material + node_idx = entry._node_index + // Get node world matrix (from pose or computed) - var node_world if (pose && pose.node_matrices && pose.node_matrices[node_idx]) { node_world = pose.node_matrices[node_idx] } else { node_world = resources_mod.get_transform_world_matrix(internal.nodes[node_idx]) } - + // Apply extra transform - var world_matrix = extra_transform + world_matrix = extra_transform ? model_c.mat4_mul(extra_transform, node_world) : node_world - - var tex = mesh.texture || mat.color_map || backend.get_white_texture() - var uniforms = _build_uniforms(world_matrix, view_matrix, proj_matrix, mat) - - var palette = null + + tex = mesh.texture || mat.color_map || backend.get_white_texture() + uniforms = _build_uniforms(world_matrix, view_matrix, proj_matrix, mat) + + palette = null if (mesh.skinned && length(skin_palettes) > 0) { palette = skin_palettes[0] } - - _queue_draw(mesh, uniforms, tex, mat, palette) + + _queue_draw(mesh, uniforms, tex, { + coverage: mat.coverage, + face: mat.face, + palette: palette + }) } } @@ -515,40 +568,49 @@ function draw_mesh(mesh, transform, material) { var proj_matrix = camera_mod.get_proj_matrix() var world_matrix = transform || model_c.mat4_identity() var mat = material || _default_material - + var tex = mat.color_map || backend.get_white_texture() var uniforms = _build_uniforms(world_matrix, view_matrix, proj_matrix, mat) - - _queue_draw(mesh, uniforms, tex, mat, null) + + _queue_draw(mesh, uniforms, tex, { + coverage: mat.coverage, + face: mat.face, + palette: null + }) } -function draw_billboard(texture, x, y, z, size, mat) { +function draw_billboard(texture, pos, size, mat) { if (!texture) return - size = size || 1.0 - mat = mat || _default_material - + var _size = size || 1.0 + var _mat = mat || _default_material + var cam_eye = camera_mod.get_eye() - var dx = cam_eye.x - x - var dz = cam_eye.z - z + var dx = cam_eye.x - pos.x + var dz = cam_eye.z - pos.z var yaw = math.atan2(dx, dz) var q = math_mod.euler_to_quat(0, yaw, 0) - - var transform = math_mod.trs_matrix(x, y, z, q.x, q.y, q.z, q.w, size, size, 1) - + + var transform = math_mod.trs_matrix( + [pos.x, pos.y, pos.z], + [q.x, q.y, q.z, q.w], + [_size, _size, 1] + ) + var quad = make_plane(1, 1) var billboard_mat = { color_map: texture, - paint: mat.paint || [1, 1, 1, 1], - coverage: mat.coverage || "cutoff", + paint: _mat.paint || [1, 1, 1, 1], + coverage: _mat.coverage || "cutoff", face: "double", lamp: "unlit" } - + draw_mesh(quad, transform, billboard_mat) } -function draw_sprite(texture, x, y, size, mat) { +function draw_sprite(texture, pos, size, mat) { // 2D sprite - uses orthographic projection + // pos is {x, y} // TODO: implement 2D sprite rendering } @@ -557,18 +619,18 @@ function draw_sprite(texture, x, y, size, mat) { // ============================================================================ function debug_point(vertex, size) { - size = size || 1.0 + var _size = size || 1.0 // TODO: implement point rendering } function debug_line(vertex_a, vertex_b, width) { - width = width || 1.0 + var _width = width || 1.0 // TODO: implement line rendering } function debug_grid(size, step, norm, color) { - norm = norm || {x: 0, y: 1, z: 0} - color = color || [0.5, 0.5, 0.5, 1] + var _norm = norm || {x: 0, y: 1, z: 0} + var _color = color || [0.5, 0.5, 0.5, 1] // TODO: implement grid rendering } @@ -577,8 +639,8 @@ function debug_grid(size, step, norm, color) { // ============================================================================ function clear(r, g, b, a) { - if (a == null) a = 1.0 - _state._clear_color = [r, g, b, a] + var _a = a != null ? a : 1.0 + _state._clear_color = [r, g, b, _a] _state._clear_depth = true } @@ -617,10 +679,11 @@ function _end_frame() { // Check if triangle count exceeds platform budget, warn once per minute function _check_tri_budget() { var style = _styles[_state.style] + var now = null if (!style || !style.tri_budget) return - + if (_state.triangles > style.tri_budget) { - var now = time_mod.number() + now = time_mod.number() // Only warn once per minute (60 seconds) if (now - _tri_warning_state.last_warn_time >= 60) { log.console("[lance3d] WARNING: Triangle count " + text(_state.triangles) + @@ -634,14 +697,14 @@ function _check_tri_budget() { // Internal helpers // ============================================================================ -function _queue_draw(mesh, uniforms, texture, mat, palette) { +function _queue_draw(mesh, uniforms, texture, opts) { _state._pending_draws.push({ mesh: mesh, uniforms: uniforms, texture: texture, - coverage: mat.coverage || "opaque", - face: mat.face || "single", - palette: palette + coverage: opts.coverage || "opaque", + face: opts.face || "single", + palette: opts.palette }) } diff --git a/examples/forest.ce b/examples/forest.ce index 8021477..17ad3c4 100644 --- a/examples/forest.ce +++ b/examples/forest.ce @@ -71,9 +71,12 @@ function _init() { player_mat = { paint: [0.85, 0.25, 0.20, 1.0], coverage: "opaque", face: "single", lamp: "lit" } // Generate trees - for (var i = 0; i < num_trees; i++) { - var x = (lance3d.rand() - 0.5) * 90 - var z = (lance3d.rand() - 0.5) * 90 + var i = null + var x = null + var z = null + for (i = 0; i < num_trees; i++) { + x = (lance3d.rand() - 0.5) * 90 + z = (lance3d.rand() - 0.5) * 90 trees.push({ x: x, @@ -98,6 +101,9 @@ function _update(dt) { // Movement input var forward = 0 var right = 0 + var fx = null + var fz = null + var len = null if (lance3d.key('w')) forward += 1 if (lance3d.key('s')) forward -= 1 if (lance3d.key('d')) right -= 1 @@ -106,11 +112,11 @@ function _update(dt) { if (forward != 0 || right != 0) { // Update yaw based on movement direction player.yaw = math.arc_tangent(forward, -right) - - var fx = math.sine(player.yaw) - var fz = math.cosine(player.yaw) - var len = math.sqrt(fx * fx + fz * fz) + fx = math.sine(player.yaw) + fz = math.cosine(player.yaw) + + len = math.sqrt(fx * fx + fz * fz) if (len > 0) { fx /= len fz /= len @@ -122,48 +128,53 @@ function _update(dt) { } function _draw() { + var i = null + var tree = null + var trunk_transform = null + var canopy_transform = null + var q = null + var player_transform = null + lance3d.clear(0.55, 0.70, 0.90, 1.0) // Set up camera following player lance3d.camera_perspective(55, 0.1, 300) lance3d.camera_look_at( - player.x + cam_offset.x, - cam_offset.y, - player.z + cam_offset.z, - player.x, 0, player.z + {x: player.x + cam_offset.x, y: cam_offset.y, z: player.z + cam_offset.z}, + {x: player.x, y: 0, z: player.z} ) // Draw ground lance3d.draw_mesh(ground_mesh, null, ground_mat) // Draw tree trunks - for (var i = 0; i < length(trees); i++) { - var tree = trees[i] - var trunk_transform = lance3d.trs_matrix( - tree.x, tree.trunk_h / 2, tree.z, - 0, 0, 0, 1, - tree.trunk_r, tree.trunk_h, tree.trunk_r + for (i = 0; i < length(trees); i++) { + tree = trees[i] + trunk_transform = lance3d.trs_matrix( + [tree.x, tree.trunk_h / 2, tree.z], + [0, 0, 0, 1], + [tree.trunk_r, tree.trunk_h, tree.trunk_r] ) lance3d.draw_mesh(trunk_mesh, trunk_transform, trunk_mat) } // Draw tree canopies - for (var i = 0; i < length(trees); i++) { - var tree = trees[i] - var canopy_transform = lance3d.trs_matrix( - tree.x, tree.trunk_h + tree.canopy_s / 2, tree.z, - 0, 0, 0, 1, - tree.canopy_s, tree.canopy_s, tree.canopy_s + for (i = 0; i < length(trees); i++) { + tree = trees[i] + canopy_transform = lance3d.trs_matrix( + [tree.x, tree.trunk_h + tree.canopy_s / 2, tree.z], + [0, 0, 0, 1], + [tree.canopy_s, tree.canopy_s, tree.canopy_s] ) lance3d.draw_mesh(canopy_mesh, canopy_transform, tree.canopy_mat) } // Draw player - var q = lance3d.euler_to_quat(0, player.yaw, 0) - var player_transform = lance3d.trs_matrix( - player.x, player.y, player.z, - q.x, q.y, q.z, q.w, - 0.7, 1.0, 0.7 + q = lance3d.euler_to_quat(0, player.yaw, 0) + player_transform = lance3d.trs_matrix( + [player.x, player.y, player.z], + [q.x, q.y, q.z, q.w], + [0.7, 1.0, 0.7] ) lance3d.draw_mesh(player_mesh, player_transform, player_mat) } diff --git a/examples/modelview.ce b/examples/modelview.ce index a0c535f..f4b36bf 100644 --- a/examples/modelview.ce +++ b/examples/modelview.ce @@ -55,9 +55,10 @@ function _init() { log.console("Model loaded with " + text(length(model)) + " mesh(es)") // Get animation info + var i = null animations = lance3d.anim_info(model) log.console(" Animations: " + text(length(animations))) - for (var i = 0; i < length(animations); i++) { + for (i = 0; i < length(animations); i++) { log.console(" " + text(i) + ": " + animations[i].name + " (" + text(animations[i].duration) + "s)") } @@ -87,6 +88,10 @@ function _init() { } function _update(dt) { + var i = null + var clip_idx = null + var duration = null + // Handle input for camera orbit if (lance3d.key('a')) { cam_yaw -= orbit_speed * dt @@ -134,9 +139,9 @@ function _update(dt) { // Switch animation clips with number keys if (length(animations) > 0) { - for (var i = 1; i <= 9; i++) { + for (i = 1; i <= 9; i++) { if (lance3d.keyp(text(i))) { - var clip_idx = i - 1 + clip_idx = i - 1 if (clip_idx < length(animations)) { current_anim = clip_idx anim_time = 0 @@ -160,7 +165,7 @@ function _update(dt) { // Update animation time if (anim_playing && length(animations) > 0) { anim_time += dt * anim_speed - var duration = animations[current_anim].duration + duration = animations[current_anim].duration if (duration > 0) { while (anim_time > duration) { anim_time -= duration @@ -177,6 +182,8 @@ function _switch_to_style(new_style) { } function _draw() { + var pose = null + // Clear with a nice color based on style if (style == "ps1") { lance3d.clear(0.1, 0.05, 0.15, 1.0) @@ -194,11 +201,11 @@ function _draw() { var cam_y = math.sine(cam_pitch) * cam_distance + cam_target_y var cam_z = math.cosine(cam_yaw) * math.cosine(cam_pitch) * cam_distance - lance3d.camera_look_at(cam_x, cam_y, cam_z, 0, cam_target_y, 0) + lance3d.camera_look_at({x: cam_x, y: cam_y, z: cam_z}, {x: 0, y: cam_target_y, z: 0}) // Draw the model if (model) { - var pose = null + pose = null if (length(animations) > 0) { pose = lance3d.sample_pose(model, current_anim, anim_time) } diff --git a/input.cm b/input.cm index d29ac4f..4d29e05 100644 --- a/input.cm +++ b/input.cm @@ -29,20 +29,23 @@ function begin_frame() { // Process SDL events, returns false if quit requested function process_events() { - var ev + var ev = null + var key_name = null + var pressed = null + var btn_pressed = null while ((ev = events.poll()) != null) { if (ev.type == "quit" || ev.type == "window_close_requested") { return false } if (ev.type == "key_down" || ev.type == "key_up") { - var key_name = lower(keyboard.get_key_name(ev.key)) - var pressed = ev.type == "key_down" - + key_name = lower(keyboard.get_key_name(ev.key)) + pressed = ev.type == "key_down" + if (pressed && !_keys_held[key_name]) { _keys_pressed[key_name] = true } _keys_held[key_name] = pressed - + // Map WASD to axes if (key_name == "w") _axes[1] = pressed ? -1 : 0 if (key_name == "s") _axes[1] = pressed ? 1 : 0 @@ -50,7 +53,7 @@ function process_events() { if (key_name == "d") _axes[0] = pressed ? 1 : 0 } if (ev.type == "mouse_button_down" || ev.type == "mouse_button_up") { - var btn_pressed = ev.type == "mouse_button_down" + btn_pressed = ev.type == "mouse_button_down" if (btn_pressed && !_mouse_buttons[ev.button]) { _mouse_buttons_pressed[ev.button] = true } diff --git a/math.cm b/math.cm index 9c89934..ece85be 100644 --- a/math.cm +++ b/math.cm @@ -37,8 +37,8 @@ function scale_matrix(sx, sy, sz) { return model_c.mat4_from_trs(0, 0, 0, 0, 0, 0, 1, sx, sy, sz) } -function trs_matrix(x, y, z, qx, qy, qz, qw, sx, sy, sz) { - return model_c.mat4_from_trs(x, y, z, qx, qy, qz, qw, sx, sy, sz) +function trs_matrix(pos, rot, scale) { + return model_c.mat4_from_trs(pos[0], pos[1], pos[2], rot[0], rot[1], rot[2], rot[3], scale[0], scale[1], scale[2]) } function euler_to_quat(pitch, yaw, roll) { diff --git a/model.c b/model.c index 0c756a4..949b230 100644 --- a/model.c +++ b/model.c @@ -284,7 +284,7 @@ JSValue js_model_mat4_from_trs(JSContext *js, JSValue this_val, int argc, JSValu // Create matrix from 16-element array (column-major) JSValue js_model_mat4_from_array(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { - if (argc < 1 || !JS_IsArray(js, argv[0])) + if (argc < 1 || !JS_IsArray(argv[0])) return JS_ThrowTypeError(js, "mat4_from_array requires an array of 16 numbers"); int len = JS_ArrayLength(js, argv[0]); @@ -292,7 +292,7 @@ JSValue js_model_mat4_from_array(JSContext *js, JSValue this_val, int argc, JSVa float m[16]; for (int i = 0; i < 16; i++) { - JSValue v = JS_GetPropertyUint32(js, argv[0], i); + JSValue v = JS_GetPropertyNumber(js, argv[0], i); double d = 0.0; JS_ToFloat64(js, &d, v); JS_FreeValue(js, v); @@ -550,14 +550,15 @@ JSValue js_model_pack_vertices(JSContext *js, JSValue this_val, int argc, JSValu JS_FreeValue(js, joints_v); JS_FreeValue(js, weights_v); - JSValue result = JS_NewObject(js); - JS_SetPropertyStr(js, result, "data", js_new_blob_stoned_copy(js, packed, total_size)); - JS_SetPropertyStr(js, result, "stride", JS_NewInt32(js, stride)); - JS_SetPropertyStr(js, result, "vertex_count", JS_NewInt32(js, vertex_count)); - JS_SetPropertyStr(js, result, "skinned", JS_NewBool(js, skinned)); + JS_FRAME(js); + JS_ROOT(result, JS_NewObject(js)); + JS_SetPropertyStr(js, result.val, "data", js_new_blob_stoned_copy(js, packed, total_size)); + JS_SetPropertyStr(js, result.val, "stride", JS_NewInt32(js, stride)); + JS_SetPropertyStr(js, result.val, "vertex_count", JS_NewInt32(js, vertex_count)); + JS_SetPropertyStr(js, result.val, "skinned", JS_NewBool(js, skinned)); free(packed); - return result; + JS_RETURN(result.val); } // Build uniform buffer for retro3d rendering @@ -617,7 +618,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal JSValue ambient_v = JS_GetPropertyStr(js, params, "ambient"); if (!JS_IsNull(ambient_v)) { for (int i = 0; i < 3; i++) { - JSValue c = JS_GetPropertyUint32(js, ambient_v, i); + JSValue c = JS_GetPropertyNumber(js, ambient_v, i); double val = 0; JS_ToFloat64(js, &val, c); uniforms[64 + i] = val; @@ -633,7 +634,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal JSValue light_dir_v = JS_GetPropertyStr(js, params, "light_dir"); if (!JS_IsNull(light_dir_v)) { for (int i = 0; i < 3; i++) { - JSValue c = JS_GetPropertyUint32(js, light_dir_v, i); + JSValue c = JS_GetPropertyNumber(js, light_dir_v, i); double val = 0; JS_ToFloat64(js, &val, c); uniforms[68 + i] = val; @@ -649,7 +650,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal JSValue light_color_v = JS_GetPropertyStr(js, params, "light_color"); if (!JS_IsNull(light_color_v)) { for (int i = 0; i < 3; i++) { - JSValue c = JS_GetPropertyUint32(js, light_color_v, i); + JSValue c = JS_GetPropertyNumber(js, light_color_v, i); double val = 0; JS_ToFloat64(js, &val, c); uniforms[72 + i] = val; @@ -683,7 +684,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal // Fog color at offset 80-83 (rgb, unused) if (!JS_IsNull(fog_color_v)) { for (int i = 0; i < 3; i++) { - JSValue c = JS_GetPropertyUint32(js, fog_color_v, i); + JSValue c = JS_GetPropertyNumber(js, fog_color_v, i); double val = 0; JS_ToFloat64(js, &val, c); uniforms[80 + i] = val; @@ -702,7 +703,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal JSValue tint_v = JS_GetPropertyStr(js, params, "tint"); if (!JS_IsNull(tint_v)) { for (int i = 0; i < 4; i++) { - JSValue c = JS_GetPropertyUint32(js, tint_v, i); + JSValue c = JS_GetPropertyNumber(js, tint_v, i); double val = 1.0; JS_ToFloat64(js, &val, c); uniforms[84 + i] = val; @@ -772,7 +773,7 @@ JSValue js_model_build_uniforms(JSContext *js, JSValue this_val, int argc, JSVal // Pack JS array of numbers into a float32 blob JSValue js_model_f32_blob(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { - if (argc < 1 || !JS_IsArray(js, argv[0])) + if (argc < 1 || !JS_IsArray(argv[0])) return JS_ThrowTypeError(js, "f32_blob requires an array"); JSValue arr = argv[0]; @@ -783,7 +784,7 @@ JSValue js_model_f32_blob(JSContext *js, JSValue this_val, int argc, JSValueCons if (!data) return JS_ThrowOutOfMemory(js); for (int i = 0; i < len; i++) { - JSValue v = JS_GetPropertyUint32(js, arr, (uint32_t)i); + JSValue v = JS_GetPropertyNumber(js, arr, (uint32_t)i); double d = 0.0; JS_ToFloat64(js, &d, v); JS_FreeValue(js, v); @@ -883,11 +884,12 @@ JSValue js_model_sample_vec3(JSContext *js, JSValue this_val, int argc, JSValueC JS_FreeCString(js, interp); if (count <= 0) { - JSValue arr = JS_NewArray(js); - JS_SetPropertyUint32(js, arr, 0, JS_NewFloat64(js, 0)); - JS_SetPropertyUint32(js, arr, 1, JS_NewFloat64(js, 0)); - JS_SetPropertyUint32(js, arr, 2, JS_NewFloat64(js, 0)); - return arr; + JS_FRAME(js); + JS_ROOT(arr, JS_NewArray(js)); + JS_SetPropertyNumber(js, arr.val, 0, JS_NewFloat64(js, 0)); + JS_SetPropertyNumber(js, arr.val, 1, JS_NewFloat64(js, 0)); + JS_SetPropertyNumber(js, arr.val, 2, JS_NewFloat64(js, 0)); + JS_RETURN(arr.val); } int idx = find_keyframe(times, count, t); @@ -913,11 +915,12 @@ JSValue js_model_sample_vec3(JSContext *js, JSValue this_val, int argc, JSValueC z = v0[2] + factor * (v1[2] - v0[2]); } - JSValue arr = JS_NewArray(js); - JS_SetPropertyUint32(js, arr, 0, JS_NewFloat64(js, x)); - JS_SetPropertyUint32(js, arr, 1, JS_NewFloat64(js, y)); - JS_SetPropertyUint32(js, arr, 2, JS_NewFloat64(js, z)); - return arr; + JS_FRAME(js); + JS_ROOT(arr, JS_NewArray(js)); + JS_SetPropertyNumber(js, arr.val, 0, JS_NewFloat64(js, x)); + JS_SetPropertyNumber(js, arr.val, 1, JS_NewFloat64(js, y)); + JS_SetPropertyNumber(js, arr.val, 2, JS_NewFloat64(js, z)); + JS_RETURN(arr.val); } // Sample quaternion animation track (with slerp for LINEAR) @@ -943,12 +946,13 @@ JSValue js_model_sample_quat(JSContext *js, JSValue this_val, int argc, JSValueC JS_FreeCString(js, interp); if (count <= 0) { - JSValue arr = JS_NewArray(js); - JS_SetPropertyUint32(js, arr, 0, JS_NewFloat64(js, 0)); - JS_SetPropertyUint32(js, arr, 1, JS_NewFloat64(js, 0)); - JS_SetPropertyUint32(js, arr, 2, JS_NewFloat64(js, 0)); - JS_SetPropertyUint32(js, arr, 3, JS_NewFloat64(js, 1)); - return arr; + JS_FRAME(js); + JS_ROOT(arr, JS_NewArray(js)); + JS_SetPropertyNumber(js, arr.val, 0, JS_NewFloat64(js, 0)); + JS_SetPropertyNumber(js, arr.val, 1, JS_NewFloat64(js, 0)); + JS_SetPropertyNumber(js, arr.val, 2, JS_NewFloat64(js, 0)); + JS_SetPropertyNumber(js, arr.val, 3, JS_NewFloat64(js, 1)); + JS_RETURN(arr.val); } int idx = find_keyframe(times, count, t); @@ -972,12 +976,13 @@ JSValue js_model_sample_quat(JSContext *js, JSValue this_val, int argc, JSValueC result = quat_slerp(q0, q1, factor); } - JSValue arr = JS_NewArray(js); - JS_SetPropertyUint32(js, arr, 0, JS_NewFloat64(js, result.x)); - JS_SetPropertyUint32(js, arr, 1, JS_NewFloat64(js, result.y)); - JS_SetPropertyUint32(js, arr, 2, JS_NewFloat64(js, result.z)); - JS_SetPropertyUint32(js, arr, 3, JS_NewFloat64(js, result.w)); - return arr; + JS_FRAME(js); + JS_ROOT(arr, JS_NewArray(js)); + JS_SetPropertyNumber(js, arr.val, 0, JS_NewFloat64(js, result.x)); + JS_SetPropertyNumber(js, arr.val, 1, JS_NewFloat64(js, result.y)); + JS_SetPropertyNumber(js, arr.val, 2, JS_NewFloat64(js, result.z)); + JS_SetPropertyNumber(js, arr.val, 3, JS_NewFloat64(js, result.w)); + JS_RETURN(arr.val); } // Build joint palette for skinning @@ -988,7 +993,7 @@ JSValue js_model_build_joint_palette(JSContext *js, JSValue this_val, int argc, if (argc < 3) return JS_ThrowTypeError(js, "build_joint_palette requires 3 arguments"); JSValue worlds_arr = argv[0]; - if (!JS_IsArray(js, worlds_arr)) return JS_ThrowTypeError(js, "first arg must be array of world matrices"); + if (!JS_IsArray(worlds_arr)) return JS_ThrowTypeError(js, "first arg must be array of world matrices"); size_t inv_bind_size; float *inv_bind = js_get_blob_data(js, &inv_bind_size, argv[1]); @@ -1004,7 +1009,7 @@ JSValue js_model_build_joint_palette(JSContext *js, JSValue this_val, int argc, if (!palette) return JS_ThrowOutOfMemory(js); for (int j = 0; j < joint_count; j++) { - JSValue world_v = JS_GetPropertyUint32(js, worlds_arr, j); + JSValue world_v = JS_GetPropertyNumber(js, worlds_arr, j); size_t world_size; float *world_m = js_get_blob_data(js, &world_size, world_v); JS_FreeValue(js, world_v); @@ -1034,12 +1039,12 @@ JSValue js_model_get_node_world(JSContext *js, JSValue this_val, int argc, JSVal { if (argc < 2) return JS_ThrowTypeError(js, "get_node_world requires 2 arguments"); - if (!JS_IsArray(js, argv[0])) return JS_ThrowTypeError(js, "first arg must be array"); + if (!JS_IsArray(argv[0])) return JS_ThrowTypeError(js, "first arg must be array"); int node_idx; JS_ToInt32(js, &node_idx, argv[1]); - JSValue mat_v = JS_GetPropertyUint32(js, argv[0], node_idx); + JSValue mat_v = JS_GetPropertyNumber(js, argv[0], node_idx); if (JS_IsNull(mat_v)) { JS_FreeValue(js, mat_v); mat4 id = mat4_identity(); @@ -1127,7 +1132,7 @@ JSValue js_model_mat4_invert(JSContext *js, JSValue this_val, int argc, JSValueC // Pack JS array of numbers into a uint16 blob (little-endian) JSValue js_model_u16_blob(JSContext *js, JSValue this_val, int argc, JSValueConst *argv) { - if (argc < 1 || !JS_IsArray(js, argv[0])) + if (argc < 1 || !JS_IsArray(argv[0])) return JS_ThrowTypeError(js, "u16_blob requires an array"); JSValue arr = argv[0]; @@ -1138,7 +1143,7 @@ JSValue js_model_u16_blob(JSContext *js, JSValue this_val, int argc, JSValueCons if (!data) return JS_ThrowOutOfMemory(js); for (int i = 0; i < len; i++) { - JSValue v = JS_GetPropertyUint32(js, arr, (uint32_t)i); + JSValue v = JS_GetPropertyNumber(js, arr, (uint32_t)i); uint32_t u = 0; JS_ToUint32(js, &u, v); JS_FreeValue(js, v); diff --git a/resources.cm b/resources.cm index d659fa1..12bb524 100644 --- a/resources.cm +++ b/resources.cm @@ -58,37 +58,40 @@ function resize_image_for_platform(img, style, tier) { } // Create a texture with platform-appropriate sizing, storing original for re-resize -function create_texture_for_platform(w, h, pixels, style, tier) { - var original = { width: w, height: h, pixels: pixels } +function create_texture_for_platform(src, style, tier) { + var original = { width: src.width, height: src.height, pixels: src.pixels } var img = resize_image_for_platform(original, style, tier) var tex = _backend.create_texture(img.width, img.height, img.pixels) - + // Tag texture with current style and tier for cache invalidation tex._style_name = style ? style.name : null tex._tier = tier || "normal" tex[TEX_ORIGINAL] = original - + return tex } // Get or create resized texture for current platform function get_platform_texture(tex, style, tier) { if (!tex) return _backend.get_white_texture() - + // Check if texture needs re-resizing (style changed) var style_name = style ? style.name : null + var original = null + var img = null + var new_tex = null if (tex._style_name != style_name || tex._tier != tier) { - var original = tex[TEX_ORIGINAL] + original = tex[TEX_ORIGINAL] if (original) { - var img = resize_image_for_platform(original, style, tier) - var new_tex = _backend.create_texture(img.width, img.height, img.pixels) + img = resize_image_for_platform(original, style, tier) + new_tex = _backend.create_texture(img.width, img.height, img.pixels) new_tex._style_name = style_name new_tex._tier = tier new_tex[TEX_ORIGINAL] = original return new_tex } } - + return tex } @@ -100,7 +103,7 @@ function load_texture(path, style, tier) { if (!img) return null if (style) { - return create_texture_for_platform(img.width, img.height, img.pixels, style, tier) + return create_texture_for_platform(img, style, tier) } else { return _backend.create_texture(img.width, img.height, img.pixels) } @@ -127,11 +130,32 @@ function load_model(path, style, tier) { var textures = [] var materials = [] var original_images = [] + var ti = null + var mi = null + var ni = null + var ci = null + var pi = null + var img = null + var tex = null + var gmat = null + var paint = null + var color_map = null + var tex_info = null + var tex_obj = null + var coverage = null + var node = null + var t = null + var child_idx = null + var mesh = null + var prim = null + var gpu_mesh = null + var mat_idx = null + var mat = null // Load textures with platform-appropriate sizing - for (var ti = 0; ti < length(g.images); ti++) { - var img = g.images[ti] - var tex = null + for (ti = 0; ti < length(g.images); ti++) { + img = g.images[ti] + tex = null if (img && img.pixels) { original_images.push({ width: img.pixels.width, @@ -139,7 +163,7 @@ function load_model(path, style, tier) { pixels: img.pixels.pixels }) if (style) { - tex = create_texture_for_platform(img.pixels.width, img.pixels.height, img.pixels.pixels, style, tier) + tex = create_texture_for_platform(img.pixels, style, tier) } else { tex = _backend.create_texture(img.pixels.width, img.pixels.height, img.pixels.pixels) } @@ -151,27 +175,27 @@ function load_model(path, style, tier) { // Create materials var gltf_mats = g.materials || [] - for (var mi = 0; mi < length(gltf_mats); mi++) { - var gmat = gltf_mats[mi] - - var paint = [1, 1, 1, 1] + for (mi = 0; mi < length(gltf_mats); mi++) { + gmat = gltf_mats[mi] + + paint = [1, 1, 1, 1] if (gmat.pbr && gmat.pbr.base_color_factor) { paint = array(gmat.pbr.base_color_factor) } - - var color_map = null + + color_map = null if (gmat.pbr && gmat.pbr.base_color_texture) { - var tex_info = gmat.pbr.base_color_texture - var tex_obj = g.textures[tex_info.texture] + tex_info = gmat.pbr.base_color_texture + tex_obj = g.textures[tex_info.texture] if (tex_obj && tex_obj.image != null && textures[tex_obj.image]) { color_map = textures[tex_obj.image] } } - - var coverage = "opaque" + + coverage = "opaque" if (gmat.alpha_mode == "MASK") coverage = "cutoff" else if (gmat.alpha_mode == "BLEND") coverage = "blend" - + materials.push({ color_map: color_map, paint: paint, @@ -196,9 +220,9 @@ function load_model(path, style, tier) { } // Build node transforms - for (var ni = 0; ni < length(g.nodes); ni++) { - var node = g.nodes[ni] - var t = _make_internal_transform(node) + for (ni = 0; ni < length(g.nodes); ni++) { + node = g.nodes[ni] + t = _make_internal_transform(node) t.mesh_index = node.mesh t.name = node.name t.gltf_children = node.children || [] @@ -206,10 +230,10 @@ function load_model(path, style, tier) { } // Set up parent-child relationships - for (var ni = 0; ni < length(internal_model.nodes); ni++) { - var t = internal_model.nodes[ni] - for (var ci = 0; ci < length(t.gltf_children); ci++) { - var child_idx = t.gltf_children[ci] + for (ni = 0; ni < length(internal_model.nodes); ni++) { + t = internal_model.nodes[ni] + for (ci = 0; ci < length(t.gltf_children); ci++) { + child_idx = t.gltf_children[ci] if (child_idx < length(internal_model.nodes)) { _transform_set_parent(internal_model.nodes[child_idx], t) } @@ -217,18 +241,18 @@ function load_model(path, style, tier) { } // Find root nodes - for (var ni = 0; ni < length(internal_model.nodes); ni++) { + for (ni = 0; ni < length(internal_model.nodes); ni++) { if (!internal_model.nodes[ni].parent) { internal_model.root_nodes.push(internal_model.nodes[ni]) } } // Process meshes - for (var mi = 0; mi < length(g.meshes); mi++) { - var mesh = g.meshes[mi] - for (var pi = 0; pi < length(mesh.primitives); pi++) { - var prim = mesh.primitives[pi] - var gpu_mesh = _process_gltf_primitive(g, buffer_blob, prim, textures) + for (mi = 0; mi < length(g.meshes); mi++) { + mesh = g.meshes[mi] + for (pi = 0; pi < length(mesh.primitives); pi++) { + prim = mesh.primitives[pi] + gpu_mesh = _process_gltf_primitive(g, buffer_blob, prim, textures) if (gpu_mesh) { gpu_mesh.name = mesh.name gpu_mesh.mesh_index = mi @@ -243,16 +267,16 @@ function load_model(path, style, tier) { internal_model.skins = skin_mod.prepare_skins(internal_model) // Build result array: [{mesh, material, node_index}] - for (var ni = 0; ni < length(internal_model.nodes); ni++) { - var node = internal_model.nodes[ni] + for (ni = 0; ni < length(internal_model.nodes); ni++) { + node = internal_model.nodes[ni] if (node.mesh_index == null) continue - for (var mi = 0; mi < length(internal_model.meshes); mi++) { - var mesh = internal_model.meshes[mi] + for (mi = 0; mi < length(internal_model.meshes); mi++) { + mesh = internal_model.meshes[mi] if (mesh.mesh_index != node.mesh_index) continue - var mat_idx = mesh.material_index - var mat = (mat_idx != null && materials[mat_idx]) ? materials[mat_idx] : meme(_default_material) + mat_idx = mesh.material_index + mat = (mat_idx != null && materials[mat_idx]) ? materials[mat_idx] : meme(_default_material) result.push({ mesh: mesh, @@ -272,42 +296,53 @@ function load_model(path, style, tier) { function recalc_model_textures(model, style, tier) { if (!model || !model._internal) return var internal = model._internal - - for (var ti = 0; ti < length(internal._original_images); ti++) { - var original = internal._original_images[ti] + var ti = null + var mi = null + var i = null + var original = null + var new_tex = null + var gmat = null + var tex_info = null + var tex_obj = null + var mesh = null + var mat_idx = null + var entry = null + + for (ti = 0; ti < length(internal._original_images); ti++) { + original = internal._original_images[ti] if (!original) continue - - var new_tex = create_texture_for_platform(original.width, original.height, original.pixels, style, tier) + + new_tex = create_texture_for_platform(original, style, tier) internal.textures[ti] = new_tex } - + // Update material references var g = internal._gltf var gltf_mats = g.materials || [] - for (var mi = 0; mi < length(gltf_mats); mi++) { - var gmat = gltf_mats[mi] + for (mi = 0; mi < length(gltf_mats); mi++) { + gmat = gltf_mats[mi] if (gmat.pbr && gmat.pbr.base_color_texture) { - var tex_info = gmat.pbr.base_color_texture - var tex_obj = g.textures[tex_info.texture] + tex_info = gmat.pbr.base_color_texture + tex_obj = g.textures[tex_info.texture] if (tex_obj && tex_obj.image != null && internal.textures[tex_obj.image]) { internal.materials[mi].color_map = internal.textures[tex_obj.image] } } } - + // Update mesh textures - for (var mi = 0; mi < length(internal.meshes); mi++) { - var mesh = internal.meshes[mi] - var mat_idx = mesh.material_index + for (mi = 0; mi < length(internal.meshes); mi++) { + mesh = internal.meshes[mi] + mat_idx = mesh.material_index if (mat_idx != null && internal.materials[mat_idx]) { mesh.texture = internal.materials[mat_idx].color_map } } - + // Update result array materials - for (var i = 0; i < length(model); i++) { - var entry = model[i] - var mat_idx = entry.mesh.material_index + for (i = 0; i < length(model); i++) { + entry = model[i] + mat_idx = entry.mesh.material_index if (mat_idx != null && internal.materials[mat_idx]) { entry.material = internal.materials[mat_idx] } @@ -329,24 +364,28 @@ function _make_internal_transform(node) { dirty_world: true } + var trans = null + var rot = null + var scl = null if (node.matrix) { t.local_mat = model_c.mat4_from_array(node.matrix) t.has_local_mat = true } else { - var trans = node.translation || [0, 0, 0] - var rot = node.rotation || [0, 0, 0, 1] - var scale = node.scale || [1, 1, 1] + trans = node.translation || [0, 0, 0] + rot = node.rotation || [0, 0, 0, 1] + scl = node.scale || [1, 1, 1] t.x = trans[0]; t.y = trans[1]; t.z = trans[2] t.qx = rot[0]; t.qy = rot[1]; t.qz = rot[2]; t.qw = rot[3] - t.sx = scale[0]; t.sy = scale[1]; t.sz = scale[2] + t.sx = scl[0]; t.sy = scl[1]; t.sz = scl[2] } return t } function _transform_set_parent(child, parent) { + var idx = null if (child.parent) { - var idx = find(child.parent.children, child) + idx = find(child.parent.children, child) if (idx != null) child.parent.children.splice(idx, 1) } child.parent = parent @@ -375,8 +414,9 @@ function _transform_get_world_matrix(t) { if (!t.dirty_world && t.world_mat) return t.world_mat var local = _transform_get_local_matrix(t) + var parent_world = null if (t.parent) { - var parent_world = _transform_get_world_matrix(t.parent) + parent_world = _transform_get_world_matrix(t.parent) t.world_mat = model_c.mat4_mul(parent_world, local) } else { t.world_mat = local @@ -411,8 +451,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { ) var normals = null + var norm_view = null if (norm_acc) { - var norm_view = g.views[norm_acc.view] + norm_view = g.views[norm_acc.view] normals = model_c.extract_accessor( buffer_blob, norm_view.byte_offset || 0, @@ -425,8 +466,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { } var uvs = null + var uv_view = null if (uv_acc) { - var uv_view = g.views[uv_acc.view] + uv_view = g.views[uv_acc.view] uvs = model_c.extract_accessor( buffer_blob, uv_view.byte_offset || 0, @@ -439,8 +481,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { } var colors = null + var color_view = null if (color_acc) { - var color_view = g.views[color_acc.view] + color_view = g.views[color_acc.view] colors = model_c.extract_accessor( buffer_blob, color_view.byte_offset || 0, @@ -453,8 +496,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { } var joints = null + var joints_view = null if (joints_acc) { - var joints_view = g.views[joints_acc.view] + joints_view = g.views[joints_acc.view] joints = model_c.extract_accessor( buffer_blob, joints_view.byte_offset || 0, @@ -467,8 +511,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { } var weights = null + var weights_view = null if (weights_acc) { - var weights_view = g.views[weights_acc.view] + weights_view = g.views[weights_acc.view] weights = model_c.extract_accessor( buffer_blob, weights_view.byte_offset || 0, @@ -483,8 +528,9 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { var indices = null var index_count = 0 var index_type = "uint16" + var idx_view = null if (idx_acc) { - var idx_view = g.views[idx_acc.view] + idx_view = g.views[idx_acc.view] indices = model_c.extract_indices( buffer_blob, idx_view.byte_offset || 0, @@ -511,11 +557,14 @@ function _process_gltf_primitive(g, buffer_blob, prim, textures) { var index_buffer = indices ? _backend.create_index_buffer(indices) : null var texture = null + var mat = null + var tex_info = null + var tex_obj = null if (prim.material != null && g.materials[prim.material]) { - var mat = g.materials[prim.material] + mat = g.materials[prim.material] if (mat.pbr && mat.pbr.base_color_texture) { - var tex_info = mat.pbr.base_color_texture - var tex_obj = g.textures[tex_info.texture] + tex_info = mat.pbr.base_color_texture + tex_obj = g.textures[tex_info.texture] if (tex_obj && tex_obj.image != null && textures[tex_obj.image]) { texture = textures[tex_obj.image] } diff --git a/sdl.cm b/sdl.cm index 628ceff..3ac0202 100644 --- a/sdl.cm +++ b/sdl.cm @@ -21,12 +21,12 @@ var _resolution_h = 480 // Initialize the GPU backend function init(opts) { - opts = opts || {} - _resolution_w = opts.width || 640 - _resolution_h = opts.height || 480 - + var _opts = opts || {} + _resolution_w = _opts.width || 640 + _resolution_h = _opts.height || 480 + _window = video.window({ - title: opts.title || "lance3d", + title: _opts.title || "lance3d", width: _resolution_w, height: _resolution_h }) @@ -337,19 +337,25 @@ function submit_frame(draws, clear_color, clear_depth, style_id) { var k = order[d.coverage] return k == null ? 0 : k }) - draws = sort(draws, keys) + var sorted_draws = sort(draws, keys) var draw_calls = 0 var triangles = 0 var sampler = get_sampler(style_id) + var i = null + var d = null + var skinned = null + var cull = null + var alpha_mode = null + var pipeline = null - for (var i = 0; i < length(draws); i++) { - var d = draws[i] + for (i = 0; i < length(sorted_draws); i++) { + d = sorted_draws[i] - var skinned = d.mesh.skinned && d.palette - var cull = d.face == "double" ? "none" : "back" - var alpha_mode = d.coverage == "blend" ? "blend" : (d.coverage == "cutoff" || d.coverage == "mask" ? "mask" : "opaque") - var pipeline = get_pipeline(skinned, alpha_mode, cull) + skinned = d.mesh.skinned && d.palette + cull = d.face == "double" ? "none" : "back" + alpha_mode = d.coverage == "blend" ? "blend" : (d.coverage == "cutoff" || d.coverage == "mask" ? "mask" : "opaque") + pipeline = get_pipeline(skinned, alpha_mode, cull) if (!pipeline) continue diff --git a/skin.cm b/skin.cm index 3bd16a3..4d4bd86 100644 --- a/skin.cm +++ b/skin.cm @@ -12,16 +12,21 @@ function prepare_skins(model) { if (!buffer_blob) return [] var prepared = [] + var si = null + var skin = null + var inv_bind_blob = null + var acc = null + var view = null - for (var si = 0; si < length(g.skins); si++) { - var skin = g.skins[si] + for (si = 0; si < length(g.skins); si++) { + skin = g.skins[si] // Extract inverse bind matrices - var inv_bind_blob = null + inv_bind_blob = null if (skin.inverse_bind_matrices != null) { - var acc = g.accessors[skin.inverse_bind_matrices] + acc = g.accessors[skin.inverse_bind_matrices] if (acc) { - var view = g.views[acc.view] + view = g.views[acc.view] inv_bind_blob = model_c.extract_accessor( buffer_blob, view.byte_offset || 0, @@ -54,11 +59,15 @@ function build_palette(skin, model, retro3d) { // Collect world matrices for each joint var world_matrices = [] - for (var j = 0; j < length(skin.joints); j++) { - var node_idx = skin.joints[j] - var node = model.nodes[node_idx] + var j = null + var node_idx = null + var node = null + var world_mat = null + for (j = 0; j < length(skin.joints); j++) { + node_idx = skin.joints[j] + node = model.nodes[node_idx] if (node) { - var world_mat = retro3d.transform_get_world_matrix(node) + world_mat = retro3d.transform_get_world_matrix(node) world_matrices.push(world_mat) } else { world_matrices.push(model_c.mat4_identity()) @@ -85,10 +94,12 @@ function get_joint_world(skin, joint_index, model, retro3d) { // Find joint index by name function find_joint(skin, name, model) { if (!skin || !skin.joints) return -1 - - for (var j = 0; j < length(skin.joints); j++) { - var node_idx = skin.joints[j] - var node = model.nodes[node_idx] + var j = null + var node_idx = null + var node = null + for (j = 0; j < length(skin.joints); j++) { + node_idx = skin.joints[j] + node = model.nodes[node_idx] if (node && node.name == name) return j } return -1 @@ -97,8 +108,8 @@ function find_joint(skin, name, model) { // Find joint index by node index function joint_from_node(skin, node_idx) { if (!skin || !skin.joints) return -1 - - for (var j = 0; j < length(skin.joints); j++) { + var j = null + for (j = 0; j < length(skin.joints); j++) { if (skin.joints[j] == node_idx) return j } return -1 @@ -112,11 +123,11 @@ function node_from_joint(skin, joint_idx) { // Compute attachment transform // Returns world matrix for an object attached to a joint with an offset -function attachment_transform(skin, joint_index, model, retro3d, offset_mat) { - var joint_world = get_joint_world(skin, joint_index, model, retro3d) +function attachment_transform(skin, joint_index, ctx) { + var joint_world = get_joint_world(skin, joint_index, ctx.model, ctx.retro3d) - if (offset_mat) { - return model_c.mat4_mul(joint_world, offset_mat) + if (ctx.offset_mat) { + return model_c.mat4_mul(joint_world, ctx.offset_mat) } return joint_world }