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

View file

@ -221,7 +221,7 @@ let api = mesh.api = {
model,
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());
}
};

View file

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

View file

@ -16,6 +16,9 @@ gapp.register("mesh.model", [
let mesh = self.mesh = self.mesh || {};
if (mesh.model) return;
let space = moto.Space;
let worker = moto.client.fn;
/** default materials **/
let materials = mesh.material = {
unselected: new THREE.MeshPhongMaterial({
@ -49,16 +52,19 @@ mesh.model = class MeshModel extends mesh.object {
this.mesh = this.load(mesh);
}
type() {
return "model";
}
object() {
return this.mesh;
}
sync() {
moto.client.fn.model_sync({
debug() {
worker.model_debug({
matrix: this.mesh.matrixWorld.elements,
id: this.id
}).then(data => {
if (data && data.length)
// for debugging matrix ops
return mesh.api.group.new([new mesh.model({
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
meh.model = this;
// 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;
}
@ -93,8 +99,8 @@ mesh.model = class MeshModel extends mesh.object {
geo.attributes.index.needsUpdate = true;
}
geo.attributes.position.needsUpdate = true;
geo.computeFaceNormals();
geo.computeVertexNormals();
worker.model_load({id: this.id, name: this.name, vertices, indices});
}
get group() {
@ -122,7 +128,7 @@ mesh.model = class MeshModel extends mesh.object {
mat.opacity = ov;
mat.visible = true;
}
moto.Space.update();
space.update();
}
wireframe(bool, opt = {}) {
@ -151,7 +157,7 @@ mesh.model = class MeshModel extends mesh.object {
this.mesh.add(this._wire);
this.opacity(opt.opacity || 0);
}
moto.Space.update();
space.update();
}
material(mat) {
@ -163,7 +169,7 @@ mesh.model = class MeshModel extends mesh.object {
// direct call requires pass through group
} else {
// 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 || {};
if (mesh.object) return;
let space = moto.Space;
let worker = moto.client.fn;
mesh.object = class MeshObject {
constructor() {
this.id = mesh.util.uuid();
worker.object_create({id: this.id, type: this.type()});
}
type() {
throw "type() requires implementation";
}
// @returns {THREE.Object3D}
@ -54,7 +62,8 @@ mesh.object = class MeshObject {
let pos = this.position();
pos.set(pos.x + x, pos.y + y, pos.z + z);
this.updateBoundsBox();
moto.Space.update();
space.update();
moto.client.fn.object_move({id: this.id, x, y, z});
return this;
}
@ -65,7 +74,9 @@ mesh.object = class MeshObject {
}
scale.set(...arguments);
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) {
@ -73,25 +84,30 @@ mesh.object = class MeshObject {
if (y) this.object().rotateOnWorldAxis(new THREE.Vector3(0,1,0), y);
if (z) this.object().rotateOnWorldAxis(new THREE.Vector3(0,0,1), z);
this.updateBoundsBox();
moto.Space.update();
worker.object_rotate({id: this.id, x, y, z});
space.update();
return this;
}
rotation() {
let rot = this.object().rotation;
let rotation = this.object().rotation;
if (arguments.length === 0) {
return rot;
return rotation;
}
rot.set(...arguments);
rotation.set(...arguments);
this.updateBoundsBox();
moto.Space.update();
let { x, y, z } = rotation;
worker.object_rotation({id: this.id, x, y, z});
space.update();
return this;
}
qrotation(quaternion) {
this.object().setRotationFromQuaternion(quaternion);
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;
}
@ -101,7 +117,9 @@ mesh.object = class MeshObject {
return pos;
}
pos.set(...arguments);
moto.Space.update();
let [ x, y, z ] = arguments;
worker.object_position({id: this.id, x, y, z});
space.update();
}
showBounds(bool) {
@ -114,7 +132,7 @@ mesh.object = class MeshObject {
updateBoundsBox() {
let helper = this._boundsBox;
let world = moto.Space.world;
let world = space.world;
if (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 cache = {};
function cacheUpdate(id, data) {
Object.assign(cache[id], data);
}
worker.bind("debug", (data, send) => {
console.log({cache});
console.log({work_cache: cache});
send.done();
});
worker.bind("model_load", (data, send) => {
let { vertices, name, id } = data;
let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.computeVertexNormals();
cache[id] = { id, name, geo };
send.done();
});
let model = {
load(data) {
let { vertices, name, id } = data;
let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.computeVertexNormals();
cacheUpdate(id, { name, geo });
},
worker.bind("model_remove", (data, send) => {
delete cache[data.id];
send.done();
});
debug(data) {
let { matrix, id } = data;
let rec = cache[id];
let geo = rec.geo.clone();
let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
geo.applyMatrix4(m4);
// for debugging state / matrix ops
return geo.attributes.position.array;
}
};
worker.bind("model_sync", (data, send) => {
let { matrix, id } = data;
let rec = cache[id];
let geo = rec.geo.clone();
let m4 = space_rotation.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
geo.applyMatrix4(m4);
// for debugging matrix ops
send.done(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,
stream: ondata ? true : false
};
try {
worker.postMessage(msg, direct);
} catch (error) {

View file

@ -15,32 +15,52 @@ let endpoints = {};
// code is running in the worker / server context
const dispatch = moto.worker = {
bind: (name, fn) => {
// bind an endpoint name to a function
bind(name, fn) {
endpoints[name] = fn;
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);
},
onmessage: (e) => {
onmessage(e) {
let msg = e.data,
run = endpoints[msg.task],
async = false,
done = false,
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)) {
dispatch.send({ done: false, data: data }, direct);
}
},
done: (data, direct) => {
done(data, direct) {
if (canSend(data)) {
dispatch.send({ done: true, data: data }, direct);
done = true;
}
},
error: (data) => {
error(data) {
if (canSend(data)) {
done = true;
dispatch.send({ error: data });
@ -57,7 +77,7 @@ const dispatch = moto.worker = {
if (run) {
try {
let data = run(msg.data, send);
if (data) {
if (data || async === false) {
send.done(data);
}
} catch (error) {