fix syntax
This commit is contained in:
75
animation.cm
75
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)
|
||||
}
|
||||
|
||||
28
camera.cm
28
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() {
|
||||
|
||||
207
collision.cm
207
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,
|
||||
|
||||
311
core.cm
311
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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
15
input.cm
15
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
|
||||
}
|
||||
|
||||
4
math.cm
4
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) {
|
||||
|
||||
91
model.c
91
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);
|
||||
|
||||
203
resources.cm
203
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]
|
||||
}
|
||||
|
||||
30
sdl.cm
30
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
|
||||
|
||||
|
||||
49
skin.cm
49
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user