@needle-tools/engine 3.28.7-beta → 3.28.7-beta.1
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.
- package/CHANGELOG.md +2259 -2256
- package/LICENSE.md +10 -10
- package/README.md +52 -52
- package/dist/needle-engine.js +9 -12
- package/dist/needle-engine.light.js +9 -12
- package/dist/needle-engine.light.min.js +8 -11
- package/dist/needle-engine.light.umd.cjs +8 -11
- package/dist/needle-engine.min.js +8 -11
- package/dist/needle-engine.umd.cjs +8 -11
- package/lib/engine/api.d.ts +52 -52
- package/lib/engine/api.js +51 -51
- package/lib/engine/assets/index.d.ts +1 -1
- package/lib/engine/assets/index.js +4 -4
- package/lib/engine/codegen/register_types.d.ts +1 -1
- package/lib/engine/codegen/register_types.js +439 -439
- package/lib/engine/debug/debug.d.ts +12 -12
- package/lib/engine/debug/debug.js +26 -26
- package/lib/engine/debug/debug_console.d.ts +2 -2
- package/lib/engine/debug/debug_console.js +204 -204
- package/lib/engine/debug/debug_overlay.d.ts +10 -10
- package/lib/engine/debug/debug_overlay.js +277 -277
- package/lib/engine/debug/index.d.ts +1 -1
- package/lib/engine/debug/index.js +1 -1
- package/lib/engine/engine_addressables.d.ts +75 -75
- package/lib/engine/engine_addressables.js +441 -441
- package/lib/engine/engine_application.d.ts +19 -19
- package/lib/engine/engine_application.js +45 -45
- package/lib/engine/engine_assetdatabase.d.ts +25 -25
- package/lib/engine/engine_assetdatabase.js +341 -341
- package/lib/engine/engine_camera.d.ts +6 -6
- package/lib/engine/engine_camera.js +23 -23
- package/lib/engine/engine_components.d.ts +17 -17
- package/lib/engine/engine_components.js +273 -273
- package/lib/engine/engine_components_internal.d.ts +11 -11
- package/lib/engine/engine_components_internal.js +41 -41
- package/lib/engine/engine_constants.d.ts +5 -5
- package/lib/engine/engine_constants.js +32 -32
- package/lib/engine/engine_context.d.ts +269 -269
- package/lib/engine/engine_context.js +1242 -1242
- package/lib/engine/engine_context_registry.d.ts +50 -50
- package/lib/engine/engine_context_registry.js +89 -89
- package/lib/engine/engine_coroutine.d.ts +4 -4
- package/lib/engine/engine_coroutine.js +21 -21
- package/lib/engine/engine_create_objects.d.ts +13 -13
- package/lib/engine/engine_create_objects.js +33 -33
- package/lib/engine/engine_default_parameters.d.ts +2 -2
- package/lib/engine/engine_default_parameters.js +3 -3
- package/lib/engine/engine_editor-sync.d.ts +17 -17
- package/lib/engine/engine_editor-sync.js +7 -7
- package/lib/engine/engine_element.d.ts +55 -55
- package/lib/engine/engine_element.js +559 -559
- package/lib/engine/engine_element_attributes.d.ts +49 -49
- package/lib/engine/engine_element_attributes.js +1 -1
- package/lib/engine/engine_element_extras.d.ts +6 -6
- package/lib/engine/engine_element_extras.js +13 -13
- package/lib/engine/engine_element_loading.d.ts +40 -40
- package/lib/engine/engine_element_loading.js +312 -312
- package/lib/engine/engine_element_overlay.d.ts +19 -19
- package/lib/engine/engine_element_overlay.js +143 -143
- package/lib/engine/engine_fileloader.d.ts +3 -3
- package/lib/engine/engine_fileloader.js +7 -7
- package/lib/engine/engine_gameobject.d.ts +39 -39
- package/lib/engine/engine_gameobject.js +559 -559
- package/lib/engine/engine_generic_utils.d.ts +1 -1
- package/lib/engine/engine_generic_utils.js +13 -13
- package/lib/engine/engine_gizmos.d.ts +26 -26
- package/lib/engine/engine_gizmos.js +282 -282
- package/lib/engine/engine_gltf.d.ts +13 -13
- package/lib/engine/engine_gltf.js +15 -15
- package/lib/engine/engine_gltf_builtin_components.d.ts +7 -7
- package/lib/engine/engine_gltf_builtin_components.js +298 -298
- package/lib/engine/engine_hot_reload.d.ts +5 -5
- package/lib/engine/engine_hot_reload.js +182 -182
- package/lib/engine/engine_input.d.ts +129 -129
- package/lib/engine/engine_input.js +799 -799
- package/lib/engine/engine_input_utils.d.ts +2 -2
- package/lib/engine/engine_input_utils.js +22 -22
- package/lib/engine/engine_instancing.d.ts +16 -16
- package/lib/engine/engine_instancing.js +36 -36
- package/lib/engine/engine_license.d.ts +4 -4
- package/lib/engine/engine_license.js +398 -398
- package/lib/engine/engine_lifecycle_api.d.ts +14 -14
- package/lib/engine/engine_lifecycle_api.js +24 -24
- package/lib/engine/engine_lifecycle_functions_internal.d.ts +6 -6
- package/lib/engine/engine_lifecycle_functions_internal.js +28 -28
- package/lib/engine/engine_lightdata.d.ts +23 -23
- package/lib/engine/engine_lightdata.js +86 -86
- package/lib/engine/engine_loaders.d.ts +7 -7
- package/lib/engine/engine_loaders.js +69 -69
- package/lib/engine/engine_mainloop_utils.d.ts +13 -13
- package/lib/engine/engine_mainloop_utils.js +426 -426
- package/lib/engine/engine_math.d.ts +43 -43
- package/lib/engine/engine_math.js +147 -147
- package/lib/engine/engine_networking.d.ts +176 -176
- package/lib/engine/engine_networking.js +649 -649
- package/lib/engine/engine_networking_auto.d.ts +24 -24
- package/lib/engine/engine_networking_auto.js +324 -324
- package/lib/engine/engine_networking_files.d.ts +23 -23
- package/lib/engine/engine_networking_files.js +176 -176
- package/lib/engine/engine_networking_files_default_components.d.ts +3 -3
- package/lib/engine/engine_networking_files_default_components.js +39 -39
- package/lib/engine/engine_networking_instantiate.d.ts +39 -39
- package/lib/engine/engine_networking_instantiate.js +302 -302
- package/lib/engine/engine_networking_peer.d.ts +15 -15
- package/lib/engine/engine_networking_peer.js +132 -132
- package/lib/engine/engine_networking_streams.d.ts +90 -90
- package/lib/engine/engine_networking_streams.js +428 -428
- package/lib/engine/engine_networking_types.d.ts +14 -14
- package/lib/engine/engine_networking_types.js +7 -7
- package/lib/engine/engine_networking_utils.d.ts +2 -2
- package/lib/engine/engine_networking_utils.js +20 -20
- package/lib/engine/engine_patcher.d.ts +10 -10
- package/lib/engine/engine_patcher.js +142 -142
- package/lib/engine/engine_physics.d.ts +115 -115
- package/lib/engine/engine_physics.js +228 -228
- package/lib/engine/engine_physics.types.d.ts +37 -37
- package/lib/engine/engine_physics.types.js +33 -33
- package/lib/engine/engine_physics_rapier.d.ts +112 -112
- package/lib/engine/engine_physics_rapier.js +1266 -1266
- package/lib/engine/engine_playerview.d.ts +26 -26
- package/lib/engine/engine_playerview.js +64 -64
- package/lib/engine/engine_scenelighting.d.ts +74 -74
- package/lib/engine/engine_scenelighting.js +285 -285
- package/lib/engine/engine_scenetools.d.ts +35 -35
- package/lib/engine/engine_scenetools.js +212 -212
- package/lib/engine/engine_serialization.d.ts +4 -4
- package/lib/engine/engine_serialization.js +4 -4
- package/lib/engine/engine_serialization_builtin_serializer.d.ts +62 -62
- package/lib/engine/engine_serialization_builtin_serializer.js +369 -369
- package/lib/engine/engine_serialization_core.d.ts +84 -84
- package/lib/engine/engine_serialization_core.js +576 -576
- package/lib/engine/engine_serialization_decorator.d.ts +15 -15
- package/lib/engine/engine_serialization_decorator.js +54 -54
- package/lib/engine/engine_setup.d.ts +1 -1
- package/lib/engine/engine_setup.js +2 -2
- package/lib/engine/engine_shaders.d.ts +31 -31
- package/lib/engine/engine_shaders.js +229 -229
- package/lib/engine/engine_shims.d.ts +3 -3
- package/lib/engine/engine_shims.js +22 -22
- package/lib/engine/engine_texture.d.ts +20 -20
- package/lib/engine/engine_texture.js +57 -57
- package/lib/engine/engine_three_utils.d.ts +51 -51
- package/lib/engine/engine_three_utils.js +342 -342
- package/lib/engine/engine_time.d.ts +19 -19
- package/lib/engine/engine_time.js +47 -47
- package/lib/engine/engine_types.d.ts +358 -358
- package/lib/engine/engine_types.js +72 -72
- package/lib/engine/engine_typestore.d.ts +16 -16
- package/lib/engine/engine_typestore.js +35 -35
- package/lib/engine/engine_util_decorator.d.ts +12 -12
- package/lib/engine/engine_util_decorator.js +115 -115
- package/lib/engine/engine_utils.d.ts +104 -104
- package/lib/engine/engine_utils.js +518 -518
- package/lib/engine/engine_utils_screenshot.d.ts +10 -10
- package/lib/engine/engine_utils_screenshot.js +70 -70
- package/lib/engine/engine_web_api.d.ts +12 -12
- package/lib/engine/engine_web_api.js +112 -112
- package/lib/engine/extensions/EXT_texture_exr.d.ts +8 -8
- package/lib/engine/extensions/EXT_texture_exr.js +32 -32
- package/lib/engine/extensions/NEEDLE_animator_controller_model.d.ts +116 -116
- package/lib/engine/extensions/NEEDLE_animator_controller_model.js +91 -91
- package/lib/engine/extensions/NEEDLE_components.d.ts +33 -33
- package/lib/engine/extensions/NEEDLE_components.js +206 -206
- package/lib/engine/extensions/NEEDLE_gameobject_data.d.ts +10 -10
- package/lib/engine/extensions/NEEDLE_gameobject_data.js +57 -57
- package/lib/engine/extensions/NEEDLE_lighting_settings.d.ts +38 -38
- package/lib/engine/extensions/NEEDLE_lighting_settings.js +183 -183
- package/lib/engine/extensions/NEEDLE_lightmaps.d.ts +18 -18
- package/lib/engine/extensions/NEEDLE_lightmaps.js +108 -108
- package/lib/engine/extensions/NEEDLE_persistent_assets.d.ts +11 -11
- package/lib/engine/extensions/NEEDLE_persistent_assets.js +63 -63
- package/lib/engine/extensions/NEEDLE_progressive.d.ts +41 -41
- package/lib/engine/extensions/NEEDLE_progressive.js +366 -366
- package/lib/engine/extensions/NEEDLE_render_objects.d.ts +13 -13
- package/lib/engine/extensions/NEEDLE_render_objects.js +159 -159
- package/lib/engine/extensions/NEEDLE_techniques_webgl.d.ts +39 -39
- package/lib/engine/extensions/NEEDLE_techniques_webgl.js +544 -544
- package/lib/engine/extensions/extension_resolver.d.ts +4 -4
- package/lib/engine/extensions/extension_resolver.js +1 -1
- package/lib/engine/extensions/extension_utils.d.ts +2 -2
- package/lib/engine/extensions/extension_utils.js +140 -140
- package/lib/engine/extensions/extensions.d.ts +21 -21
- package/lib/engine/extensions/extensions.js +94 -94
- package/lib/engine/extensions/index.d.ts +5 -5
- package/lib/engine/extensions/index.js +5 -5
- package/lib/engine/extensions/usage_tracker.d.ts +13 -13
- package/lib/engine/extensions/usage_tracker.js +61 -61
- package/lib/engine/js-extensions/Camera.d.ts +1 -1
- package/lib/engine/js-extensions/Camera.js +36 -36
- package/lib/engine/js-extensions/Layers.d.ts +3 -3
- package/lib/engine/js-extensions/Layers.js +19 -19
- package/lib/engine/js-extensions/index.d.ts +2 -2
- package/lib/engine/js-extensions/index.js +2 -2
- package/lib/engine/shaders/shaderData.d.ts +55 -55
- package/lib/engine/shaders/shaderData.js +58 -58
- package/lib/engine/tests/test_utils.d.ts +2 -2
- package/lib/engine/tests/test_utils.js +53 -53
- package/lib/engine-components/AlignmentConstraint.d.ts +10 -10
- package/lib/engine-components/AlignmentConstraint.js +39 -39
- package/lib/engine-components/Animation.d.ts +53 -53
- package/lib/engine-components/Animation.js +333 -333
- package/lib/engine-components/AnimationCurve.d.ts +16 -16
- package/lib/engine-components/AnimationCurve.js +97 -97
- package/lib/engine-components/AnimationUtils.d.ts +8 -8
- package/lib/engine-components/AnimationUtils.js +110 -110
- package/lib/engine-components/Animator.d.ts +81 -81
- package/lib/engine-components/Animator.js +229 -229
- package/lib/engine-components/AnimatorController.d.ts +57 -57
- package/lib/engine-components/AnimatorController.js +887 -887
- package/lib/engine-components/AnimatorController.js.map +1 -1
- package/lib/engine-components/AudioListener.d.ts +7 -7
- package/lib/engine-components/AudioListener.js +30 -30
- package/lib/engine-components/AudioSource.d.ts +61 -61
- package/lib/engine-components/AudioSource.js +422 -422
- package/lib/engine-components/AvatarLoader.d.ts +19 -19
- package/lib/engine-components/AvatarLoader.js +173 -173
- package/lib/engine-components/AxesHelper.d.ts +9 -9
- package/lib/engine-components/AxesHelper.js +44 -44
- package/lib/engine-components/BasicIKConstraint.d.ts +9 -9
- package/lib/engine-components/BasicIKConstraint.js +43 -43
- package/lib/engine-components/BoxHelperComponent.d.ts +16 -16
- package/lib/engine-components/BoxHelperComponent.js +89 -89
- package/lib/engine-components/Camera.d.ts +70 -70
- package/lib/engine-components/Camera.js +450 -450
- package/lib/engine-components/CameraUtils.d.ts +1 -1
- package/lib/engine-components/CameraUtils.js +77 -77
- package/lib/engine-components/CharacterController.d.ts +46 -46
- package/lib/engine-components/CharacterController.js +227 -227
- package/lib/engine-components/Collider.d.ts +46 -46
- package/lib/engine-components/Collider.js +153 -153
- package/lib/engine-components/Component.d.ts +228 -228
- package/lib/engine-components/Component.js +541 -541
- package/lib/engine-components/ContactShadows.d.ts +23 -23
- package/lib/engine-components/ContactShadows.js +233 -233
- package/lib/engine-components/DeleteBox.d.ts +9 -9
- package/lib/engine-components/DeleteBox.js +30 -30
- package/lib/engine-components/DeviceFlag.d.ts +12 -12
- package/lib/engine-components/DeviceFlag.js +43 -43
- package/lib/engine-components/DragControls.d.ts +51 -51
- package/lib/engine-components/DragControls.js +516 -516
- package/lib/engine-components/DropListener.d.ts +15 -15
- package/lib/engine-components/DropListener.js +120 -120
- package/lib/engine-components/Duplicatable.d.ts +16 -16
- package/lib/engine-components/Duplicatable.js +150 -150
- package/lib/engine-components/EventList.d.ts +28 -28
- package/lib/engine-components/EventList.js +105 -105
- package/lib/engine-components/EventTrigger.d.ts +12 -12
- package/lib/engine-components/EventTrigger.js +50 -50
- package/lib/engine-components/EventType.d.ts +19 -19
- package/lib/engine-components/EventType.js +71 -71
- package/lib/engine-components/FlyControls.d.ts +7 -7
- package/lib/engine-components/FlyControls.js +25 -25
- package/lib/engine-components/Fog.d.ts +20 -20
- package/lib/engine-components/Fog.js +60 -60
- package/lib/engine-components/Gizmos.d.ts +12 -12
- package/lib/engine-components/Gizmos.js +60 -60
- package/lib/engine-components/GridHelper.d.ts +12 -12
- package/lib/engine-components/GridHelper.js +47 -47
- package/lib/engine-components/GroundProjection.d.ts +21 -21
- package/lib/engine-components/GroundProjection.js +97 -97
- package/lib/engine-components/Interactable.d.ts +10 -10
- package/lib/engine-components/Interactable.js +11 -11
- package/lib/engine-components/Joints.d.ts +19 -19
- package/lib/engine-components/Joints.js +51 -51
- package/lib/engine-components/LODGroup.d.ts +30 -30
- package/lib/engine-components/LODGroup.js +145 -145
- package/lib/engine-components/Light.d.ts +75 -75
- package/lib/engine-components/Light.js +475 -475
- package/lib/engine-components/LookAtConstraint.d.ts +7 -7
- package/lib/engine-components/LookAtConstraint.js +17 -17
- package/lib/engine-components/NestedGltf.d.ts +11 -11
- package/lib/engine-components/NestedGltf.js +74 -74
- package/lib/engine-components/Networking.d.ts +11 -11
- package/lib/engine-components/Networking.js +70 -70
- package/lib/engine-components/OffsetConstraint.d.ts +14 -14
- package/lib/engine-components/OffsetConstraint.js +65 -65
- package/lib/engine-components/OrbitControls.d.ts +111 -111
- package/lib/engine-components/OrbitControls.js +646 -646
- package/lib/engine-components/ParticleSystem.d.ts +145 -145
- package/lib/engine-components/ParticleSystem.js +1077 -1077
- package/lib/engine-components/ParticleSystemModules.d.ts +489 -489
- package/lib/engine-components/ParticleSystemModules.js +1667 -1667
- package/lib/engine-components/ParticleSystemSubEmitter.d.ts +25 -25
- package/lib/engine-components/ParticleSystemSubEmitter.js +86 -86
- package/lib/engine-components/PlayerColor.d.ts +13 -13
- package/lib/engine-components/PlayerColor.js +83 -83
- package/lib/engine-components/ReflectionProbe.d.ts +22 -22
- package/lib/engine-components/ReflectionProbe.js +181 -181
- package/lib/engine-components/Renderer.d.ts +112 -112
- package/lib/engine-components/Renderer.js +1029 -1029
- package/lib/engine-components/RendererLightmap.d.ts +19 -19
- package/lib/engine-components/RendererLightmap.js +127 -127
- package/lib/engine-components/RigidBody.d.ts +120 -120
- package/lib/engine-components/RigidBody.js +452 -452
- package/lib/engine-components/SceneSwitcher.d.ts +72 -72
- package/lib/engine-components/SceneSwitcher.js +583 -583
- package/lib/engine-components/ScreenCapture.d.ts +64 -64
- package/lib/engine-components/ScreenCapture.js +405 -405
- package/lib/engine-components/ShadowCatcher.d.ts +18 -18
- package/lib/engine-components/ShadowCatcher.js +144 -144
- package/lib/engine-components/Skybox.d.ts +23 -23
- package/lib/engine-components/Skybox.js +287 -287
- package/lib/engine-components/SmoothFollow.d.ts +14 -14
- package/lib/engine-components/SmoothFollow.js +63 -63
- package/lib/engine-components/SpatialTrigger.d.ts +27 -27
- package/lib/engine-components/SpatialTrigger.js +144 -144
- package/lib/engine-components/SpectatorCamera.d.ts +45 -45
- package/lib/engine-components/SpectatorCamera.js +593 -593
- package/lib/engine-components/SpriteRenderer.d.ts +48 -48
- package/lib/engine-components/SpriteRenderer.js +257 -257
- package/lib/engine-components/SyncedCamera.d.ts +27 -27
- package/lib/engine-components/SyncedCamera.js +187 -187
- package/lib/engine-components/SyncedRoom.d.ts +24 -24
- package/lib/engine-components/SyncedRoom.js +162 -162
- package/lib/engine-components/SyncedTransform.d.ts +35 -35
- package/lib/engine-components/SyncedTransform.js +265 -265
- package/lib/engine-components/TestRunner.d.ts +13 -13
- package/lib/engine-components/TestRunner.js +99 -99
- package/lib/engine-components/TransformGizmo.d.ts +16 -16
- package/lib/engine-components/TransformGizmo.js +148 -148
- package/lib/engine-components/VideoPlayer.d.ts +86 -86
- package/lib/engine-components/VideoPlayer.js +792 -792
- package/lib/engine-components/Voip.d.ts +29 -29
- package/lib/engine-components/Voip.js +203 -203
- package/lib/engine-components/XRFlag.d.ts +33 -33
- package/lib/engine-components/XRFlag.js +128 -128
- package/lib/engine-components/api.d.ts +15 -15
- package/lib/engine-components/api.js +15 -15
- package/lib/engine-components/avatar/AvatarBlink_Simple.d.ts +10 -10
- package/lib/engine-components/avatar/AvatarBlink_Simple.js +75 -75
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.d.ts +13 -13
- package/lib/engine-components/avatar/AvatarEyeLook_Rotation.js +74 -74
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.d.ts +27 -27
- package/lib/engine-components/avatar/Avatar_Brain_LookAt.js +119 -119
- package/lib/engine-components/avatar/Avatar_MouthShapes.d.ts +13 -13
- package/lib/engine-components/avatar/Avatar_MouthShapes.js +78 -78
- package/lib/engine-components/avatar/Avatar_MustacheShake.d.ts +8 -8
- package/lib/engine-components/avatar/Avatar_MustacheShake.js +28 -28
- package/lib/engine-components/codegen/components.d.ts +216 -216
- package/lib/engine-components/codegen/components.js +217 -217
- package/lib/engine-components/debug/LogStats.d.ts +5 -5
- package/lib/engine-components/debug/LogStats.js +18 -18
- package/lib/engine-components/export/gltf/GltfExport.d.ts +25 -25
- package/lib/engine-components/export/gltf/GltfExport.js +215 -215
- package/lib/engine-components/export/index.d.ts +1 -1
- package/lib/engine-components/export/index.js +1 -1
- package/lib/engine-components/export/usdz/Extension.d.ts +10 -10
- package/lib/engine-components/export/usdz/Extension.js +1 -1
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +114 -114
- package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +1211 -1211
- package/lib/engine-components/export/usdz/USDZExporter.d.ts +59 -59
- package/lib/engine-components/export/usdz/USDZExporter.js +450 -450
- package/lib/engine-components/export/usdz/extensions/Animation.d.ts +69 -69
- package/lib/engine-components/export/usdz/extensions/Animation.js +650 -650
- package/lib/engine-components/export/usdz/extensions/DocumentExtension.d.ts +5 -5
- package/lib/engine-components/export/usdz/extensions/DocumentExtension.js +6 -6
- package/lib/engine-components/export/usdz/extensions/USDZText.d.ts +55 -55
- package/lib/engine-components/export/usdz/extensions/USDZText.js +246 -246
- package/lib/engine-components/export/usdz/extensions/USDZUI.d.ts +8 -8
- package/lib/engine-components/export/usdz/extensions/USDZUI.js +100 -100
- package/lib/engine-components/export/usdz/extensions/behavior/Actions.d.ts +30 -30
- package/lib/engine-components/export/usdz/extensions/behavior/Actions.js +88 -88
- package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +9 -9
- package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +52 -52
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +22 -22
- package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +134 -134
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +126 -126
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +824 -824
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +133 -133
- package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +464 -464
- package/lib/engine-components/export/usdz/index.d.ts +3 -3
- package/lib/engine-components/export/usdz/index.js +2 -2
- package/lib/engine-components/export/usdz/utils/animationutils.d.ts +3 -3
- package/lib/engine-components/export/usdz/utils/animationutils.js +85 -85
- package/lib/engine-components/export/usdz/utils/quicklook.d.ts +2 -2
- package/lib/engine-components/export/usdz/utils/quicklook.js +35 -35
- package/lib/engine-components/export/usdz/utils/timeutils.d.ts +1 -1
- package/lib/engine-components/export/usdz/utils/timeutils.js +14 -14
- package/lib/engine-components/js-extensions/ExtensionUtils.d.ts +6 -6
- package/lib/engine-components/js-extensions/ExtensionUtils.js +65 -65
- package/lib/engine-components/js-extensions/Object3D.d.ts +2 -2
- package/lib/engine-components/js-extensions/Object3D.js +140 -140
- package/lib/engine-components/js-extensions/RGBAColor.d.ts +14 -14
- package/lib/engine-components/js-extensions/RGBAColor.js +49 -49
- package/lib/engine-components/js-extensions/index.d.ts +3 -3
- package/lib/engine-components/js-extensions/index.js +3 -3
- package/lib/engine-components/postprocessing/Effects/Antialiasing.d.ts +13 -13
- package/lib/engine-components/postprocessing/Effects/Antialiasing.js +45 -45
- package/lib/engine-components/postprocessing/Effects/Bloom.d.ts +12 -12
- package/lib/engine-components/postprocessing/Effects/Bloom.js +77 -77
- package/lib/engine-components/postprocessing/Effects/ChromaticAberration.d.ts +8 -8
- package/lib/engine-components/postprocessing/Effects/ChromaticAberration.js +38 -38
- package/lib/engine-components/postprocessing/Effects/ColorAdjustments.d.ts +12 -12
- package/lib/engine-components/postprocessing/Effects/ColorAdjustments.js +81 -81
- package/lib/engine-components/postprocessing/Effects/DepthOfField.d.ts +21 -21
- package/lib/engine-components/postprocessing/Effects/DepthOfField.js +97 -97
- package/lib/engine-components/postprocessing/Effects/Pixelation.d.ts +7 -7
- package/lib/engine-components/postprocessing/Effects/Pixelation.js +28 -28
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.d.ts +13 -13
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.js +86 -86
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.d.ts +24 -24
- package/lib/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.js +94 -94
- package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.d.ts +13 -13
- package/lib/engine-components/postprocessing/Effects/TiltShiftEffect.js +62 -62
- package/lib/engine-components/postprocessing/Effects/Tonemapping.d.ts +16 -16
- package/lib/engine-components/postprocessing/Effects/Tonemapping.js +51 -51
- package/lib/engine-components/postprocessing/Effects/Vignette.d.ts +11 -11
- package/lib/engine-components/postprocessing/Effects/Vignette.js +56 -56
- package/lib/engine-components/postprocessing/PostProcessingEffect.d.ts +33 -33
- package/lib/engine-components/postprocessing/PostProcessingEffect.js +126 -126
- package/lib/engine-components/postprocessing/PostProcessingHandler.d.ts +22 -22
- package/lib/engine-components/postprocessing/PostProcessingHandler.js +201 -201
- package/lib/engine-components/postprocessing/Volume.d.ts +25 -25
- package/lib/engine-components/postprocessing/Volume.js +193 -193
- package/lib/engine-components/postprocessing/VolumeParameter.d.ts +22 -22
- package/lib/engine-components/postprocessing/VolumeParameter.js +80 -80
- package/lib/engine-components/postprocessing/VolumeProfile.d.ts +7 -7
- package/lib/engine-components/postprocessing/VolumeProfile.js +41 -41
- package/lib/engine-components/postprocessing/index.d.ts +4 -4
- package/lib/engine-components/postprocessing/index.js +4 -4
- package/lib/engine-components/timeline/PlayableDirector.d.ts +107 -107
- package/lib/engine-components/timeline/PlayableDirector.js +624 -624
- package/lib/engine-components/timeline/SignalAsset.d.ts +18 -18
- package/lib/engine-components/timeline/SignalAsset.js +124 -124
- package/lib/engine-components/timeline/TimelineModels.d.ts +88 -88
- package/lib/engine-components/timeline/TimelineModels.js +22 -22
- package/lib/engine-components/timeline/TimelineTracks.d.ts +90 -90
- package/lib/engine-components/timeline/TimelineTracks.js +825 -825
- package/lib/engine-components/timeline/index.d.ts +4 -4
- package/lib/engine-components/timeline/index.js +3 -3
- package/lib/engine-components/ui/BaseUIComponent.d.ts +31 -31
- package/lib/engine-components/ui/BaseUIComponent.js +161 -161
- package/lib/engine-components/ui/Button.d.ts +56 -56
- package/lib/engine-components/ui/Button.js +282 -282
- package/lib/engine-components/ui/Canvas.d.ts +67 -67
- package/lib/engine-components/ui/Canvas.js +382 -382
- package/lib/engine-components/ui/CanvasGroup.d.ts +15 -15
- package/lib/engine-components/ui/CanvasGroup.js +53 -53
- package/lib/engine-components/ui/EventSystem.d.ts +102 -102
- package/lib/engine-components/ui/EventSystem.js +641 -641
- package/lib/engine-components/ui/Graphic.d.ts +45 -45
- package/lib/engine-components/ui/Graphic.js +236 -236
- package/lib/engine-components/ui/Image.d.ts +27 -27
- package/lib/engine-components/ui/Image.js +107 -107
- package/lib/engine-components/ui/InputField.d.ts +34 -34
- package/lib/engine-components/ui/InputField.js +234 -234
- package/lib/engine-components/ui/Interfaces.d.ts +38 -38
- package/lib/engine-components/ui/Interfaces.js +12 -12
- package/lib/engine-components/ui/Layout.d.ts +72 -72
- package/lib/engine-components/ui/Layout.js +318 -318
- package/lib/engine-components/ui/Outline.d.ts +7 -7
- package/lib/engine-components/ui/Outline.js +20 -20
- package/lib/engine-components/ui/PointerEvents.d.ts +64 -64
- package/lib/engine-components/ui/PointerEvents.js +68 -68
- package/lib/engine-components/ui/RaycastUtils.d.ts +11 -11
- package/lib/engine-components/ui/RaycastUtils.js +67 -67
- package/lib/engine-components/ui/Raycaster.d.ts +18 -18
- package/lib/engine-components/ui/Raycaster.js +69 -69
- package/lib/engine-components/ui/RectTransform.d.ts +61 -61
- package/lib/engine-components/ui/RectTransform.js +343 -343
- package/lib/engine-components/ui/SpatialHtml.d.ts +6 -6
- package/lib/engine-components/ui/SpatialHtml.js +57 -57
- package/lib/engine-components/ui/Text.d.ts +74 -74
- package/lib/engine-components/ui/Text.js +534 -534
- package/lib/engine-components/ui/Utils.d.ts +23 -23
- package/lib/engine-components/ui/Utils.js +90 -90
- package/lib/engine-components/ui/index.d.ts +1 -1
- package/lib/engine-components/ui/index.js +1 -1
- package/lib/engine-components/utils/LookAt.d.ts +13 -13
- package/lib/engine-components/utils/LookAt.js +59 -59
- package/lib/engine-components/utils/OpenURL.d.ts +21 -21
- package/lib/engine-components/utils/OpenURL.js +124 -124
- package/lib/engine-components/webxr/WebARCameraBackground.d.ts +19 -19
- package/lib/engine-components/webxr/WebARCameraBackground.js +193 -193
- package/lib/engine-components/webxr/WebARSessionRoot.d.ts +38 -38
- package/lib/engine-components/webxr/WebARSessionRoot.js +407 -407
- package/lib/engine-components/webxr/WebXR.d.ts +110 -110
- package/lib/engine-components/webxr/WebXR.js +672 -672
- package/lib/engine-components/webxr/WebXRAvatar.d.ts +61 -61
- package/lib/engine-components/webxr/WebXRAvatar.js +289 -289
- package/lib/engine-components/webxr/WebXRController.d.ts +154 -154
- package/lib/engine-components/webxr/WebXRController.js +1028 -1028
- package/lib/engine-components/webxr/WebXRGrabRendering.d.ts +42 -42
- package/lib/engine-components/webxr/WebXRGrabRendering.js +137 -137
- package/lib/engine-components/webxr/WebXRImageTracking.d.ts +49 -49
- package/lib/engine-components/webxr/WebXRImageTracking.js +336 -336
- package/lib/engine-components/webxr/WebXRPlaneTracking.d.ts +49 -49
- package/lib/engine-components/webxr/WebXRPlaneTracking.js +372 -372
- package/lib/engine-components/webxr/WebXRRig.d.ts +4 -4
- package/lib/engine-components/webxr/WebXRRig.js +19 -19
- package/lib/engine-components/webxr/WebXRSync.d.ts +54 -54
- package/lib/engine-components/webxr/WebXRSync.js +410 -410
- package/lib/engine-components/webxr/index.d.ts +4 -4
- package/lib/engine-components/webxr/index.js +4 -4
- package/lib/engine-components-experimental/Presentation.d.ts +6 -6
- package/lib/engine-components-experimental/Presentation.js +9 -9
- package/lib/engine-components-experimental/api.d.ts +1 -1
- package/lib/engine-components-experimental/api.js +1 -1
- package/lib/engine-components-experimental/networking/PlayerSync.d.ts +50 -50
- package/lib/engine-components-experimental/networking/PlayerSync.js +200 -200
- package/lib/engine-schemes/api.d.ts +1 -1
- package/lib/engine-schemes/api.js +1 -1
- package/lib/engine-schemes/schemes.d.ts +7 -7
- package/lib/engine-schemes/schemes.js +19 -19
- package/lib/engine-schemes/synced-camera-model.d.ts +25 -25
- package/lib/engine-schemes/synced-camera-model.js +67 -67
- package/lib/engine-schemes/synced-transform-model.d.ts +31 -31
- package/lib/engine-schemes/synced-transform-model.js +66 -66
- package/lib/engine-schemes/transform.d.ts +12 -12
- package/lib/engine-schemes/transform.js +39 -39
- package/lib/engine-schemes/vec3.d.ts +11 -11
- package/lib/engine-schemes/vec3.js +29 -29
- package/lib/engine-schemes/vec4.d.ts +12 -12
- package/lib/engine-schemes/vec4.js +33 -33
- package/lib/engine-schemes/vr-user-state-buffer.d.ts +36 -36
- package/lib/engine-schemes/vr-user-state-buffer.js +103 -103
- package/lib/include/three/ARButton.d.ts +3 -3
- package/lib/include/three/ARButton.js +151 -151
- package/lib/include/three/EXT_mesh_gpu_instancing_exporter.d.ts +6 -6
- package/lib/include/three/EXT_mesh_gpu_instancing_exporter.js +45 -45
- package/lib/include/three/VRButton.d.ts +5 -5
- package/lib/include/three/VRButton.js +118 -118
- package/lib/needle-engine.d.ts +6 -6
- package/lib/needle-engine.js +49 -49
- package/package.json +1 -1
- package/plugins/common/config.cjs +14 -14
- package/plugins/common/config.js +19 -19
- package/plugins/common/generator.js +10 -10
- package/plugins/common/license.cjs +30 -30
- package/plugins/common/version.js +11 -11
- package/plugins/next/license.cjs +4 -4
- package/plugins/next/next.js +70 -70
- package/plugins/types/index.d.ts +1 -1
- package/plugins/types/needleConfig.d.ts +21 -21
- package/plugins/types/userconfig.d.ts +42 -42
- package/plugins/vite/alias.js +70 -70
- package/plugins/vite/build.js +19 -19
- package/plugins/vite/config.js +73 -73
- package/plugins/vite/copyfiles.js +134 -134
- package/plugins/vite/defines.js +45 -45
- package/plugins/vite/dependency-watcher.js +224 -224
- package/plugins/vite/drop-client.js +76 -76
- package/plugins/vite/drop.js +82 -82
- package/plugins/vite/editor-connection.js +121 -121
- package/plugins/vite/facebook-instant-games.js +99 -99
- package/plugins/vite/gzip.js +5 -5
- package/plugins/vite/imports-logger.js +143 -143
- package/plugins/vite/index.js +81 -81
- package/plugins/vite/license.js +42 -42
- package/plugins/vite/meta.js +149 -149
- package/plugins/vite/peer.js +31 -31
- package/plugins/vite/poster-client.js +59 -59
- package/plugins/vite/poster.js +73 -73
- package/plugins/vite/reload-client.js +15 -15
- package/plugins/vite/reload.js +363 -363
- package/plugins/vite/transform-codegen.js +55 -55
- package/plugins/vite/vite-4.4-hack.js +31 -31
- package/src/engine/api.ts +54 -54
- package/src/engine/assets/index.ts +4 -4
- package/src/engine/codegen/register_types.ts +441 -441
- package/src/engine/debug/debug.ts +29 -29
- package/src/engine/debug/debug_console.ts +213 -213
- package/src/engine/debug/debug_overlay.ts +283 -283
- package/src/engine/engine.ts +13 -13
- package/src/engine/engine_addressables.ts +494 -494
- package/src/engine/engine_application.ts +53 -53
- package/src/engine/engine_assetdatabase.ts +383 -383
- package/src/engine/engine_camera.ts +32 -32
- package/src/engine/engine_components.ts +266 -266
- package/src/engine/engine_components_internal.ts +42 -42
- package/src/engine/engine_constants.ts +42 -42
- package/src/engine/engine_context.ts +1386 -1386
- package/src/engine/engine_context_registry.ts +103 -103
- package/src/engine/engine_coroutine.ts +24 -24
- package/src/engine/engine_create_objects.ts +39 -39
- package/src/engine/engine_default_parameters.ts +3 -3
- package/src/engine/engine_editor-sync.ts +29 -29
- package/src/engine/engine_element.ts +592 -592
- package/src/engine/engine_element_attributes.ts +61 -61
- package/src/engine/engine_element_extras.ts +16 -16
- package/src/engine/engine_element_loading.ts +341 -341
- package/src/engine/engine_element_overlay.ts +160 -160
- package/src/engine/engine_fileloader.js +8 -8
- package/src/engine/engine_gameobject.ts +621 -621
- package/src/engine/engine_generic_utils.js +13 -13
- package/src/engine/engine_gizmos.ts +321 -321
- package/src/engine/engine_gltf.ts +30 -30
- package/src/engine/engine_gltf_builtin_components.ts +350 -350
- package/src/engine/engine_hot_reload.ts +196 -196
- package/src/engine/engine_input.ts +879 -879
- package/src/engine/engine_input_utils.ts +23 -23
- package/src/engine/engine_instancing.ts +42 -42
- package/src/engine/engine_license.ts +413 -413
- package/src/engine/engine_lifecycle_api.ts +29 -29
- package/src/engine/engine_lifecycle_functions_internal.ts +36 -36
- package/src/engine/engine_lightdata.ts +113 -113
- package/src/engine/engine_loaders.ts +77 -77
- package/src/engine/engine_mainloop_utils.ts +431 -431
- package/src/engine/engine_math.ts +174 -174
- package/src/engine/engine_networking.ts +742 -742
- package/src/engine/engine_networking_auto.ts +373 -373
- package/src/engine/engine_networking_files.ts +206 -206
- package/src/engine/engine_networking_files_default_components.ts +54 -54
- package/src/engine/engine_networking_instantiate.ts +362 -362
- package/src/engine/engine_networking_peer.ts +158 -158
- package/src/engine/engine_networking_streams.ts +489 -489
- package/src/engine/engine_networking_types.ts +18 -18
- package/src/engine/engine_networking_utils.ts +23 -23
- package/src/engine/engine_networking_websocket.ts +2 -2
- package/src/engine/engine_patcher.ts +199 -199
- package/src/engine/engine_physics.ts +287 -287
- package/src/engine/engine_physics.types.ts +43 -43
- package/src/engine/engine_physics_rapier.ts +1385 -1385
- package/src/engine/engine_playerview.ts +79 -79
- package/src/engine/engine_scenelighting.ts +313 -313
- package/src/engine/engine_scenetools.ts +242 -242
- package/src/engine/engine_serialization.ts +6 -6
- package/src/engine/engine_serialization_builtin_serializer.ts +415 -415
- package/src/engine/engine_serialization_core.ts +680 -680
- package/src/engine/engine_serialization_decorator.ts +68 -68
- package/src/engine/engine_setup.ts +1 -1
- package/src/engine/engine_shaders.ts +242 -242
- package/src/engine/engine_shims.ts +28 -28
- package/src/engine/engine_texture.ts +70 -70
- package/src/engine/engine_three_utils.ts +382 -382
- package/src/engine/engine_time.ts +55 -55
- package/src/engine/engine_types.ts +489 -489
- package/src/engine/engine_typestore.ts +41 -41
- package/src/engine/engine_util_decorator.ts +134 -134
- package/src/engine/engine_utils.ts +605 -605
- package/src/engine/engine_utils_screenshot.ts +84 -84
- package/src/engine/engine_web_api.ts +119 -119
- package/src/engine/extensions/EXT_texture_exr.ts +49 -49
- package/src/engine/extensions/NEEDLE_animator_controller_model.ts +193 -193
- package/src/engine/extensions/NEEDLE_components.ts +250 -250
- package/src/engine/extensions/NEEDLE_gameobject_data.ts +82 -82
- package/src/engine/extensions/NEEDLE_lighting_settings.ts +210 -210
- package/src/engine/extensions/NEEDLE_lightmaps.ts +130 -130
- package/src/engine/extensions/NEEDLE_persistent_assets.ts +75 -75
- package/src/engine/extensions/NEEDLE_progressive.ts +412 -412
- package/src/engine/extensions/NEEDLE_render_objects.ts +209 -209
- package/src/engine/extensions/NEEDLE_techniques_webgl.ts +618 -618
- package/src/engine/extensions/extension_resolver.ts +4 -4
- package/src/engine/extensions/extension_utils.ts +149 -149
- package/src/engine/extensions/extensions.ts +118 -118
- package/src/engine/extensions/index.ts +4 -4
- package/src/engine/extensions/usage_tracker.ts +95 -95
- package/src/engine/js-extensions/Camera.ts +34 -34
- package/src/engine/js-extensions/Layers.ts +19 -19
- package/src/engine/js-extensions/index.ts +1 -1
- package/src/engine/shaders/shaderData.ts +67 -67
- package/src/engine/tests/test_utils.ts +63 -63
- package/src/engine-components/AlignmentConstraint.ts +35 -35
- package/src/engine-components/Animation.ts +345 -345
- package/src/engine-components/AnimationCurve.ts +83 -83
- package/src/engine-components/AnimationUtils.ts +117 -117
- package/src/engine-components/Animator.ts +243 -243
- package/src/engine-components/AnimatorController.ts +1020 -1020
- package/src/engine-components/AudioListener.ts +32 -32
- package/src/engine-components/AudioSource.ts +419 -419
- package/src/engine-components/AvatarLoader.ts +204 -204
- package/src/engine-components/AxesHelper.ts +33 -33
- package/src/engine-components/BasicIKConstraint.ts +53 -53
- package/src/engine-components/BoxCollider.ts +1 -1
- package/src/engine-components/BoxHelperComponent.ts +100 -100
- package/src/engine-components/Camera.ts +454 -454
- package/src/engine-components/CameraUtils.ts +89 -89
- package/src/engine-components/CharacterController.ts +243 -243
- package/src/engine-components/Collider.ts +160 -160
- package/src/engine-components/Component.ts +670 -670
- package/src/engine-components/ContactShadows.ts +265 -265
- package/src/engine-components/DeleteBox.ts +35 -35
- package/src/engine-components/DeviceFlag.ts +42 -42
- package/src/engine-components/DragControls.ts +574 -574
- package/src/engine-components/DropListener.ts +112 -112
- package/src/engine-components/Duplicatable.ts +146 -146
- package/src/engine-components/EventList.ts +125 -125
- package/src/engine-components/EventTrigger.ts +47 -47
- package/src/engine-components/EventType.ts +87 -87
- package/src/engine-components/FlyControls.ts +31 -31
- package/src/engine-components/Fog.ts +59 -59
- package/src/engine-components/Gizmos.ts +52 -52
- package/src/engine-components/GridHelper.ts +40 -40
- package/src/engine-components/GroundProjection.ts +97 -97
- package/src/engine-components/Interactable.ts +18 -18
- package/src/engine-components/Joints.ts +51 -51
- package/src/engine-components/LODGroup.ts +145 -145
- package/src/engine-components/Light.ts +493 -493
- package/src/engine-components/LookAtConstraint.ts +11 -11
- package/src/engine-components/NestedGltf.ts +70 -70
- package/src/engine-components/Networking.ts +72 -72
- package/src/engine-components/OffsetConstraint.ts +59 -59
- package/src/engine-components/OrbitControls.ts +653 -653
- package/src/engine-components/ParticleSystem.ts +1192 -1192
- package/src/engine-components/ParticleSystemModules.ts +1481 -1481
- package/src/engine-components/ParticleSystemSubEmitter.ts +110 -110
- package/src/engine-components/PlayerColor.ts +93 -93
- package/src/engine-components/ReflectionProbe.ts +192 -192
- package/src/engine-components/Renderer.ts +1125 -1125
- package/src/engine-components/RendererLightmap.ts +145 -145
- package/src/engine-components/RigidBody.ts +453 -453
- package/src/engine-components/SceneSwitcher.ts +594 -594
- package/src/engine-components/ScreenCapture.ts +437 -437
- package/src/engine-components/ShadowCatcher.ts +149 -149
- package/src/engine-components/Skybox.ts +281 -281
- package/src/engine-components/SmoothFollow.ts +57 -57
- package/src/engine-components/SpatialTrigger.ts +142 -142
- package/src/engine-components/SpectatorCamera.ts +675 -675
- package/src/engine-components/SphereCollider.ts +1 -1
- package/src/engine-components/SpriteRenderer.ts +244 -244
- package/src/engine-components/SyncedCamera.ts +208 -208
- package/src/engine-components/SyncedRoom.ts +166 -166
- package/src/engine-components/SyncedTransform.ts +336 -336
- package/src/engine-components/TestRunner.ts +114 -114
- package/src/engine-components/TransformGizmo.ts +157 -157
- package/src/engine-components/VideoPlayer.ts +831 -831
- package/src/engine-components/Voip.ts +214 -214
- package/src/engine-components/XRFlag.ts +138 -138
- package/src/engine-components/api.ts +22 -22
- package/src/engine-components/avatar/AvatarBlink_Simple.ts +67 -67
- package/src/engine-components/avatar/AvatarEyeLook_Rotation.ts +68 -68
- package/src/engine-components/avatar/Avatar_Brain_LookAt.ts +136 -136
- package/src/engine-components/avatar/Avatar_MouthShapes.ts +81 -81
- package/src/engine-components/avatar/Avatar_MustacheShake.ts +28 -28
- package/src/engine-components/codegen/components.ts +216 -216
- package/src/engine-components/debug/LogStats.ts +21 -21
- package/src/engine-components/export/gltf/GltfExport.ts +231 -231
- package/src/engine-components/export/usdz/Extension.ts +11 -11
- package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +1773 -1773
- package/src/engine-components/export/usdz/USDZExporter.ts +477 -477
- package/src/engine-components/export/usdz/extensions/Animation.ts +774 -774
- package/src/engine-components/export/usdz/extensions/DocumentExtension.ts +9 -9
- package/src/engine-components/export/usdz/extensions/USDZText.ts +287 -287
- package/src/engine-components/export/usdz/extensions/USDZUI.ts +119 -119
- package/src/engine-components/export/usdz/extensions/behavior/Actions.ts +98 -98
- package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +67 -67
- package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +202 -202
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +963 -963
- package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +517 -517
- package/src/engine-components/export/usdz/index.ts +2 -2
- package/src/engine-components/export/usdz/utils/animationutils.ts +100 -100
- package/src/engine-components/export/usdz/utils/quicklook.ts +42 -42
- package/src/engine-components/export/usdz/utils/timeutils.ts +19 -19
- package/src/engine-components/js-extensions/ExtensionUtils.ts +81 -81
- package/src/engine-components/js-extensions/Object3D.ts +181 -181
- package/src/engine-components/js-extensions/RGBAColor.ts +54 -54
- package/src/engine-components/js-extensions/Vector.ts +16 -16
- package/src/engine-components/js-extensions/index.ts +2 -2
- package/src/engine-components/postprocessing/Effects/Antialiasing.ts +51 -51
- package/src/engine-components/postprocessing/Effects/Bloom.ts +76 -76
- package/src/engine-components/postprocessing/Effects/ChromaticAberration.ts +35 -35
- package/src/engine-components/postprocessing/Effects/ColorAdjustments.ts +96 -96
- package/src/engine-components/postprocessing/Effects/DepthOfField.ts +93 -93
- package/src/engine-components/postprocessing/Effects/Pixelation.ts +26 -26
- package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusion.ts +84 -84
- package/src/engine-components/postprocessing/Effects/ScreenspaceAmbientOcclusionN8.ts +98 -98
- package/src/engine-components/postprocessing/Effects/TiltShiftEffect.ts +55 -55
- package/src/engine-components/postprocessing/Effects/Tonemapping.ts +54 -54
- package/src/engine-components/postprocessing/Effects/Vignette.ts +54 -54
- package/src/engine-components/postprocessing/PostProcessingEffect.ts +148 -148
- package/src/engine-components/postprocessing/PostProcessingHandler.ts +232 -232
- package/src/engine-components/postprocessing/Volume.ts +216 -216
- package/src/engine-components/postprocessing/VolumeParameter.ts +92 -92
- package/src/engine-components/postprocessing/VolumeProfile.ts +40 -40
- package/src/engine-components/postprocessing/index.ts +3 -3
- package/src/engine-components/timeline/PlayableDirector.ts +666 -666
- package/src/engine-components/timeline/SignalAsset.ts +138 -138
- package/src/engine-components/timeline/TimelineModels.ts +93 -93
- package/src/engine-components/timeline/TimelineTracks.ts +906 -906
- package/src/engine-components/timeline/index.ts +3 -3
- package/src/engine-components/ui/BaseUIComponent.ts +195 -195
- package/src/engine-components/ui/Button.ts +283 -283
- package/src/engine-components/ui/Canvas.ts +390 -390
- package/src/engine-components/ui/CanvasGroup.ts +49 -49
- package/src/engine-components/ui/EventSystem.ts +736 -736
- package/src/engine-components/ui/Graphic.ts +255 -255
- package/src/engine-components/ui/Image.ts +102 -102
- package/src/engine-components/ui/InputField.ts +290 -290
- package/src/engine-components/ui/Interfaces.ts +57 -57
- package/src/engine-components/ui/Layout.ts +322 -322
- package/src/engine-components/ui/Outline.ts +12 -12
- package/src/engine-components/ui/PointerEvents.ts +118 -118
- package/src/engine-components/ui/RaycastUtils.ts +68 -68
- package/src/engine-components/ui/Raycaster.ts +73 -73
- package/src/engine-components/ui/RectTransform.ts +364 -364
- package/src/engine-components/ui/SpatialHtml.ts +63 -63
- package/src/engine-components/ui/Text.ts +572 -572
- package/src/engine-components/ui/Utils.ts +110 -110
- package/src/engine-components/utils/LookAt.ts +65 -65
- package/src/engine-components/utils/OpenURL.ts +118 -118
- package/src/engine-components/webxr/WebARCameraBackground.ts +224 -224
- package/src/engine-components/webxr/WebARSessionRoot.ts +446 -446
- package/src/engine-components/webxr/WebXR.ts +761 -761
- package/src/engine-components/webxr/WebXRAvatar.ts +356 -356
- package/src/engine-components/webxr/WebXRController.ts +1168 -1168
- package/src/engine-components/webxr/WebXRGrabRendering.ts +150 -150
- package/src/engine-components/webxr/WebXRImageTracking.ts +371 -371
- package/src/engine-components/webxr/WebXRPlaneTracking.ts +429 -429
- package/src/engine-components/webxr/WebXRRig.ts +21 -21
- package/src/engine-components/webxr/WebXRSync.ts +463 -463
- package/src/engine-components/webxr/index.ts +3 -3
- package/src/engine-components-experimental/Presentation.ts +12 -12
- package/src/engine-components-experimental/networking/PlayerSync.ts +217 -217
- package/src/engine-schemes/COMPILE_SCHEMES.bat +3 -3
- package/src/engine-schemes/COMPILE_TS.bat +11 -11
- package/src/engine-schemes/schemes.ts +27 -27
- package/src/engine-schemes/synced-camera-model.ts +92 -92
- package/src/engine-schemes/synced-transform-model.ts +90 -90
- package/src/engine-schemes/syncedCamera.fbs +10 -10
- package/src/engine-schemes/transform.ts +50 -50
- package/src/engine-schemes/transforms.fbs +25 -25
- package/src/engine-schemes/vec.fbs +19 -19
- package/src/engine-schemes/vec2.ts +33 -33
- package/src/engine-schemes/vec3.ts +38 -38
- package/src/engine-schemes/vec4.ts +43 -43
- package/src/engine-schemes/vr-user-state-buffer.ts +138 -138
- package/src/engine-schemes/vrUserStateBuffer.fbs +16 -16
- package/src/include/draco/draco_decoder.js +34 -34
- package/src/include/draco/draco_wasm_wrapper.js +117 -117
- package/src/include/ktx2/basis_transcoder.js +21 -21
- package/src/include/needle/arial-msdf.json +1471 -1471
- package/src/include/three/ARButton.js +231 -231
- package/src/include/three/DragControls.js +231 -231
- package/src/include/three/EXT_mesh_gpu_instancing_exporter.js +66 -66
- package/src/include/three/VRButton.js +194 -194
- package/src/needle-engine.ts +55 -55
- package/src/engine/dist/api.js +0 -73
- package/src/engine/dist/api.js.meta +0 -7
- package/src/engine/dist/engine_networking_streams.js +0 -474
- package/src/engine/dist/engine_networking_streams.js.meta +0 -7
- package/src/engine-schemes/dist/api.js +0 -17
- package/src/engine-schemes/dist/api.js.meta +0 -7
- package/src/engine-schemes/dist/schemes.js +0 -25
- package/src/engine-schemes/dist/schemes.js.meta +0 -7
- package/src/engine-schemes/dist/synced-camera-model.js +0 -74
- package/src/engine-schemes/dist/synced-camera-model.js.meta +0 -7
- package/src/engine-schemes/dist/synced-transform-model.js +0 -73
- package/src/engine-schemes/dist/synced-transform-model.js.meta +0 -7
- package/src/engine-schemes/dist/transform.js +0 -46
- package/src/engine-schemes/dist/transform.js.meta +0 -7
- package/src/engine-schemes/dist/vec2.js +0 -32
- package/src/engine-schemes/dist/vec2.js.meta +0 -7
- package/src/engine-schemes/dist/vec3.js +0 -36
- package/src/engine-schemes/dist/vec3.js.meta +0 -7
- package/src/engine-schemes/dist/vec4.js +0 -40
- package/src/engine-schemes/dist/vec4.js.meta +0 -7
- package/src/engine-schemes/dist/vr-user-state-buffer.js +0 -110
- package/src/engine-schemes/dist/vr-user-state-buffer.js.meta +0 -7
- package/src/engine-schemes/flatc.exe +0 -0
|
@@ -1,1212 +1,1212 @@
|
|
|
1
|
-
import { Renderer } from '../../Renderer.js';
|
|
2
|
-
import { GameObject } from '../../Component.js';
|
|
3
|
-
import '../../../engine/engine_shims.js';
|
|
4
|
-
import { PlaneGeometry, Texture, Uniform, PerspectiveCamera, Scene, Mesh, ShaderMaterial, WebGLRenderer, MathUtils, Matrix4, DoubleSide, Color, MeshStandardMaterial, MeshPhysicalMaterial, MeshBasicMaterial, SkinnedMesh, SRGBColorSpace, OrthographicCamera, BufferAttribute, Vector4 } from 'three';
|
|
5
|
-
import * as fflate from 'three/examples/jsm/libs/fflate.module.js';
|
|
6
|
-
function makeNameSafe(str) {
|
|
7
|
-
str = str.replace(/[^a-zA-Z0-9_]/g, '');
|
|
8
|
-
// if str does not start with a-zA-Z_ add _ to the beginning
|
|
9
|
-
if (!str.match(/^[a-zA-Z_]/))
|
|
10
|
-
str = '_' + str;
|
|
11
|
-
return str;
|
|
12
|
-
}
|
|
13
|
-
// TODO check if this works when bones in the skeleton are completely unordered
|
|
14
|
-
function findCommonAncestor(objects) {
|
|
15
|
-
if (objects.length === 0)
|
|
16
|
-
return null;
|
|
17
|
-
const ancestors = objects.map((obj) => {
|
|
18
|
-
const objAncestors = new Array();
|
|
19
|
-
while (obj.parent) {
|
|
20
|
-
objAncestors.unshift(obj.parent);
|
|
21
|
-
obj = obj.parent;
|
|
22
|
-
}
|
|
23
|
-
return objAncestors;
|
|
24
|
-
});
|
|
25
|
-
//@ts-ignore – findLast seems to be missing in TypeScript types pre-5.x
|
|
26
|
-
const commonAncestor = ancestors[0].findLast((ancestor) => {
|
|
27
|
-
return ancestors.every((a) => a.includes(ancestor));
|
|
28
|
-
});
|
|
29
|
-
return commonAncestor || null;
|
|
30
|
-
}
|
|
31
|
-
function findStructuralNodesInBoneHierarchy(bones) {
|
|
32
|
-
const commonAncestor = findCommonAncestor(bones);
|
|
33
|
-
// find all structural nodes – parents of bones that are not bones themselves
|
|
34
|
-
const structuralNodes = new Set();
|
|
35
|
-
for (const bone of bones) {
|
|
36
|
-
let current = bone.parent;
|
|
37
|
-
while (current && current !== commonAncestor) {
|
|
38
|
-
if (!bones.includes(current)) {
|
|
39
|
-
structuralNodes.add(current);
|
|
40
|
-
}
|
|
41
|
-
current = current.parent;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return structuralNodes;
|
|
45
|
-
}
|
|
46
|
-
class USDObject {
|
|
47
|
-
static USDObject_export_id = 0;
|
|
48
|
-
uuid;
|
|
49
|
-
name;
|
|
50
|
-
matrix;
|
|
51
|
-
_isDynamic;
|
|
52
|
-
get isDynamic() { return this._isDynamic; }
|
|
53
|
-
set isDynamic(value) { this._isDynamic = value; }
|
|
54
|
-
geometry;
|
|
55
|
-
material;
|
|
56
|
-
camera;
|
|
57
|
-
parent;
|
|
58
|
-
skinnedMesh;
|
|
59
|
-
children = [];
|
|
60
|
-
animations;
|
|
61
|
-
_eventListeners;
|
|
62
|
-
static createEmptyParent(object) {
|
|
63
|
-
const emptyParent = new USDObject(MathUtils.generateUUID(), object.name + '_empty_' + (USDObject.USDObject_export_id++), object.matrix);
|
|
64
|
-
const parent = object.parent;
|
|
65
|
-
parent.add(emptyParent);
|
|
66
|
-
emptyParent.add(object);
|
|
67
|
-
emptyParent.isDynamic = true;
|
|
68
|
-
object.matrix = new Matrix4().identity();
|
|
69
|
-
return emptyParent;
|
|
70
|
-
}
|
|
71
|
-
static createEmpty() {
|
|
72
|
-
const empty = new USDObject(MathUtils.generateUUID(), 'Empty_' + (USDObject.USDObject_export_id++), new Matrix4());
|
|
73
|
-
empty.isDynamic = true;
|
|
74
|
-
return empty;
|
|
75
|
-
}
|
|
76
|
-
constructor(id, name, matrix, mesh = null, material = null, camera = null, skinnedMesh = null, animations = null) {
|
|
77
|
-
this.uuid = id;
|
|
78
|
-
this.name = makeNameSafe(name);
|
|
79
|
-
this.matrix = matrix.clone();
|
|
80
|
-
this.geometry = mesh;
|
|
81
|
-
this.material = material;
|
|
82
|
-
this.camera = camera;
|
|
83
|
-
this.parent = null;
|
|
84
|
-
this.children = [];
|
|
85
|
-
this._eventListeners = {};
|
|
86
|
-
this._isDynamic = false;
|
|
87
|
-
this.skinnedMesh = skinnedMesh;
|
|
88
|
-
this.animations = animations;
|
|
89
|
-
}
|
|
90
|
-
is(obj) {
|
|
91
|
-
if (!obj)
|
|
92
|
-
return false;
|
|
93
|
-
return this.uuid === obj.uuid;
|
|
94
|
-
}
|
|
95
|
-
isEmpty() {
|
|
96
|
-
return !this.geometry;
|
|
97
|
-
}
|
|
98
|
-
clone() {
|
|
99
|
-
const clone = new USDObject(MathUtils.generateUUID(), this.name, this.matrix, this.geometry, this.material);
|
|
100
|
-
clone.isDynamic = this.isDynamic;
|
|
101
|
-
return clone;
|
|
102
|
-
}
|
|
103
|
-
deepClone() {
|
|
104
|
-
const clone = this.clone();
|
|
105
|
-
for (const child of this.children) {
|
|
106
|
-
if (!child)
|
|
107
|
-
continue;
|
|
108
|
-
clone.add(child.deepClone());
|
|
109
|
-
}
|
|
110
|
-
return clone;
|
|
111
|
-
}
|
|
112
|
-
getPath() {
|
|
113
|
-
let current = this.parent;
|
|
114
|
-
let path = this.name;
|
|
115
|
-
while (current) {
|
|
116
|
-
// StageRoot has a special path right now since there's additional Xforms for encapsulation.
|
|
117
|
-
// Better would be to actually model them as part of our object graph, but they're written separately,
|
|
118
|
-
// so currently we don't and instead work around that here.
|
|
119
|
-
const currentName = current.parent ? current.name : (current.name + "/Scenes/Scene");
|
|
120
|
-
path = currentName + '/' + path;
|
|
121
|
-
current = current.parent;
|
|
122
|
-
}
|
|
123
|
-
return '</' + path + '>';
|
|
124
|
-
}
|
|
125
|
-
add(child) {
|
|
126
|
-
if (child.parent) {
|
|
127
|
-
child.parent.remove(child);
|
|
128
|
-
}
|
|
129
|
-
child.parent = this;
|
|
130
|
-
this.children.push(child);
|
|
131
|
-
}
|
|
132
|
-
remove(child) {
|
|
133
|
-
const index = this.children.indexOf(child);
|
|
134
|
-
if (index >= 0) {
|
|
135
|
-
if (child.parent === this)
|
|
136
|
-
child.parent = null;
|
|
137
|
-
this.children.splice(index, 1);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
addEventListener(evt, listener) {
|
|
141
|
-
if (!this._eventListeners[evt])
|
|
142
|
-
this._eventListeners[evt] = [];
|
|
143
|
-
this._eventListeners[evt].push(listener);
|
|
144
|
-
}
|
|
145
|
-
removeEventListener(evt, listener) {
|
|
146
|
-
if (!this._eventListeners[evt])
|
|
147
|
-
return;
|
|
148
|
-
const index = this._eventListeners[evt].indexOf(listener);
|
|
149
|
-
if (index >= 0) {
|
|
150
|
-
this._eventListeners[evt].splice(index, 1);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
onSerialize(writer, context) {
|
|
154
|
-
const listeners = this._eventListeners['serialize'];
|
|
155
|
-
if (listeners)
|
|
156
|
-
listeners.forEach(listener => listener(writer, context));
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
class USDDocument extends USDObject {
|
|
160
|
-
stageLength;
|
|
161
|
-
get isDocumentRoot() {
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
get isDynamic() {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
constructor() {
|
|
168
|
-
super(undefined, 'StageRoot', new Matrix4(), null, null, null);
|
|
169
|
-
this.children = [];
|
|
170
|
-
this.stageLength = 200;
|
|
171
|
-
}
|
|
172
|
-
add(child) {
|
|
173
|
-
child.parent = this;
|
|
174
|
-
this.children.push(child);
|
|
175
|
-
}
|
|
176
|
-
remove(child) {
|
|
177
|
-
const index = this.children.indexOf(child);
|
|
178
|
-
if (index >= 0) {
|
|
179
|
-
if (child.parent === this)
|
|
180
|
-
child.parent = null;
|
|
181
|
-
this.children.splice(index, 1);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
traverse(callback, current = null) {
|
|
185
|
-
if (current !== null)
|
|
186
|
-
callback(current);
|
|
187
|
-
else
|
|
188
|
-
current = this;
|
|
189
|
-
if (current.children) {
|
|
190
|
-
for (const child of current.children) {
|
|
191
|
-
this.traverse(callback, child);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
findById(uuid) {
|
|
196
|
-
let found = false;
|
|
197
|
-
function search(current) {
|
|
198
|
-
if (found)
|
|
199
|
-
return;
|
|
200
|
-
if (current.uuid === uuid) {
|
|
201
|
-
found = true;
|
|
202
|
-
return current;
|
|
203
|
-
}
|
|
204
|
-
if (current.children) {
|
|
205
|
-
for (const child of current.children) {
|
|
206
|
-
const res = search(child);
|
|
207
|
-
if (res)
|
|
208
|
-
return res;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return search(this);
|
|
213
|
-
}
|
|
214
|
-
buildHeader(endTimeCode) {
|
|
215
|
-
return `#usda 1.0
|
|
216
|
-
(
|
|
217
|
-
customLayerData = {
|
|
218
|
-
string creator = "Needle Engine USDZExporter"
|
|
219
|
-
}
|
|
220
|
-
defaultPrim = "${makeNameSafe(this.name)}"
|
|
221
|
-
metersPerUnit = 1
|
|
222
|
-
upAxis = "Y"
|
|
223
|
-
startTimeCode = 0
|
|
224
|
-
endTimeCode = ${endTimeCode}
|
|
225
|
-
timeCodesPerSecond = 60
|
|
226
|
-
framesPerSecond = 60
|
|
227
|
-
)
|
|
228
|
-
`;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const newLine = '\n';
|
|
232
|
-
const materialRoot = '</StageRoot/Materials';
|
|
233
|
-
class USDWriter {
|
|
234
|
-
str;
|
|
235
|
-
indent;
|
|
236
|
-
constructor() {
|
|
237
|
-
this.str = '';
|
|
238
|
-
this.indent = 0;
|
|
239
|
-
}
|
|
240
|
-
clear() {
|
|
241
|
-
this.str = '';
|
|
242
|
-
this.indent = 0;
|
|
243
|
-
}
|
|
244
|
-
beginBlock(str = undefined, char = '{', createNewLine = true) {
|
|
245
|
-
if (str !== undefined) {
|
|
246
|
-
str = this.applyIndent(str);
|
|
247
|
-
this.str += str;
|
|
248
|
-
if (createNewLine) {
|
|
249
|
-
this.str += newLine;
|
|
250
|
-
this.str += this.applyIndent(char);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
this.str += " " + char;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
this.str += this.applyIndent(char);
|
|
258
|
-
}
|
|
259
|
-
this.str += newLine;
|
|
260
|
-
this.indent += 1;
|
|
261
|
-
}
|
|
262
|
-
closeBlock(char = '}') {
|
|
263
|
-
this.indent -= 1;
|
|
264
|
-
this.str += this.applyIndent(char) + newLine;
|
|
265
|
-
}
|
|
266
|
-
beginArray(str) {
|
|
267
|
-
str = this.applyIndent(str + ' = [');
|
|
268
|
-
this.str += str;
|
|
269
|
-
this.str += newLine;
|
|
270
|
-
this.indent += 1;
|
|
271
|
-
}
|
|
272
|
-
closeArray() {
|
|
273
|
-
this.indent -= 1;
|
|
274
|
-
this.str += this.applyIndent(']') + newLine;
|
|
275
|
-
}
|
|
276
|
-
appendLine(str = '') {
|
|
277
|
-
str = this.applyIndent(str);
|
|
278
|
-
this.str += str;
|
|
279
|
-
this.str += newLine;
|
|
280
|
-
}
|
|
281
|
-
toString() {
|
|
282
|
-
return this.str;
|
|
283
|
-
}
|
|
284
|
-
applyIndent(str) {
|
|
285
|
-
let indents = '';
|
|
286
|
-
for (let i = 0; i < this.indent; i++)
|
|
287
|
-
indents += '\t';
|
|
288
|
-
return indents + str;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
class USDZExporterContext {
|
|
292
|
-
root;
|
|
293
|
-
exporter;
|
|
294
|
-
extensions;
|
|
295
|
-
quickLookCompatible;
|
|
296
|
-
materials;
|
|
297
|
-
textures;
|
|
298
|
-
files;
|
|
299
|
-
document;
|
|
300
|
-
output;
|
|
301
|
-
animations;
|
|
302
|
-
constructor(root, exporter, extensions, quickLookCompatible) {
|
|
303
|
-
this.root = root;
|
|
304
|
-
this.exporter = exporter;
|
|
305
|
-
this.quickLookCompatible = quickLookCompatible;
|
|
306
|
-
if (extensions)
|
|
307
|
-
this.extensions = extensions;
|
|
308
|
-
this.materials = new Map();
|
|
309
|
-
this.textures = {};
|
|
310
|
-
this.files = {};
|
|
311
|
-
this.document = new USDDocument();
|
|
312
|
-
this.output = '';
|
|
313
|
-
this.animations = [];
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
class USDZExporterOptions {
|
|
317
|
-
ar = {
|
|
318
|
-
anchoring: { type: 'plane' },
|
|
319
|
-
planeAnchoring: { alignment: 'horizontal' }
|
|
320
|
-
};
|
|
321
|
-
quickLookCompatible = false;
|
|
322
|
-
extensions = [];
|
|
323
|
-
maxTextureSize = 4096;
|
|
324
|
-
}
|
|
325
|
-
class USDZExporter {
|
|
326
|
-
debug;
|
|
327
|
-
sceneAnchoringOptions = {};
|
|
328
|
-
extensions;
|
|
329
|
-
constructor() {
|
|
330
|
-
this.debug = false;
|
|
331
|
-
}
|
|
332
|
-
getEndTimeCode(animations) {
|
|
333
|
-
let endTimeCode = 0;
|
|
334
|
-
for (const animation of animations) {
|
|
335
|
-
const currentEndTimeCode = animation.duration * 60;
|
|
336
|
-
if (endTimeCode < currentEndTimeCode) {
|
|
337
|
-
endTimeCode = currentEndTimeCode;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return endTimeCode;
|
|
341
|
-
}
|
|
342
|
-
async parse(scene, options = new USDZExporterOptions()) {
|
|
343
|
-
options = Object.assign(new USDZExporterOptions(), options);
|
|
344
|
-
this.sceneAnchoringOptions = options;
|
|
345
|
-
const context = new USDZExporterContext(scene, this, options.extensions, options.quickLookCompatible);
|
|
346
|
-
this.extensions = context.extensions;
|
|
347
|
-
const files = context.files;
|
|
348
|
-
const modelFileName = 'model.usda';
|
|
349
|
-
// model file should be first in USDZ archive so we init it here
|
|
350
|
-
files[modelFileName] = null;
|
|
351
|
-
const materials = context.materials;
|
|
352
|
-
const textures = context.textures;
|
|
353
|
-
await invokeAll(context, 'onBeforeBuildDocument');
|
|
354
|
-
// HACK let's find all skeletons and reparent them to their skelroot / armature / uppermost bone parent
|
|
355
|
-
const reparentings = [];
|
|
356
|
-
scene.traverseVisible(object => {
|
|
357
|
-
if (object.isSkinnedMesh) {
|
|
358
|
-
const bones = object.skeleton.bones;
|
|
359
|
-
const commonAncestor = findCommonAncestor(bones);
|
|
360
|
-
if (commonAncestor) {
|
|
361
|
-
reparentings.push({ object, originalParent: object.parent, newParent: commonAncestor });
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
for (const reparenting of reparentings) {
|
|
366
|
-
const { object, originalParent, newParent } = reparenting;
|
|
367
|
-
if (this.debug)
|
|
368
|
-
console.log("REPARENTING", object, "from", originalParent, "to", newParent);
|
|
369
|
-
newParent.add(object);
|
|
370
|
-
}
|
|
371
|
-
traverseVisible(scene, context.document, context);
|
|
372
|
-
await invokeAll(context, 'onAfterBuildDocument');
|
|
373
|
-
parseDocument(context, () => {
|
|
374
|
-
// injected after stageRoot.
|
|
375
|
-
// TODO property use context/writer instead of string concat
|
|
376
|
-
return buildMaterials(materials, textures, options.quickLookCompatible);
|
|
377
|
-
});
|
|
378
|
-
await invokeAll(context, 'onAfterSerialize');
|
|
379
|
-
// repair the parenting again
|
|
380
|
-
for (const reparenting of reparentings) {
|
|
381
|
-
const { object, originalParent, newParent } = reparenting;
|
|
382
|
-
originalParent.add(object);
|
|
383
|
-
}
|
|
384
|
-
// Moved into parseDocument callback for proper defaultPrim encapsulation
|
|
385
|
-
// context.output += buildMaterials( materials, textures, options.quickLookCompatible );
|
|
386
|
-
const endTimeCode = this.getEndTimeCode(context.animations);
|
|
387
|
-
const header = context.document.buildHeader(endTimeCode);
|
|
388
|
-
const final = header + '\n' + context.output;
|
|
389
|
-
// full output file
|
|
390
|
-
if (this.debug)
|
|
391
|
-
console.log(final);
|
|
392
|
-
files[modelFileName] = fflate.strToU8(final);
|
|
393
|
-
context.output = '';
|
|
394
|
-
const decompressionRenderer = new WebGLRenderer({ antialias: false, alpha: true });
|
|
395
|
-
for (const id in textures) {
|
|
396
|
-
const textureData = textures[id];
|
|
397
|
-
let texture = textureData.texture;
|
|
398
|
-
const isRGBA = formatsWithAlphaChannel.includes(texture.format);
|
|
399
|
-
//@ts-ignore
|
|
400
|
-
if (texture.isCompressedTexture || texture.isRenderTargetTexture) {
|
|
401
|
-
texture = decompressGpuTexture(texture, options.maxTextureSize, decompressionRenderer);
|
|
402
|
-
}
|
|
403
|
-
const canvas = await imageToCanvas(texture.image, textureData.scale, false, options.maxTextureSize).catch(err => {
|
|
404
|
-
console.error("Error converting texture to canvas", texture, err);
|
|
405
|
-
});
|
|
406
|
-
if (canvas) {
|
|
407
|
-
const blob = await canvas.convertToBlob({ type: isRGBA ? 'image/png' : 'image/jpeg', quality: 0.95 });
|
|
408
|
-
files[`textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}`] = new Uint8Array(await blob.arrayBuffer());
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
console.warn('Can`t export texture: ', texture);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
decompressionRenderer.dispose();
|
|
415
|
-
// 64 byte alignment
|
|
416
|
-
// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
|
|
417
|
-
let offset = 0;
|
|
418
|
-
for (const filename in files) {
|
|
419
|
-
const file = files[filename];
|
|
420
|
-
const headerSize = 34 + filename.length;
|
|
421
|
-
offset += headerSize;
|
|
422
|
-
const offsetMod64 = offset & 63;
|
|
423
|
-
if (offsetMod64 !== 4) {
|
|
424
|
-
const padLength = 64 - offsetMod64;
|
|
425
|
-
const padding = new Uint8Array(padLength);
|
|
426
|
-
files[filename] = [file, { extra: { 12345: padding } }];
|
|
427
|
-
}
|
|
428
|
-
offset = file.length;
|
|
429
|
-
}
|
|
430
|
-
return fflate.zipSync(files, { level: 0 });
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function traverseVisible(object, parentModel, context) {
|
|
434
|
-
if (!object.visible)
|
|
435
|
-
return;
|
|
436
|
-
let model = undefined;
|
|
437
|
-
let geometry = undefined;
|
|
438
|
-
let material = undefined;
|
|
439
|
-
if (object instanceof Mesh || object instanceof SkinnedMesh) {
|
|
440
|
-
geometry = object.geometry;
|
|
441
|
-
material = object.material;
|
|
442
|
-
}
|
|
443
|
-
// TODO what should be do with disabled renderers?
|
|
444
|
-
// Here we just assume they're off, and don't export them
|
|
445
|
-
const renderer = GameObject.getComponent(object, Renderer);
|
|
446
|
-
if (renderer && !renderer.enabled) {
|
|
447
|
-
geometry = undefined;
|
|
448
|
-
material = undefined;
|
|
449
|
-
}
|
|
450
|
-
if ((object instanceof Mesh || object instanceof SkinnedMesh) && material && (material instanceof MeshStandardMaterial || material instanceof MeshBasicMaterial)) {
|
|
451
|
-
const name = getObjectId(object);
|
|
452
|
-
const skinnedMeshObject = object instanceof SkinnedMesh ? object : null;
|
|
453
|
-
model = new USDObject(object.uuid, name, object.matrix, geometry, material, undefined, skinnedMeshObject, object.animations);
|
|
454
|
-
}
|
|
455
|
-
else if (object instanceof PerspectiveCamera || object instanceof OrthographicCamera) {
|
|
456
|
-
const name = getObjectId(object);
|
|
457
|
-
model = new USDObject(object.uuid, name, object.matrix, undefined, undefined, object);
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
const name = getObjectId(object);
|
|
461
|
-
model = new USDObject(object.uuid, name, object.matrix, undefined, undefined, undefined, undefined, object.animations);
|
|
462
|
-
}
|
|
463
|
-
if (model) {
|
|
464
|
-
if (parentModel) {
|
|
465
|
-
parentModel.add(model);
|
|
466
|
-
}
|
|
467
|
-
parentModel = model;
|
|
468
|
-
if (context.extensions) {
|
|
469
|
-
for (const ext of context.extensions) {
|
|
470
|
-
if (ext.onExportObject)
|
|
471
|
-
ext.onExportObject.call(ext, object, model, context);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
const name = getObjectId(object);
|
|
477
|
-
const empty = new USDObject(object.uuid, name, object.matrix);
|
|
478
|
-
if (parentModel) {
|
|
479
|
-
parentModel.add(empty);
|
|
480
|
-
}
|
|
481
|
-
parentModel = empty;
|
|
482
|
-
}
|
|
483
|
-
for (const ch of object.children) {
|
|
484
|
-
traverseVisible(ch, parentModel, context);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
async function parseDocument(context, afterStageRoot) {
|
|
488
|
-
for (const child of context.document.children) {
|
|
489
|
-
addResources(child, context);
|
|
490
|
-
}
|
|
491
|
-
const writer = new USDWriter();
|
|
492
|
-
writer.beginBlock(`def Xform "${context.document.name}"`);
|
|
493
|
-
writer.beginBlock(`def Scope "Scenes" (
|
|
494
|
-
kind = "sceneLibrary"
|
|
495
|
-
)`);
|
|
496
|
-
writer.beginBlock(`def Xform "Scene" (
|
|
497
|
-
apiSchemas = ["Preliminary_AnchoringAPI"]
|
|
498
|
-
customData = {
|
|
499
|
-
bool preliminary_collidesWithEnvironment = 0
|
|
500
|
-
string sceneName = "Scene"
|
|
501
|
-
}
|
|
502
|
-
sceneName = "Scene"
|
|
503
|
-
)`);
|
|
504
|
-
writer.appendLine(`token preliminary:anchoring:type = "${context.exporter.sceneAnchoringOptions.ar.anchoring.type}"`);
|
|
505
|
-
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'plane')
|
|
506
|
-
writer.appendLine(`token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.ar.planeAnchoring.alignment}"`);
|
|
507
|
-
// bit hacky as we don't have a callback here yet. Relies on the fact that the image is named identical in the ImageTracking extension.
|
|
508
|
-
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'image')
|
|
509
|
-
writer.appendLine(`rel preliminary:imageAnchoring:referenceImage = </${context.document.name}/Scenes/Scene/AnchoringReferenceImage>`);
|
|
510
|
-
writer.appendLine();
|
|
511
|
-
for (const child of context.document.children) {
|
|
512
|
-
buildXform(child, writer, context);
|
|
513
|
-
}
|
|
514
|
-
invokeAll(context, 'onAfterHierarchy', writer);
|
|
515
|
-
writer.closeBlock();
|
|
516
|
-
writer.closeBlock();
|
|
517
|
-
writer.appendLine(afterStageRoot());
|
|
518
|
-
writer.closeBlock();
|
|
519
|
-
context.output += writer.toString();
|
|
520
|
-
}
|
|
521
|
-
function addResources(object, context) {
|
|
522
|
-
if (object == null) {
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
const geometry = object.geometry;
|
|
526
|
-
const material = object.material;
|
|
527
|
-
if (geometry) {
|
|
528
|
-
if (material && ('isMeshStandardMaterial' in material && material.isMeshStandardMaterial || 'isMeshBasicMaterial' in material && material.isMeshBasicMaterial)) { // TODO convert unlit to lit+emissive
|
|
529
|
-
const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
|
|
530
|
-
if (!(geometryFileName in context.files)) {
|
|
531
|
-
const meshObject = buildMeshObject(geometry, object.skinnedMesh?.skeleton?.bones);
|
|
532
|
-
context.files[geometryFileName] = buildUSDFileAsString(meshObject, context);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
console.warn('THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', name);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
if (material) {
|
|
540
|
-
if (!(material.uuid in context.materials)) {
|
|
541
|
-
context.materials[material.uuid] = material;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
for (const ch of object.children) {
|
|
545
|
-
addResources(ch, context);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
async function invokeAll(context, name, writer = null) {
|
|
549
|
-
if (context.extensions) {
|
|
550
|
-
for (const ext of context.extensions) {
|
|
551
|
-
if (!ext)
|
|
552
|
-
continue;
|
|
553
|
-
if (typeof ext[name] === 'function') {
|
|
554
|
-
const method = ext[name];
|
|
555
|
-
const res = method.call(ext, context, writer);
|
|
556
|
-
if (res instanceof Promise) {
|
|
557
|
-
await res;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
let _renderer = null;
|
|
564
|
-
let fullscreenQuadGeometry;
|
|
565
|
-
let fullscreenQuadMaterial;
|
|
566
|
-
let fullscreenQuad;
|
|
567
|
-
function decompressGpuTexture(texture, maxTextureSize = Infinity, renderer = null) {
|
|
568
|
-
if (!fullscreenQuadGeometry)
|
|
569
|
-
fullscreenQuadGeometry = new PlaneGeometry(2, 2, 1, 1);
|
|
570
|
-
if (!fullscreenQuadMaterial)
|
|
571
|
-
fullscreenQuadMaterial = new ShaderMaterial({
|
|
572
|
-
uniforms: { blitTexture: new Uniform(texture) },
|
|
573
|
-
vertexShader: `
|
|
574
|
-
varying vec2 vUv;
|
|
575
|
-
void main(){
|
|
576
|
-
vUv = uv;
|
|
577
|
-
vUv.y = 1. - vUv.y;
|
|
578
|
-
gl_Position = vec4(position.xy * 1.0,0.,.999999);
|
|
579
|
-
}`,
|
|
580
|
-
fragmentShader: `
|
|
581
|
-
uniform sampler2D blitTexture;
|
|
582
|
-
varying vec2 vUv;
|
|
583
|
-
|
|
584
|
-
void main(){
|
|
585
|
-
gl_FragColor = vec4(vUv.xy, 0, 1);
|
|
586
|
-
|
|
587
|
-
#ifdef IS_SRGB
|
|
588
|
-
gl_FragColor = LinearTosRGB( texture2D( blitTexture, vUv) );
|
|
589
|
-
#else
|
|
590
|
-
gl_FragColor = texture2D( blitTexture, vUv);
|
|
591
|
-
#endif
|
|
592
|
-
gl_FragColor.rgb *= gl_FragColor.a;
|
|
593
|
-
}`
|
|
594
|
-
});
|
|
595
|
-
fullscreenQuadMaterial.uniforms.blitTexture.value = texture;
|
|
596
|
-
fullscreenQuadMaterial.defines.IS_SRGB = texture.colorSpace == SRGBColorSpace;
|
|
597
|
-
fullscreenQuadMaterial.needsUpdate = true;
|
|
598
|
-
if (!fullscreenQuad) {
|
|
599
|
-
fullscreenQuad = new Mesh(fullscreenQuadGeometry, fullscreenQuadMaterial);
|
|
600
|
-
fullscreenQuad.frustumCulled = false;
|
|
601
|
-
}
|
|
602
|
-
const _camera = new PerspectiveCamera();
|
|
603
|
-
const _scene = new Scene();
|
|
604
|
-
_scene.add(fullscreenQuad);
|
|
605
|
-
if (!renderer) {
|
|
606
|
-
renderer = _renderer = new WebGLRenderer({ antialias: false, alpha: true });
|
|
607
|
-
}
|
|
608
|
-
renderer.setSize(Math.min(texture.image.width, maxTextureSize), Math.min(texture.image.height, maxTextureSize));
|
|
609
|
-
renderer.clear();
|
|
610
|
-
renderer.render(_scene, _camera);
|
|
611
|
-
const readableTexture = new Texture(renderer.domElement);
|
|
612
|
-
readableTexture.minFilter = texture.minFilter;
|
|
613
|
-
readableTexture.magFilter = texture.magFilter;
|
|
614
|
-
readableTexture.wrapS = texture.wrapS;
|
|
615
|
-
readableTexture.wrapT = texture.wrapT;
|
|
616
|
-
readableTexture.name = texture.name;
|
|
617
|
-
if (_renderer) {
|
|
618
|
-
_renderer.dispose();
|
|
619
|
-
_renderer = null;
|
|
620
|
-
}
|
|
621
|
-
return readableTexture;
|
|
622
|
-
}
|
|
623
|
-
function isImageBitmap(image) {
|
|
624
|
-
return (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement) ||
|
|
625
|
-
(typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
|
|
626
|
-
(typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) ||
|
|
627
|
-
(typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap);
|
|
628
|
-
}
|
|
629
|
-
async function imageToCanvas(image, color = undefined, flipY = false, maxTextureSize = 4096) {
|
|
630
|
-
if (isImageBitmap(image)) {
|
|
631
|
-
// max. canvas size on Safari is still 4096x4096
|
|
632
|
-
const scale = maxTextureSize / Math.max(image.width, image.height);
|
|
633
|
-
const canvas = new OffscreenCanvas(image.width * Math.min(1, scale), image.height * Math.min(1, scale));
|
|
634
|
-
const context = canvas.getContext('2d');
|
|
635
|
-
if (!context)
|
|
636
|
-
throw new Error('Could not get canvas 2D context');
|
|
637
|
-
if (flipY === true) {
|
|
638
|
-
context.translate(0, canvas.height);
|
|
639
|
-
context.scale(1, -1);
|
|
640
|
-
}
|
|
641
|
-
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
642
|
-
// Currently only used to apply opacity scale since QuickLook and usdview don't support that yet
|
|
643
|
-
if (color !== undefined) {
|
|
644
|
-
const r = color.x;
|
|
645
|
-
const g = color.y;
|
|
646
|
-
const b = color.z;
|
|
647
|
-
const a = color.w;
|
|
648
|
-
const imagedata = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
649
|
-
const data = imagedata.data;
|
|
650
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
651
|
-
data[i + 0] = data[i + 0] * r;
|
|
652
|
-
data[i + 1] = data[i + 1] * g;
|
|
653
|
-
data[i + 2] = data[i + 2] * b;
|
|
654
|
-
data[i + 3] = data[i + 3] * a;
|
|
655
|
-
}
|
|
656
|
-
context.putImageData(imagedata, 0, 0);
|
|
657
|
-
}
|
|
658
|
-
return canvas;
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
throw new Error('THREE.USDZExporter: No valid image data found. Unable to process texture.');
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
//
|
|
665
|
-
const PRECISION = 7;
|
|
666
|
-
function buildHeader() {
|
|
667
|
-
return `#usda 1.0
|
|
668
|
-
(
|
|
669
|
-
customLayerData = {
|
|
670
|
-
string creator = "Needle Engine USDZExporter"
|
|
671
|
-
}
|
|
672
|
-
metersPerUnit = 1
|
|
673
|
-
upAxis = "Y"
|
|
674
|
-
)
|
|
675
|
-
`;
|
|
676
|
-
}
|
|
677
|
-
function buildUSDFileAsString(dataToInsert, _context) {
|
|
678
|
-
let output = buildHeader();
|
|
679
|
-
output += dataToInsert;
|
|
680
|
-
return fflate.strToU8(output);
|
|
681
|
-
}
|
|
682
|
-
function getObjectId(object) {
|
|
683
|
-
return object.name.replace(/[-<>\(\)\[\]§$%&\/\\\=\?\,\;]/g, '') + '_' + object.id;
|
|
684
|
-
}
|
|
685
|
-
function getBoneName(bone) {
|
|
686
|
-
return makeNameSafe(bone.name || 'bone_' + bone.uuid);
|
|
687
|
-
}
|
|
688
|
-
function getPathToSkeleton(bone, assumedRoot) {
|
|
689
|
-
let path = getBoneName(bone);
|
|
690
|
-
let current = bone.parent;
|
|
691
|
-
while (current && current !== assumedRoot) {
|
|
692
|
-
path = getBoneName(current) + '/' + path;
|
|
693
|
-
current = current.parent;
|
|
694
|
-
}
|
|
695
|
-
return path;
|
|
696
|
-
}
|
|
697
|
-
// Xform
|
|
698
|
-
export function buildXform(model, writer, context) {
|
|
699
|
-
if (model == null)
|
|
700
|
-
return;
|
|
701
|
-
const matrix = model.matrix;
|
|
702
|
-
const geometry = model.geometry;
|
|
703
|
-
const material = model.material;
|
|
704
|
-
const camera = model.camera;
|
|
705
|
-
const name = model.name;
|
|
706
|
-
if (model.animations) {
|
|
707
|
-
for (const animation of model.animations) {
|
|
708
|
-
context.animations.push(animation);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
const transform = buildMatrix(matrix);
|
|
712
|
-
if (matrix.determinant() < 0) {
|
|
713
|
-
console.warn('THREE.USDZExporter: USDZ does not support negative scales', name);
|
|
714
|
-
}
|
|
715
|
-
const isSkinnedMesh = geometry && geometry.isBufferGeometry && geometry.attributes.skinIndex !== undefined && geometry.attributes.skinIndex.count > 0;
|
|
716
|
-
const objType = isSkinnedMesh ? 'SkelRoot' : 'Xform';
|
|
717
|
-
const apiSchemas = isSkinnedMesh ? '"MaterialBindingAPI", "SkelBindingAPI"' : '"MaterialBindingAPI"';
|
|
718
|
-
if (geometry) {
|
|
719
|
-
writer.beginBlock(`def ${objType} "${name}"`, "(", false);
|
|
720
|
-
if (context.quickLookCompatible && material && material.side === DoubleSide)
|
|
721
|
-
writer.appendLine(`prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry_doubleSided>`);
|
|
722
|
-
else
|
|
723
|
-
writer.appendLine(`prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>`);
|
|
724
|
-
writer.appendLine(`prepend apiSchemas = [${apiSchemas}]`);
|
|
725
|
-
writer.closeBlock(")");
|
|
726
|
-
writer.beginBlock();
|
|
727
|
-
}
|
|
728
|
-
else if (camera)
|
|
729
|
-
writer.beginBlock(`def Camera "${name}"`);
|
|
730
|
-
else
|
|
731
|
-
writer.beginBlock(`def Xform "${name}"`);
|
|
732
|
-
if (geometry && material) {
|
|
733
|
-
writer.appendLine(`rel material:binding = </StageRoot/Materials/Material_${material.id}>`);
|
|
734
|
-
// Turns out QuickLook / RealityKit doesn't support the doubleSided attribute, so we
|
|
735
|
-
// work around that by emitting additional indices above, and then we shouldn't emit the attribute either as geometry is
|
|
736
|
-
// already doubleSided then.
|
|
737
|
-
if (!context.quickLookCompatible && material.side === DoubleSide) {
|
|
738
|
-
// double-sided is a mesh property in USD, we can apply it as `over` here
|
|
739
|
-
writer.beginBlock(`over "Geometry" `);
|
|
740
|
-
writer.appendLine(`uniform bool doubleSided = 1`);
|
|
741
|
-
writer.closeBlock();
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
if (isSkinnedMesh) {
|
|
745
|
-
writer.appendLine(`rel skel:skeleton = <Rig>`);
|
|
746
|
-
writer.appendLine(`rel skel:animationSource = <Rig/_anim>`);
|
|
747
|
-
writer.appendLine(`matrix4d xformOp:transform = ${buildMatrix(new Matrix4())}`); // always identity / in world space
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
writer.appendLine(`matrix4d xformOp:transform = ${transform}`);
|
|
751
|
-
}
|
|
752
|
-
writer.appendLine('uniform token[] xformOpOrder = ["xformOp:transform"]');
|
|
753
|
-
if (camera) {
|
|
754
|
-
if ('isOrthographicCamera' in camera && camera.isOrthographicCamera) {
|
|
755
|
-
writer.appendLine(`float2 clippingRange = (${camera.near}, ${camera.far})`);
|
|
756
|
-
writer.appendLine(`float horizontalAperture = ${((Math.abs(camera.left) + Math.abs(camera.right)) * 10).toPrecision(PRECISION)}`);
|
|
757
|
-
writer.appendLine(`float verticalAperture = ${((Math.abs(camera.top) + Math.abs(camera.bottom)) * 10).toPrecision(PRECISION)}`);
|
|
758
|
-
writer.appendLine('token projection = "orthographic"');
|
|
759
|
-
}
|
|
760
|
-
else if ('isPerspectiveCamera' in camera && camera.isPerspectiveCamera) {
|
|
761
|
-
writer.appendLine(`float2 clippingRange = (${camera.near.toPrecision(PRECISION)}, ${camera.far.toPrecision(PRECISION)})`);
|
|
762
|
-
writer.appendLine(`float focalLength = ${camera.getFocalLength().toPrecision(PRECISION)}`);
|
|
763
|
-
writer.appendLine(`float focusDistance = ${camera.focus.toPrecision(PRECISION)}`);
|
|
764
|
-
writer.appendLine(`float horizontalAperture = ${camera.getFilmWidth().toPrecision(PRECISION)}`);
|
|
765
|
-
writer.appendLine('token projection = "perspective"');
|
|
766
|
-
writer.appendLine(`float verticalAperture = ${camera.getFilmHeight().toPrecision(PRECISION)}`);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
if (model.onSerialize) {
|
|
770
|
-
model.onSerialize(writer, context);
|
|
771
|
-
}
|
|
772
|
-
if (model.children) {
|
|
773
|
-
writer.appendLine();
|
|
774
|
-
for (const ch of model.children) {
|
|
775
|
-
buildXform(ch, writer, context);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
writer.closeBlock();
|
|
779
|
-
}
|
|
780
|
-
function fn(num) {
|
|
781
|
-
return Number.isInteger(num) ? num.toString() : num.toFixed(10);
|
|
782
|
-
}
|
|
783
|
-
function buildMatrix(matrix) {
|
|
784
|
-
const array = matrix.elements;
|
|
785
|
-
return `( ${buildMatrixRow(array, 0)}, ${buildMatrixRow(array, 4)}, ${buildMatrixRow(array, 8)}, ${buildMatrixRow(array, 12)} )`;
|
|
786
|
-
}
|
|
787
|
-
function buildMatrixRow(array, offset) {
|
|
788
|
-
return `(${fn(array[offset + 0])}, ${fn(array[offset + 1])}, ${fn(array[offset + 2])}, ${fn(array[offset + 3])})`;
|
|
789
|
-
}
|
|
790
|
-
// Mesh
|
|
791
|
-
function buildMeshObject(geometry, bonesArray = []) {
|
|
792
|
-
const mesh = buildMesh(geometry, bonesArray);
|
|
793
|
-
return `
|
|
794
|
-
def "Geometry"
|
|
795
|
-
${mesh}
|
|
796
|
-
`;
|
|
797
|
-
}
|
|
798
|
-
function buildMesh(geometry, bones = []) {
|
|
799
|
-
const name = 'Geometry';
|
|
800
|
-
const attributes = geometry.attributes;
|
|
801
|
-
const count = attributes.position.count;
|
|
802
|
-
const hasBones = bones && bones.length > 0;
|
|
803
|
-
// We need to sort bones and all skinning data by path –
|
|
804
|
-
// Neither glTF nor three.js care, but in USD they must be sorted
|
|
805
|
-
// since the array defines the virtual hierarchy and is evaluated in that order
|
|
806
|
-
const sortedBones = [];
|
|
807
|
-
const indexMapping = [];
|
|
808
|
-
let sortedSkinIndex = new Array();
|
|
809
|
-
let sortedSkinIndexAttribute = attributes.skinIndex;
|
|
810
|
-
let bonesArray = "";
|
|
811
|
-
if (hasBones) {
|
|
812
|
-
for (const index in bones) {
|
|
813
|
-
sortedBones.push({ bone: bones[index], index: parseInt(index) });
|
|
814
|
-
}
|
|
815
|
-
// add structural nodes to the list of bones
|
|
816
|
-
for (const structuralNode of findStructuralNodesInBoneHierarchy(bones)) {
|
|
817
|
-
sortedBones.push({ bone: structuralNode, index: sortedBones.length });
|
|
818
|
-
}
|
|
819
|
-
// sort bones by path – need to be sorted in the same order as during mesh export
|
|
820
|
-
const assumedRoot = bones[0].parent;
|
|
821
|
-
sortedBones.sort((a, b) => getPathToSkeleton(a.bone, assumedRoot) > getPathToSkeleton(b.bone, assumedRoot) ? 1 : -1);
|
|
822
|
-
bonesArray = sortedBones.map(x => "\"" + getPathToSkeleton(x.bone, assumedRoot) + "\"").join(', ');
|
|
823
|
-
// TODO we can probably skip the expensive attribute re-ordering if the bones were already in a correct order.
|
|
824
|
-
// That doesn't mean that they are strictly sorted by path – just that all parents strictly need to come first.
|
|
825
|
-
// build index mapping
|
|
826
|
-
for (const i in sortedBones) {
|
|
827
|
-
indexMapping[sortedBones[i].index] = parseInt(i);
|
|
828
|
-
}
|
|
829
|
-
// remap skin index attributes
|
|
830
|
-
const skinIndex = attributes.skinIndex;
|
|
831
|
-
sortedSkinIndex = new Array();
|
|
832
|
-
for (let i = 0; i < skinIndex.count; i++) {
|
|
833
|
-
const x = skinIndex.getX(i);
|
|
834
|
-
const y = skinIndex.getY(i);
|
|
835
|
-
const z = skinIndex.getZ(i);
|
|
836
|
-
const w = skinIndex.getW(i);
|
|
837
|
-
sortedSkinIndex.push(indexMapping[x], indexMapping[y], indexMapping[z], indexMapping[w]);
|
|
838
|
-
}
|
|
839
|
-
// turn it back into an attribute so the rest of the code doesn't need to learn a new thing
|
|
840
|
-
sortedSkinIndexAttribute = new BufferAttribute(new Uint16Array(sortedSkinIndex), 4);
|
|
841
|
-
}
|
|
842
|
-
const isSkinnedMesh = attributes.skinWeight && attributes.skinIndex;
|
|
843
|
-
return `
|
|
844
|
-
{
|
|
845
|
-
def Mesh "${name}" ${isSkinnedMesh ? `(
|
|
846
|
-
prepend apiSchemas = ["SkelBindingAPI"]
|
|
847
|
-
)` : ''}
|
|
848
|
-
{
|
|
849
|
-
int[] faceVertexCounts = [${buildMeshVertexCount(geometry)}]
|
|
850
|
-
int[] faceVertexIndices = [${buildMeshVertexIndices(geometry)}]
|
|
851
|
-
normal3f[] normals = [${buildVector3Array(attributes.normal, count)}] (
|
|
852
|
-
interpolation = "vertex"
|
|
853
|
-
)
|
|
854
|
-
point3f[] points = [${buildVector3Array(attributes.position, count)}]
|
|
855
|
-
${attributes.uv ?
|
|
856
|
-
`texCoord2f[] primvars:st = [${buildVector2Array(attributes.uv, count)}] (
|
|
857
|
-
interpolation = "vertex"
|
|
858
|
-
)` : ''}
|
|
859
|
-
${attributes.uv2 ?
|
|
860
|
-
`texCoord2f[] primvars:st2 = [${buildVector2Array(attributes.uv2, count)}] (
|
|
861
|
-
interpolation = "vertex"
|
|
862
|
-
)` : ''}
|
|
863
|
-
${isSkinnedMesh ?
|
|
864
|
-
`matrix4d primvars:skel:geomBindTransform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) ) (
|
|
865
|
-
elementSize = 1
|
|
866
|
-
interpolation = "constant"
|
|
867
|
-
)` : ''}
|
|
868
|
-
${attributes.skinIndex ?
|
|
869
|
-
`int[] primvars:skel:jointIndices = [${buildVector4Array(sortedSkinIndexAttribute, true)}] (
|
|
870
|
-
elementSize = 4
|
|
871
|
-
interpolation = "vertex"
|
|
872
|
-
)` : ''}
|
|
873
|
-
${attributes.skinWeight ?
|
|
874
|
-
`float[] primvars:skel:jointWeights = [${buildVector4Array(attributes.skinWeight)}] (
|
|
875
|
-
elementSize = 4
|
|
876
|
-
interpolation = "vertex"
|
|
877
|
-
)` : ''}
|
|
878
|
-
${hasBones ?
|
|
879
|
-
//`uniform token[] skel:blendShapes
|
|
880
|
-
`uniform token[] skel:joints = [${bonesArray}]` : ''}
|
|
881
|
-
uniform token subdivisionScheme = "none"
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
# This is a workaround for QuickLook/RealityKit not supporting the doubleSided attribute. We're adding a second
|
|
886
|
-
# geometry definition here, that uses the same mesh data but appends extra faces with reversed winding order.
|
|
887
|
-
def "${name}_doubleSided" (
|
|
888
|
-
prepend references = </Geometry>
|
|
889
|
-
)
|
|
890
|
-
{
|
|
891
|
-
over "Geometry"
|
|
892
|
-
{
|
|
893
|
-
int[] faceVertexCounts = [${buildMeshVertexCount(geometry) + ", " + buildMeshVertexCount(geometry)}]
|
|
894
|
-
int[] faceVertexIndices = [${buildMeshVertexIndices(geometry) + ", " + buildMeshVertexIndices(geometry, true)}]
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
`;
|
|
898
|
-
}
|
|
899
|
-
function buildMeshVertexCount(geometry) {
|
|
900
|
-
const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
|
|
901
|
-
return Array(count / 3).fill(3).join(', ');
|
|
902
|
-
}
|
|
903
|
-
function buildMeshVertexIndices(geometry, reverseWinding = false) {
|
|
904
|
-
const index = geometry.index;
|
|
905
|
-
const array = [];
|
|
906
|
-
if (index !== null) {
|
|
907
|
-
for (let i = 0; i < index.count; i++) {
|
|
908
|
-
let val = i;
|
|
909
|
-
if (reverseWinding)
|
|
910
|
-
val = i % 3 === 0 ? i + 2 : i % 3 === 2 ? i - 2 : i;
|
|
911
|
-
array.push(index.getX(val));
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
else {
|
|
915
|
-
const length = geometry.attributes.position.count;
|
|
916
|
-
for (let i = 0; i < length; i++) {
|
|
917
|
-
let val = i;
|
|
918
|
-
if (reverseWinding)
|
|
919
|
-
val = i % 3 === 0 ? i + 2 : i % 3 === 2 ? i - 2 : i;
|
|
920
|
-
array.push(val);
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
return array.join(', ');
|
|
924
|
-
}
|
|
925
|
-
function buildVector3Array(attribute, count) {
|
|
926
|
-
if (attribute === undefined) {
|
|
927
|
-
console.warn('USDZExporter: Normals missing.');
|
|
928
|
-
return Array(count).fill('(0, 0, 0)').join(', ');
|
|
929
|
-
}
|
|
930
|
-
const array = [];
|
|
931
|
-
for (let i = 0; i < attribute.count; i++) {
|
|
932
|
-
const x = attribute.getX(i);
|
|
933
|
-
const y = attribute.getY(i);
|
|
934
|
-
const z = attribute.getZ(i);
|
|
935
|
-
array.push(`(${x.toPrecision(PRECISION)}, ${y.toPrecision(PRECISION)}, ${z.toPrecision(PRECISION)})`);
|
|
936
|
-
}
|
|
937
|
-
return array.join(', ');
|
|
938
|
-
}
|
|
939
|
-
function buildVector4Array(attribute, ints = false) {
|
|
940
|
-
const array = [];
|
|
941
|
-
for (let i = 0; i < attribute.count; i++) {
|
|
942
|
-
const x = attribute.getX(i);
|
|
943
|
-
const y = attribute.getY(i);
|
|
944
|
-
const z = attribute.getZ(i);
|
|
945
|
-
const w = attribute.getW(i);
|
|
946
|
-
array.push(`${ints ? x : x.toPrecision(PRECISION)}`);
|
|
947
|
-
array.push(`${ints ? y : y.toPrecision(PRECISION)}`);
|
|
948
|
-
array.push(`${ints ? z : z.toPrecision(PRECISION)}`);
|
|
949
|
-
array.push(`${ints ? w : w.toPrecision(PRECISION)}`);
|
|
950
|
-
}
|
|
951
|
-
return array.join(', ');
|
|
952
|
-
}
|
|
953
|
-
function buildVector2Array(attribute, count) {
|
|
954
|
-
if (attribute === undefined) {
|
|
955
|
-
console.warn('USDZExporter: UVs missing.');
|
|
956
|
-
return Array(count).fill('(0, 0)').join(', ');
|
|
957
|
-
}
|
|
958
|
-
const array = [];
|
|
959
|
-
for (let i = 0; i < attribute.count; i++) {
|
|
960
|
-
const x = attribute.getX(i);
|
|
961
|
-
const y = attribute.getY(i);
|
|
962
|
-
array.push(`(${x.toPrecision(PRECISION)}, ${1 - y.toPrecision(PRECISION)})`);
|
|
963
|
-
}
|
|
964
|
-
return array.join(', ');
|
|
965
|
-
}
|
|
966
|
-
// Materials
|
|
967
|
-
function buildMaterials(materials, textures, quickLookCompatible = false) {
|
|
968
|
-
const array = [];
|
|
969
|
-
for (const uuid in materials) {
|
|
970
|
-
const material = materials[uuid];
|
|
971
|
-
array.push(buildMaterial(material, textures, quickLookCompatible));
|
|
972
|
-
}
|
|
973
|
-
return `def "Materials"
|
|
974
|
-
{
|
|
975
|
-
${array.join('')}
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
`;
|
|
979
|
-
}
|
|
980
|
-
function buildMaterial(material, textures, quickLookCompatible = false) {
|
|
981
|
-
// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
|
|
982
|
-
const pad = ' ';
|
|
983
|
-
const inputs = [];
|
|
984
|
-
const samplers = [];
|
|
985
|
-
function buildTexture(texture, mapType, color = undefined, opacity = undefined) {
|
|
986
|
-
const id = texture.id + (color ? '_' + color.getHexString() : '') + (opacity !== undefined ? '_' + opacity : '');
|
|
987
|
-
// Seems neither QuickLook nor usdview support scale/bias on .a values, so we need to bake opacity multipliers into
|
|
988
|
-
// the texture. This is not ideal.
|
|
989
|
-
const opacityIsAppliedToTextureAndNotAsScale = quickLookCompatible && opacity !== undefined;
|
|
990
|
-
const scaleToApply = opacityIsAppliedToTextureAndNotAsScale ? new Vector4(1, 1, 1, opacity) : undefined;
|
|
991
|
-
textures[id] = { texture, scale: scaleToApply };
|
|
992
|
-
const uv = texture.channel > 0 ? 'st' + texture.channel : 'st';
|
|
993
|
-
const isRGBA = formatsWithAlphaChannel.includes(texture.format);
|
|
994
|
-
const WRAPPINGS = {
|
|
995
|
-
1000: 'repeat',
|
|
996
|
-
1001: 'clamp',
|
|
997
|
-
1002: 'mirror' // MirroredRepeatWrapping
|
|
998
|
-
};
|
|
999
|
-
const repeat = texture.repeat.clone();
|
|
1000
|
-
const offset = texture.offset.clone();
|
|
1001
|
-
const rotation = texture.rotation;
|
|
1002
|
-
// rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
|
|
1003
|
-
const xRotationOffset = Math.sin(rotation);
|
|
1004
|
-
const yRotationOffset = Math.cos(rotation);
|
|
1005
|
-
// texture coordinates start in the opposite corner, need to correct
|
|
1006
|
-
offset.y = 1 - offset.y - repeat.y;
|
|
1007
|
-
// turns out QuickLook is buggy and interprets texture repeat inverted.
|
|
1008
|
-
// Apple Feedback: FB10036297 and FB11442287
|
|
1009
|
-
if (quickLookCompatible) {
|
|
1010
|
-
// This is NOT correct yet in QuickLook, but comes close for a range of models.
|
|
1011
|
-
// It becomes more incorrect the bigger the offset is
|
|
1012
|
-
offset.x = offset.x / repeat.x;
|
|
1013
|
-
offset.y = offset.y / repeat.y;
|
|
1014
|
-
offset.x += xRotationOffset / repeat.x;
|
|
1015
|
-
offset.y += yRotationOffset - 1;
|
|
1016
|
-
}
|
|
1017
|
-
else {
|
|
1018
|
-
// results match glTF results exactly. verified correct in usdview.
|
|
1019
|
-
offset.x += xRotationOffset * repeat.x;
|
|
1020
|
-
offset.y += (1 - yRotationOffset) * repeat.y;
|
|
1021
|
-
}
|
|
1022
|
-
const needsTextureTransform = (repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0);
|
|
1023
|
-
const textureTransformInput = `${materialRoot}/Material_${material.id}/${'uvReader_' + uv}.outputs:result>`;
|
|
1024
|
-
const textureTransformOutput = `${materialRoot}/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
|
1025
|
-
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
|
1026
|
-
const needsNormalScaleAndBias = mapType === 'normal';
|
|
1027
|
-
const normalScale = material instanceof MeshStandardMaterial ? (material.normalScale ? material.normalScale.x * 2 : 2) : 2;
|
|
1028
|
-
const normalScaleValueString = normalScale.toFixed(PRECISION);
|
|
1029
|
-
const normalBiasString = (-1 * (normalScale / 2)).toFixed(PRECISION);
|
|
1030
|
-
const normalBiasZString = (1 - normalScale).toFixed(PRECISION);
|
|
1031
|
-
return `
|
|
1032
|
-
${needsTextureTransform ? `def Shader "Transform2d_${mapType}" (
|
|
1033
|
-
sdrMetadata = {
|
|
1034
|
-
string role = "math"
|
|
1035
|
-
}
|
|
1036
|
-
)
|
|
1037
|
-
{
|
|
1038
|
-
uniform token info:id = "UsdTransform2d"
|
|
1039
|
-
float2 inputs:in.connect = ${textureTransformInput}
|
|
1040
|
-
float2 inputs:scale = ${buildVector2(repeat)}
|
|
1041
|
-
float2 inputs:translation = ${buildVector2(offset)}
|
|
1042
|
-
float inputs:rotation = ${(rotation / Math.PI * 180).toFixed(PRECISION)}
|
|
1043
|
-
float2 outputs:result
|
|
1044
|
-
}
|
|
1045
|
-
` : ''}
|
|
1046
|
-
def Shader "Texture_${texture.id}_${mapType}"
|
|
1047
|
-
{
|
|
1048
|
-
uniform token info:id = "UsdUVTexture"
|
|
1049
|
-
asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
|
|
1050
|
-
token inputs:sourceColorSpace = "${texture.colorSpace === 'srgb' ? 'sRGB' : 'raw'}"
|
|
1051
|
-
float2 inputs:st.connect = ${needsTextureTransform ? textureTransformOutput : textureTransformInput}
|
|
1052
|
-
${needsTextureScale ? `
|
|
1053
|
-
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${(opacity !== undefined && !opacityIsAppliedToTextureAndNotAsScale) ? opacity : '1'})
|
|
1054
|
-
` : ``}
|
|
1055
|
-
${needsNormalScaleAndBias ? `
|
|
1056
|
-
float4 inputs:scale = (${normalScaleValueString}, ${normalScaleValueString}, ${normalScaleValueString}, 1)
|
|
1057
|
-
float4 inputs:bias = (${normalBiasString}, ${normalBiasString}, ${normalBiasZString}, 0)
|
|
1058
|
-
` : ``}
|
|
1059
|
-
token inputs:wrapS = "${WRAPPINGS[texture.wrapS]}"
|
|
1060
|
-
token inputs:wrapT = "${WRAPPINGS[texture.wrapT]}"
|
|
1061
|
-
float outputs:r
|
|
1062
|
-
float outputs:g
|
|
1063
|
-
float outputs:b
|
|
1064
|
-
float3 outputs:rgb
|
|
1065
|
-
${material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : ''}
|
|
1066
|
-
}`;
|
|
1067
|
-
}
|
|
1068
|
-
let effectiveOpacity = (material.transparent || material.alphaTest) ? material.opacity : 1;
|
|
1069
|
-
if (material instanceof MeshPhysicalMaterial && material.transmission !== undefined) {
|
|
1070
|
-
// TODO does not help when a roughnessMap is used
|
|
1071
|
-
effectiveOpacity *= (1 - material.transmission * (1 - (material.roughness * 0.5)));
|
|
1072
|
-
}
|
|
1073
|
-
if (material.map !== null) {
|
|
1074
|
-
inputs.push(`${pad}color3f inputs:diffuseColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:rgb>`);
|
|
1075
|
-
if (material.transparent) {
|
|
1076
|
-
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
1077
|
-
}
|
|
1078
|
-
else if (material.alphaTest > 0.0) {
|
|
1079
|
-
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
1080
|
-
inputs.push(`${pad}float inputs:opacityThreshold = ${material.alphaTest}`);
|
|
1081
|
-
}
|
|
1082
|
-
samplers.push(buildTexture(material.map, 'diffuse', material.color, effectiveOpacity));
|
|
1083
|
-
}
|
|
1084
|
-
else {
|
|
1085
|
-
inputs.push(`${pad}color3f inputs:diffuseColor = ${buildColor(material.color)}`);
|
|
1086
|
-
}
|
|
1087
|
-
if (material.aoMap) {
|
|
1088
|
-
inputs.push(`${pad}float inputs:occlusion.connect = ${materialRoot}/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>`);
|
|
1089
|
-
samplers.push(buildTexture(material.aoMap, 'occlusion'));
|
|
1090
|
-
}
|
|
1091
|
-
if (material.alphaMap) {
|
|
1092
|
-
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>`);
|
|
1093
|
-
inputs.push(`${pad}float inputs:opacityThreshold = 0.0001`);
|
|
1094
|
-
samplers.push(buildTexture(material.alphaMap, 'opacity', new Color(1, 1, 1), effectiveOpacity));
|
|
1095
|
-
}
|
|
1096
|
-
else {
|
|
1097
|
-
inputs.push(`${pad}float inputs:opacity = ${effectiveOpacity}`);
|
|
1098
|
-
if (material.alphaTest > 0.0) {
|
|
1099
|
-
inputs.push(`${pad}float inputs:opacityThreshold = ${material.alphaTest}`);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (material instanceof MeshStandardMaterial) {
|
|
1103
|
-
if (material.emissiveMap) {
|
|
1104
|
-
inputs.push(`${pad}color3f inputs:emissiveColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>`);
|
|
1105
|
-
const color = material.emissive.clone();
|
|
1106
|
-
color.multiplyScalar(material.emissiveIntensity);
|
|
1107
|
-
samplers.push(buildTexture(material.emissiveMap, 'emissive', color));
|
|
1108
|
-
}
|
|
1109
|
-
else if (material.emissive?.getHex() > 0) {
|
|
1110
|
-
const color = material.emissive.clone();
|
|
1111
|
-
color.multiplyScalar(material.emissiveIntensity);
|
|
1112
|
-
inputs.push(`${pad}color3f inputs:emissiveColor = ${buildColor(color)}`);
|
|
1113
|
-
}
|
|
1114
|
-
else {
|
|
1115
|
-
inputs.push(`${pad}color3f inputs:emissiveColor = (0, 0, 0)`);
|
|
1116
|
-
}
|
|
1117
|
-
if (material.normalMap) {
|
|
1118
|
-
inputs.push(`${pad}normal3f inputs:normal.connect = ${materialRoot}/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>`);
|
|
1119
|
-
samplers.push(buildTexture(material.normalMap, 'normal'));
|
|
1120
|
-
}
|
|
1121
|
-
if (material.roughnessMap && material.roughness === 1) {
|
|
1122
|
-
inputs.push(`${pad}float inputs:roughness.connect = ${materialRoot}/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>`);
|
|
1123
|
-
samplers.push(buildTexture(material.roughnessMap, 'roughness'));
|
|
1124
|
-
}
|
|
1125
|
-
else {
|
|
1126
|
-
inputs.push(`${pad}float inputs:roughness = ${material.roughness !== undefined ? material.roughness : 1}`);
|
|
1127
|
-
}
|
|
1128
|
-
if (material.metalnessMap && material.metalness === 1) {
|
|
1129
|
-
inputs.push(`${pad}float inputs:metallic.connect = ${materialRoot}/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>`);
|
|
1130
|
-
samplers.push(buildTexture(material.metalnessMap, 'metallic'));
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
inputs.push(`${pad}float inputs:metallic = ${material.metalness !== undefined ? material.metalness : 0}`);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
if (material instanceof MeshPhysicalMaterial) {
|
|
1137
|
-
inputs.push(`${pad}float inputs:clearcoat = ${material.clearcoat}`);
|
|
1138
|
-
inputs.push(`${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}`);
|
|
1139
|
-
inputs.push(`${pad}float inputs:ior = ${material.ior}`);
|
|
1140
|
-
if (!material.transparent && !(material.alphaTest > 0.0) && material.transmissionMap) {
|
|
1141
|
-
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.transmissionMap.id}_transmission.outputs:r>`);
|
|
1142
|
-
samplers.push(buildTexture(material.transmissionMap, 'transmission'));
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
return `
|
|
1146
|
-
def Material "Material_${material.id}"
|
|
1147
|
-
{
|
|
1148
|
-
def Shader "PreviewSurface"
|
|
1149
|
-
{
|
|
1150
|
-
uniform token info:id = "UsdPreviewSurface"
|
|
1151
|
-
${inputs.join('\n')}
|
|
1152
|
-
int inputs:useSpecularWorkflow = 0
|
|
1153
|
-
token outputs:surface
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
token outputs:surface.connect = ${materialRoot}/Material_${material.id}/PreviewSurface.outputs:surface>
|
|
1157
|
-
|
|
1158
|
-
def Shader "uvReader_st"
|
|
1159
|
-
{
|
|
1160
|
-
uniform token info:id = "UsdPrimvarReader_float2"
|
|
1161
|
-
token inputs:varname = "st"
|
|
1162
|
-
float2 inputs:fallback = (0.0, 0.0)
|
|
1163
|
-
float2 outputs:result
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
def Shader "uvReader_st2"
|
|
1167
|
-
{
|
|
1168
|
-
uniform token info:id = "UsdPrimvarReader_float2"
|
|
1169
|
-
token inputs:varname = "st2"
|
|
1170
|
-
float2 inputs:fallback = (0.0, 0.0)
|
|
1171
|
-
float2 outputs:result
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
${samplers.join('\n')}
|
|
1175
|
-
|
|
1176
|
-
}
|
|
1177
|
-
`;
|
|
1178
|
-
}
|
|
1179
|
-
function buildColor(color) {
|
|
1180
|
-
return `(${color.r}, ${color.g}, ${color.b})`;
|
|
1181
|
-
}
|
|
1182
|
-
function buildVector2(vector) {
|
|
1183
|
-
return `(${vector.x}, ${vector.y})`;
|
|
1184
|
-
}
|
|
1185
|
-
const formatsWithAlphaChannel = [
|
|
1186
|
-
// uncompressed formats with alpha channel
|
|
1187
|
-
1023,
|
|
1188
|
-
// compressed formats with alpha channel
|
|
1189
|
-
33777,
|
|
1190
|
-
33778,
|
|
1191
|
-
33779,
|
|
1192
|
-
35842,
|
|
1193
|
-
35843,
|
|
1194
|
-
37496,
|
|
1195
|
-
37808,
|
|
1196
|
-
37809,
|
|
1197
|
-
37810,
|
|
1198
|
-
37811,
|
|
1199
|
-
37812,
|
|
1200
|
-
37813,
|
|
1201
|
-
37814,
|
|
1202
|
-
37815,
|
|
1203
|
-
37816,
|
|
1204
|
-
37817,
|
|
1205
|
-
37818,
|
|
1206
|
-
37819,
|
|
1207
|
-
37820,
|
|
1208
|
-
37821,
|
|
1209
|
-
36492, // RGBA_BPTC_Format
|
|
1210
|
-
];
|
|
1211
|
-
export { USDZExporter, USDZExporterContext, USDWriter, USDObject, buildMatrix, getBoneName, getPathToSkeleton, fn as usdNumberFormatting, USDDocument, makeNameSafe as makeNameSafeForUSD, imageToCanvas, decompressGpuTexture, findStructuralNodesInBoneHierarchy, };
|
|
1
|
+
import { Renderer } from '../../Renderer.js';
|
|
2
|
+
import { GameObject } from '../../Component.js';
|
|
3
|
+
import '../../../engine/engine_shims.js';
|
|
4
|
+
import { PlaneGeometry, Texture, Uniform, PerspectiveCamera, Scene, Mesh, ShaderMaterial, WebGLRenderer, MathUtils, Matrix4, DoubleSide, Color, MeshStandardMaterial, MeshPhysicalMaterial, MeshBasicMaterial, SkinnedMesh, SRGBColorSpace, OrthographicCamera, BufferAttribute, Vector4 } from 'three';
|
|
5
|
+
import * as fflate from 'three/examples/jsm/libs/fflate.module.js';
|
|
6
|
+
function makeNameSafe(str) {
|
|
7
|
+
str = str.replace(/[^a-zA-Z0-9_]/g, '');
|
|
8
|
+
// if str does not start with a-zA-Z_ add _ to the beginning
|
|
9
|
+
if (!str.match(/^[a-zA-Z_]/))
|
|
10
|
+
str = '_' + str;
|
|
11
|
+
return str;
|
|
12
|
+
}
|
|
13
|
+
// TODO check if this works when bones in the skeleton are completely unordered
|
|
14
|
+
function findCommonAncestor(objects) {
|
|
15
|
+
if (objects.length === 0)
|
|
16
|
+
return null;
|
|
17
|
+
const ancestors = objects.map((obj) => {
|
|
18
|
+
const objAncestors = new Array();
|
|
19
|
+
while (obj.parent) {
|
|
20
|
+
objAncestors.unshift(obj.parent);
|
|
21
|
+
obj = obj.parent;
|
|
22
|
+
}
|
|
23
|
+
return objAncestors;
|
|
24
|
+
});
|
|
25
|
+
//@ts-ignore – findLast seems to be missing in TypeScript types pre-5.x
|
|
26
|
+
const commonAncestor = ancestors[0].findLast((ancestor) => {
|
|
27
|
+
return ancestors.every((a) => a.includes(ancestor));
|
|
28
|
+
});
|
|
29
|
+
return commonAncestor || null;
|
|
30
|
+
}
|
|
31
|
+
function findStructuralNodesInBoneHierarchy(bones) {
|
|
32
|
+
const commonAncestor = findCommonAncestor(bones);
|
|
33
|
+
// find all structural nodes – parents of bones that are not bones themselves
|
|
34
|
+
const structuralNodes = new Set();
|
|
35
|
+
for (const bone of bones) {
|
|
36
|
+
let current = bone.parent;
|
|
37
|
+
while (current && current !== commonAncestor) {
|
|
38
|
+
if (!bones.includes(current)) {
|
|
39
|
+
structuralNodes.add(current);
|
|
40
|
+
}
|
|
41
|
+
current = current.parent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return structuralNodes;
|
|
45
|
+
}
|
|
46
|
+
class USDObject {
|
|
47
|
+
static USDObject_export_id = 0;
|
|
48
|
+
uuid;
|
|
49
|
+
name;
|
|
50
|
+
matrix;
|
|
51
|
+
_isDynamic;
|
|
52
|
+
get isDynamic() { return this._isDynamic; }
|
|
53
|
+
set isDynamic(value) { this._isDynamic = value; }
|
|
54
|
+
geometry;
|
|
55
|
+
material;
|
|
56
|
+
camera;
|
|
57
|
+
parent;
|
|
58
|
+
skinnedMesh;
|
|
59
|
+
children = [];
|
|
60
|
+
animations;
|
|
61
|
+
_eventListeners;
|
|
62
|
+
static createEmptyParent(object) {
|
|
63
|
+
const emptyParent = new USDObject(MathUtils.generateUUID(), object.name + '_empty_' + (USDObject.USDObject_export_id++), object.matrix);
|
|
64
|
+
const parent = object.parent;
|
|
65
|
+
parent.add(emptyParent);
|
|
66
|
+
emptyParent.add(object);
|
|
67
|
+
emptyParent.isDynamic = true;
|
|
68
|
+
object.matrix = new Matrix4().identity();
|
|
69
|
+
return emptyParent;
|
|
70
|
+
}
|
|
71
|
+
static createEmpty() {
|
|
72
|
+
const empty = new USDObject(MathUtils.generateUUID(), 'Empty_' + (USDObject.USDObject_export_id++), new Matrix4());
|
|
73
|
+
empty.isDynamic = true;
|
|
74
|
+
return empty;
|
|
75
|
+
}
|
|
76
|
+
constructor(id, name, matrix, mesh = null, material = null, camera = null, skinnedMesh = null, animations = null) {
|
|
77
|
+
this.uuid = id;
|
|
78
|
+
this.name = makeNameSafe(name);
|
|
79
|
+
this.matrix = matrix.clone();
|
|
80
|
+
this.geometry = mesh;
|
|
81
|
+
this.material = material;
|
|
82
|
+
this.camera = camera;
|
|
83
|
+
this.parent = null;
|
|
84
|
+
this.children = [];
|
|
85
|
+
this._eventListeners = {};
|
|
86
|
+
this._isDynamic = false;
|
|
87
|
+
this.skinnedMesh = skinnedMesh;
|
|
88
|
+
this.animations = animations;
|
|
89
|
+
}
|
|
90
|
+
is(obj) {
|
|
91
|
+
if (!obj)
|
|
92
|
+
return false;
|
|
93
|
+
return this.uuid === obj.uuid;
|
|
94
|
+
}
|
|
95
|
+
isEmpty() {
|
|
96
|
+
return !this.geometry;
|
|
97
|
+
}
|
|
98
|
+
clone() {
|
|
99
|
+
const clone = new USDObject(MathUtils.generateUUID(), this.name, this.matrix, this.geometry, this.material);
|
|
100
|
+
clone.isDynamic = this.isDynamic;
|
|
101
|
+
return clone;
|
|
102
|
+
}
|
|
103
|
+
deepClone() {
|
|
104
|
+
const clone = this.clone();
|
|
105
|
+
for (const child of this.children) {
|
|
106
|
+
if (!child)
|
|
107
|
+
continue;
|
|
108
|
+
clone.add(child.deepClone());
|
|
109
|
+
}
|
|
110
|
+
return clone;
|
|
111
|
+
}
|
|
112
|
+
getPath() {
|
|
113
|
+
let current = this.parent;
|
|
114
|
+
let path = this.name;
|
|
115
|
+
while (current) {
|
|
116
|
+
// StageRoot has a special path right now since there's additional Xforms for encapsulation.
|
|
117
|
+
// Better would be to actually model them as part of our object graph, but they're written separately,
|
|
118
|
+
// so currently we don't and instead work around that here.
|
|
119
|
+
const currentName = current.parent ? current.name : (current.name + "/Scenes/Scene");
|
|
120
|
+
path = currentName + '/' + path;
|
|
121
|
+
current = current.parent;
|
|
122
|
+
}
|
|
123
|
+
return '</' + path + '>';
|
|
124
|
+
}
|
|
125
|
+
add(child) {
|
|
126
|
+
if (child.parent) {
|
|
127
|
+
child.parent.remove(child);
|
|
128
|
+
}
|
|
129
|
+
child.parent = this;
|
|
130
|
+
this.children.push(child);
|
|
131
|
+
}
|
|
132
|
+
remove(child) {
|
|
133
|
+
const index = this.children.indexOf(child);
|
|
134
|
+
if (index >= 0) {
|
|
135
|
+
if (child.parent === this)
|
|
136
|
+
child.parent = null;
|
|
137
|
+
this.children.splice(index, 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
addEventListener(evt, listener) {
|
|
141
|
+
if (!this._eventListeners[evt])
|
|
142
|
+
this._eventListeners[evt] = [];
|
|
143
|
+
this._eventListeners[evt].push(listener);
|
|
144
|
+
}
|
|
145
|
+
removeEventListener(evt, listener) {
|
|
146
|
+
if (!this._eventListeners[evt])
|
|
147
|
+
return;
|
|
148
|
+
const index = this._eventListeners[evt].indexOf(listener);
|
|
149
|
+
if (index >= 0) {
|
|
150
|
+
this._eventListeners[evt].splice(index, 1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
onSerialize(writer, context) {
|
|
154
|
+
const listeners = this._eventListeners['serialize'];
|
|
155
|
+
if (listeners)
|
|
156
|
+
listeners.forEach(listener => listener(writer, context));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
class USDDocument extends USDObject {
|
|
160
|
+
stageLength;
|
|
161
|
+
get isDocumentRoot() {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
get isDynamic() {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
constructor() {
|
|
168
|
+
super(undefined, 'StageRoot', new Matrix4(), null, null, null);
|
|
169
|
+
this.children = [];
|
|
170
|
+
this.stageLength = 200;
|
|
171
|
+
}
|
|
172
|
+
add(child) {
|
|
173
|
+
child.parent = this;
|
|
174
|
+
this.children.push(child);
|
|
175
|
+
}
|
|
176
|
+
remove(child) {
|
|
177
|
+
const index = this.children.indexOf(child);
|
|
178
|
+
if (index >= 0) {
|
|
179
|
+
if (child.parent === this)
|
|
180
|
+
child.parent = null;
|
|
181
|
+
this.children.splice(index, 1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
traverse(callback, current = null) {
|
|
185
|
+
if (current !== null)
|
|
186
|
+
callback(current);
|
|
187
|
+
else
|
|
188
|
+
current = this;
|
|
189
|
+
if (current.children) {
|
|
190
|
+
for (const child of current.children) {
|
|
191
|
+
this.traverse(callback, child);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
findById(uuid) {
|
|
196
|
+
let found = false;
|
|
197
|
+
function search(current) {
|
|
198
|
+
if (found)
|
|
199
|
+
return;
|
|
200
|
+
if (current.uuid === uuid) {
|
|
201
|
+
found = true;
|
|
202
|
+
return current;
|
|
203
|
+
}
|
|
204
|
+
if (current.children) {
|
|
205
|
+
for (const child of current.children) {
|
|
206
|
+
const res = search(child);
|
|
207
|
+
if (res)
|
|
208
|
+
return res;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return search(this);
|
|
213
|
+
}
|
|
214
|
+
buildHeader(endTimeCode) {
|
|
215
|
+
return `#usda 1.0
|
|
216
|
+
(
|
|
217
|
+
customLayerData = {
|
|
218
|
+
string creator = "Needle Engine USDZExporter"
|
|
219
|
+
}
|
|
220
|
+
defaultPrim = "${makeNameSafe(this.name)}"
|
|
221
|
+
metersPerUnit = 1
|
|
222
|
+
upAxis = "Y"
|
|
223
|
+
startTimeCode = 0
|
|
224
|
+
endTimeCode = ${endTimeCode}
|
|
225
|
+
timeCodesPerSecond = 60
|
|
226
|
+
framesPerSecond = 60
|
|
227
|
+
)
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const newLine = '\n';
|
|
232
|
+
const materialRoot = '</StageRoot/Materials';
|
|
233
|
+
class USDWriter {
|
|
234
|
+
str;
|
|
235
|
+
indent;
|
|
236
|
+
constructor() {
|
|
237
|
+
this.str = '';
|
|
238
|
+
this.indent = 0;
|
|
239
|
+
}
|
|
240
|
+
clear() {
|
|
241
|
+
this.str = '';
|
|
242
|
+
this.indent = 0;
|
|
243
|
+
}
|
|
244
|
+
beginBlock(str = undefined, char = '{', createNewLine = true) {
|
|
245
|
+
if (str !== undefined) {
|
|
246
|
+
str = this.applyIndent(str);
|
|
247
|
+
this.str += str;
|
|
248
|
+
if (createNewLine) {
|
|
249
|
+
this.str += newLine;
|
|
250
|
+
this.str += this.applyIndent(char);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
this.str += " " + char;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
this.str += this.applyIndent(char);
|
|
258
|
+
}
|
|
259
|
+
this.str += newLine;
|
|
260
|
+
this.indent += 1;
|
|
261
|
+
}
|
|
262
|
+
closeBlock(char = '}') {
|
|
263
|
+
this.indent -= 1;
|
|
264
|
+
this.str += this.applyIndent(char) + newLine;
|
|
265
|
+
}
|
|
266
|
+
beginArray(str) {
|
|
267
|
+
str = this.applyIndent(str + ' = [');
|
|
268
|
+
this.str += str;
|
|
269
|
+
this.str += newLine;
|
|
270
|
+
this.indent += 1;
|
|
271
|
+
}
|
|
272
|
+
closeArray() {
|
|
273
|
+
this.indent -= 1;
|
|
274
|
+
this.str += this.applyIndent(']') + newLine;
|
|
275
|
+
}
|
|
276
|
+
appendLine(str = '') {
|
|
277
|
+
str = this.applyIndent(str);
|
|
278
|
+
this.str += str;
|
|
279
|
+
this.str += newLine;
|
|
280
|
+
}
|
|
281
|
+
toString() {
|
|
282
|
+
return this.str;
|
|
283
|
+
}
|
|
284
|
+
applyIndent(str) {
|
|
285
|
+
let indents = '';
|
|
286
|
+
for (let i = 0; i < this.indent; i++)
|
|
287
|
+
indents += '\t';
|
|
288
|
+
return indents + str;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
class USDZExporterContext {
|
|
292
|
+
root;
|
|
293
|
+
exporter;
|
|
294
|
+
extensions;
|
|
295
|
+
quickLookCompatible;
|
|
296
|
+
materials;
|
|
297
|
+
textures;
|
|
298
|
+
files;
|
|
299
|
+
document;
|
|
300
|
+
output;
|
|
301
|
+
animations;
|
|
302
|
+
constructor(root, exporter, extensions, quickLookCompatible) {
|
|
303
|
+
this.root = root;
|
|
304
|
+
this.exporter = exporter;
|
|
305
|
+
this.quickLookCompatible = quickLookCompatible;
|
|
306
|
+
if (extensions)
|
|
307
|
+
this.extensions = extensions;
|
|
308
|
+
this.materials = new Map();
|
|
309
|
+
this.textures = {};
|
|
310
|
+
this.files = {};
|
|
311
|
+
this.document = new USDDocument();
|
|
312
|
+
this.output = '';
|
|
313
|
+
this.animations = [];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
class USDZExporterOptions {
|
|
317
|
+
ar = {
|
|
318
|
+
anchoring: { type: 'plane' },
|
|
319
|
+
planeAnchoring: { alignment: 'horizontal' }
|
|
320
|
+
};
|
|
321
|
+
quickLookCompatible = false;
|
|
322
|
+
extensions = [];
|
|
323
|
+
maxTextureSize = 4096;
|
|
324
|
+
}
|
|
325
|
+
class USDZExporter {
|
|
326
|
+
debug;
|
|
327
|
+
sceneAnchoringOptions = {};
|
|
328
|
+
extensions;
|
|
329
|
+
constructor() {
|
|
330
|
+
this.debug = false;
|
|
331
|
+
}
|
|
332
|
+
getEndTimeCode(animations) {
|
|
333
|
+
let endTimeCode = 0;
|
|
334
|
+
for (const animation of animations) {
|
|
335
|
+
const currentEndTimeCode = animation.duration * 60;
|
|
336
|
+
if (endTimeCode < currentEndTimeCode) {
|
|
337
|
+
endTimeCode = currentEndTimeCode;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return endTimeCode;
|
|
341
|
+
}
|
|
342
|
+
async parse(scene, options = new USDZExporterOptions()) {
|
|
343
|
+
options = Object.assign(new USDZExporterOptions(), options);
|
|
344
|
+
this.sceneAnchoringOptions = options;
|
|
345
|
+
const context = new USDZExporterContext(scene, this, options.extensions, options.quickLookCompatible);
|
|
346
|
+
this.extensions = context.extensions;
|
|
347
|
+
const files = context.files;
|
|
348
|
+
const modelFileName = 'model.usda';
|
|
349
|
+
// model file should be first in USDZ archive so we init it here
|
|
350
|
+
files[modelFileName] = null;
|
|
351
|
+
const materials = context.materials;
|
|
352
|
+
const textures = context.textures;
|
|
353
|
+
await invokeAll(context, 'onBeforeBuildDocument');
|
|
354
|
+
// HACK let's find all skeletons and reparent them to their skelroot / armature / uppermost bone parent
|
|
355
|
+
const reparentings = [];
|
|
356
|
+
scene.traverseVisible(object => {
|
|
357
|
+
if (object.isSkinnedMesh) {
|
|
358
|
+
const bones = object.skeleton.bones;
|
|
359
|
+
const commonAncestor = findCommonAncestor(bones);
|
|
360
|
+
if (commonAncestor) {
|
|
361
|
+
reparentings.push({ object, originalParent: object.parent, newParent: commonAncestor });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
for (const reparenting of reparentings) {
|
|
366
|
+
const { object, originalParent, newParent } = reparenting;
|
|
367
|
+
if (this.debug)
|
|
368
|
+
console.log("REPARENTING", object, "from", originalParent, "to", newParent);
|
|
369
|
+
newParent.add(object);
|
|
370
|
+
}
|
|
371
|
+
traverseVisible(scene, context.document, context);
|
|
372
|
+
await invokeAll(context, 'onAfterBuildDocument');
|
|
373
|
+
parseDocument(context, () => {
|
|
374
|
+
// injected after stageRoot.
|
|
375
|
+
// TODO property use context/writer instead of string concat
|
|
376
|
+
return buildMaterials(materials, textures, options.quickLookCompatible);
|
|
377
|
+
});
|
|
378
|
+
await invokeAll(context, 'onAfterSerialize');
|
|
379
|
+
// repair the parenting again
|
|
380
|
+
for (const reparenting of reparentings) {
|
|
381
|
+
const { object, originalParent, newParent } = reparenting;
|
|
382
|
+
originalParent.add(object);
|
|
383
|
+
}
|
|
384
|
+
// Moved into parseDocument callback for proper defaultPrim encapsulation
|
|
385
|
+
// context.output += buildMaterials( materials, textures, options.quickLookCompatible );
|
|
386
|
+
const endTimeCode = this.getEndTimeCode(context.animations);
|
|
387
|
+
const header = context.document.buildHeader(endTimeCode);
|
|
388
|
+
const final = header + '\n' + context.output;
|
|
389
|
+
// full output file
|
|
390
|
+
if (this.debug)
|
|
391
|
+
console.log(final);
|
|
392
|
+
files[modelFileName] = fflate.strToU8(final);
|
|
393
|
+
context.output = '';
|
|
394
|
+
const decompressionRenderer = new WebGLRenderer({ antialias: false, alpha: true });
|
|
395
|
+
for (const id in textures) {
|
|
396
|
+
const textureData = textures[id];
|
|
397
|
+
let texture = textureData.texture;
|
|
398
|
+
const isRGBA = formatsWithAlphaChannel.includes(texture.format);
|
|
399
|
+
//@ts-ignore
|
|
400
|
+
if (texture.isCompressedTexture || texture.isRenderTargetTexture) {
|
|
401
|
+
texture = decompressGpuTexture(texture, options.maxTextureSize, decompressionRenderer);
|
|
402
|
+
}
|
|
403
|
+
const canvas = await imageToCanvas(texture.image, textureData.scale, false, options.maxTextureSize).catch(err => {
|
|
404
|
+
console.error("Error converting texture to canvas", texture, err);
|
|
405
|
+
});
|
|
406
|
+
if (canvas) {
|
|
407
|
+
const blob = await canvas.convertToBlob({ type: isRGBA ? 'image/png' : 'image/jpeg', quality: 0.95 });
|
|
408
|
+
files[`textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}`] = new Uint8Array(await blob.arrayBuffer());
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
console.warn('Can`t export texture: ', texture);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
decompressionRenderer.dispose();
|
|
415
|
+
// 64 byte alignment
|
|
416
|
+
// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
|
|
417
|
+
let offset = 0;
|
|
418
|
+
for (const filename in files) {
|
|
419
|
+
const file = files[filename];
|
|
420
|
+
const headerSize = 34 + filename.length;
|
|
421
|
+
offset += headerSize;
|
|
422
|
+
const offsetMod64 = offset & 63;
|
|
423
|
+
if (offsetMod64 !== 4) {
|
|
424
|
+
const padLength = 64 - offsetMod64;
|
|
425
|
+
const padding = new Uint8Array(padLength);
|
|
426
|
+
files[filename] = [file, { extra: { 12345: padding } }];
|
|
427
|
+
}
|
|
428
|
+
offset = file.length;
|
|
429
|
+
}
|
|
430
|
+
return fflate.zipSync(files, { level: 0 });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function traverseVisible(object, parentModel, context) {
|
|
434
|
+
if (!object.visible)
|
|
435
|
+
return;
|
|
436
|
+
let model = undefined;
|
|
437
|
+
let geometry = undefined;
|
|
438
|
+
let material = undefined;
|
|
439
|
+
if (object instanceof Mesh || object instanceof SkinnedMesh) {
|
|
440
|
+
geometry = object.geometry;
|
|
441
|
+
material = object.material;
|
|
442
|
+
}
|
|
443
|
+
// TODO what should be do with disabled renderers?
|
|
444
|
+
// Here we just assume they're off, and don't export them
|
|
445
|
+
const renderer = GameObject.getComponent(object, Renderer);
|
|
446
|
+
if (renderer && !renderer.enabled) {
|
|
447
|
+
geometry = undefined;
|
|
448
|
+
material = undefined;
|
|
449
|
+
}
|
|
450
|
+
if ((object instanceof Mesh || object instanceof SkinnedMesh) && material && (material instanceof MeshStandardMaterial || material instanceof MeshBasicMaterial)) {
|
|
451
|
+
const name = getObjectId(object);
|
|
452
|
+
const skinnedMeshObject = object instanceof SkinnedMesh ? object : null;
|
|
453
|
+
model = new USDObject(object.uuid, name, object.matrix, geometry, material, undefined, skinnedMeshObject, object.animations);
|
|
454
|
+
}
|
|
455
|
+
else if (object instanceof PerspectiveCamera || object instanceof OrthographicCamera) {
|
|
456
|
+
const name = getObjectId(object);
|
|
457
|
+
model = new USDObject(object.uuid, name, object.matrix, undefined, undefined, object);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
const name = getObjectId(object);
|
|
461
|
+
model = new USDObject(object.uuid, name, object.matrix, undefined, undefined, undefined, undefined, object.animations);
|
|
462
|
+
}
|
|
463
|
+
if (model) {
|
|
464
|
+
if (parentModel) {
|
|
465
|
+
parentModel.add(model);
|
|
466
|
+
}
|
|
467
|
+
parentModel = model;
|
|
468
|
+
if (context.extensions) {
|
|
469
|
+
for (const ext of context.extensions) {
|
|
470
|
+
if (ext.onExportObject)
|
|
471
|
+
ext.onExportObject.call(ext, object, model, context);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
const name = getObjectId(object);
|
|
477
|
+
const empty = new USDObject(object.uuid, name, object.matrix);
|
|
478
|
+
if (parentModel) {
|
|
479
|
+
parentModel.add(empty);
|
|
480
|
+
}
|
|
481
|
+
parentModel = empty;
|
|
482
|
+
}
|
|
483
|
+
for (const ch of object.children) {
|
|
484
|
+
traverseVisible(ch, parentModel, context);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async function parseDocument(context, afterStageRoot) {
|
|
488
|
+
for (const child of context.document.children) {
|
|
489
|
+
addResources(child, context);
|
|
490
|
+
}
|
|
491
|
+
const writer = new USDWriter();
|
|
492
|
+
writer.beginBlock(`def Xform "${context.document.name}"`);
|
|
493
|
+
writer.beginBlock(`def Scope "Scenes" (
|
|
494
|
+
kind = "sceneLibrary"
|
|
495
|
+
)`);
|
|
496
|
+
writer.beginBlock(`def Xform "Scene" (
|
|
497
|
+
apiSchemas = ["Preliminary_AnchoringAPI"]
|
|
498
|
+
customData = {
|
|
499
|
+
bool preliminary_collidesWithEnvironment = 0
|
|
500
|
+
string sceneName = "Scene"
|
|
501
|
+
}
|
|
502
|
+
sceneName = "Scene"
|
|
503
|
+
)`);
|
|
504
|
+
writer.appendLine(`token preliminary:anchoring:type = "${context.exporter.sceneAnchoringOptions.ar.anchoring.type}"`);
|
|
505
|
+
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'plane')
|
|
506
|
+
writer.appendLine(`token preliminary:planeAnchoring:alignment = "${context.exporter.sceneAnchoringOptions.ar.planeAnchoring.alignment}"`);
|
|
507
|
+
// bit hacky as we don't have a callback here yet. Relies on the fact that the image is named identical in the ImageTracking extension.
|
|
508
|
+
if (context.exporter.sceneAnchoringOptions.ar.anchoring.type === 'image')
|
|
509
|
+
writer.appendLine(`rel preliminary:imageAnchoring:referenceImage = </${context.document.name}/Scenes/Scene/AnchoringReferenceImage>`);
|
|
510
|
+
writer.appendLine();
|
|
511
|
+
for (const child of context.document.children) {
|
|
512
|
+
buildXform(child, writer, context);
|
|
513
|
+
}
|
|
514
|
+
invokeAll(context, 'onAfterHierarchy', writer);
|
|
515
|
+
writer.closeBlock();
|
|
516
|
+
writer.closeBlock();
|
|
517
|
+
writer.appendLine(afterStageRoot());
|
|
518
|
+
writer.closeBlock();
|
|
519
|
+
context.output += writer.toString();
|
|
520
|
+
}
|
|
521
|
+
function addResources(object, context) {
|
|
522
|
+
if (object == null) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const geometry = object.geometry;
|
|
526
|
+
const material = object.material;
|
|
527
|
+
if (geometry) {
|
|
528
|
+
if (material && ('isMeshStandardMaterial' in material && material.isMeshStandardMaterial || 'isMeshBasicMaterial' in material && material.isMeshBasicMaterial)) { // TODO convert unlit to lit+emissive
|
|
529
|
+
const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
|
|
530
|
+
if (!(geometryFileName in context.files)) {
|
|
531
|
+
const meshObject = buildMeshObject(geometry, object.skinnedMesh?.skeleton?.bones);
|
|
532
|
+
context.files[geometryFileName] = buildUSDFileAsString(meshObject, context);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
console.warn('THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', name);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (material) {
|
|
540
|
+
if (!(material.uuid in context.materials)) {
|
|
541
|
+
context.materials[material.uuid] = material;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
for (const ch of object.children) {
|
|
545
|
+
addResources(ch, context);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async function invokeAll(context, name, writer = null) {
|
|
549
|
+
if (context.extensions) {
|
|
550
|
+
for (const ext of context.extensions) {
|
|
551
|
+
if (!ext)
|
|
552
|
+
continue;
|
|
553
|
+
if (typeof ext[name] === 'function') {
|
|
554
|
+
const method = ext[name];
|
|
555
|
+
const res = method.call(ext, context, writer);
|
|
556
|
+
if (res instanceof Promise) {
|
|
557
|
+
await res;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
let _renderer = null;
|
|
564
|
+
let fullscreenQuadGeometry;
|
|
565
|
+
let fullscreenQuadMaterial;
|
|
566
|
+
let fullscreenQuad;
|
|
567
|
+
function decompressGpuTexture(texture, maxTextureSize = Infinity, renderer = null) {
|
|
568
|
+
if (!fullscreenQuadGeometry)
|
|
569
|
+
fullscreenQuadGeometry = new PlaneGeometry(2, 2, 1, 1);
|
|
570
|
+
if (!fullscreenQuadMaterial)
|
|
571
|
+
fullscreenQuadMaterial = new ShaderMaterial({
|
|
572
|
+
uniforms: { blitTexture: new Uniform(texture) },
|
|
573
|
+
vertexShader: `
|
|
574
|
+
varying vec2 vUv;
|
|
575
|
+
void main(){
|
|
576
|
+
vUv = uv;
|
|
577
|
+
vUv.y = 1. - vUv.y;
|
|
578
|
+
gl_Position = vec4(position.xy * 1.0,0.,.999999);
|
|
579
|
+
}`,
|
|
580
|
+
fragmentShader: `
|
|
581
|
+
uniform sampler2D blitTexture;
|
|
582
|
+
varying vec2 vUv;
|
|
583
|
+
|
|
584
|
+
void main(){
|
|
585
|
+
gl_FragColor = vec4(vUv.xy, 0, 1);
|
|
586
|
+
|
|
587
|
+
#ifdef IS_SRGB
|
|
588
|
+
gl_FragColor = LinearTosRGB( texture2D( blitTexture, vUv) );
|
|
589
|
+
#else
|
|
590
|
+
gl_FragColor = texture2D( blitTexture, vUv);
|
|
591
|
+
#endif
|
|
592
|
+
gl_FragColor.rgb *= gl_FragColor.a;
|
|
593
|
+
}`
|
|
594
|
+
});
|
|
595
|
+
fullscreenQuadMaterial.uniforms.blitTexture.value = texture;
|
|
596
|
+
fullscreenQuadMaterial.defines.IS_SRGB = texture.colorSpace == SRGBColorSpace;
|
|
597
|
+
fullscreenQuadMaterial.needsUpdate = true;
|
|
598
|
+
if (!fullscreenQuad) {
|
|
599
|
+
fullscreenQuad = new Mesh(fullscreenQuadGeometry, fullscreenQuadMaterial);
|
|
600
|
+
fullscreenQuad.frustumCulled = false;
|
|
601
|
+
}
|
|
602
|
+
const _camera = new PerspectiveCamera();
|
|
603
|
+
const _scene = new Scene();
|
|
604
|
+
_scene.add(fullscreenQuad);
|
|
605
|
+
if (!renderer) {
|
|
606
|
+
renderer = _renderer = new WebGLRenderer({ antialias: false, alpha: true });
|
|
607
|
+
}
|
|
608
|
+
renderer.setSize(Math.min(texture.image.width, maxTextureSize), Math.min(texture.image.height, maxTextureSize));
|
|
609
|
+
renderer.clear();
|
|
610
|
+
renderer.render(_scene, _camera);
|
|
611
|
+
const readableTexture = new Texture(renderer.domElement);
|
|
612
|
+
readableTexture.minFilter = texture.minFilter;
|
|
613
|
+
readableTexture.magFilter = texture.magFilter;
|
|
614
|
+
readableTexture.wrapS = texture.wrapS;
|
|
615
|
+
readableTexture.wrapT = texture.wrapT;
|
|
616
|
+
readableTexture.name = texture.name;
|
|
617
|
+
if (_renderer) {
|
|
618
|
+
_renderer.dispose();
|
|
619
|
+
_renderer = null;
|
|
620
|
+
}
|
|
621
|
+
return readableTexture;
|
|
622
|
+
}
|
|
623
|
+
function isImageBitmap(image) {
|
|
624
|
+
return (typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement) ||
|
|
625
|
+
(typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement) ||
|
|
626
|
+
(typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas) ||
|
|
627
|
+
(typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap);
|
|
628
|
+
}
|
|
629
|
+
async function imageToCanvas(image, color = undefined, flipY = false, maxTextureSize = 4096) {
|
|
630
|
+
if (isImageBitmap(image)) {
|
|
631
|
+
// max. canvas size on Safari is still 4096x4096
|
|
632
|
+
const scale = maxTextureSize / Math.max(image.width, image.height);
|
|
633
|
+
const canvas = new OffscreenCanvas(image.width * Math.min(1, scale), image.height * Math.min(1, scale));
|
|
634
|
+
const context = canvas.getContext('2d');
|
|
635
|
+
if (!context)
|
|
636
|
+
throw new Error('Could not get canvas 2D context');
|
|
637
|
+
if (flipY === true) {
|
|
638
|
+
context.translate(0, canvas.height);
|
|
639
|
+
context.scale(1, -1);
|
|
640
|
+
}
|
|
641
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
642
|
+
// Currently only used to apply opacity scale since QuickLook and usdview don't support that yet
|
|
643
|
+
if (color !== undefined) {
|
|
644
|
+
const r = color.x;
|
|
645
|
+
const g = color.y;
|
|
646
|
+
const b = color.z;
|
|
647
|
+
const a = color.w;
|
|
648
|
+
const imagedata = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
649
|
+
const data = imagedata.data;
|
|
650
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
651
|
+
data[i + 0] = data[i + 0] * r;
|
|
652
|
+
data[i + 1] = data[i + 1] * g;
|
|
653
|
+
data[i + 2] = data[i + 2] * b;
|
|
654
|
+
data[i + 3] = data[i + 3] * a;
|
|
655
|
+
}
|
|
656
|
+
context.putImageData(imagedata, 0, 0);
|
|
657
|
+
}
|
|
658
|
+
return canvas;
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
throw new Error('THREE.USDZExporter: No valid image data found. Unable to process texture.');
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
//
|
|
665
|
+
const PRECISION = 7;
|
|
666
|
+
function buildHeader() {
|
|
667
|
+
return `#usda 1.0
|
|
668
|
+
(
|
|
669
|
+
customLayerData = {
|
|
670
|
+
string creator = "Needle Engine USDZExporter"
|
|
671
|
+
}
|
|
672
|
+
metersPerUnit = 1
|
|
673
|
+
upAxis = "Y"
|
|
674
|
+
)
|
|
675
|
+
`;
|
|
676
|
+
}
|
|
677
|
+
function buildUSDFileAsString(dataToInsert, _context) {
|
|
678
|
+
let output = buildHeader();
|
|
679
|
+
output += dataToInsert;
|
|
680
|
+
return fflate.strToU8(output);
|
|
681
|
+
}
|
|
682
|
+
function getObjectId(object) {
|
|
683
|
+
return object.name.replace(/[-<>\(\)\[\]§$%&\/\\\=\?\,\;]/g, '') + '_' + object.id;
|
|
684
|
+
}
|
|
685
|
+
function getBoneName(bone) {
|
|
686
|
+
return makeNameSafe(bone.name || 'bone_' + bone.uuid);
|
|
687
|
+
}
|
|
688
|
+
function getPathToSkeleton(bone, assumedRoot) {
|
|
689
|
+
let path = getBoneName(bone);
|
|
690
|
+
let current = bone.parent;
|
|
691
|
+
while (current && current !== assumedRoot) {
|
|
692
|
+
path = getBoneName(current) + '/' + path;
|
|
693
|
+
current = current.parent;
|
|
694
|
+
}
|
|
695
|
+
return path;
|
|
696
|
+
}
|
|
697
|
+
// Xform
|
|
698
|
+
export function buildXform(model, writer, context) {
|
|
699
|
+
if (model == null)
|
|
700
|
+
return;
|
|
701
|
+
const matrix = model.matrix;
|
|
702
|
+
const geometry = model.geometry;
|
|
703
|
+
const material = model.material;
|
|
704
|
+
const camera = model.camera;
|
|
705
|
+
const name = model.name;
|
|
706
|
+
if (model.animations) {
|
|
707
|
+
for (const animation of model.animations) {
|
|
708
|
+
context.animations.push(animation);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
const transform = buildMatrix(matrix);
|
|
712
|
+
if (matrix.determinant() < 0) {
|
|
713
|
+
console.warn('THREE.USDZExporter: USDZ does not support negative scales', name);
|
|
714
|
+
}
|
|
715
|
+
const isSkinnedMesh = geometry && geometry.isBufferGeometry && geometry.attributes.skinIndex !== undefined && geometry.attributes.skinIndex.count > 0;
|
|
716
|
+
const objType = isSkinnedMesh ? 'SkelRoot' : 'Xform';
|
|
717
|
+
const apiSchemas = isSkinnedMesh ? '"MaterialBindingAPI", "SkelBindingAPI"' : '"MaterialBindingAPI"';
|
|
718
|
+
if (geometry) {
|
|
719
|
+
writer.beginBlock(`def ${objType} "${name}"`, "(", false);
|
|
720
|
+
if (context.quickLookCompatible && material && material.side === DoubleSide)
|
|
721
|
+
writer.appendLine(`prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry_doubleSided>`);
|
|
722
|
+
else
|
|
723
|
+
writer.appendLine(`prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>`);
|
|
724
|
+
writer.appendLine(`prepend apiSchemas = [${apiSchemas}]`);
|
|
725
|
+
writer.closeBlock(")");
|
|
726
|
+
writer.beginBlock();
|
|
727
|
+
}
|
|
728
|
+
else if (camera)
|
|
729
|
+
writer.beginBlock(`def Camera "${name}"`);
|
|
730
|
+
else
|
|
731
|
+
writer.beginBlock(`def Xform "${name}"`);
|
|
732
|
+
if (geometry && material) {
|
|
733
|
+
writer.appendLine(`rel material:binding = </StageRoot/Materials/Material_${material.id}>`);
|
|
734
|
+
// Turns out QuickLook / RealityKit doesn't support the doubleSided attribute, so we
|
|
735
|
+
// work around that by emitting additional indices above, and then we shouldn't emit the attribute either as geometry is
|
|
736
|
+
// already doubleSided then.
|
|
737
|
+
if (!context.quickLookCompatible && material.side === DoubleSide) {
|
|
738
|
+
// double-sided is a mesh property in USD, we can apply it as `over` here
|
|
739
|
+
writer.beginBlock(`over "Geometry" `);
|
|
740
|
+
writer.appendLine(`uniform bool doubleSided = 1`);
|
|
741
|
+
writer.closeBlock();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (isSkinnedMesh) {
|
|
745
|
+
writer.appendLine(`rel skel:skeleton = <Rig>`);
|
|
746
|
+
writer.appendLine(`rel skel:animationSource = <Rig/_anim>`);
|
|
747
|
+
writer.appendLine(`matrix4d xformOp:transform = ${buildMatrix(new Matrix4())}`); // always identity / in world space
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
writer.appendLine(`matrix4d xformOp:transform = ${transform}`);
|
|
751
|
+
}
|
|
752
|
+
writer.appendLine('uniform token[] xformOpOrder = ["xformOp:transform"]');
|
|
753
|
+
if (camera) {
|
|
754
|
+
if ('isOrthographicCamera' in camera && camera.isOrthographicCamera) {
|
|
755
|
+
writer.appendLine(`float2 clippingRange = (${camera.near}, ${camera.far})`);
|
|
756
|
+
writer.appendLine(`float horizontalAperture = ${((Math.abs(camera.left) + Math.abs(camera.right)) * 10).toPrecision(PRECISION)}`);
|
|
757
|
+
writer.appendLine(`float verticalAperture = ${((Math.abs(camera.top) + Math.abs(camera.bottom)) * 10).toPrecision(PRECISION)}`);
|
|
758
|
+
writer.appendLine('token projection = "orthographic"');
|
|
759
|
+
}
|
|
760
|
+
else if ('isPerspectiveCamera' in camera && camera.isPerspectiveCamera) {
|
|
761
|
+
writer.appendLine(`float2 clippingRange = (${camera.near.toPrecision(PRECISION)}, ${camera.far.toPrecision(PRECISION)})`);
|
|
762
|
+
writer.appendLine(`float focalLength = ${camera.getFocalLength().toPrecision(PRECISION)}`);
|
|
763
|
+
writer.appendLine(`float focusDistance = ${camera.focus.toPrecision(PRECISION)}`);
|
|
764
|
+
writer.appendLine(`float horizontalAperture = ${camera.getFilmWidth().toPrecision(PRECISION)}`);
|
|
765
|
+
writer.appendLine('token projection = "perspective"');
|
|
766
|
+
writer.appendLine(`float verticalAperture = ${camera.getFilmHeight().toPrecision(PRECISION)}`);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (model.onSerialize) {
|
|
770
|
+
model.onSerialize(writer, context);
|
|
771
|
+
}
|
|
772
|
+
if (model.children) {
|
|
773
|
+
writer.appendLine();
|
|
774
|
+
for (const ch of model.children) {
|
|
775
|
+
buildXform(ch, writer, context);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
writer.closeBlock();
|
|
779
|
+
}
|
|
780
|
+
function fn(num) {
|
|
781
|
+
return Number.isInteger(num) ? num.toString() : num.toFixed(10);
|
|
782
|
+
}
|
|
783
|
+
function buildMatrix(matrix) {
|
|
784
|
+
const array = matrix.elements;
|
|
785
|
+
return `( ${buildMatrixRow(array, 0)}, ${buildMatrixRow(array, 4)}, ${buildMatrixRow(array, 8)}, ${buildMatrixRow(array, 12)} )`;
|
|
786
|
+
}
|
|
787
|
+
function buildMatrixRow(array, offset) {
|
|
788
|
+
return `(${fn(array[offset + 0])}, ${fn(array[offset + 1])}, ${fn(array[offset + 2])}, ${fn(array[offset + 3])})`;
|
|
789
|
+
}
|
|
790
|
+
// Mesh
|
|
791
|
+
function buildMeshObject(geometry, bonesArray = []) {
|
|
792
|
+
const mesh = buildMesh(geometry, bonesArray);
|
|
793
|
+
return `
|
|
794
|
+
def "Geometry"
|
|
795
|
+
${mesh}
|
|
796
|
+
`;
|
|
797
|
+
}
|
|
798
|
+
function buildMesh(geometry, bones = []) {
|
|
799
|
+
const name = 'Geometry';
|
|
800
|
+
const attributes = geometry.attributes;
|
|
801
|
+
const count = attributes.position.count;
|
|
802
|
+
const hasBones = bones && bones.length > 0;
|
|
803
|
+
// We need to sort bones and all skinning data by path –
|
|
804
|
+
// Neither glTF nor three.js care, but in USD they must be sorted
|
|
805
|
+
// since the array defines the virtual hierarchy and is evaluated in that order
|
|
806
|
+
const sortedBones = [];
|
|
807
|
+
const indexMapping = [];
|
|
808
|
+
let sortedSkinIndex = new Array();
|
|
809
|
+
let sortedSkinIndexAttribute = attributes.skinIndex;
|
|
810
|
+
let bonesArray = "";
|
|
811
|
+
if (hasBones) {
|
|
812
|
+
for (const index in bones) {
|
|
813
|
+
sortedBones.push({ bone: bones[index], index: parseInt(index) });
|
|
814
|
+
}
|
|
815
|
+
// add structural nodes to the list of bones
|
|
816
|
+
for (const structuralNode of findStructuralNodesInBoneHierarchy(bones)) {
|
|
817
|
+
sortedBones.push({ bone: structuralNode, index: sortedBones.length });
|
|
818
|
+
}
|
|
819
|
+
// sort bones by path – need to be sorted in the same order as during mesh export
|
|
820
|
+
const assumedRoot = bones[0].parent;
|
|
821
|
+
sortedBones.sort((a, b) => getPathToSkeleton(a.bone, assumedRoot) > getPathToSkeleton(b.bone, assumedRoot) ? 1 : -1);
|
|
822
|
+
bonesArray = sortedBones.map(x => "\"" + getPathToSkeleton(x.bone, assumedRoot) + "\"").join(', ');
|
|
823
|
+
// TODO we can probably skip the expensive attribute re-ordering if the bones were already in a correct order.
|
|
824
|
+
// That doesn't mean that they are strictly sorted by path – just that all parents strictly need to come first.
|
|
825
|
+
// build index mapping
|
|
826
|
+
for (const i in sortedBones) {
|
|
827
|
+
indexMapping[sortedBones[i].index] = parseInt(i);
|
|
828
|
+
}
|
|
829
|
+
// remap skin index attributes
|
|
830
|
+
const skinIndex = attributes.skinIndex;
|
|
831
|
+
sortedSkinIndex = new Array();
|
|
832
|
+
for (let i = 0; i < skinIndex.count; i++) {
|
|
833
|
+
const x = skinIndex.getX(i);
|
|
834
|
+
const y = skinIndex.getY(i);
|
|
835
|
+
const z = skinIndex.getZ(i);
|
|
836
|
+
const w = skinIndex.getW(i);
|
|
837
|
+
sortedSkinIndex.push(indexMapping[x], indexMapping[y], indexMapping[z], indexMapping[w]);
|
|
838
|
+
}
|
|
839
|
+
// turn it back into an attribute so the rest of the code doesn't need to learn a new thing
|
|
840
|
+
sortedSkinIndexAttribute = new BufferAttribute(new Uint16Array(sortedSkinIndex), 4);
|
|
841
|
+
}
|
|
842
|
+
const isSkinnedMesh = attributes.skinWeight && attributes.skinIndex;
|
|
843
|
+
return `
|
|
844
|
+
{
|
|
845
|
+
def Mesh "${name}" ${isSkinnedMesh ? `(
|
|
846
|
+
prepend apiSchemas = ["SkelBindingAPI"]
|
|
847
|
+
)` : ''}
|
|
848
|
+
{
|
|
849
|
+
int[] faceVertexCounts = [${buildMeshVertexCount(geometry)}]
|
|
850
|
+
int[] faceVertexIndices = [${buildMeshVertexIndices(geometry)}]
|
|
851
|
+
normal3f[] normals = [${buildVector3Array(attributes.normal, count)}] (
|
|
852
|
+
interpolation = "vertex"
|
|
853
|
+
)
|
|
854
|
+
point3f[] points = [${buildVector3Array(attributes.position, count)}]
|
|
855
|
+
${attributes.uv ?
|
|
856
|
+
`texCoord2f[] primvars:st = [${buildVector2Array(attributes.uv, count)}] (
|
|
857
|
+
interpolation = "vertex"
|
|
858
|
+
)` : ''}
|
|
859
|
+
${attributes.uv2 ?
|
|
860
|
+
`texCoord2f[] primvars:st2 = [${buildVector2Array(attributes.uv2, count)}] (
|
|
861
|
+
interpolation = "vertex"
|
|
862
|
+
)` : ''}
|
|
863
|
+
${isSkinnedMesh ?
|
|
864
|
+
`matrix4d primvars:skel:geomBindTransform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1) ) (
|
|
865
|
+
elementSize = 1
|
|
866
|
+
interpolation = "constant"
|
|
867
|
+
)` : ''}
|
|
868
|
+
${attributes.skinIndex ?
|
|
869
|
+
`int[] primvars:skel:jointIndices = [${buildVector4Array(sortedSkinIndexAttribute, true)}] (
|
|
870
|
+
elementSize = 4
|
|
871
|
+
interpolation = "vertex"
|
|
872
|
+
)` : ''}
|
|
873
|
+
${attributes.skinWeight ?
|
|
874
|
+
`float[] primvars:skel:jointWeights = [${buildVector4Array(attributes.skinWeight)}] (
|
|
875
|
+
elementSize = 4
|
|
876
|
+
interpolation = "vertex"
|
|
877
|
+
)` : ''}
|
|
878
|
+
${hasBones ?
|
|
879
|
+
//`uniform token[] skel:blendShapes
|
|
880
|
+
`uniform token[] skel:joints = [${bonesArray}]` : ''}
|
|
881
|
+
uniform token subdivisionScheme = "none"
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
# This is a workaround for QuickLook/RealityKit not supporting the doubleSided attribute. We're adding a second
|
|
886
|
+
# geometry definition here, that uses the same mesh data but appends extra faces with reversed winding order.
|
|
887
|
+
def "${name}_doubleSided" (
|
|
888
|
+
prepend references = </Geometry>
|
|
889
|
+
)
|
|
890
|
+
{
|
|
891
|
+
over "Geometry"
|
|
892
|
+
{
|
|
893
|
+
int[] faceVertexCounts = [${buildMeshVertexCount(geometry) + ", " + buildMeshVertexCount(geometry)}]
|
|
894
|
+
int[] faceVertexIndices = [${buildMeshVertexIndices(geometry) + ", " + buildMeshVertexIndices(geometry, true)}]
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
`;
|
|
898
|
+
}
|
|
899
|
+
function buildMeshVertexCount(geometry) {
|
|
900
|
+
const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
|
|
901
|
+
return Array(count / 3).fill(3).join(', ');
|
|
902
|
+
}
|
|
903
|
+
function buildMeshVertexIndices(geometry, reverseWinding = false) {
|
|
904
|
+
const index = geometry.index;
|
|
905
|
+
const array = [];
|
|
906
|
+
if (index !== null) {
|
|
907
|
+
for (let i = 0; i < index.count; i++) {
|
|
908
|
+
let val = i;
|
|
909
|
+
if (reverseWinding)
|
|
910
|
+
val = i % 3 === 0 ? i + 2 : i % 3 === 2 ? i - 2 : i;
|
|
911
|
+
array.push(index.getX(val));
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
const length = geometry.attributes.position.count;
|
|
916
|
+
for (let i = 0; i < length; i++) {
|
|
917
|
+
let val = i;
|
|
918
|
+
if (reverseWinding)
|
|
919
|
+
val = i % 3 === 0 ? i + 2 : i % 3 === 2 ? i - 2 : i;
|
|
920
|
+
array.push(val);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return array.join(', ');
|
|
924
|
+
}
|
|
925
|
+
function buildVector3Array(attribute, count) {
|
|
926
|
+
if (attribute === undefined) {
|
|
927
|
+
console.warn('USDZExporter: Normals missing.');
|
|
928
|
+
return Array(count).fill('(0, 0, 0)').join(', ');
|
|
929
|
+
}
|
|
930
|
+
const array = [];
|
|
931
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
932
|
+
const x = attribute.getX(i);
|
|
933
|
+
const y = attribute.getY(i);
|
|
934
|
+
const z = attribute.getZ(i);
|
|
935
|
+
array.push(`(${x.toPrecision(PRECISION)}, ${y.toPrecision(PRECISION)}, ${z.toPrecision(PRECISION)})`);
|
|
936
|
+
}
|
|
937
|
+
return array.join(', ');
|
|
938
|
+
}
|
|
939
|
+
function buildVector4Array(attribute, ints = false) {
|
|
940
|
+
const array = [];
|
|
941
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
942
|
+
const x = attribute.getX(i);
|
|
943
|
+
const y = attribute.getY(i);
|
|
944
|
+
const z = attribute.getZ(i);
|
|
945
|
+
const w = attribute.getW(i);
|
|
946
|
+
array.push(`${ints ? x : x.toPrecision(PRECISION)}`);
|
|
947
|
+
array.push(`${ints ? y : y.toPrecision(PRECISION)}`);
|
|
948
|
+
array.push(`${ints ? z : z.toPrecision(PRECISION)}`);
|
|
949
|
+
array.push(`${ints ? w : w.toPrecision(PRECISION)}`);
|
|
950
|
+
}
|
|
951
|
+
return array.join(', ');
|
|
952
|
+
}
|
|
953
|
+
function buildVector2Array(attribute, count) {
|
|
954
|
+
if (attribute === undefined) {
|
|
955
|
+
console.warn('USDZExporter: UVs missing.');
|
|
956
|
+
return Array(count).fill('(0, 0)').join(', ');
|
|
957
|
+
}
|
|
958
|
+
const array = [];
|
|
959
|
+
for (let i = 0; i < attribute.count; i++) {
|
|
960
|
+
const x = attribute.getX(i);
|
|
961
|
+
const y = attribute.getY(i);
|
|
962
|
+
array.push(`(${x.toPrecision(PRECISION)}, ${1 - y.toPrecision(PRECISION)})`);
|
|
963
|
+
}
|
|
964
|
+
return array.join(', ');
|
|
965
|
+
}
|
|
966
|
+
// Materials
|
|
967
|
+
function buildMaterials(materials, textures, quickLookCompatible = false) {
|
|
968
|
+
const array = [];
|
|
969
|
+
for (const uuid in materials) {
|
|
970
|
+
const material = materials[uuid];
|
|
971
|
+
array.push(buildMaterial(material, textures, quickLookCompatible));
|
|
972
|
+
}
|
|
973
|
+
return `def "Materials"
|
|
974
|
+
{
|
|
975
|
+
${array.join('')}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
`;
|
|
979
|
+
}
|
|
980
|
+
function buildMaterial(material, textures, quickLookCompatible = false) {
|
|
981
|
+
// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
|
|
982
|
+
const pad = ' ';
|
|
983
|
+
const inputs = [];
|
|
984
|
+
const samplers = [];
|
|
985
|
+
function buildTexture(texture, mapType, color = undefined, opacity = undefined) {
|
|
986
|
+
const id = texture.id + (color ? '_' + color.getHexString() : '') + (opacity !== undefined ? '_' + opacity : '');
|
|
987
|
+
// Seems neither QuickLook nor usdview support scale/bias on .a values, so we need to bake opacity multipliers into
|
|
988
|
+
// the texture. This is not ideal.
|
|
989
|
+
const opacityIsAppliedToTextureAndNotAsScale = quickLookCompatible && opacity !== undefined;
|
|
990
|
+
const scaleToApply = opacityIsAppliedToTextureAndNotAsScale ? new Vector4(1, 1, 1, opacity) : undefined;
|
|
991
|
+
textures[id] = { texture, scale: scaleToApply };
|
|
992
|
+
const uv = texture.channel > 0 ? 'st' + texture.channel : 'st';
|
|
993
|
+
const isRGBA = formatsWithAlphaChannel.includes(texture.format);
|
|
994
|
+
const WRAPPINGS = {
|
|
995
|
+
1000: 'repeat',
|
|
996
|
+
1001: 'clamp',
|
|
997
|
+
1002: 'mirror' // MirroredRepeatWrapping
|
|
998
|
+
};
|
|
999
|
+
const repeat = texture.repeat.clone();
|
|
1000
|
+
const offset = texture.offset.clone();
|
|
1001
|
+
const rotation = texture.rotation;
|
|
1002
|
+
// rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
|
|
1003
|
+
const xRotationOffset = Math.sin(rotation);
|
|
1004
|
+
const yRotationOffset = Math.cos(rotation);
|
|
1005
|
+
// texture coordinates start in the opposite corner, need to correct
|
|
1006
|
+
offset.y = 1 - offset.y - repeat.y;
|
|
1007
|
+
// turns out QuickLook is buggy and interprets texture repeat inverted.
|
|
1008
|
+
// Apple Feedback: FB10036297 and FB11442287
|
|
1009
|
+
if (quickLookCompatible) {
|
|
1010
|
+
// This is NOT correct yet in QuickLook, but comes close for a range of models.
|
|
1011
|
+
// It becomes more incorrect the bigger the offset is
|
|
1012
|
+
offset.x = offset.x / repeat.x;
|
|
1013
|
+
offset.y = offset.y / repeat.y;
|
|
1014
|
+
offset.x += xRotationOffset / repeat.x;
|
|
1015
|
+
offset.y += yRotationOffset - 1;
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
// results match glTF results exactly. verified correct in usdview.
|
|
1019
|
+
offset.x += xRotationOffset * repeat.x;
|
|
1020
|
+
offset.y += (1 - yRotationOffset) * repeat.y;
|
|
1021
|
+
}
|
|
1022
|
+
const needsTextureTransform = (repeat.x != 1 || repeat.y != 1 || offset.x != 0 || offset.y != 0 || rotation != 0);
|
|
1023
|
+
const textureTransformInput = `${materialRoot}/Material_${material.id}/${'uvReader_' + uv}.outputs:result>`;
|
|
1024
|
+
const textureTransformOutput = `${materialRoot}/Material_${material.id}/Transform2d_${mapType}.outputs:result>`;
|
|
1025
|
+
const needsTextureScale = mapType !== 'normal' && (color && (color.r !== 1 || color.g !== 1 || color.b !== 1 || opacity !== 1)) || false;
|
|
1026
|
+
const needsNormalScaleAndBias = mapType === 'normal';
|
|
1027
|
+
const normalScale = material instanceof MeshStandardMaterial ? (material.normalScale ? material.normalScale.x * 2 : 2) : 2;
|
|
1028
|
+
const normalScaleValueString = normalScale.toFixed(PRECISION);
|
|
1029
|
+
const normalBiasString = (-1 * (normalScale / 2)).toFixed(PRECISION);
|
|
1030
|
+
const normalBiasZString = (1 - normalScale).toFixed(PRECISION);
|
|
1031
|
+
return `
|
|
1032
|
+
${needsTextureTransform ? `def Shader "Transform2d_${mapType}" (
|
|
1033
|
+
sdrMetadata = {
|
|
1034
|
+
string role = "math"
|
|
1035
|
+
}
|
|
1036
|
+
)
|
|
1037
|
+
{
|
|
1038
|
+
uniform token info:id = "UsdTransform2d"
|
|
1039
|
+
float2 inputs:in.connect = ${textureTransformInput}
|
|
1040
|
+
float2 inputs:scale = ${buildVector2(repeat)}
|
|
1041
|
+
float2 inputs:translation = ${buildVector2(offset)}
|
|
1042
|
+
float inputs:rotation = ${(rotation / Math.PI * 180).toFixed(PRECISION)}
|
|
1043
|
+
float2 outputs:result
|
|
1044
|
+
}
|
|
1045
|
+
` : ''}
|
|
1046
|
+
def Shader "Texture_${texture.id}_${mapType}"
|
|
1047
|
+
{
|
|
1048
|
+
uniform token info:id = "UsdUVTexture"
|
|
1049
|
+
asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
|
|
1050
|
+
token inputs:sourceColorSpace = "${texture.colorSpace === 'srgb' ? 'sRGB' : 'raw'}"
|
|
1051
|
+
float2 inputs:st.connect = ${needsTextureTransform ? textureTransformOutput : textureTransformInput}
|
|
1052
|
+
${needsTextureScale ? `
|
|
1053
|
+
float4 inputs:scale = (${color ? color.r + ', ' + color.g + ', ' + color.b : '1, 1, 1'}, ${(opacity !== undefined && !opacityIsAppliedToTextureAndNotAsScale) ? opacity : '1'})
|
|
1054
|
+
` : ``}
|
|
1055
|
+
${needsNormalScaleAndBias ? `
|
|
1056
|
+
float4 inputs:scale = (${normalScaleValueString}, ${normalScaleValueString}, ${normalScaleValueString}, 1)
|
|
1057
|
+
float4 inputs:bias = (${normalBiasString}, ${normalBiasString}, ${normalBiasZString}, 0)
|
|
1058
|
+
` : ``}
|
|
1059
|
+
token inputs:wrapS = "${WRAPPINGS[texture.wrapS]}"
|
|
1060
|
+
token inputs:wrapT = "${WRAPPINGS[texture.wrapT]}"
|
|
1061
|
+
float outputs:r
|
|
1062
|
+
float outputs:g
|
|
1063
|
+
float outputs:b
|
|
1064
|
+
float3 outputs:rgb
|
|
1065
|
+
${material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : ''}
|
|
1066
|
+
}`;
|
|
1067
|
+
}
|
|
1068
|
+
let effectiveOpacity = (material.transparent || material.alphaTest) ? material.opacity : 1;
|
|
1069
|
+
if (material instanceof MeshPhysicalMaterial && material.transmission !== undefined) {
|
|
1070
|
+
// TODO does not help when a roughnessMap is used
|
|
1071
|
+
effectiveOpacity *= (1 - material.transmission * (1 - (material.roughness * 0.5)));
|
|
1072
|
+
}
|
|
1073
|
+
if (material.map !== null) {
|
|
1074
|
+
inputs.push(`${pad}color3f inputs:diffuseColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:rgb>`);
|
|
1075
|
+
if (material.transparent) {
|
|
1076
|
+
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
1077
|
+
}
|
|
1078
|
+
else if (material.alphaTest > 0.0) {
|
|
1079
|
+
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>`);
|
|
1080
|
+
inputs.push(`${pad}float inputs:opacityThreshold = ${material.alphaTest}`);
|
|
1081
|
+
}
|
|
1082
|
+
samplers.push(buildTexture(material.map, 'diffuse', material.color, effectiveOpacity));
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
inputs.push(`${pad}color3f inputs:diffuseColor = ${buildColor(material.color)}`);
|
|
1086
|
+
}
|
|
1087
|
+
if (material.aoMap) {
|
|
1088
|
+
inputs.push(`${pad}float inputs:occlusion.connect = ${materialRoot}/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>`);
|
|
1089
|
+
samplers.push(buildTexture(material.aoMap, 'occlusion'));
|
|
1090
|
+
}
|
|
1091
|
+
if (material.alphaMap) {
|
|
1092
|
+
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>`);
|
|
1093
|
+
inputs.push(`${pad}float inputs:opacityThreshold = 0.0001`);
|
|
1094
|
+
samplers.push(buildTexture(material.alphaMap, 'opacity', new Color(1, 1, 1), effectiveOpacity));
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
inputs.push(`${pad}float inputs:opacity = ${effectiveOpacity}`);
|
|
1098
|
+
if (material.alphaTest > 0.0) {
|
|
1099
|
+
inputs.push(`${pad}float inputs:opacityThreshold = ${material.alphaTest}`);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
if (material instanceof MeshStandardMaterial) {
|
|
1103
|
+
if (material.emissiveMap) {
|
|
1104
|
+
inputs.push(`${pad}color3f inputs:emissiveColor.connect = ${materialRoot}/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>`);
|
|
1105
|
+
const color = material.emissive.clone();
|
|
1106
|
+
color.multiplyScalar(material.emissiveIntensity);
|
|
1107
|
+
samplers.push(buildTexture(material.emissiveMap, 'emissive', color));
|
|
1108
|
+
}
|
|
1109
|
+
else if (material.emissive?.getHex() > 0) {
|
|
1110
|
+
const color = material.emissive.clone();
|
|
1111
|
+
color.multiplyScalar(material.emissiveIntensity);
|
|
1112
|
+
inputs.push(`${pad}color3f inputs:emissiveColor = ${buildColor(color)}`);
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
inputs.push(`${pad}color3f inputs:emissiveColor = (0, 0, 0)`);
|
|
1116
|
+
}
|
|
1117
|
+
if (material.normalMap) {
|
|
1118
|
+
inputs.push(`${pad}normal3f inputs:normal.connect = ${materialRoot}/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>`);
|
|
1119
|
+
samplers.push(buildTexture(material.normalMap, 'normal'));
|
|
1120
|
+
}
|
|
1121
|
+
if (material.roughnessMap && material.roughness === 1) {
|
|
1122
|
+
inputs.push(`${pad}float inputs:roughness.connect = ${materialRoot}/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>`);
|
|
1123
|
+
samplers.push(buildTexture(material.roughnessMap, 'roughness'));
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
inputs.push(`${pad}float inputs:roughness = ${material.roughness !== undefined ? material.roughness : 1}`);
|
|
1127
|
+
}
|
|
1128
|
+
if (material.metalnessMap && material.metalness === 1) {
|
|
1129
|
+
inputs.push(`${pad}float inputs:metallic.connect = ${materialRoot}/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>`);
|
|
1130
|
+
samplers.push(buildTexture(material.metalnessMap, 'metallic'));
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
inputs.push(`${pad}float inputs:metallic = ${material.metalness !== undefined ? material.metalness : 0}`);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (material instanceof MeshPhysicalMaterial) {
|
|
1137
|
+
inputs.push(`${pad}float inputs:clearcoat = ${material.clearcoat}`);
|
|
1138
|
+
inputs.push(`${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}`);
|
|
1139
|
+
inputs.push(`${pad}float inputs:ior = ${material.ior}`);
|
|
1140
|
+
if (!material.transparent && !(material.alphaTest > 0.0) && material.transmissionMap) {
|
|
1141
|
+
inputs.push(`${pad}float inputs:opacity.connect = ${materialRoot}/Material_${material.id}/Texture_${material.transmissionMap.id}_transmission.outputs:r>`);
|
|
1142
|
+
samplers.push(buildTexture(material.transmissionMap, 'transmission'));
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return `
|
|
1146
|
+
def Material "Material_${material.id}"
|
|
1147
|
+
{
|
|
1148
|
+
def Shader "PreviewSurface"
|
|
1149
|
+
{
|
|
1150
|
+
uniform token info:id = "UsdPreviewSurface"
|
|
1151
|
+
${inputs.join('\n')}
|
|
1152
|
+
int inputs:useSpecularWorkflow = 0
|
|
1153
|
+
token outputs:surface
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
token outputs:surface.connect = ${materialRoot}/Material_${material.id}/PreviewSurface.outputs:surface>
|
|
1157
|
+
|
|
1158
|
+
def Shader "uvReader_st"
|
|
1159
|
+
{
|
|
1160
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
|
1161
|
+
token inputs:varname = "st"
|
|
1162
|
+
float2 inputs:fallback = (0.0, 0.0)
|
|
1163
|
+
float2 outputs:result
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
def Shader "uvReader_st2"
|
|
1167
|
+
{
|
|
1168
|
+
uniform token info:id = "UsdPrimvarReader_float2"
|
|
1169
|
+
token inputs:varname = "st2"
|
|
1170
|
+
float2 inputs:fallback = (0.0, 0.0)
|
|
1171
|
+
float2 outputs:result
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
${samplers.join('\n')}
|
|
1175
|
+
|
|
1176
|
+
}
|
|
1177
|
+
`;
|
|
1178
|
+
}
|
|
1179
|
+
function buildColor(color) {
|
|
1180
|
+
return `(${color.r}, ${color.g}, ${color.b})`;
|
|
1181
|
+
}
|
|
1182
|
+
function buildVector2(vector) {
|
|
1183
|
+
return `(${vector.x}, ${vector.y})`;
|
|
1184
|
+
}
|
|
1185
|
+
const formatsWithAlphaChannel = [
|
|
1186
|
+
// uncompressed formats with alpha channel
|
|
1187
|
+
1023,
|
|
1188
|
+
// compressed formats with alpha channel
|
|
1189
|
+
33777,
|
|
1190
|
+
33778,
|
|
1191
|
+
33779,
|
|
1192
|
+
35842,
|
|
1193
|
+
35843,
|
|
1194
|
+
37496,
|
|
1195
|
+
37808,
|
|
1196
|
+
37809,
|
|
1197
|
+
37810,
|
|
1198
|
+
37811,
|
|
1199
|
+
37812,
|
|
1200
|
+
37813,
|
|
1201
|
+
37814,
|
|
1202
|
+
37815,
|
|
1203
|
+
37816,
|
|
1204
|
+
37817,
|
|
1205
|
+
37818,
|
|
1206
|
+
37819,
|
|
1207
|
+
37820,
|
|
1208
|
+
37821,
|
|
1209
|
+
36492, // RGBA_BPTC_Format
|
|
1210
|
+
];
|
|
1211
|
+
export { USDZExporter, USDZExporterContext, USDWriter, USDObject, buildMatrix, getBoneName, getPathToSkeleton, fn as usdNumberFormatting, USDDocument, makeNameSafe as makeNameSafeForUSD, imageToCanvas, decompressGpuTexture, findStructuralNodesInBoneHierarchy, };
|
|
1212
1212
|
//# sourceMappingURL=ThreeUSDZExporter.js.map
|