@jfk-solutions/demo3d-file-format 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +225 -0
  2. package/dist/examples/export-embedded-viewer-standalone.html +1 -1
  3. package/dist/examples/three-render-smoke-standalone.html +295 -231
  4. package/dist/index.d.ts +8 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +7 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/model.d.ts +1 -0
  9. package/dist/model.d.ts.map +1 -1
  10. package/dist/model.js +6 -0
  11. package/dist/model.js.map +1 -1
  12. package/dist/raw3d-animation.d.ts +63 -0
  13. package/dist/raw3d-animation.d.ts.map +1 -0
  14. package/dist/raw3d-animation.js +200 -0
  15. package/dist/raw3d-animation.js.map +1 -0
  16. package/dist/raw3d-effects.d.ts +66 -0
  17. package/dist/raw3d-effects.d.ts.map +1 -0
  18. package/dist/raw3d-effects.js +172 -0
  19. package/dist/raw3d-effects.js.map +1 -0
  20. package/dist/raw3d-skinning.d.ts +8 -0
  21. package/dist/raw3d-skinning.d.ts.map +1 -0
  22. package/dist/raw3d-skinning.js +37 -0
  23. package/dist/raw3d-skinning.js.map +1 -0
  24. package/dist/raw3d.d.ts +77 -2
  25. package/dist/raw3d.d.ts.map +1 -1
  26. package/dist/raw3d.js +103 -7
  27. package/dist/raw3d.js.map +1 -1
  28. package/dist/rigid-interpolation.d.ts +16 -0
  29. package/dist/rigid-interpolation.d.ts.map +1 -0
  30. package/dist/rigid-interpolation.js +52 -0
  31. package/dist/rigid-interpolation.js.map +1 -0
  32. package/dist/three/backend.d.ts +13 -0
  33. package/dist/three/backend.d.ts.map +1 -0
  34. package/dist/three/backend.js +9 -0
  35. package/dist/three/backend.js.map +1 -0
  36. package/dist/three/effects.d.ts +7 -0
  37. package/dist/three/effects.d.ts.map +1 -0
  38. package/dist/three/effects.js +127 -0
  39. package/dist/three/effects.js.map +1 -0
  40. package/dist/three/index.d.ts +2 -1
  41. package/dist/three/index.d.ts.map +1 -1
  42. package/dist/three/index.js +72 -14
  43. package/dist/three/index.js.map +1 -1
  44. package/dist/three/native-lighting.d.ts +5 -0
  45. package/dist/three/native-lighting.d.ts.map +1 -0
  46. package/dist/three/native-lighting.js +86 -0
  47. package/dist/three/native-lighting.js.map +1 -0
  48. package/dist/three/parallax.d.ts +5 -0
  49. package/dist/three/parallax.d.ts.map +1 -0
  50. package/dist/three/parallax.js +63 -0
  51. package/dist/three/parallax.js.map +1 -0
  52. package/dist/three/raw3d.d.ts +25 -0
  53. package/dist/three/raw3d.d.ts.map +1 -1
  54. package/dist/three/raw3d.js +404 -16
  55. package/dist/three/raw3d.js.map +1 -1
  56. package/dist/three/renderer.d.ts.map +1 -1
  57. package/dist/three/renderer.js +3 -0
  58. package/dist/three/renderer.js.map +1 -1
  59. package/dist/three/skinning.d.ts +11 -0
  60. package/dist/three/skinning.d.ts.map +1 -0
  61. package/dist/three/skinning.js +92 -0
  62. package/dist/three/skinning.js.map +1 -0
  63. package/dist/three/tsl.d.ts +60 -0
  64. package/dist/three/tsl.d.ts.map +1 -0
  65. package/dist/three/tsl.js +3 -0
  66. package/dist/three/tsl.js.map +1 -0
  67. package/dist/three/webgpu-native.d.ts +3 -0
  68. package/dist/three/webgpu-native.d.ts.map +1 -0
  69. package/dist/three/webgpu-native.js +58 -0
  70. package/dist/three/webgpu-native.js.map +1 -0
  71. package/dist/three/webgpu-relief.d.ts +3 -0
  72. package/dist/three/webgpu-relief.d.ts.map +1 -0
  73. package/dist/three/webgpu-relief.js +61 -0
  74. package/dist/three/webgpu-relief.js.map +1 -0
  75. package/package.json +1 -1
package/README.md CHANGED
@@ -58,6 +58,217 @@ submeshes, and shadow flags.
58
58
 
59
59
  Runtime code has no production dependencies and uses browser APIs only. ZIP method `8` entries are decompressed with `DecompressionStream("deflate-raw")`.
60
60
 
61
+ ## RAW3D animation without a renderer
62
+
63
+ The root package has no Three.js dependency. It reads animation channels and
64
+ samples native node state independently of any renderer, UI, or playback clock:
65
+
66
+ ```ts
67
+ import { parseRaw3D } from "@jfk-solutions/demo3d-file-format";
68
+
69
+ const parsed = await parseRaw3D(await file.arrayBuffer());
70
+ const animation = parsed.model.animation; // Lazy, cached typed Updates parser.
71
+ console.log(animation.duration, animation.tracks, animation.warnings);
72
+ const states = animation.sample(1.25); // Absolute simulation seconds.
73
+ const node = animation.sampleNode(0, 1.25);
74
+ console.log(node.location, node.quaternion, node.layerIndex);
75
+ ```
76
+
77
+ Supported tracks include position, rotation, scale, own-geometry visibility,
78
+ deletion, parent, mesh, layer, interaction mode, highlight, optimization metadata,
79
+ and material diffuse ARGB, texture offsets/scales, reflectivity, transparency,
80
+ luminosity, normal scale, and point size. Unknown tracks remain available through
81
+ their original XML and produce warnings; their timestamps still contribute to
82
+ the clip duration. Reading metadata does not execute its native behavior.
83
+ Invalid supported tracks fail explicitly. Duplicate key times select the last
84
+ value, and seeking before a first key restores the node's initial state.
85
+
86
+ `Raw3DInterpolationMode` matches the Emulate3D 2026 enum: `Default=0`, `None=1`,
87
+ `Linear=2`, `DualQuaternion=3`. `None` holds the previous key; `Linear` interpolates
88
+ numeric channels, with shortest-path angle interpolation. This library treats
89
+ `Default` as linear. Discrete properties and ARGB colors switch at key times.
90
+
91
+ `DualQuaternion` couples position and orientation using normalized dual-quaternion
92
+ linear blending (DLB). Scale stays separate. `quaternion` and `rotation` describe
93
+ the same sampled orientation in native coordinates. Transform keys are evaluated
94
+ on the union of the node's position/rotation key times. This original implementation
95
+ is derived from the mathematical representation and normalization in
96
+ [Kavan et al., Dual Quaternions for Rigid Transformation Blending (2006)](https://users.cs.utah.edu/~ladislav/kavan06dual/kavan06dual.pdf).
97
+ DLB follows a rigid screw path but is not constant-speed ScLERP. Exact parity
98
+ with Demo3D's interpolation variant/default policy has not yet been established.
99
+ No vendor or paper implementation code is copied.
100
+
101
+ Native node visibility, interpolation mode, highlight, user data, owner,
102
+ decal/billboard flags, material luminosity/offsets/point size, and scene floor,
103
+ lighting, ambient color and quality properties have typed accessors. Unknown XML
104
+ is still retained; these accessors do not imply full native shader, billboard,
105
+ decal, floor, interaction, or scripting behavior.
106
+
107
+ ### Optional Three.js playback
108
+
109
+ Only the `/three` entry point binds the sampled state to Three.js objects:
110
+
111
+ ```ts
112
+ import { createRaw3DThreePlayback } from "@jfk-solutions/demo3d-file-format/three";
113
+
114
+ const playback = await createRaw3DThreePlayback(parsed, { three: THREE });
115
+ scene.add(playback.group);
116
+ playback.seek(1.25);
117
+ renderer.render(scene, camera);
118
+ // The application owns its animation clock, play/pause, speed and looping.
119
+ scene.remove(playback.group);
120
+ playback.dispose();
121
+ ```
122
+
123
+ The binding applies transforms, visibility, deletion, parenting, mesh swaps,
124
+ layer-material fallback, and supported material changes. It preserves explicit
125
+ node materials when a layer changes and isolates animated materials between
126
+ nodes. Structural groups stay enabled when only their own geometry is hidden.
127
+ Retired mesh samples are released, and `dispose()` releases owned GPU resources.
128
+ Playback does not run simulation scripts, physics, or PLC connections. Both
129
+ native bone/weight buffers and baked deforming mesh updates are supported.
130
+
131
+ The demo and generated standalone viewer offer Play/Pause and an absolute-time
132
+ slider for animated RAW3D files. Embedding the original RAW3D retains animation;
133
+ the source-protected Three.js snapshot export remains a static snapshot.
134
+
135
+ ### Native effects, surfaces and skinning
136
+
137
+ Use `animation.sampleFrame(time)` for a complete renderer-independent frame:
138
+ `nodes`, `lights`, `buttons`, and `scene`. The same current frame is available as
139
+ `playback.state` after `seek()`. Extended tracks and original XML are exposed by
140
+ `animation.effects.tracks`.
141
+
142
+ - Material `Texture`, `NormalMap`/`NormalTexture` references switch discretely,
143
+ including clearing a texture with a negative index. Referenced archive textures
144
+ are preloaded even when they are only used by later animation keys. Color and
145
+ normal textures are isolated per animated node and restored on backward seeks.
146
+ - Native light channels include `LightType`, `LightEnabled`, `LightMaterial`,
147
+ `LightAR`, `LightAC`, `LightAL`, `LightAQ`, `LightSF`, `LightSIA`, `LightSOA`, and
148
+ `LightCS`. Range, constant/linear/quadratic attenuation, total inner/outer cone
149
+ angles, falloff and shadow flags are applied. WebGL hooks and the TSL node backend implement
150
+ the distance/cone equations described in [Microsoft's Direct3D lighting
151
+ documentation](https://learn.microsoft.com/en-us/windows/win32/direct3d9/attenuation-and-spotlight-factor).
152
+ `evaluateRaw3DLightAttenuation()` exposes the same calculation without Three.js.
153
+ Shadow rendering still requires the host renderer's shadow map to be enabled.
154
+ - `SceneShowDefaultFloor`, `SceneAllowTeleportOnDefaultFloor`,
155
+ `SceneUseDefaultLighting`, and `ALR`/`ALG`/`ALB` update the sampled scene.
156
+ The Three binding provides ambient/default light and a finite floor preview on
157
+ the recorded plane. Teleport permission is metadata; browser navigation is a
158
+ host concern. Host-added lighting remains under host control.
159
+ - Native button visibility and checked-state channels are sampled and displayed
160
+ by the demo. Their original native serializer names `UpdateUIVisible` and
161
+ `UpdateUIChecked`, as well as `UIVisible`/`UIChecked`, are accepted. Displayed
162
+ buttons are read-only; the library does not execute their model actions.
163
+ - Unambiguous `UserData`/`ClientOwner` string keys are sampled as metadata. The
164
+ native serializer joins string arrays with spaces without escaping boundaries.
165
+ A single key may contain spaces; multi-key values with ambiguous boundaries
166
+ remain in XML with `RAW3D_STRING_KEYS_AMBIGUOUS` instead of being guessed.
167
+ - The native `Default`, `FlatText`, and `PointCloud` material shader modes map to
168
+ lit, unlit, and vertex-colored point rendering. Packed ARGB/byte/short vertex
169
+ attributes retain their native strides. RAW3D material `A` is opacity; the
170
+ public `transparency` property is its complement. This corrects older builds
171
+ which inverted partially transparent native materials.
172
+ - Billboards orient their own drawable toward the render camera while leaving
173
+ the node hierarchy intact. Decal flags apply depth bias and suppress depth
174
+ writes on the provided surface geometry; no projection onto an unrelated mesh
175
+ is invented.
176
+ - Mesh `Bones` expose driving scene-node references, bind offsets and native
177
+ skin index/weight buffer references. `decodeRaw3DSkinInfluences()` validates and
178
+ normalizes weights without a renderer. The Three adapter constructs GPU
179
+ `SkinnedMesh`/`Skeleton` bindings for up to four influences per vertex. Larger
180
+ influence sets use CPU skinning with positions, normals and tangents; no
181
+ weights are discarded. Unweighted vertices retain their mesh position.
182
+
183
+ Native channel names, bone fields and shader enums have a checked-in fixture
184
+ generated by the installed Emulate3D 2026 SDK serializer. To regenerate it on
185
+ Windows, run `./scripts/generate-native-feature-fixture.ps1`; other platforms can
186
+ run the tests using the existing fixture without that proprietary SDK.
187
+ Synthetic rigs exercise GPU/CPU deformation, bind offsets and reversible seeks.
188
+ Comparison against a captured native skinned person remains a separate parity
189
+ check; the current Bauhaus recording contains baked human geometry.
190
+
191
+ ### Lossless strings and external resources
192
+
193
+ Native string arrays lose their boundaries when values containing spaces are
194
+ joined into a single attribute. Supply authoritative keys from a sidecar through
195
+ the renderer-independent parser option:
196
+
197
+ ```ts
198
+ const parsed = await parseRaw3D(bytes, {
199
+ resolveStringKeys: ({ nodeIndex, property, times, encoded, xml }) => {
200
+ return sidecar.lookup(nodeIndex, property, times); // string[] or undefined
201
+ },
202
+ resolveResource: async path => resources.get(path) // Uint8Array or undefined
203
+ });
204
+ ```
205
+
206
+ The resolver runs for string tracks and must return one string per timestamp,
207
+ including empty values. Incorrect lengths/types fail explicitly. Archive entries
208
+ take precedence over `resolveResource`; no filesystem/network request is made
209
+ automatically. A library-side extension also accepts `<Values><Value>...</Value>
210
+ ...</Values>` children, or `ValueEncoding="json"` with a JSON array in `Value`.
211
+ These extensions are explicit conventions for this reader, not a claim that the
212
+ native serializer emits them. The original XML stays available. Missing
213
+ authoritative data still produces `RAW3D_STRING_KEYS_AMBIGUOUS`; impossible
214
+ boundaries are not guessed.
215
+
216
+ `parsed.model.streamableTextures` exposes native index, ID, name, dimensions,
217
+ path and any archived/resolved image bytes. Material `StreamableTexture` selects
218
+ the corresponding image. For video, a live camera or a recorded frame sequence,
219
+ provide Three.js textures and use asynchronous seeking:
220
+
221
+ ```ts
222
+ const playback = await createRaw3DThreePlayback(parsed, {
223
+ three: THREE,
224
+ resolveStreamTexture: async ({ index, time, source, signal }) => {
225
+ return frames.textureAt(index, time, signal); // Texture or undefined
226
+ }
227
+ });
228
+ await playback.seekAsync(2.5); // false if superseded by a newer seek
229
+ ```
230
+
231
+ Resolvers receive clip time, stream metadata and an abort signal. A newer seek,
232
+ synchronous seek, or disposal cancels pending requests; stale responses never
233
+ replace newer imagery. Returned textures remain caller-owned. The player owns
234
+ only its clones. Returning `undefined` restores the archived fallback or clears
235
+ the unavailable frame and reports a warning. A synchronous `seek()` retains the
236
+ currently resolved stream image; use `seekAsync()` for frame-accurate seeking.
237
+ Missing video frames still need an external source; a file reader cannot
238
+ reconstruct imagery that was never recorded. No URL or camera is opened without
239
+ an explicit host resolver.
240
+
241
+ ### Relief shading and the node renderer
242
+
243
+ `NormalDepth` drives layered parallax/relief UV ray marching with 32 layers and
244
+ five binary refinements in both the WebGL shader and TSL material paths. Heights
245
+ are read from the normal image's alpha channel by default; select a different
246
+ packed channel with `parallaxHeightChannel: "r" | "g" | "b" | "a"`. Opaque alpha
247
+ represents a flat top surface. Color and normal samples share the relief offset,
248
+ and animated depth updates are reversible. Depth controls texture-coordinate
249
+ displacement; this does not add geometry or alter silhouettes. A normal image
250
+ without height data cannot supply missing surface depth by itself. The height
251
+ packing and scale may need adjustment for a particular native export.
252
+
253
+ The renderer factory automatically registers native point/spot light nodes and
254
+ node materials with `WebGPURenderer`. Native attenuation, range and cone falloff
255
+ are calculated through TSL, which supports both the WebGPU and node-renderer
256
+ WebGL backends. Existing lights and materials outside the native binding keep
257
+ their standard behavior. For a manually constructed renderer, configure it
258
+ before creating RAW3D groups:
259
+
260
+ ```ts
261
+ import * as THREE from "three/webgpu";
262
+ import { configureRaw3DWebGPURenderer } from "@jfk-solutions/demo3d-file-format/three";
263
+ await configureRaw3DWebGPURenderer(renderer, THREE);
264
+ ```
265
+
266
+ Unbundled hosts must map `three/tsl` alongside `three/webgpu` to their matching
267
+ Three.js release. Node rendering code is loaded on demand; importing the parser
268
+ still loads no renderer. Browser tests compare visible relief and attenuation
269
+ changes plus backward restoration under WebGL and the TSL WebGL backend. The
270
+ hardware WebGPU case runs only when an adapter is available.
271
+
61
272
  The default single-pass XML parser builds the Demo3D object tree directly, avoiding the time and memory cost of an intermediate browser DOM. For unusual XML inputs, callers can opt into the browser parser or inject another DOM implementation:
62
273
 
63
274
  ```ts
@@ -175,6 +386,20 @@ default, including inherited child-layer visibility. Set
175
386
  `includeHiddenLayers: true` only when a tool needs to inspect hidden project
176
387
  content.
177
388
 
389
+ Editable Demo3D triangle meshes support the native `Position`, `PositionColor`,
390
+ `PositionTexture`, `PositionNormal`, `PositionNormalColor`,
391
+ `PositionNormalTexture`, and `PositionNormalTextureTangent` vertex layouts.
392
+ Packed ARGB vertex colors retain alpha and enable vertex-color materials without
393
+ altering materials shared by uncolored geometry. Tangents retain their fourth
394
+ component and change handedness when converted to Three.js coordinates. Meshes
395
+ without stored normals receive computed normals. These layouts follow the
396
+ native `Demo3D.Raw.VertexBuffer` attribute definitions; runtime-generated C#
397
+ geometry still needs a native RAW3D recording rather than script execution.
398
+
399
+ Saved `NormalTexture`/`NormalTextureReference` material references bind normal
400
+ maps alongside color textures. Normal maps use a separate linear-data texture
401
+ cache so a shared image used as color does not acquire the wrong color space.
402
+
178
403
  ### WebGPU With WebGL Fallback
179
404
 
180
405
  Tools can lazily select a canvas renderer before creating the Demo3D scene. The