@plastic-software/three 0.183.3 → 0.184.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/three.cjs +783 -290
- package/build/three.core.js +372 -110
- package/build/three.core.min.js +1 -1
- package/build/three.module.js +436 -184
- package/build/three.module.min.js +1 -1
- package/build/three.tsl.js +7 -1
- package/build/three.tsl.min.js +1 -1
- package/build/three.webgpu.js +2979 -1281
- package/build/three.webgpu.min.js +1 -1
- package/build/three.webgpu.nodes.js +2942 -1281
- package/build/three.webgpu.nodes.min.js +1 -1
- package/examples/jsm/Addons.js +11 -0
- package/examples/jsm/animation/CCDIKSolver.js +5 -1
- package/examples/jsm/controls/ArcballControls.js +4 -1
- package/examples/jsm/controls/DragControls.js +2 -2
- package/examples/jsm/controls/FirstPersonControls.js +58 -54
- package/examples/jsm/controls/FlyControls.js +4 -0
- package/examples/jsm/controls/OrbitControls.js +2 -2
- package/examples/jsm/controls/TrackballControls.js +2 -2
- package/examples/jsm/controls/TransformControls.js +34 -2
- package/examples/jsm/csm/CSMShadowNode.js +6 -2
- package/examples/jsm/exporters/GLTFExporter.js +21 -5
- package/examples/jsm/geometries/TextGeometry.js +18 -0
- package/examples/jsm/helpers/LightProbeGridHelper.js +221 -0
- package/examples/jsm/inspector/Extension.js +13 -0
- package/examples/jsm/inspector/Inspector.js +169 -114
- package/examples/jsm/inspector/RendererInspector.js +2 -2
- package/examples/jsm/inspector/extensions/extensions.json +6 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphEditor.js +916 -0
- package/examples/jsm/inspector/extensions/tsl-graph/TSLGraphLoader.js +281 -0
- package/examples/jsm/inspector/tabs/Memory.js +128 -0
- package/examples/jsm/inspector/tabs/Parameters.js +34 -2
- package/examples/jsm/inspector/tabs/Performance.js +2 -2
- package/examples/jsm/inspector/tabs/Settings.js +264 -0
- package/examples/jsm/inspector/tabs/Timeline.js +1611 -0
- package/examples/jsm/inspector/tabs/Viewer.js +105 -3
- package/examples/jsm/inspector/ui/Graph.js +2 -2
- package/examples/jsm/inspector/ui/List.js +1 -1
- package/examples/jsm/inspector/ui/Profiler.js +273 -176
- package/examples/jsm/inspector/ui/Style.js +64 -10
- package/examples/jsm/inspector/ui/Tab.js +39 -7
- package/examples/jsm/inspector/ui/Values.js +39 -2
- package/examples/jsm/inspector/ui/utils.js +13 -0
- package/examples/jsm/interaction/InteractionManager.js +226 -0
- package/examples/jsm/libs/meshopt_decoder.module.js +8 -8
- package/examples/jsm/lighting/DynamicLighting.js +82 -0
- package/examples/jsm/lighting/LightProbeGrid.js +651 -0
- package/examples/jsm/lines/LineMaterial.js +1 -1
- package/examples/jsm/loaders/EXRLoader.js +682 -43
- package/examples/jsm/loaders/FBXLoader.js +233 -33
- package/examples/jsm/loaders/GLTFLoader.js +24 -7
- package/examples/jsm/loaders/HDRLoader.js +1 -1
- package/examples/jsm/loaders/KTX2Loader.js +8 -2
- package/examples/jsm/loaders/LDrawLoader.js +39 -47
- package/examples/jsm/loaders/SVGLoader.js +1 -1
- package/examples/jsm/loaders/VTKLoader.js +5 -1
- package/examples/jsm/loaders/collada/ColladaComposer.js +101 -7
- package/examples/jsm/loaders/collada/ColladaParser.js +19 -4
- package/examples/jsm/loaders/usd/USDAParser.js +6 -0
- package/examples/jsm/loaders/usd/USDCParser.js +26 -0
- package/examples/jsm/loaders/usd/USDComposer.js +656 -103
- package/examples/jsm/misc/GPUComputationRenderer.js +2 -0
- package/examples/jsm/misc/RollerCoaster.js +42 -4
- package/examples/jsm/modifiers/TessellateModifier.js +1 -1
- package/examples/jsm/objects/Reflector.js +73 -25
- package/examples/jsm/objects/Sky.js +14 -2
- package/examples/jsm/objects/SkyMesh.js +23 -6
- package/examples/jsm/renderers/Projector.js +18 -38
- package/examples/jsm/renderers/SVGRenderer.js +6 -25
- package/examples/jsm/transpiler/GLSLDecoder.js +2 -2
- package/examples/jsm/tsl/WebGLNodesHandler.js +605 -0
- package/examples/jsm/tsl/display/AfterImageNode.js +10 -0
- package/examples/jsm/tsl/display/AnamorphicNode.js +11 -0
- package/examples/jsm/tsl/display/BilateralBlurNode.js +10 -0
- package/examples/jsm/tsl/display/ChromaticAberrationNode.js +3 -36
- package/examples/jsm/tsl/display/FSR1Node.js +477 -0
- package/examples/jsm/tsl/display/GTAONode.js +2 -1
- package/examples/jsm/tsl/display/GaussianBlurNode.js +10 -0
- package/examples/jsm/tsl/display/GodraysNode.js +2 -11
- package/examples/jsm/tsl/display/OutlineNode.js +66 -16
- package/examples/jsm/tsl/display/SSGINode.js +0 -4
- package/examples/jsm/tsl/display/SharpenNode.js +283 -0
- package/examples/jsm/tsl/display/TAAUNode.js +835 -0
- package/examples/jsm/tsl/display/TRAANode.js +48 -7
- package/examples/jsm/tsl/lighting/DynamicLightsNode.js +300 -0
- package/examples/jsm/tsl/lighting/data/AmbientLightDataNode.js +61 -0
- package/examples/jsm/tsl/lighting/data/DirectionalLightDataNode.js +111 -0
- package/examples/jsm/tsl/lighting/data/HemisphereLightDataNode.js +99 -0
- package/examples/jsm/tsl/lighting/data/PointLightDataNode.js +134 -0
- package/examples/jsm/tsl/lighting/data/SpotLightDataNode.js +161 -0
- package/examples/jsm/tsl/math/Bayer.js +13 -2
- package/examples/jsm/utils/BufferGeometryUtils.js +2 -3
- package/examples/jsm/utils/ColorUtils.js +76 -0
- package/examples/jsm/utils/SkeletonUtils.js +14 -8
- package/examples/jsm/webxr/XRHandMeshModel.js +36 -10
- package/examples/jsm/webxr/XRHandModelFactory.js +2 -1
- package/package.json +4 -4
- package/src/Three.Core.js +1 -0
- package/src/Three.TSL.js +6 -0
- package/src/Three.WebGPU.Nodes.js +3 -0
- package/src/Three.WebGPU.js +6 -0
- package/src/animation/AnimationAction.js +11 -1
- package/src/audio/AudioContext.js +2 -2
- package/src/constants.js +1 -1
- package/src/core/BufferAttribute.js +13 -1
- package/src/core/Clock.js +1 -1
- package/src/core/Object3D.js +1 -5
- package/src/core/RenderTarget.js +1 -0
- package/src/extras/PMREMGenerator.js +1 -1
- package/src/extras/curves/CatmullRomCurve3.js +3 -2
- package/src/loaders/AudioLoader.js +11 -1
- package/src/loaders/DataTextureLoader.js +6 -4
- package/src/loaders/FileLoader.js +1 -2
- package/src/loaders/ImageBitmapLoader.js +4 -6
- package/src/loaders/MaterialLoader.js +1 -1
- package/src/loaders/ObjectLoader.js +25 -4
- package/src/loaders/nodes/NodeObjectLoader.js +18 -0
- package/src/materials/MeshToonMaterial.js +1 -1
- package/src/materials/nodes/Line2NodeMaterial.js +27 -0
- package/src/materials/nodes/NodeMaterial.js +0 -27
- package/src/materials/nodes/manager/NodeMaterialObserver.js +188 -89
- package/src/math/Line3.js +3 -0
- package/src/math/Matrix2.js +13 -9
- package/src/math/Matrix3.js +13 -9
- package/src/math/Matrix4.js +13 -9
- package/src/math/Plane.js +4 -3
- package/src/math/Triangle.js +1 -1
- package/src/math/Vector2.js +11 -7
- package/src/math/Vector3.js +12 -8
- package/src/math/Vector4.js +13 -9
- package/src/nodes/Nodes.js +0 -1
- package/src/nodes/TSL.js +1 -1
- package/src/nodes/accessors/BufferAttributeNode.js +9 -3
- package/src/nodes/accessors/CubeTextureNode.js +7 -1
- package/src/nodes/accessors/MaterialProperties.js +2 -5
- package/src/nodes/accessors/Object3DNode.js +1 -1
- package/src/nodes/accessors/ReferenceBaseNode.js +2 -2
- package/src/nodes/accessors/ReferenceNode.js +4 -4
- package/src/nodes/accessors/SceneProperties.js +2 -8
- package/src/nodes/accessors/StorageBufferNode.js +10 -4
- package/src/nodes/accessors/StorageTextureNode.js +4 -9
- package/src/nodes/accessors/TextureNode.js +10 -2
- package/src/nodes/accessors/UniformArrayNode.js +2 -2
- package/src/nodes/code/FunctionCallNode.js +1 -1
- package/src/nodes/code/FunctionNode.js +1 -1
- package/src/nodes/core/ArrayNode.js +1 -1
- package/src/nodes/core/AssignNode.js +1 -1
- package/src/nodes/core/AttributeNode.js +1 -1
- package/src/nodes/core/BypassNode.js +1 -1
- package/src/nodes/core/ContextNode.js +1 -1
- package/src/nodes/core/IndexNode.js +2 -1
- package/src/nodes/core/InputNode.js +1 -1
- package/src/nodes/core/InspectorNode.js +1 -1
- package/src/nodes/core/IsolateNode.js +1 -1
- package/src/nodes/core/Node.js +83 -12
- package/src/nodes/core/NodeBuilder.js +117 -16
- package/src/nodes/core/NodeUtils.js +1 -1
- package/src/nodes/core/OutputStructNode.js +1 -1
- package/src/nodes/core/ParameterNode.js +1 -1
- package/src/nodes/core/StackNode.js +1 -1
- package/src/nodes/core/StructNode.js +1 -1
- package/src/nodes/core/StructTypeNode.js +1 -1
- package/src/nodes/core/SubBuildNode.js +1 -1
- package/src/nodes/core/UniformGroupNode.js +36 -6
- package/src/nodes/core/VarNode.js +1 -1
- package/src/nodes/core/VaryingNode.js +1 -1
- package/src/nodes/display/NormalMapNode.js +2 -2
- package/src/nodes/display/PassNode.js +27 -7
- package/src/nodes/display/RenderOutputNode.js +4 -4
- package/src/nodes/display/ScreenNode.js +1 -1
- package/src/nodes/display/ViewportDepthTextureNode.js +11 -15
- package/src/nodes/display/ViewportTextureNode.js +18 -7
- package/src/nodes/functions/BSDF/V_GGX_SmithCorrelated_Anisotropic.js +2 -2
- package/src/nodes/geometry/RangeNode.js +1 -1
- package/src/nodes/gpgpu/AtomicFunctionNode.js +1 -1
- package/src/nodes/gpgpu/BarrierNode.js +9 -0
- package/src/nodes/gpgpu/ComputeBuiltinNode.js +1 -1
- package/src/nodes/gpgpu/ComputeNode.js +69 -44
- package/src/nodes/gpgpu/SubgroupFunctionNode.js +1 -1
- package/src/nodes/lighting/LightsNode.js +6 -27
- package/src/nodes/lighting/ShadowNode.js +24 -2
- package/src/nodes/math/BitcastNode.js +1 -1
- package/src/nodes/math/ConditionalNode.js +1 -1
- package/src/nodes/math/MathNode.js +73 -1
- package/src/nodes/math/OperatorNode.js +1 -1
- package/src/nodes/math/PackFloatNode.js +1 -1
- package/src/nodes/math/UnpackFloatNode.js +1 -1
- package/src/nodes/tsl/TSLBase.js +1 -1
- package/src/nodes/tsl/TSLCore.js +21 -3
- package/src/nodes/utils/ArrayElementNode.js +1 -1
- package/src/nodes/utils/ConvertNode.js +1 -1
- package/src/nodes/utils/DebugNode.js +1 -1
- package/src/nodes/utils/EventNode.js +30 -0
- package/src/nodes/utils/FlipNode.js +1 -1
- package/src/nodes/utils/FunctionOverloadingNode.js +1 -1
- package/src/nodes/utils/JoinNode.js +1 -1
- package/src/nodes/utils/MemberNode.js +1 -1
- package/src/nodes/utils/Remap.js +48 -0
- package/src/nodes/utils/RotateNode.js +1 -1
- package/src/nodes/utils/SetNode.js +1 -1
- package/src/nodes/utils/SplitNode.js +1 -1
- package/src/objects/BatchedMesh.js +17 -2
- package/src/objects/InstancedMesh.js +19 -3
- package/src/objects/SkinnedMesh.js +26 -9
- package/src/renderers/WebGLRenderer.js +148 -49
- package/src/renderers/common/Animation.js +3 -3
- package/src/renderers/common/Attributes.js +15 -1
- package/src/renderers/common/Backend.js +0 -8
- package/src/renderers/common/Background.js +2 -2
- package/src/renderers/common/BindGroup.js +1 -8
- package/src/renderers/common/Bindings.js +2 -2
- package/src/renderers/common/ComputePipeline.js +1 -1
- package/src/renderers/common/CubeRenderTarget.js +1 -1
- package/src/renderers/common/Info.js +333 -4
- package/src/renderers/common/InspectorBase.js +6 -1
- package/src/renderers/common/Pipelines.js +36 -3
- package/src/renderers/common/ReadbackBuffer.js +78 -0
- package/src/renderers/common/RenderBundle.js +3 -1
- package/src/renderers/common/RenderBundles.js +5 -2
- package/src/renderers/common/RenderObject.js +2 -2
- package/src/renderers/common/RenderObjects.js +3 -3
- package/src/renderers/common/RenderPipeline.js +35 -6
- package/src/renderers/common/Renderer.js +232 -53
- package/src/renderers/common/Textures.js +72 -3
- package/src/renderers/common/UniformsGroup.js +1 -1
- package/src/renderers/common/XRManager.js +34 -27
- package/src/renderers/common/extras/PMREMGenerator.js +23 -15
- package/src/renderers/common/nodes/NodeBuilderState.js +1 -1
- package/src/renderers/common/nodes/NodeManager.js +230 -99
- package/src/renderers/shaders/ShaderChunk/batching_pars_vertex.glsl.js +20 -0
- package/src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl.js +9 -1
- package/src/renderers/shaders/ShaderChunk/envmap_common_pars_fragment.glsl.js +0 -1
- package/src/renderers/shaders/ShaderChunk/envmap_fragment.glsl.js +1 -1
- package/src/renderers/shaders/ShaderChunk/lightprobes_pars_fragment.glsl.js +80 -0
- package/src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js +8 -0
- package/src/renderers/shaders/ShaderChunk/lights_pars_begin.glsl.js +2 -0
- package/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +1 -3
- package/src/renderers/shaders/ShaderChunk/normal_fragment_maps.glsl.js +7 -0
- package/src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl.js +0 -1
- package/src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl.js +12 -2
- package/src/renderers/shaders/ShaderChunk.js +2 -0
- package/src/renderers/shaders/ShaderLib/backgroundCube.glsl.js +1 -2
- package/src/renderers/shaders/ShaderLib.js +0 -1
- package/src/renderers/shaders/UniformsLib.js +7 -2
- package/src/renderers/shaders/UniformsUtils.js +27 -5
- package/src/renderers/webgl/WebGLAnimation.js +2 -1
- package/src/renderers/webgl/WebGLBackground.js +13 -13
- package/src/renderers/webgl/WebGLBufferRenderer.js +0 -32
- package/src/renderers/webgl/WebGLCapabilities.js +6 -0
- package/src/renderers/webgl/WebGLIndexedBufferRenderer.js +0 -32
- package/src/renderers/webgl/WebGLMaterials.js +12 -13
- package/src/renderers/webgl/WebGLOutput.js +4 -1
- package/src/renderers/webgl/WebGLProgram.js +5 -0
- package/src/renderers/webgl/WebGLPrograms.js +24 -3
- package/src/renderers/webgl/WebGLRenderStates.js +13 -2
- package/src/renderers/webgl/WebGLState.js +43 -0
- package/src/renderers/webgl/WebGLTextures.js +129 -26
- package/src/renderers/webgl/WebGLUniformsGroups.js +19 -0
- package/src/renderers/webgl-fallback/WebGLBackend.js +106 -65
- package/src/renderers/webgl-fallback/WebGLBufferRenderer.js +0 -41
- package/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +29 -51
- package/src/renderers/webgl-fallback/utils/WebGLAttributeUtils.js +53 -19
- package/src/renderers/webgl-fallback/utils/WebGLCapabilities.js +25 -0
- package/src/renderers/webgl-fallback/utils/WebGLState.js +42 -1
- package/src/renderers/webgl-fallback/utils/WebGLTextureUtils.js +63 -50
- package/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +1 -1
- package/src/renderers/webgpu/WebGPUBackend.js +160 -146
- package/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +55 -33
- package/src/renderers/webgpu/utils/WebGPUAttributeUtils.js +103 -17
- package/src/renderers/webgpu/utils/WebGPUBindingUtils.js +1 -1
- package/src/renderers/webgpu/utils/WebGPUCapabilities.js +48 -0
- package/src/renderers/webgpu/utils/WebGPUConstants.js +8 -0
- package/src/renderers/webgpu/utils/WebGPUTextureUtils.js +91 -17
- package/src/renderers/webgpu/utils/WebGPUUtils.js +18 -2
- package/src/renderers/webxr/WebXRController.js +12 -0
- package/src/textures/HTMLTexture.js +74 -0
- package/src/textures/Source.js +1 -1
- package/src/textures/Texture.js +13 -2
- package/src/utils.js +23 -1
- package/src/nodes/utils/RemapNode.js +0 -125
|
@@ -83,7 +83,7 @@ import { unzlibSync } from '../libs/fflate.module.js';
|
|
|
83
83
|
/**
|
|
84
84
|
* A loader for the OpenEXR texture format.
|
|
85
85
|
*
|
|
86
|
-
* `EXRLoader` currently supports uncompressed, ZIP(S), RLE, PIZ and DWA/B compression.
|
|
86
|
+
* `EXRLoader` currently supports uncompressed, ZIP(S), RLE, PIZ, B44/A and DWA/B compression.
|
|
87
87
|
* Supports reading as UnsignedByte, HalfFloat and Float type data texture.
|
|
88
88
|
*
|
|
89
89
|
* ```js
|
|
@@ -121,6 +121,14 @@ class EXRLoader extends DataTextureLoader {
|
|
|
121
121
|
*/
|
|
122
122
|
this.outputFormat = RGBAFormat;
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* For multi-part EXR files, the index of the part to load.
|
|
126
|
+
*
|
|
127
|
+
* @type {number}
|
|
128
|
+
* @default 0
|
|
129
|
+
*/
|
|
130
|
+
this.part = 0;
|
|
131
|
+
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
/**
|
|
@@ -164,6 +172,8 @@ class EXRLoader extends DataTextureLoader {
|
|
|
164
172
|
|
|
165
173
|
const logBase = Math.pow( 2.7182818, 2.2 );
|
|
166
174
|
|
|
175
|
+
let b44LogTable = null; // lazily initialized for pLinear B44 channels
|
|
176
|
+
|
|
167
177
|
function reverseLutFromBitmap( bitmap, lut ) {
|
|
168
178
|
|
|
169
179
|
let k = 0;
|
|
@@ -1548,6 +1558,205 @@ class EXRLoader extends DataTextureLoader {
|
|
|
1548
1558
|
|
|
1549
1559
|
}
|
|
1550
1560
|
|
|
1561
|
+
function uncompressB44( info ) {
|
|
1562
|
+
|
|
1563
|
+
const src = info.array;
|
|
1564
|
+
let srcOffset = info.offset.value;
|
|
1565
|
+
|
|
1566
|
+
const width = info.columns;
|
|
1567
|
+
const height = info.lines;
|
|
1568
|
+
const channels = info.inputChannels;
|
|
1569
|
+
const totalBytes = info.totalBytes;
|
|
1570
|
+
|
|
1571
|
+
// B44A allows 3-byte flat blocks; B44 always uses 14-byte blocks
|
|
1572
|
+
const isB44A = EXRHeader.compression === 'B44A_COMPRESSION';
|
|
1573
|
+
|
|
1574
|
+
// Output buffer organised as:
|
|
1575
|
+
// for each scanline y: [ ch0 pixels (w×2 bytes) | ch1 pixels | … ]
|
|
1576
|
+
const outBuffer = new Uint8Array( height * width * totalBytes );
|
|
1577
|
+
|
|
1578
|
+
// Reusable 4×4 block buffer
|
|
1579
|
+
const block = new Uint16Array( 16 );
|
|
1580
|
+
|
|
1581
|
+
// chByteOffset mirrors channelByteOffsets accumulation in setupDecoder
|
|
1582
|
+
let chByteOffset = 0;
|
|
1583
|
+
|
|
1584
|
+
for ( let c = 0; c < channels.length; c ++ ) {
|
|
1585
|
+
|
|
1586
|
+
const channel = channels[ c ];
|
|
1587
|
+
const pixelSize = channel.pixelType * 2; // HALF=2, FLOAT=4
|
|
1588
|
+
|
|
1589
|
+
// Effective dimensions for this channel (subsampled channels are smaller)
|
|
1590
|
+
const chanWidth = Math.ceil( width / channel.xSampling );
|
|
1591
|
+
const chanHeight = Math.ceil( height / channel.ySampling );
|
|
1592
|
+
const isFullRes = channel.xSampling === 1 && channel.ySampling === 1;
|
|
1593
|
+
|
|
1594
|
+
if ( channel.pixelType !== 1 ) {
|
|
1595
|
+
|
|
1596
|
+
// Non-HALF channels are stored raw, scanline by scanline
|
|
1597
|
+
for ( let y = 0; y < chanHeight; y ++ ) {
|
|
1598
|
+
|
|
1599
|
+
if ( isFullRes ) {
|
|
1600
|
+
|
|
1601
|
+
const lineBase = y * width * totalBytes + chByteOffset * width;
|
|
1602
|
+
for ( let x = 0; x < chanWidth * pixelSize; x ++ ) {
|
|
1603
|
+
|
|
1604
|
+
outBuffer[ lineBase + x ] = src[ srcOffset ++ ];
|
|
1605
|
+
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
} else {
|
|
1609
|
+
|
|
1610
|
+
srcOffset += chanWidth * pixelSize;
|
|
1611
|
+
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
chByteOffset += pixelSize;
|
|
1617
|
+
continue;
|
|
1618
|
+
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// HALF channel — process 4×4 blocks at effective channel dimensions
|
|
1622
|
+
const numBlocksX = Math.ceil( chanWidth / 4 );
|
|
1623
|
+
const numBlocksY = Math.ceil( chanHeight / 4 );
|
|
1624
|
+
|
|
1625
|
+
for ( let by = 0; by < numBlocksY; by ++ ) {
|
|
1626
|
+
|
|
1627
|
+
for ( let bx = 0; bx < numBlocksX; bx ++ ) {
|
|
1628
|
+
|
|
1629
|
+
// B44A only: flat-block when shift ≥ 13 (byte[2] ≥ 52)
|
|
1630
|
+
if ( isB44A && src[ srcOffset + 2 ] >= 52 ) {
|
|
1631
|
+
|
|
1632
|
+
// 3-byte flat block — all 16 pixels share one value
|
|
1633
|
+
const t = ( src[ srcOffset ] << 8 ) | src[ srcOffset + 1 ];
|
|
1634
|
+
const h = ( t & 0x8000 ) ? ( t & 0x7fff ) : ( ( ~ t ) & 0xffff );
|
|
1635
|
+
block.fill( h );
|
|
1636
|
+
srcOffset += 3;
|
|
1637
|
+
|
|
1638
|
+
} else {
|
|
1639
|
+
|
|
1640
|
+
// 14-byte B44 block
|
|
1641
|
+
const s0 = ( src[ srcOffset ] << 8 ) | src[ srcOffset + 1 ];
|
|
1642
|
+
const shift = src[ srcOffset + 2 ] >> 2;
|
|
1643
|
+
const bias = 0x20 << shift;
|
|
1644
|
+
|
|
1645
|
+
// Reconstruct 16 ordered-magnitude values from 6-bit running deltas.
|
|
1646
|
+
// Prediction structure (row = 4 pixels wide):
|
|
1647
|
+
// column 0 top-to-bottom: s0 → s4 → s8 → s12
|
|
1648
|
+
// then row-wise: s0 → s1 → s2 → s3
|
|
1649
|
+
// s4 → s5 → s6 → s7 etc.
|
|
1650
|
+
|
|
1651
|
+
const s4 = ( s0 + ( ( ( src[ srcOffset + 2 ] << 4 ) | ( src[ srcOffset + 3 ] >> 4 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1652
|
+
const s8 = ( s4 + ( ( ( src[ srcOffset + 3 ] << 2 ) | ( src[ srcOffset + 4 ] >> 6 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1653
|
+
const s12 = ( s8 + ( src[ srcOffset + 4 ] & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1654
|
+
|
|
1655
|
+
const s1 = ( s0 + ( ( src[ srcOffset + 5 ] >> 2 ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1656
|
+
const s5 = ( s4 + ( ( ( src[ srcOffset + 5 ] << 4 ) | ( src[ srcOffset + 6 ] >> 4 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1657
|
+
const s9 = ( s8 + ( ( ( src[ srcOffset + 6 ] << 2 ) | ( src[ srcOffset + 7 ] >> 6 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1658
|
+
const s13 = ( s12 + ( src[ srcOffset + 7 ] & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1659
|
+
|
|
1660
|
+
const s2 = ( s1 + ( ( src[ srcOffset + 8 ] >> 2 ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1661
|
+
const s6 = ( s5 + ( ( ( src[ srcOffset + 8 ] << 4 ) | ( src[ srcOffset + 9 ] >> 4 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1662
|
+
const s10 = ( s9 + ( ( ( src[ srcOffset + 9 ] << 2 ) | ( src[ srcOffset + 10 ] >> 6 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1663
|
+
const s14 = ( s13 + ( src[ srcOffset + 10 ] & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1664
|
+
|
|
1665
|
+
const s3 = ( s2 + ( ( src[ srcOffset + 11 ] >> 2 ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1666
|
+
const s7 = ( s6 + ( ( ( src[ srcOffset + 11 ] << 4 ) | ( src[ srcOffset + 12 ] >> 4 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1667
|
+
const s11 = ( s10 + ( ( ( src[ srcOffset + 12 ] << 2 ) | ( src[ srcOffset + 13 ] >> 6 ) ) & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1668
|
+
const s15 = ( s14 + ( src[ srcOffset + 13 ] & 0x3f ) * ( 1 << shift ) - bias ) & 0xffff;
|
|
1669
|
+
|
|
1670
|
+
// Convert ordered-magnitude → half-float:
|
|
1671
|
+
// positive (bit15=1): clear sign bit; negative (bit15=0): invert all bits
|
|
1672
|
+
const t = [ s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15 ];
|
|
1673
|
+
for ( let i = 0; i < 16; i ++ ) {
|
|
1674
|
+
|
|
1675
|
+
block[ i ] = ( t[ i ] & 0x8000 ) ? ( t[ i ] & 0x7fff ) : ( ( ~ t[ i ] ) & 0xffff );
|
|
1676
|
+
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
srcOffset += 14;
|
|
1680
|
+
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// pLinear channels: data was stored as exp(x/8), convert back with 8·log(x)
|
|
1684
|
+
if ( channel.pLinear ) {
|
|
1685
|
+
|
|
1686
|
+
if ( b44LogTable === null ) {
|
|
1687
|
+
|
|
1688
|
+
b44LogTable = new Uint16Array( 65536 );
|
|
1689
|
+
for ( let i = 0; i < 65536; i ++ ) {
|
|
1690
|
+
|
|
1691
|
+
if ( ( i & 0x7c00 ) === 0x7c00 || i > 0x8000 ) {
|
|
1692
|
+
|
|
1693
|
+
b44LogTable[ i ] = 0;
|
|
1694
|
+
|
|
1695
|
+
} else {
|
|
1696
|
+
|
|
1697
|
+
const f = decodeFloat16( i );
|
|
1698
|
+
b44LogTable[ i ] = ( f <= 0 ) ? 0 : DataUtils.toHalfFloat( 8 * Math.log( f ) );
|
|
1699
|
+
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
for ( let i = 0; i < 16; i ++ ) block[ i ] = b44LogTable[ block[ i ] ];
|
|
1707
|
+
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// Scatter the 16 pixels into the scanline-interleaved output buffer.
|
|
1711
|
+
// For subsampled channels (e.g. RY/BY with xSampling=ySampling=2) each decoded
|
|
1712
|
+
// pixel is replicated across its xSampling×ySampling footprint so the output
|
|
1713
|
+
// buffer has uniform full-resolution scanlines that parseScanline can read directly.
|
|
1714
|
+
for ( let py = 0; py < 4; py ++ ) {
|
|
1715
|
+
|
|
1716
|
+
const chanY = by * 4 + py;
|
|
1717
|
+
if ( chanY >= chanHeight ) continue;
|
|
1718
|
+
|
|
1719
|
+
for ( let px = 0; px < 4; px ++ ) {
|
|
1720
|
+
|
|
1721
|
+
const chanX = bx * 4 + px;
|
|
1722
|
+
if ( chanX >= chanWidth ) continue;
|
|
1723
|
+
|
|
1724
|
+
const val = block[ py * 4 + px ];
|
|
1725
|
+
|
|
1726
|
+
for ( let dy = 0; dy < channel.ySampling; dy ++ ) {
|
|
1727
|
+
|
|
1728
|
+
const fullY = chanY * channel.ySampling + dy;
|
|
1729
|
+
if ( fullY >= height ) continue;
|
|
1730
|
+
|
|
1731
|
+
for ( let dx = 0; dx < channel.xSampling; dx ++ ) {
|
|
1732
|
+
|
|
1733
|
+
const fullX = chanX * channel.xSampling + dx;
|
|
1734
|
+
if ( fullX >= width ) continue;
|
|
1735
|
+
|
|
1736
|
+
const outIdx = fullY * width * totalBytes + chByteOffset * width + fullX * 2;
|
|
1737
|
+
outBuffer[ outIdx ] = val & 0xff;
|
|
1738
|
+
outBuffer[ outIdx + 1 ] = ( val >> 8 ) & 0xff;
|
|
1739
|
+
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
chByteOffset += 2; // HALF = 2 bytes per pixel
|
|
1753
|
+
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
return new DataView( outBuffer.buffer );
|
|
1757
|
+
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1551
1760
|
function uncompressDWA( info ) {
|
|
1552
1761
|
|
|
1553
1762
|
const inDataView = info.viewer;
|
|
@@ -1625,11 +1834,14 @@ class EXRLoader extends DataTextureLoader {
|
|
|
1625
1834
|
|
|
1626
1835
|
const cd = channelData[ offset ];
|
|
1627
1836
|
|
|
1837
|
+
const dotIndex = cd.name.lastIndexOf( '.' );
|
|
1838
|
+
const suffix = dotIndex >= 0 ? cd.name.substring( dotIndex + 1 ) : cd.name;
|
|
1839
|
+
|
|
1628
1840
|
for ( let i = 0; i < channelRules.length; ++ i ) {
|
|
1629
1841
|
|
|
1630
1842
|
const rule = channelRules[ i ];
|
|
1631
1843
|
|
|
1632
|
-
if (
|
|
1844
|
+
if ( suffix === rule.name && cd.type === rule.type ) {
|
|
1633
1845
|
|
|
1634
1846
|
cd.compression = rule.compression;
|
|
1635
1847
|
|
|
@@ -1870,17 +2082,7 @@ class EXRLoader extends DataTextureLoader {
|
|
|
1870
2082
|
|
|
1871
2083
|
const parseInt64 = function ( dataView, offset ) {
|
|
1872
2084
|
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
if ( 'getBigInt64' in DataView.prototype ) {
|
|
1876
|
-
|
|
1877
|
-
int = Number( dataView.getBigInt64( offset.value, true ) );
|
|
1878
|
-
|
|
1879
|
-
} else {
|
|
1880
|
-
|
|
1881
|
-
int = dataView.getUint32( offset.value + 4, true ) + Number( dataView.getUint32( offset.value, true ) << 32 );
|
|
1882
|
-
|
|
1883
|
-
}
|
|
2085
|
+
const int = Number( dataView.getBigInt64( offset.value, true ) );
|
|
1884
2086
|
|
|
1885
2087
|
offset.value += ULONG_SIZE;
|
|
1886
2088
|
|
|
@@ -2145,8 +2347,9 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2145
2347
|
|
|
2146
2348
|
return parseTimecode( dataView, offset );
|
|
2147
2349
|
|
|
2148
|
-
} else if ( type === 'preview' ) {
|
|
2350
|
+
} else if ( type === 'preview' || type === 'deepImageState' || type === 'idmanifest' ) {
|
|
2149
2351
|
|
|
2352
|
+
// Known metadata-only types: silently skip, they carry no pixel data.
|
|
2150
2353
|
offset.value += size;
|
|
2151
2354
|
return 'skipped';
|
|
2152
2355
|
|
|
@@ -2316,9 +2519,300 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2316
2519
|
|
|
2317
2520
|
}
|
|
2318
2521
|
|
|
2319
|
-
function
|
|
2522
|
+
function parseMultiPartScanline() {
|
|
2523
|
+
|
|
2524
|
+
const EXRDecoder = this;
|
|
2525
|
+
const chunkOffsets = EXRDecoder.chunkOffsets;
|
|
2526
|
+
const tmpOffset = { value: 0 };
|
|
2527
|
+
|
|
2528
|
+
for ( let chunkIdx = 0; chunkIdx < chunkOffsets.length; chunkIdx ++ ) {
|
|
2529
|
+
|
|
2530
|
+
const offset = { value: chunkOffsets[ chunkIdx ] };
|
|
2531
|
+
|
|
2532
|
+
offset.value += INT32_SIZE; // skip part number
|
|
2533
|
+
|
|
2534
|
+
const line = parseInt32( EXRDecoder.viewer, offset ) - EXRHeader.dataWindow.yMin;
|
|
2535
|
+
EXRDecoder.size = parseUint32( EXRDecoder.viewer, offset );
|
|
2536
|
+
EXRDecoder.lines = ( ( line + EXRDecoder.blockHeight > EXRDecoder.height ) ? ( EXRDecoder.height - line ) : EXRDecoder.blockHeight );
|
|
2537
|
+
|
|
2538
|
+
const bytesPerLine = EXRDecoder.columns * EXRDecoder.totalBytes;
|
|
2539
|
+
const isCompressed = EXRDecoder.size < EXRDecoder.lines * bytesPerLine;
|
|
2540
|
+
|
|
2541
|
+
const savedOffset = EXRDecoder.offset;
|
|
2542
|
+
EXRDecoder.offset = offset;
|
|
2543
|
+
const viewer = isCompressed ? EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder );
|
|
2544
|
+
EXRDecoder.offset = savedOffset;
|
|
2545
|
+
|
|
2546
|
+
for ( let line_y = 0; line_y < EXRDecoder.blockHeight; line_y ++ ) {
|
|
2547
|
+
|
|
2548
|
+
const true_y = line_y + line;
|
|
2549
|
+
if ( true_y >= EXRDecoder.height ) continue;
|
|
2550
|
+
|
|
2551
|
+
const lineOffset = line_y * bytesPerLine;
|
|
2552
|
+
const outLineOffset = ( EXRDecoder.height - 1 - true_y ) * EXRDecoder.outLineWidth;
|
|
2553
|
+
|
|
2554
|
+
for ( let channelID = 0; channelID < EXRDecoder.inputChannels.length; channelID ++ ) {
|
|
2555
|
+
|
|
2556
|
+
const name = EXRHeader.channels[ channelID ].name;
|
|
2557
|
+
const lOff = EXRDecoder.channelByteOffsets[ name ] * EXRDecoder.columns;
|
|
2558
|
+
const cOff = EXRDecoder.decodeChannels[ name ];
|
|
2559
|
+
|
|
2560
|
+
if ( cOff === undefined ) continue;
|
|
2561
|
+
|
|
2562
|
+
tmpOffset.value = lineOffset + lOff;
|
|
2563
|
+
|
|
2564
|
+
for ( let x = 0; x < EXRDecoder.columns; x ++ ) {
|
|
2565
|
+
|
|
2566
|
+
const outIndex = outLineOffset + x * EXRDecoder.outputChannels + cOff;
|
|
2567
|
+
EXRDecoder.byteArray[ outIndex ] = EXRDecoder.getter( viewer, tmpOffset );
|
|
2568
|
+
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
function decompressDeepData( array, compressedOffset, compressedSize, compression ) {
|
|
2320
2580
|
|
|
2321
|
-
|
|
2581
|
+
if ( compressedSize === 0 ) return null;
|
|
2582
|
+
|
|
2583
|
+
const compressed = array.slice( compressedOffset, compressedOffset + compressedSize );
|
|
2584
|
+
|
|
2585
|
+
switch ( compression ) {
|
|
2586
|
+
|
|
2587
|
+
case 'NO_COMPRESSION':
|
|
2588
|
+
return new DataView( compressed.buffer, compressed.byteOffset, compressed.byteLength );
|
|
2589
|
+
|
|
2590
|
+
case 'RLE_COMPRESSION': {
|
|
2591
|
+
|
|
2592
|
+
const rawBuffer = new Uint8Array( decodeRunLength( compressed.buffer.slice( compressed.byteOffset, compressed.byteOffset + compressed.byteLength ) ) );
|
|
2593
|
+
const tmpBuffer = new Uint8Array( rawBuffer.length );
|
|
2594
|
+
predictor( rawBuffer );
|
|
2595
|
+
interleaveScalar( rawBuffer, tmpBuffer );
|
|
2596
|
+
return new DataView( tmpBuffer.buffer );
|
|
2597
|
+
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
case 'ZIPS_COMPRESSION': {
|
|
2601
|
+
|
|
2602
|
+
const rawBuffer = unzlibSync( compressed );
|
|
2603
|
+
const tmpBuffer = new Uint8Array( rawBuffer.length );
|
|
2604
|
+
predictor( rawBuffer );
|
|
2605
|
+
interleaveScalar( rawBuffer, tmpBuffer );
|
|
2606
|
+
return new DataView( tmpBuffer.buffer );
|
|
2607
|
+
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
default:
|
|
2611
|
+
throw new Error( 'EXRLoader.parse: ' + compression + ' is unsupported for deep data' );
|
|
2612
|
+
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
function parseDeepScanline() {
|
|
2618
|
+
|
|
2619
|
+
const EXRDecoder = this;
|
|
2620
|
+
const chunkOffsets = EXRDecoder.chunkOffsets;
|
|
2621
|
+
const width = EXRDecoder.width;
|
|
2622
|
+
const height = EXRDecoder.height;
|
|
2623
|
+
const deepChannels = EXRDecoder.deepChannels;
|
|
2624
|
+
const compression = EXRHeader.compression;
|
|
2625
|
+
const isMultiPart = EXRDecoder.multiPart;
|
|
2626
|
+
|
|
2627
|
+
// Build a map from channel name to decode output slot
|
|
2628
|
+
const decodeChannels = EXRDecoder.decodeChannels;
|
|
2629
|
+
const outputChannels = EXRDecoder.outputChannels;
|
|
2630
|
+
const isHalfOutput = EXRDecoder.byteArray instanceof Uint16Array;
|
|
2631
|
+
|
|
2632
|
+
// Find the alpha channel index in deepChannels (for compositing)
|
|
2633
|
+
let alphaChannelIdx = - 1;
|
|
2634
|
+
|
|
2635
|
+
for ( let i = 0; i < deepChannels.length; i ++ ) {
|
|
2636
|
+
|
|
2637
|
+
if ( deepChannels[ i ].name === 'A' ) {
|
|
2638
|
+
|
|
2639
|
+
alphaChannelIdx = i;
|
|
2640
|
+
break;
|
|
2641
|
+
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
for ( let chunkIdx = 0; chunkIdx < chunkOffsets.length; chunkIdx ++ ) {
|
|
2647
|
+
|
|
2648
|
+
const chunkOffset = { value: chunkOffsets[ chunkIdx ] };
|
|
2649
|
+
|
|
2650
|
+
// Multi-part files have a part number prefix per chunk
|
|
2651
|
+
if ( isMultiPart ) chunkOffset.value += INT32_SIZE;
|
|
2652
|
+
|
|
2653
|
+
const line = parseInt32( EXRDecoder.viewer, chunkOffset ) - EXRHeader.dataWindow.yMin;
|
|
2654
|
+
|
|
2655
|
+
// Read deep scanline sizes
|
|
2656
|
+
const sctCompressedSize = parseInt64( EXRDecoder.viewer, chunkOffset );
|
|
2657
|
+
const dataCompressedSize = parseInt64( EXRDecoder.viewer, chunkOffset );
|
|
2658
|
+
parseInt64( EXRDecoder.viewer, chunkOffset ); // uncompressed data size (unused)
|
|
2659
|
+
|
|
2660
|
+
// Decompress sample count table
|
|
2661
|
+
const sctView = decompressDeepData( EXRDecoder.array, chunkOffset.value, sctCompressedSize, compression );
|
|
2662
|
+
chunkOffset.value += sctCompressedSize;
|
|
2663
|
+
|
|
2664
|
+
if ( sctView === null ) continue;
|
|
2665
|
+
|
|
2666
|
+
// Parse cumulative sample counts
|
|
2667
|
+
const cumulativeCounts = new Uint32Array( width );
|
|
2668
|
+
|
|
2669
|
+
for ( let x = 0; x < width; x ++ ) {
|
|
2670
|
+
|
|
2671
|
+
cumulativeCounts[ x ] = sctView.getUint32( x * 4, true );
|
|
2672
|
+
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
const totalSamples = cumulativeCounts[ width - 1 ];
|
|
2676
|
+
|
|
2677
|
+
if ( totalSamples === 0 ) {
|
|
2678
|
+
|
|
2679
|
+
chunkOffset.value += dataCompressedSize;
|
|
2680
|
+
continue;
|
|
2681
|
+
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
// Decompress pixel data
|
|
2685
|
+
const pixelView = decompressDeepData( EXRDecoder.array, chunkOffset.value, dataCompressedSize, compression );
|
|
2686
|
+
|
|
2687
|
+
// Compute channel byte offsets within the decompressed pixel data.
|
|
2688
|
+
// Deep data layout: channels are contiguous, each has totalSamples values.
|
|
2689
|
+
const channelOffsets = [];
|
|
2690
|
+
let bytePos = 0;
|
|
2691
|
+
|
|
2692
|
+
for ( let i = 0; i < deepChannels.length; i ++ ) {
|
|
2693
|
+
|
|
2694
|
+
channelOffsets.push( bytePos );
|
|
2695
|
+
bytePos += totalSamples * deepChannels[ i ].bytesPerSample;
|
|
2696
|
+
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
// Flatten deep samples: front-to-back composite with premultiplied alpha
|
|
2700
|
+
const outLineOffset = ( height - 1 - line ) * EXRDecoder.outLineWidth;
|
|
2701
|
+
|
|
2702
|
+
for ( let x = 0; x < width; x ++ ) {
|
|
2703
|
+
|
|
2704
|
+
const startSample = x === 0 ? 0 : cumulativeCounts[ x - 1 ];
|
|
2705
|
+
const endSample = cumulativeCounts[ x ];
|
|
2706
|
+
const numSamples = endSample - startSample;
|
|
2707
|
+
|
|
2708
|
+
if ( numSamples === 0 ) continue;
|
|
2709
|
+
|
|
2710
|
+
// Composite samples front-to-back (premultiplied alpha)
|
|
2711
|
+
const composited = new Float32Array( outputChannels );
|
|
2712
|
+
let compositedAlpha = 0;
|
|
2713
|
+
|
|
2714
|
+
for ( let s = 0; s < numSamples; s ++ ) {
|
|
2715
|
+
|
|
2716
|
+
const sampleIdx = startSample + s;
|
|
2717
|
+
const factor = 1 - compositedAlpha;
|
|
2718
|
+
|
|
2719
|
+
if ( factor <= 0 ) break;
|
|
2720
|
+
|
|
2721
|
+
// Read alpha for this sample
|
|
2722
|
+
let sampleAlpha = 1;
|
|
2723
|
+
|
|
2724
|
+
if ( alphaChannelIdx >= 0 ) {
|
|
2725
|
+
|
|
2726
|
+
const aBps = deepChannels[ alphaChannelIdx ].bytesPerSample;
|
|
2727
|
+
const aOff = channelOffsets[ alphaChannelIdx ] + sampleIdx * aBps;
|
|
2728
|
+
|
|
2729
|
+
sampleAlpha = aBps === 2
|
|
2730
|
+
? decodeFloat16( pixelView.getUint16( aOff, true ) )
|
|
2731
|
+
: pixelView.getFloat32( aOff, true );
|
|
2732
|
+
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
// Read and composite each output channel
|
|
2736
|
+
for ( let ci = 0; ci < deepChannels.length; ci ++ ) {
|
|
2737
|
+
|
|
2738
|
+
const ch = deepChannels[ ci ];
|
|
2739
|
+
const cOff = decodeChannels[ ch.name ];
|
|
2740
|
+
|
|
2741
|
+
if ( cOff === undefined ) continue;
|
|
2742
|
+
|
|
2743
|
+
const bps = ch.bytesPerSample;
|
|
2744
|
+
const dataOff = channelOffsets[ ci ] + sampleIdx * bps;
|
|
2745
|
+
|
|
2746
|
+
const value = bps === 2
|
|
2747
|
+
? decodeFloat16( pixelView.getUint16( dataOff, true ) )
|
|
2748
|
+
: pixelView.getFloat32( dataOff, true );
|
|
2749
|
+
|
|
2750
|
+
composited[ cOff ] += value * factor;
|
|
2751
|
+
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
compositedAlpha += sampleAlpha * factor;
|
|
2755
|
+
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
// If alpha channel is being output, set it
|
|
2759
|
+
if ( decodeChannels[ 'A' ] !== undefined ) {
|
|
2760
|
+
|
|
2761
|
+
composited[ decodeChannels[ 'A' ] ] = compositedAlpha;
|
|
2762
|
+
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
// Write to output buffer
|
|
2766
|
+
const outIndex = outLineOffset + x * outputChannels;
|
|
2767
|
+
|
|
2768
|
+
for ( let c = 0; c < outputChannels; c ++ ) {
|
|
2769
|
+
|
|
2770
|
+
EXRDecoder.byteArray[ outIndex + c ] = isHalfOutput
|
|
2771
|
+
? DataUtils.toHalfFloat( composited[ c ] )
|
|
2772
|
+
: composited[ c ];
|
|
2773
|
+
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
function parsePartHeader( dataView, buffer, offset ) {
|
|
2783
|
+
|
|
2784
|
+
const header = {};
|
|
2785
|
+
let hasAttributes = false;
|
|
2786
|
+
|
|
2787
|
+
while ( true ) {
|
|
2788
|
+
|
|
2789
|
+
const attributeName = parseNullTerminatedString( buffer, offset );
|
|
2790
|
+
|
|
2791
|
+
if ( attributeName === '' ) break;
|
|
2792
|
+
|
|
2793
|
+
hasAttributes = true;
|
|
2794
|
+
|
|
2795
|
+
const attributeType = parseNullTerminatedString( buffer, offset );
|
|
2796
|
+
const attributeSize = parseUint32( dataView, offset );
|
|
2797
|
+
const attributeValue = parseValue( dataView, buffer, offset, attributeType, attributeSize );
|
|
2798
|
+
|
|
2799
|
+
if ( attributeValue === undefined ) {
|
|
2800
|
+
|
|
2801
|
+
console.warn( `THREE.EXRLoader: Skipped unknown header attribute type \'${attributeType}\'.` );
|
|
2802
|
+
|
|
2803
|
+
} else {
|
|
2804
|
+
|
|
2805
|
+
header[ attributeName ] = attributeValue;
|
|
2806
|
+
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
return hasAttributes ? header : null;
|
|
2812
|
+
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
function parseHeader( dataView, buffer, offset ) {
|
|
2322
2816
|
|
|
2323
2817
|
if ( dataView.getUint32( 0, true ) != 20000630 ) { // magic
|
|
2324
2818
|
|
|
@@ -2326,11 +2820,11 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2326
2820
|
|
|
2327
2821
|
}
|
|
2328
2822
|
|
|
2329
|
-
|
|
2823
|
+
const version = dataView.getUint8( 4 );
|
|
2330
2824
|
|
|
2331
2825
|
const spec = dataView.getUint8( 5 ); // fullMask
|
|
2332
2826
|
|
|
2333
|
-
|
|
2827
|
+
const flags = {
|
|
2334
2828
|
singleTile: !! ( spec & 2 ),
|
|
2335
2829
|
longName: !! ( spec & 4 ),
|
|
2336
2830
|
deepFormat: !! ( spec & 8 ),
|
|
@@ -2341,44 +2835,43 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2341
2835
|
|
|
2342
2836
|
offset.value = 8; // start at 8 - after pre-amble
|
|
2343
2837
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
while ( keepReading ) {
|
|
2838
|
+
const headers = [];
|
|
2347
2839
|
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
if ( attributeName === '' ) {
|
|
2840
|
+
if ( flags.multiPart ) {
|
|
2351
2841
|
|
|
2352
|
-
|
|
2842
|
+
// Multi-part files: parse all part headers.
|
|
2843
|
+
// Each part header ends with an empty attribute name (null byte).
|
|
2844
|
+
// The header section ends when a null byte is read with no preceding attributes.
|
|
2353
2845
|
|
|
2354
|
-
|
|
2846
|
+
while ( true ) {
|
|
2355
2847
|
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2358
|
-
const attributeValue = parseValue( dataView, buffer, offset, attributeType, attributeSize );
|
|
2848
|
+
const header = parsePartHeader( dataView, buffer, offset );
|
|
2849
|
+
if ( header === null ) break;
|
|
2359
2850
|
|
|
2360
|
-
|
|
2851
|
+
header.version = version;
|
|
2852
|
+
header.spec = flags;
|
|
2853
|
+
headers.push( header );
|
|
2361
2854
|
|
|
2362
|
-
|
|
2855
|
+
}
|
|
2363
2856
|
|
|
2364
|
-
|
|
2857
|
+
if ( headers.length === 0 ) {
|
|
2365
2858
|
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
}
|
|
2859
|
+
throw new Error( 'THREE.EXRLoader: No valid part headers found.' );
|
|
2369
2860
|
|
|
2370
2861
|
}
|
|
2371
2862
|
|
|
2372
|
-
}
|
|
2863
|
+
} else {
|
|
2373
2864
|
|
|
2374
|
-
|
|
2865
|
+
// Single-part (standard or deep): one header
|
|
2375
2866
|
|
|
2376
|
-
|
|
2377
|
-
|
|
2867
|
+
const header = parsePartHeader( dataView, buffer, offset );
|
|
2868
|
+
header.version = version;
|
|
2869
|
+
header.spec = flags;
|
|
2870
|
+
headers.push( header );
|
|
2378
2871
|
|
|
2379
2872
|
}
|
|
2380
2873
|
|
|
2381
|
-
return
|
|
2874
|
+
return headers;
|
|
2382
2875
|
|
|
2383
2876
|
}
|
|
2384
2877
|
|
|
@@ -2394,6 +2887,7 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2394
2887
|
inputChannels: EXRHeader.channels,
|
|
2395
2888
|
channelByteOffsets: {},
|
|
2396
2889
|
shouldExpand: false,
|
|
2890
|
+
yCbCr: false,
|
|
2397
2891
|
scanOrder: null,
|
|
2398
2892
|
totalBytes: null,
|
|
2399
2893
|
columns: null,
|
|
@@ -2437,6 +2931,12 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2437
2931
|
EXRDecoder.uncompress = uncompressPXR;
|
|
2438
2932
|
break;
|
|
2439
2933
|
|
|
2934
|
+
case 'B44_COMPRESSION':
|
|
2935
|
+
case 'B44A_COMPRESSION':
|
|
2936
|
+
EXRDecoder.blockHeight = 32;
|
|
2937
|
+
EXRDecoder.uncompress = uncompressB44;
|
|
2938
|
+
break;
|
|
2939
|
+
|
|
2440
2940
|
case 'DWAA_COMPRESSION':
|
|
2441
2941
|
EXRDecoder.blockHeight = 32;
|
|
2442
2942
|
EXRDecoder.uncompress = uncompressDWA;
|
|
@@ -2457,6 +2957,8 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2457
2957
|
|
|
2458
2958
|
switch ( channel.name ) {
|
|
2459
2959
|
|
|
2960
|
+
case 'BY':
|
|
2961
|
+
case 'RY':
|
|
2460
2962
|
case 'Y':
|
|
2461
2963
|
case 'R':
|
|
2462
2964
|
case 'G':
|
|
@@ -2474,7 +2976,12 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2474
2976
|
let invalidOutput = false;
|
|
2475
2977
|
|
|
2476
2978
|
// Validate if input texture contain supported channels
|
|
2477
|
-
if ( channels.
|
|
2979
|
+
if ( channels.Y && channels.RY && channels.BY ) {
|
|
2980
|
+
|
|
2981
|
+
EXRDecoder.outputChannels = 4;
|
|
2982
|
+
EXRDecoder.yCbCr = true;
|
|
2983
|
+
|
|
2984
|
+
} else if ( channels.R && channels.G && channels.B ) {
|
|
2478
2985
|
|
|
2479
2986
|
EXRDecoder.outputChannels = 4;
|
|
2480
2987
|
|
|
@@ -2565,6 +3072,16 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2565
3072
|
|
|
2566
3073
|
if ( invalidOutput ) throw new Error( 'EXRLoader.parse: invalid output format for specified file.' );
|
|
2567
3074
|
|
|
3075
|
+
// Luminance/chroma images always decode to RGBA; override whatever the output-format switch selected.
|
|
3076
|
+
if ( EXRDecoder.yCbCr ) {
|
|
3077
|
+
|
|
3078
|
+
EXRDecoder.format = RGBAFormat;
|
|
3079
|
+
EXRDecoder.outputChannels = 4;
|
|
3080
|
+
EXRDecoder.decodeChannels = { Y: 0, RY: 1, BY: 2 };
|
|
3081
|
+
fillAlpha = true;
|
|
3082
|
+
|
|
3083
|
+
}
|
|
3084
|
+
|
|
2568
3085
|
if ( EXRDecoder.type == 1 ) {
|
|
2569
3086
|
|
|
2570
3087
|
// half
|
|
@@ -2654,7 +3171,33 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2654
3171
|
|
|
2655
3172
|
}
|
|
2656
3173
|
|
|
2657
|
-
if ( EXRHeader.spec.
|
|
3174
|
+
if ( EXRHeader.spec.deepFormat ) {
|
|
3175
|
+
|
|
3176
|
+
// Deep format: offset tables are already parsed in the main flow.
|
|
3177
|
+
// Compute per-channel byte sizes for the deep pixel data layout.
|
|
3178
|
+
|
|
3179
|
+
EXRDecoder.deepChannels = [];
|
|
3180
|
+
let deepBytesPerSample = 0;
|
|
3181
|
+
|
|
3182
|
+
for ( const channel of EXRHeader.channels ) {
|
|
3183
|
+
|
|
3184
|
+
// UINT=0→4bytes, HALF=1→2bytes, FLOAT=2→4bytes
|
|
3185
|
+
const bytesPerSample = channel.pixelType === 0 ? 4 : channel.pixelType * 2;
|
|
3186
|
+
EXRDecoder.deepChannels.push( {
|
|
3187
|
+
name: channel.name,
|
|
3188
|
+
pixelType: channel.pixelType,
|
|
3189
|
+
bytesPerSample: bytesPerSample,
|
|
3190
|
+
} );
|
|
3191
|
+
deepBytesPerSample += bytesPerSample;
|
|
3192
|
+
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
EXRDecoder.deepBytesPerSample = deepBytesPerSample;
|
|
3196
|
+
EXRDecoder.chunkOffsets = EXRHeader._chunkOffsets;
|
|
3197
|
+
EXRDecoder.multiPart = EXRHeader.spec.multiPart;
|
|
3198
|
+
EXRDecoder.decode = parseDeepScanline.bind( EXRDecoder );
|
|
3199
|
+
|
|
3200
|
+
} else if ( EXRHeader.spec.singleTile ) {
|
|
2658
3201
|
|
|
2659
3202
|
EXRDecoder.blockHeight = EXRHeader.tiles.ySize;
|
|
2660
3203
|
EXRDecoder.blockWidth = EXRHeader.tiles.xSize;
|
|
@@ -2674,6 +3217,13 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2674
3217
|
|
|
2675
3218
|
EXRDecoder.decode = parseTiles.bind( EXRDecoder );
|
|
2676
3219
|
|
|
3220
|
+
} else if ( EXRHeader.spec.multiPart ) {
|
|
3221
|
+
|
|
3222
|
+
// Multi-part scanline: offsets already parsed in main flow.
|
|
3223
|
+
EXRDecoder.blockWidth = EXRDecoder.width;
|
|
3224
|
+
EXRDecoder.chunkOffsets = EXRHeader._chunkOffsets;
|
|
3225
|
+
EXRDecoder.decode = parseMultiPartScanline.bind( EXRDecoder );
|
|
3226
|
+
|
|
2677
3227
|
} else {
|
|
2678
3228
|
|
|
2679
3229
|
EXRDecoder.blockWidth = EXRDecoder.width;
|
|
@@ -2696,7 +3246,38 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2696
3246
|
const uInt8Array = new Uint8Array( buffer );
|
|
2697
3247
|
|
|
2698
3248
|
// get header information and validate format.
|
|
2699
|
-
const
|
|
3249
|
+
const EXRHeaders = parseHeader( bufferDataView, buffer, offset );
|
|
3250
|
+
|
|
3251
|
+
// select part to decode
|
|
3252
|
+
const partIndex = Math.max( 0, Math.min( this.part, EXRHeaders.length - 1 ) );
|
|
3253
|
+
const EXRHeader = EXRHeaders[ partIndex ];
|
|
3254
|
+
|
|
3255
|
+
// for multi-part deep files, skip offset tables for other parts
|
|
3256
|
+
if ( EXRHeader.spec.multiPart || EXRHeader.spec.deepFormat ) {
|
|
3257
|
+
|
|
3258
|
+
for ( let p = 0; p < EXRHeaders.length; p ++ ) {
|
|
3259
|
+
|
|
3260
|
+
const chunkCount = EXRHeaders[ p ].chunkCount;
|
|
3261
|
+
|
|
3262
|
+
if ( p === partIndex ) {
|
|
3263
|
+
|
|
3264
|
+
// store offset table for the selected part
|
|
3265
|
+
EXRHeader._chunkOffsets = [];
|
|
3266
|
+
|
|
3267
|
+
for ( let i = 0; i < chunkCount; i ++ )
|
|
3268
|
+
EXRHeader._chunkOffsets.push( parseInt64( bufferDataView, offset ) );
|
|
3269
|
+
|
|
3270
|
+
} else {
|
|
3271
|
+
|
|
3272
|
+
// skip other parts' offset tables
|
|
3273
|
+
for ( let i = 0; i < chunkCount; i ++ )
|
|
3274
|
+
parseInt64( bufferDataView, offset );
|
|
3275
|
+
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
}
|
|
2700
3281
|
|
|
2701
3282
|
// get input compression information and prepare decoding.
|
|
2702
3283
|
const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type, this.outputFormat );
|
|
@@ -2723,6 +3304,51 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2723
3304
|
|
|
2724
3305
|
}
|
|
2725
3306
|
|
|
3307
|
+
// Luminance/chroma → RGB conversion (second pass).
|
|
3308
|
+
// Y/RY/BY were decoded into output slots 0/1/2; convert in-place using Rec.709 coefficients:
|
|
3309
|
+
// R = ( 1 + RY ) * Y, B = ( 1 + BY ) * Y, G = ( Y − R·0.2126 − B·0.0722 ) / 0.7152
|
|
3310
|
+
if ( EXRDecoder.yCbCr ) {
|
|
3311
|
+
|
|
3312
|
+
const byteArray = EXRDecoder.byteArray;
|
|
3313
|
+
const nPixels = EXRDecoder.width * EXRDecoder.height;
|
|
3314
|
+
|
|
3315
|
+
if ( this.type === HalfFloatType ) {
|
|
3316
|
+
|
|
3317
|
+
for ( let i = 0; i < nPixels; i ++ ) {
|
|
3318
|
+
|
|
3319
|
+
const base = i * 4;
|
|
3320
|
+
const Y = decodeFloat16( byteArray[ base ] );
|
|
3321
|
+
const RY = decodeFloat16( byteArray[ base + 1 ] );
|
|
3322
|
+
const BY = decodeFloat16( byteArray[ base + 2 ] );
|
|
3323
|
+
const R = ( 1 + RY ) * Y;
|
|
3324
|
+
const B = ( 1 + BY ) * Y;
|
|
3325
|
+
const G = ( Y - R * 0.2126 - B * 0.0722 ) / 0.7152;
|
|
3326
|
+
byteArray[ base ] = DataUtils.toHalfFloat( Math.max( 0, R ) );
|
|
3327
|
+
byteArray[ base + 1 ] = DataUtils.toHalfFloat( Math.max( 0, G ) );
|
|
3328
|
+
byteArray[ base + 2 ] = DataUtils.toHalfFloat( Math.max( 0, B ) );
|
|
3329
|
+
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
} else {
|
|
3333
|
+
|
|
3334
|
+
for ( let i = 0; i < nPixels; i ++ ) {
|
|
3335
|
+
|
|
3336
|
+
const base = i * 4;
|
|
3337
|
+
const Y = byteArray[ base ];
|
|
3338
|
+
const RY = byteArray[ base + 1 ];
|
|
3339
|
+
const BY = byteArray[ base + 2 ];
|
|
3340
|
+
const R = ( 1 + RY ) * Y;
|
|
3341
|
+
const B = ( 1 + BY ) * Y;
|
|
3342
|
+
byteArray[ base ] = Math.max( 0, R );
|
|
3343
|
+
byteArray[ base + 1 ] = Math.max( 0, ( Y - R * 0.2126 - B * 0.0722 ) / 0.7152 );
|
|
3344
|
+
byteArray[ base + 2 ] = Math.max( 0, B );
|
|
3345
|
+
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
}
|
|
3351
|
+
|
|
2726
3352
|
return {
|
|
2727
3353
|
header: EXRHeader,
|
|
2728
3354
|
width: EXRDecoder.width,
|
|
@@ -2761,6 +3387,19 @@ class EXRLoader extends DataTextureLoader {
|
|
|
2761
3387
|
|
|
2762
3388
|
}
|
|
2763
3389
|
|
|
3390
|
+
/**
|
|
3391
|
+
* For multi-part EXR files, sets which part to load.
|
|
3392
|
+
*
|
|
3393
|
+
* @param {number} value - The part index to load.
|
|
3394
|
+
* @return {EXRLoader} A reference to this loader.
|
|
3395
|
+
*/
|
|
3396
|
+
setPart( value ) {
|
|
3397
|
+
|
|
3398
|
+
this.part = value;
|
|
3399
|
+
return this;
|
|
3400
|
+
|
|
3401
|
+
}
|
|
3402
|
+
|
|
2764
3403
|
load( url, onLoad, onProgress, onError ) {
|
|
2765
3404
|
|
|
2766
3405
|
function onLoadCallback( texture, texData ) {
|