From 1c2f07025c340cf8694e7bbce4311d6948d60fb9 Mon Sep 17 00:00:00 2001 From: Bob G Date: Tue, 27 Oct 2026 13:46:14 -0700 Subject: [PATCH] Place brick-built (UGC) property models at their saved position/rotation (#131) UGC LXFML brick transforms are relative to the model's own origin, but the LDD viewer added the bricks straight to the scene and never applied the model's properties_contents position/rotation, so the models ended up around (0,0,0), far below the property ground plane. Properties with only brick-built models showed just the green ground. Build each LXFML into its own THREE.Group placed with the saved position/quaternion (like pre-built models), load each file into a fresh Scene, and target the single-model camera at the placed model. Co-authored-by: bob-g-code <346622190+bob-g-code@users.noreply.github.com> --- app/templates/ldd/ldd.html.j2 | 38 +++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/app/templates/ldd/ldd.html.j2 b/app/templates/ldd/ldd.html.j2 index 3a07942..7b184b3 100644 --- a/app/templates/ldd/ldd.html.j2 +++ b/app/templates/ldd/ldd.html.j2 @@ -533,11 +533,19 @@ LoadScene(filename){ if(this.database.initok){ + // Each LXFML file is its own model with its own placement, + // so start from a fresh scene instead of accumulating bricks + this.scene = new Scene() this.scene.AddScene(filename) } } - Export(filename){ + // Builds the meshes for the currently loaded LXFML scene. + // The brick transforms in a UGC LXFML are relative to the model's own origin, + // so the meshes are collected into a group which is then placed in the world + // using the position/rotation stored for the model (properties_contents). + Export(filename, positions = []){ + let modelGroup = new THREE.Group() let allMaterials = new Materials(this.database.filelist['Materials.xml'].read()) let invert = new Matrix3D() //invert.n33 = -1 //uncomment to invert the Z-Axis @@ -717,15 +725,26 @@ let mesh = new THREE.Mesh(geometry, materials); mesh.matrixAutoUpdate = false mesh.matrix = m - {% if not center %} - brick_pos.setFromMatrixPosition( m ); - {% endif %} - scene.add(mesh); + modelGroup.add(mesh); // let vnh = new VertexNormalsHelper( mesh, 5 ); // scene.add( vnh ); } } } + + if (positions.length === 0) { + positions = [{x: 0, y: 0, z: 0, rx: 0, ry: 0, rz: 0, rw: 1}] + } + for (const [j, pos] of positions.entries()) { + let model = (j === 0) ? modelGroup : modelGroup.clone() + model.position.set(pos.x, pos.y, pos.z) + model.quaternion.set(pos.rx, pos.ry, pos.rz, pos.rw) + model.updateMatrixWorld(true) + scene.add(model) + {% if not center %} + new THREE.Box3().setFromObject(model).getCenter(brick_pos) + {% endif %} + } } } @@ -1036,7 +1055,10 @@ let lxfml_file_list = [ {% for model in content %} {% if model.lot == 14 %} - "{{url_for('properties.get_model', id=model.id, file_format='lxfml', lod=lod)}}"{{ ", " if not loop.last else "" }} + { + "url": "{{url_for('properties.get_model', id=model.id, file_format='lxfml', lod=lod)}}", + "pos": {{ model.pos|tojson }} + }, {% endif %} {% endfor %} ] @@ -1047,9 +1069,9 @@ let converter = new Converter() converter.LoadDBURL(ldddburl) for (let i = 0; i < lxfml_file_list.length; i++) { - converter.LoadScene(lxfml_file_list[i]) + converter.LoadScene(lxfml_file_list[i].url) + converter.Export('test.webgl', lxfml_file_list[i].pos) } - converter.Export('test.webgl') } else {