feat: multi-format 3D viewer with large-file OBJ streaming, Z-up, projection/outline UI

Three.js viewer supporting glb/gltf/obj/fbx/dae/ifc/ply via both server (SSR)
and drag&drop (CSR) paths.

- Streaming OBJ parser (src/viewer/objStream.ts) for files past the V8 max
  string length (>~1GB text) that OBJLoader can't handle; indexed geometry,
  per-vertex color from MTL Kd, float64 recenter baked in.
- In-viewer float64 recenter (objRecenter.ts) for huge CAD/survey coordinates
  (~1e8) so float32 vertex buffers keep precision (no cracked faces).
- Z-up right-handed world; Y-up formats rotated on load.
- OBJ+MTL+texture drag&drop (LoadingManager URL-modifier maps dropped images).
- OrbitControls ground-plane panning (road/rail alignment workflow).
- UI: Zoom Fit, perspective/orthographic toggle, feature-edge outline.
- DoubleSide for CAD OBJ; PLY mesh + point-cloud support.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 13:54:46 +09:00
co-authored by Claude Opus 4.8
commit a71790070d
78 changed files with 15921 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Cube.dae - hand-authored minimal COLLADA 1.4.1 unit cube for hmwebviewer ColladaLoader testing.
Single <geometry> (8 verts, 12 triangles) in one <visual_scene>. Edge length 2 (range -1..1),
centered at origin. Works for Path A (SSR ?model=) and Path B (drag & drop). No network required. -->
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<asset>
<contributor>
<author>hmwebviewer</author>
<authoring_tool>hand-authored</authoring_tool>
</contributor>
<created>2026-06-19T00:00:00Z</created>
<modified>2026-06-19T00:00:00Z</modified>
<unit name="meter" meter="1"/>
<up_axis>Y_UP</up_axis>
</asset>
<library_geometries>
<geometry id="Cube-mesh" name="Cube">
<mesh>
<source id="Cube-positions">
<float_array id="Cube-positions-array" count="24">
-1 -1 -1 -1 -1 1 -1 1 -1 -1 1 1 1 -1 -1 1 -1 1 1 1 -1 1 1 1
</float_array>
<technique_common>
<accessor source="#Cube-positions-array" count="8" stride="3">
<param name="X" type="float"/>
<param name="Y" type="float"/>
<param name="Z" type="float"/>
</accessor>
</technique_common>
</source>
<vertices id="Cube-vertices">
<input semantic="POSITION" source="#Cube-positions"/>
</vertices>
<triangles count="12">
<input semantic="VERTEX" source="#Cube-vertices" offset="0"/>
<p>
0 1 3 0 3 2
4 6 7 4 7 5
0 4 5 0 5 1
2 3 7 2 7 6
0 2 6 0 6 4
1 5 7 1 7 3
</p>
</triangles>
</mesh>
</geometry>
</library_geometries>
<library_visual_scenes>
<visual_scene id="Scene" name="Scene">
<node id="Cube" name="Cube" type="NODE">
<matrix sid="transform">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
<instance_geometry url="#Cube-mesh" name="Cube"/>
</node>
</visual_scene>
</library_visual_scenes>
<scene>
<instance_visual_scene url="#Scene"/>
</scene>
</COLLADA>