refactor worker with cleaner object function bindings

This commit is contained in:
Stewart Allen 2022-01-02 14:25:28 -05:00
commit eb2fbf2a09
8 changed files with 168 additions and 59 deletions

View file

@ -83,7 +83,8 @@ broker.subscribe('space_init', data => {
platform.setColor(platcolor); platform.setColor(platcolor);
}, },
'keypress', evt => { 'keypress', evt => {
switch (evt.code) { let { shiftKey, metaKey, ctrlKey, code } = evt;
switch (code) {
case 'KeyC': case 'KeyC':
selection.centerXY().focus(); selection.centerXY().focus();
break; break;
@ -96,11 +97,6 @@ broker.subscribe('space_init', data => {
case 'KeyB': case 'KeyB':
selection.boundsBox({toggle:true}); selection.boundsBox({toggle:true});
break; break;
case 'KeyS':
for (let m of selection.models()) {
m.sync();
}
break;
case 'KeyH': case 'KeyH':
space.view.home(); space.view.home();
break; break;
@ -126,7 +122,13 @@ broker.subscribe('space_init', data => {
} }
break; break;
case 'KeyD': case 'KeyD':
if (shiftKey) {
for (let m of selection.models()) {
m.debug();
}
} else {
broker.send.space_debug(); broker.send.space_debug();
}
break; break;
case 'Escape': case 'Escape':
selection.clear(); selection.clear();
@ -213,19 +215,21 @@ broker.subscribe('space_load', data => {
broker.subscribe('space_debug', () => { broker.subscribe('space_debug', () => {
for (let g of mesh.api.group.list()) { for (let g of mesh.api.group.list()) {
let { center, size } = g.bounds(); let { center, size } = g.bounds();
console.group('group', { console.group(g.id);
console.log({
center, center,
size, size,
pos: g.group.position pos: g.group.position
}); });
for (let m of g.models) { for (let m of g.models) {
console.log('model', { console.log(m.id, {
box: m.mesh.getBoundingBox(), box: m.mesh.getBoundingBox(),
pos: m.mesh.position pos: m.mesh.position
}); });
} }
console.groupEnd(); console.groupEnd();
} }
moto.client.fn.debug();
}); });
// remove version cache bust from url // remove version cache bust from url

View file

@ -221,7 +221,7 @@ let api = mesh.api = {
model, model,
objects() { objects() {
// return models, not groups // return model objects suitable for finding ray intersections
return group.list().map(o => o.models).flat().map(o => o.object()); return group.list().map(o => o.models).flat().map(o => o.object());
} }
}; };

View file

@ -16,6 +16,9 @@ gapp.register("mesh.group", [
let mesh = self.mesh = self.mesh || {}; let mesh = self.mesh = self.mesh || {};
if (mesh.group) return; if (mesh.group) return;
let space = moto.Space;
let worker = moto.client.fn;
mesh.group = class MeshGroup extends mesh.object { mesh.group = class MeshGroup extends mesh.object {
// @param group {mesh.model[]} // @param group {mesh.model[]}
@ -28,6 +31,10 @@ mesh.group = class MeshGroup extends mesh.object {
} }
} }
type() {
return "group";
}
object() { object() {
return this.group; return this.group;
} }
@ -37,7 +44,8 @@ mesh.group = class MeshGroup extends mesh.object {
model.group = this; model.group = this;
this.models.addOnce(model); this.models.addOnce(model);
this.group.add(model.mesh); this.group.add(model.mesh);
moto.Space.update(); space.update();
worker.group_add({id: this.id, model:model.id});
return this; return this;
} }
@ -54,12 +62,14 @@ mesh.group = class MeshGroup extends mesh.object {
this.models.remove(model); this.models.remove(model);
this.group.remove(model.mesh); this.group.remove(model.mesh);
// trigger sync with worker // trigger sync with worker
worker.group_remove({id: this.id, model:model.id});
model.remove(true); model.remove(true);
// auto-remove group when empty // auto-remove group when empty
if (this.group.children.length === 0) { if (this.group.children.length === 0) {
mesh.api.group.remove(this); mesh.api.group.remove(this);
worker.object_destroy({id: this.id});
} }
moto.Space.update(); space.update();
return this; return this;
} }

View file

@ -16,6 +16,9 @@ gapp.register("mesh.model", [
let mesh = self.mesh = self.mesh || {}; let mesh = self.mesh = self.mesh || {};
if (mesh.model) return; if (mesh.model) return;
let space = moto.Space;
let worker = moto.client.fn;
/** default materials **/ /** default materials **/
let materials = mesh.material = { let materials = mesh.material = {
unselected: new THREE.MeshPhongMaterial({ unselected: new THREE.MeshPhongMaterial({
@ -49,16 +52,19 @@ mesh.model = class MeshModel extends mesh.object {
this.mesh = this.load(mesh); this.mesh = this.load(mesh);
} }
type() {
return "model";
}
object() { object() {
return this.mesh; return this.mesh;
} }
sync() { debug() {
moto.client.fn.model_sync({ worker.model_debug({
matrix: this.mesh.matrixWorld.elements, matrix: this.mesh.matrixWorld.elements,
id: this.id id: this.id
}).then(data => { }).then(data => {
if (data && data.length)
// for debugging matrix ops // for debugging matrix ops
return mesh.api.group.new([new mesh.model({ return mesh.api.group.new([new mesh.model({
file: `synth-${this.name}`, file: `synth-${this.name}`,
@ -80,7 +86,7 @@ mesh.model = class MeshModel extends mesh.object {
// this ref allows clicks to be traced to models and groups // this ref allows clicks to be traced to models and groups
meh.model = this; meh.model = this;
// sync data to worker // sync data to worker
moto.client.fn.model_load({id: this.id, name: this.name, vertices, indices}); worker.model_load({id: this.id, name: this.file, vertices, indices});
return meh; return meh;
} }
@ -93,8 +99,8 @@ mesh.model = class MeshModel extends mesh.object {
geo.attributes.index.needsUpdate = true; geo.attributes.index.needsUpdate = true;
} }
geo.attributes.position.needsUpdate = true; geo.attributes.position.needsUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals(); geo.computeVertexNormals();
worker.model_load({id: this.id, name: this.name, vertices, indices});
} }
get group() { get group() {
@ -122,7 +128,7 @@ mesh.model = class MeshModel extends mesh.object {
mat.opacity = ov; mat.opacity = ov;
mat.visible = true; mat.visible = true;
} }
moto.Space.update(); space.update();
} }
wireframe(bool, opt = {}) { wireframe(bool, opt = {}) {
@ -151,7 +157,7 @@ mesh.model = class MeshModel extends mesh.object {
this.mesh.add(this._wire); this.mesh.add(this._wire);
this.opacity(opt.opacity || 0); this.opacity(opt.opacity || 0);
} }
moto.Space.update(); space.update();
} }
material(mat) { material(mat) {
@ -163,7 +169,7 @@ mesh.model = class MeshModel extends mesh.object {
// direct call requires pass through group // direct call requires pass through group
} else { } else {
// called from group // called from group
moto.client.fn.model_remove({id: this.id}); worker.object_destroy({id: this.id});
} }
} }
}; };

View file

@ -13,10 +13,18 @@ gapp.register("mesh.object", [
let mesh = self.mesh = self.mesh || {}; let mesh = self.mesh = self.mesh || {};
if (mesh.object) return; if (mesh.object) return;
let space = moto.Space;
let worker = moto.client.fn;
mesh.object = class MeshObject { mesh.object = class MeshObject {
constructor() { constructor() {
this.id = mesh.util.uuid(); this.id = mesh.util.uuid();
worker.object_create({id: this.id, type: this.type()});
}
type() {
throw "type() requires implementation";
} }
// @returns {THREE.Object3D} // @returns {THREE.Object3D}
@ -54,7 +62,8 @@ mesh.object = class MeshObject {
let pos = this.position(); let pos = this.position();
pos.set(pos.x + x, pos.y + y, pos.z + z); pos.set(pos.x + x, pos.y + y, pos.z + z);
this.updateBoundsBox(); this.updateBoundsBox();
moto.Space.update(); space.update();
moto.client.fn.object_move({id: this.id, x, y, z});
return this; return this;
} }
@ -65,7 +74,9 @@ mesh.object = class MeshObject {
} }
scale.set(...arguments); scale.set(...arguments);
this.updateBoundsBox(); this.updateBoundsBox();
moto.Space.update(); let [ x, y, z ] = arguments;
moto.client.fn.object_scale({id: this.id, x, y, z});
space.update();
} }
rotate(x = 0, y = 0, z = 0) { rotate(x = 0, y = 0, z = 0) {
@ -73,25 +84,30 @@ mesh.object = class MeshObject {
if (y) this.object().rotateOnWorldAxis(new THREE.Vector3(0,1,0), y); if (y) this.object().rotateOnWorldAxis(new THREE.Vector3(0,1,0), y);
if (z) this.object().rotateOnWorldAxis(new THREE.Vector3(0,0,1), z); if (z) this.object().rotateOnWorldAxis(new THREE.Vector3(0,0,1), z);
this.updateBoundsBox(); this.updateBoundsBox();
moto.Space.update(); worker.object_rotate({id: this.id, x, y, z});
space.update();
return this; return this;
} }
rotation() { rotation() {
let rot = this.object().rotation; let rotation = this.object().rotation;
if (arguments.length === 0) { if (arguments.length === 0) {
return rot; return rotation;
} }
rot.set(...arguments); rotation.set(...arguments);
this.updateBoundsBox(); this.updateBoundsBox();
moto.Space.update(); let { x, y, z } = rotation;
worker.object_rotation({id: this.id, x, y, z});
space.update();
return this; return this;
} }
qrotation(quaternion) { qrotation(quaternion) {
this.object().setRotationFromQuaternion(quaternion); this.object().setRotationFromQuaternion(quaternion);
this.updateBoundsBox(); this.updateBoundsBox();
moto.Space.update(); let { w, x, y, z } = quaternion;
worker.object_qrotation({id: this.id, w, x, y, z});
space.update();
return this; return this;
} }
@ -101,7 +117,9 @@ mesh.object = class MeshObject {
return pos; return pos;
} }
pos.set(...arguments); pos.set(...arguments);
moto.Space.update(); let [ x, y, z ] = arguments;
worker.object_position({id: this.id, x, y, z});
space.update();
} }
showBounds(bool) { showBounds(bool) {
@ -114,7 +132,7 @@ mesh.object = class MeshObject {
updateBoundsBox() { updateBoundsBox() {
let helper = this._boundsBox; let helper = this._boundsBox;
let world = moto.Space.world; let world = space.world;
if (helper) { if (helper) {
world.remove(helper); world.remove(helper);
} }

View file

@ -23,33 +23,85 @@ let space_rotation = new THREE.Matrix4().makeRotationX(Math.PI / 2);
let { client, worker } = moto; let { client, worker } = moto;
let cache = {}; let cache = {};
function cacheUpdate(id, data) {
Object.assign(cache[id], data);
}
worker.bind("debug", (data, send) => { worker.bind("debug", (data, send) => {
console.log({cache}); console.log({work_cache: cache});
send.done(); send.done();
}); });
worker.bind("model_load", (data, send) => { let model = {
load(data) {
let { vertices, name, id } = data; let { vertices, name, id } = data;
let geo = new THREE.BufferGeometry(); let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3)); geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.computeVertexNormals(); geo.computeVertexNormals();
cache[id] = { id, name, geo }; cacheUpdate(id, { name, geo });
send.done(); },
});
worker.bind("model_remove", (data, send) => { debug(data) {
delete cache[data.id];
send.done();
});
worker.bind("model_sync", (data, send) => {
let { matrix, id } = data; let { matrix, id } = data;
let rec = cache[id]; let rec = cache[id];
let geo = rec.geo.clone(); let geo = rec.geo.clone();
let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) ); let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
geo.applyMatrix4(m4); geo.applyMatrix4(m4);
// for debugging matrix ops // for debugging state / matrix ops
send.done(geo.attributes.position.array); return geo.attributes.position.array;
}
};
let group = {
add(data) {
let { id, model } = data;
},
remove(data) {
let { id, model } = data;
},
create(data) {
let { id, type } = data;
cache[id] = { id, type };
}
};
let object = {
create(data) {
let { id, type } = data;
cache[id] = { id, type };
},
destroy(data) {
delete cache[data.id];
},
move(data) {
let { id, type, x, y, z } = data;
},
position(data) {
let { id, type, x, y, z } = data;
},
rotate(data) {
let { id, type, x, y, z } = data;
},
rotation(data) {
let { id, type, x, y, z } = data;
},
qrotation(data) {
let { id, type, w, x, y, z } = data;
}
};
worker.bindObject({
model,
group,
object
}); });
})(); })();

View file

@ -120,7 +120,6 @@ let client = moto.client = {
data, data,
stream: ondata ? true : false stream: ondata ? true : false
}; };
try { try {
worker.postMessage(msg, direct); worker.postMessage(msg, direct);
} catch (error) { } catch (error) {

View file

@ -15,32 +15,52 @@ let endpoints = {};
// code is running in the worker / server context // code is running in the worker / server context
const dispatch = moto.worker = { const dispatch = moto.worker = {
bind: (name, fn) => { // bind an endpoint name to a function
bind(name, fn) {
endpoints[name] = fn; endpoints[name] = fn;
dispatch.send({bind: name}); dispatch.send({bind: name});
}, },
send: (msg) => { // bind all functions in an object
bindObject(object, root, recurse) {
recurse = recurse || root === undefined;
for (let [key, fn] of Object.entries(object)) {
key = root ? `${root}_${key}` : key;
if (typeof fn === 'function') {
dispatch.bind(key, fn);
} else if (recurse) {
dispatch.bindObject(fn, key, recurse);
}
}
},
send(msg) {
self.postMessage(msg); self.postMessage(msg);
}, },
onmessage: (e) => { onmessage(e) {
let msg = e.data, let msg = e.data,
run = endpoints[msg.task], run = endpoints[msg.task],
async = false,
done = false, done = false,
send = { send = {
data: (data, direct) => { // puts function in async mode
// prevents auto send.done() on function return
async() {
async = true;
},
data(data, direct) {
if (canSend(data)) { if (canSend(data)) {
dispatch.send({ done: false, data: data }, direct); dispatch.send({ done: false, data: data }, direct);
} }
}, },
done: (data, direct) => { done(data, direct) {
if (canSend(data)) { if (canSend(data)) {
dispatch.send({ done: true, data: data }, direct); dispatch.send({ done: true, data: data }, direct);
done = true; done = true;
} }
}, },
error: (data) => { error(data) {
if (canSend(data)) { if (canSend(data)) {
done = true; done = true;
dispatch.send({ error: data }); dispatch.send({ error: data });
@ -57,7 +77,7 @@ const dispatch = moto.worker = {
if (run) { if (run) {
try { try {
let data = run(msg.data, send); let data = run(msg.data, send);
if (data) { if (data || async === false) {
send.done(data); send.done(data);
} }
} catch (error) { } catch (error) {