add simple mesh/group list

This commit is contained in:
Stewart Allen 2022-01-02 16:30:05 -05:00
commit 930e591c44
7 changed files with 149 additions and 29 deletions

View file

@ -5,6 +5,7 @@
(function() {
let broker = gapp.broker;
let call = broker.send;
function init() {
let moto = self.moto,
@ -36,22 +37,78 @@ function init() {
});
space.view.setZoom(zoomrev, zoomspd);
// trigger space event binding
broker.send.space_init({ space, platform });
// start worker
moto.client.start(`/code/mesh_work?${gapp.version}`);
// set app version
$h('app-name', "Mesh:Tool");
$h('app-vers', gapp.version);
// trigger space event binding
call.space_init({ space, platform });
// trigger ui building
call.ui_build();
// hide loading curtain
$d('curtain','none');
}
function ui_build() {
// set app version
$h('app-name', "Mesh:Tool");
$h('app-vers', gapp.version);
let bound = h.bind($('app-body'), [
h.div({id: 'grouplist'})
]);
let { grouplist } = bound;
let api = mesh.api;
let maker = {
update() {
clearTimeout(maker.timer);
maker.timer = setTimeout(maker.do, 10);
},
do() {
let groups = api.group.list()
// map groups to divs
.map(g => h.div([
h.button({
_: g.id,
class: [
"group",
api.selection.contains(g) ? 'selected' : undefined
],
onclick() { api.selection.toggle(g); }
}),
h.div({ class: "vsep" }),
h.div({ class: "models"},
// map models to buttons
g.models.map(m => h.button({
_: m.id,
class: api.selection.contains(m) ? [ 'selected' ] : [],
onclick() { api.selection.toggle(m); }
}))
)
]));
h.bind(grouplist, groups);
}
};
function update(object, topic) {
console.log({update: object, topic});
}
// listen for api calls
broker.listeners({
model_add: maker.update,
group_add: maker.update,
model_remove: maker.update,
group_remove: maker.update,
selection_update: maker.update,
})
}
// add space event bindings
broker.subscribe('space_init', data => {
function space_init(data) {
let { space, platform } = data;
let platcolor = 0x00ff00;
let api = mesh.api;
@ -64,7 +121,7 @@ broker.subscribe('space_init', data => {
platform.setColor(platcolor);
load.File.load([...evt.dataTransfer.files])
.then(data => {
broker.send.space_load(data);
call.space_load(data);
})
.catch(error => {
dbug.error(error);
@ -127,7 +184,7 @@ broker.subscribe('space_init', data => {
m.debug();
}
} else {
broker.send.space_debug();
call.space_debug();
}
break;
case 'Escape':
@ -199,20 +256,19 @@ broker.subscribe('space_init', data => {
return api.objects().length > 0;
}
});
});
}
// add object loader
broker.subscribe('space_load', data => {
function space_load(data) {
mesh.api.group.new(data.flat().map(el => new mesh.model(el)))
.centerModels()
.centerXY()
.floor()
.focus();
});
}
// on debug key press
broker.subscribe('space_debug', () => {
function space_debug() {
for (let g of mesh.api.group.list()) {
let { center, size } = g.bounds();
console.group(g.id);
@ -230,6 +286,14 @@ broker.subscribe('space_debug', () => {
console.groupEnd();
}
moto.client.fn.debug();
}
// bind functions to topics
broker.listeners({
ui_build,
space_init,
space_load,
space_debug,
});
// remove version cache bust from url

View file

@ -50,6 +50,10 @@ let selection = {
return mdl;
},
contains(object) {
return selected.contains(object);
},
// @param group {MeshObject[]}
set(objects) {
selected = objects;
@ -82,6 +86,9 @@ let selection = {
for (let group of groups) {
group.material(mesh.material.unselected);
}
// prevent selection of model and its group
let mgsel = selected.filter(s => s instanceof mesh.model).map(m => m.group);
selected = selected.filter(sel => !mgsel.contains(sel));
for (let object of selected) {
object.material(mesh.material.selected);
}

View file

@ -8,6 +8,7 @@ gapp.register("mesh.group", [
"add.array", // dep: add.array
"add.three", // dep: add.three
"moto.license", // dep: moto.license
"moto.broker", // dep: moto.broker
"mesh.object", // dep: mesh.object
"mesh.model", // dep: mesh.model
"mesh.api", // dep: mesh.api
@ -16,12 +17,15 @@ gapp.register("mesh.group", [
let mesh = self.mesh = self.mesh || {};
if (mesh.group) return;
let broker = gapp.broker;
let call = broker.send;
let space = moto.Space;
let worker = moto.client.fn;
mesh.group = class MeshGroup extends mesh.object {
// @param group {mesh.model[]}
// @param group {MeshModel[]}
constructor(models = []) {
super();
this.group = new THREE.Group();
@ -45,6 +49,7 @@ mesh.group = class MeshGroup extends mesh.object {
this.models.addOnce(model);
this.group.add(model.mesh);
space.update();
call.model_add({model, group:this});
worker.group_add({id: this.id, model:model.id});
return this;
}
@ -61,6 +66,8 @@ mesh.group = class MeshGroup extends mesh.object {
model.group = undefined;
this.models.remove(model);
this.group.remove(model.mesh);
// create message for listeners
call.model_remove({model, group:this});
// trigger sync with worker
worker.group_remove({id: this.id, model:model.id});
model.remove(true);

View file

@ -18,7 +18,7 @@ gapp.finalize("mesh.work", [
]);
// compensation for space/world/platform rotation
let space_rotation = new THREE.Matrix4().makeRotationX(Math.PI / 2);
let core_matrix = new THREE.Matrix4().makeRotationX(Math.PI / 2);
let { client, worker } = moto;
let cache = {};
@ -38,14 +38,14 @@ let model = {
let geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
geo.computeVertexNormals();
cacheUpdate(id, { name, geo });
cacheUpdate(id, { name, geo, matrix: core_matrix.clone() });
},
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) );
let m4 = core_matrix.clone().multiply( new THREE.Matrix4().fromArray(matrix) );
geo.applyMatrix4(m4);
// for debugging state / matrix ops
return geo.attributes.position.array;
@ -59,11 +59,6 @@ let group = {
remove(data) {
let { id, model } = data;
},
create(data) {
let { id, type } = data;
cache[id] = { id, type };
}
};
@ -78,23 +73,23 @@ let object = {
},
move(data) {
let { id, type, x, y, z } = data;
let { id, x, y, z } = data;
},
position(data) {
let { id, type, x, y, z } = data;
let { id, x, y, z } = data;
},
rotate(data) {
let { id, type, x, y, z } = data;
let { id, x, y, z } = data;
},
rotation(data) {
let { id, type, x, y, z } = data;
let { id, x, y, z } = data;
},
qrotation(data) {
let { id, type, w, x, y, z } = data;
let { id, w, x, y, z } = data;
}
};

View file

@ -23,6 +23,20 @@ gapp.broker = new class Broker {
return Object.keys(this.topics);
}
// create subscriptions for all functions in an object
// a complement wrapObject on the subscription side
listeners(object, root) {
for (let [key, fn] of Object.entries(object)) {
if (typeof fn === 'function') {
key = root ? `${root}_${key}` : key;
this.subscribe(key, fn);
}
}
}
// attach function to a topic
// creates topic if new topic
// creates a send function if new topic
subscribe(topic, listener) {
if (Array.isArray(topic)) {
for (let t of topic) {

View file

@ -58,8 +58,9 @@ function build(data, context) {
func[key] = val;
elid = elid || `_${nextid++}`;
} else if (tov === 'object') {
html.push(` ${key}="${val.join(' ')}"`);
} else {
if (val.length)
html.push(` ${key}="${val.filter(v => v !== undefined).join(' ')}"`);
} else if (tov !== 'undefined') {
html.push(` ${key}="${val}"`);
}
}

View file

@ -140,3 +140,35 @@ body, div {
margin-right: -14px !important;
margin-bottom: -14px !important;
}
#grouplist {
position: absolute;
margin: 3px 3px 0 0;
right: 0;
top: 0;
}
#grouplist, #grouplist div {
flex-direction: column;
background-color: transparent;
}
#grouplist > div {
border: 1px solid gray;
border-radius: 3px;
padding: 3px;
}
#grouplist .models button {
margin-top: 1px;
}
#grouplist .selected {
background-color: rgba(0,255,0,0.75)
}
#grouplist .vsep {
height: 1px;
margin: 2px 0 1px 0;
background-color: gray;
}