rayzee 8.6.1 → 8.8.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/README.md +35 -6
- package/dist/rayzee.es.js +4114 -2994
- package/dist/rayzee.umd.js +256 -18
- package/package.json +1 -1
- package/src/EngineDefaults.js +2 -0
- package/src/Passes/OIDNDenoiser.js +36 -10
- package/src/Passes/OIDNTemporalHistory.js +569 -0
- package/src/PathTracerApp.js +131 -7
- package/src/Processor/AssetLoader.js +14 -8
- package/src/Processor/EmissiveTriangleBuilder.js +70 -0
- package/src/Processor/PBRT/PBRTAnimation.js +646 -0
- package/src/Processor/PBRT/PBRTParser.js +179 -52
- package/src/Processor/PBRT/PBRTSceneBuilder.js +181 -25
- package/src/Processor/PBRT/index.js +73 -14
- package/src/Processor/SceneProcessor.js +22 -0
- package/src/Stages/NormalDepth.js +94 -3
- package/src/Stages/PathTracer.js +1 -0
- package/src/Stages/PathTracerStage.js +20 -1
- package/src/managers/AnimationManager.js +202 -8
- package/src/managers/DenoisingManager.js +254 -5
package/src/PathTracerApp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { WebGPURenderer, RectAreaLightNode, SRGBColorSpace, LinearSRGBColorSpace } from 'three/webgpu';
|
|
2
2
|
import { texture as _tslTexture, cubeTexture as _tslCubeTexture } from 'three/tsl';
|
|
3
3
|
import {
|
|
4
|
-
Scene, EventDispatcher, Box3
|
|
4
|
+
Scene, EventDispatcher, Box3, Vector3
|
|
5
5
|
} from 'three';
|
|
6
6
|
import { RectAreaLightTexturesLib } from 'three/addons/lights/RectAreaLightTexturesLib.js';
|
|
7
7
|
import { SceneHelpers } from './SceneHelpers.js';
|
|
@@ -195,6 +195,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
195
195
|
this.assetLoader = null;
|
|
196
196
|
this._sdf = null;
|
|
197
197
|
this._animRefitInFlight = false;
|
|
198
|
+
this._emittersMoved = false;
|
|
198
199
|
// Max material-texture dimension (longest edge); applied on each scene build.
|
|
199
200
|
this._maxTextureSize = DEFAULT_STATE.maxTextureSize;
|
|
200
201
|
|
|
@@ -446,6 +447,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
446
447
|
}
|
|
447
448
|
|
|
448
449
|
this.pipeline.render();
|
|
450
|
+
this.denoisingManager?.afterTrace();
|
|
449
451
|
|
|
450
452
|
if ( ! this.stages.pathTracer.isComplete ) {
|
|
451
453
|
|
|
@@ -566,8 +568,15 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
566
568
|
/**
|
|
567
569
|
* Resets the accumulation buffer.
|
|
568
570
|
* @param {boolean} soft - When true, preserves ASVGF temporal history
|
|
571
|
+
* @param {Object} [options]
|
|
572
|
+
* @param {boolean} [options.motion] - only object placements or geometry moved; keeps the OIDN
|
|
573
|
+
* motion history, which rejects what moved per pixel
|
|
569
574
|
*/
|
|
570
|
-
reset( soft = false ) {
|
|
575
|
+
reset( soft = false, { motion = false } = {} ) {
|
|
576
|
+
|
|
577
|
+
// Objects moving is the view changing, as far as the live denoiser's refresh cadence goes.
|
|
578
|
+
if ( motion ) this.stages.pathTracer?.noteViewChanged();
|
|
579
|
+
this.denoisingManager?.beforeReset( { keepHistory: soft || motion } );
|
|
571
580
|
|
|
572
581
|
if ( this.pipeline ) {
|
|
573
582
|
|
|
@@ -581,6 +590,8 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
581
590
|
|
|
582
591
|
}
|
|
583
592
|
|
|
593
|
+
this.denoisingManager?.afterReset();
|
|
594
|
+
|
|
584
595
|
// Whatever is on screen stays until its replacement is ready, including while the camera
|
|
585
596
|
// moves: the denoising manager decides, since only it knows something is coming.
|
|
586
597
|
this._abortPostProcess( { keepDisplay: true } );
|
|
@@ -1165,6 +1176,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1165
1176
|
// Stop any running animation before rebuilding scene data
|
|
1166
1177
|
this.animationManager.dispose();
|
|
1167
1178
|
this._animRefitInFlight = false;
|
|
1179
|
+
this._emittersMoved = false;
|
|
1168
1180
|
|
|
1169
1181
|
// Tag the primary (replace-loaded) model so it appears in the scene-object list.
|
|
1170
1182
|
this._tagPrimarySceneObject();
|
|
@@ -1615,7 +1627,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1615
1627
|
|
|
1616
1628
|
this.stages.pathTracer.updateTriangleData( this._sdf.triangles );
|
|
1617
1629
|
this.stages.pathTracer.updateBVHData( this._sdf.bvh );
|
|
1618
|
-
this.reset();
|
|
1630
|
+
this.reset( false, { motion: true } );
|
|
1619
1631
|
|
|
1620
1632
|
return result;
|
|
1621
1633
|
|
|
@@ -1637,7 +1649,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1637
1649
|
|
|
1638
1650
|
const { triRanges, bvhRanges } = this._sdf.computeBLASDirtyRanges( affectedMeshIndices );
|
|
1639
1651
|
this.stages.pathTracer.updateBufferRanges( triRanges, bvhRanges );
|
|
1640
|
-
this.reset();
|
|
1652
|
+
this.reset( false, { motion: true } );
|
|
1641
1653
|
|
|
1642
1654
|
// Kick off background rebuild for optimal SAH quality
|
|
1643
1655
|
this._sdf.scheduleBackgroundRebuild( affectedMeshIndices, ( meshIndex ) => {
|
|
@@ -1646,7 +1658,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1646
1658
|
// re-upload here is gigabytes on a large scene, for one mesh's worth of change.
|
|
1647
1659
|
const dirty = this._sdf.computeBLASDirtyRanges( [ meshIndex ] );
|
|
1648
1660
|
this.stages.pathTracer.updateBufferRanges( dirty.triRanges, dirty.bvhRanges );
|
|
1649
|
-
this.reset();
|
|
1661
|
+
this.reset( false, { motion: true } );
|
|
1650
1662
|
|
|
1651
1663
|
} );
|
|
1652
1664
|
|
|
@@ -1654,6 +1666,113 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1654
1666
|
|
|
1655
1667
|
}
|
|
1656
1668
|
|
|
1669
|
+
/** Apply a pose from AnimationManager: placements, visibility, and a followed camera. @private */
|
|
1670
|
+
_applyAnimationPose( { meshIndices, visibilityChanged, cameras } ) {
|
|
1671
|
+
|
|
1672
|
+
if ( ! this._sdf?.instanceTable ) return;
|
|
1673
|
+
let changed = false;
|
|
1674
|
+
|
|
1675
|
+
if ( meshIndices.length > 0 ) {
|
|
1676
|
+
|
|
1677
|
+
this._notePlacementsMoving( meshIndices );
|
|
1678
|
+
this._sdf.updateMeshTransforms( meshIndices );
|
|
1679
|
+
this.stages.pathTracer?.updateBufferRanges( [], [ this._sdf.computeTLASDirtyRange() ] );
|
|
1680
|
+
if ( this._sdf.movesEmitters( meshIndices ) ) {
|
|
1681
|
+
|
|
1682
|
+
this._emittersMoved = true;
|
|
1683
|
+
// Deferred while playing: the rebuild uploads a scene-sized map, and sampling
|
|
1684
|
+
// already reads each emitter through its placement.
|
|
1685
|
+
if ( ! this.animationManager.isPlaying ) this._refreshMovedEmitters();
|
|
1686
|
+
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
changed = true;
|
|
1690
|
+
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
if ( visibilityChanged ) {
|
|
1694
|
+
|
|
1695
|
+
this.stages.pathTracer?.updateAllMeshVisibility();
|
|
1696
|
+
this._refreshEmissiveForVisibility();
|
|
1697
|
+
changed = true;
|
|
1698
|
+
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
if ( cameras.length > 0 && this._followAnimatedCamera( cameras ) ) changed = true;
|
|
1702
|
+
|
|
1703
|
+
if ( changed ) this.reset( false, { motion: true } );
|
|
1704
|
+
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/** @private */
|
|
1708
|
+
_notePlacementsMoving( meshIndices ) {
|
|
1709
|
+
|
|
1710
|
+
const dm = this.denoisingManager;
|
|
1711
|
+
const table = this._sdf?.instanceTable;
|
|
1712
|
+
if ( ! dm?.historyActive || ! table ) return;
|
|
1713
|
+
|
|
1714
|
+
for ( const meshIndex of meshIndices ) {
|
|
1715
|
+
|
|
1716
|
+
const run = table.placementRunOf( meshIndex );
|
|
1717
|
+
if ( ! run ) continue;
|
|
1718
|
+
for ( let p = run.start; p < run.start + run.count; p ++ ) dm.notePlacementMoving( table.tlasLeafIndex[ p ], table.world, p * 16 );
|
|
1719
|
+
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/** @private */
|
|
1725
|
+
_refreshMovedEmitters() {
|
|
1726
|
+
|
|
1727
|
+
if ( ! this._emittersMoved ) return;
|
|
1728
|
+
this._emittersMoved = false;
|
|
1729
|
+
this._uploadEmissivePayload( this._sdf?.refreshEmissiveTransforms() ?? null );
|
|
1730
|
+
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
/**
|
|
1734
|
+
* Move the view with the selected camera's animated original — the camera list holds copies.
|
|
1735
|
+
* @returns {boolean} whether the view moved
|
|
1736
|
+
* @private
|
|
1737
|
+
*/
|
|
1738
|
+
_followAnimatedCamera( cameras ) {
|
|
1739
|
+
|
|
1740
|
+
const cm = this.cameraManager;
|
|
1741
|
+
const selected = cm.currentCameraIndex > 0 ? cm.cameras[ cm.currentCameraIndex ] : null;
|
|
1742
|
+
const uuid = selected?.userData?.__rayzeeSourceUuid;
|
|
1743
|
+
const source = uuid && cameras.find( c => c.uuid === uuid );
|
|
1744
|
+
if ( ! source ) return false;
|
|
1745
|
+
|
|
1746
|
+
source.updateWorldMatrix( true, false );
|
|
1747
|
+
const worldScale = new Vector3();
|
|
1748
|
+
source.matrixWorld.decompose( selected.position, selected.quaternion, worldScale );
|
|
1749
|
+
// Only the mirror survives.
|
|
1750
|
+
selected.scale.set( Math.sign( worldScale.x ) || 1, Math.sign( worldScale.y ) || 1, Math.sign( worldScale.z ) || 1 );
|
|
1751
|
+
if ( source.isPerspectiveCamera ) selected.fov = source.fov;
|
|
1752
|
+
|
|
1753
|
+
const camera = cm.camera;
|
|
1754
|
+
const controls = cm.controls;
|
|
1755
|
+
const distance = controls ? controls.target.distanceTo( camera.position ) : 0;
|
|
1756
|
+
|
|
1757
|
+
camera.position.copy( selected.position );
|
|
1758
|
+
camera.quaternion.copy( selected.quaternion );
|
|
1759
|
+
camera.scale.copy( selected.scale );
|
|
1760
|
+
if ( camera.isPerspectiveCamera && selected.isPerspectiveCamera ) camera.fov = selected.fov;
|
|
1761
|
+
camera.updateProjectionMatrix();
|
|
1762
|
+
camera.updateMatrixWorld( true );
|
|
1763
|
+
|
|
1764
|
+
// Pivot ahead at the same distance, so controls.update() keeps the pose.
|
|
1765
|
+
if ( controls ) {
|
|
1766
|
+
|
|
1767
|
+
const forward = new Vector3( 0, 0, - 1 ).applyQuaternion( camera.quaternion );
|
|
1768
|
+
controls.target.copy( camera.position ).addScaledVector( forward, distance || 1 );
|
|
1769
|
+
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
return true;
|
|
1773
|
+
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1657
1776
|
/**
|
|
1658
1777
|
* Apply new transforms for objects that moved, without touching their geometry.
|
|
1659
1778
|
*
|
|
@@ -1667,10 +1786,11 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
1667
1786
|
*/
|
|
1668
1787
|
updateMeshTransforms( meshIndices ) {
|
|
1669
1788
|
|
|
1789
|
+
this._notePlacementsMoving( meshIndices );
|
|
1670
1790
|
const result = this._sdf.updateMeshTransforms( meshIndices );
|
|
1671
1791
|
|
|
1672
1792
|
this.stages.pathTracer.updateBufferRanges( [], [ this._sdf.computeTLASDirtyRange() ] );
|
|
1673
|
-
this.reset();
|
|
1793
|
+
this.reset( false, { motion: true } );
|
|
1674
1794
|
|
|
1675
1795
|
return result;
|
|
1676
1796
|
|
|
@@ -2873,10 +2993,11 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
2873
2993
|
const table = this._sdf?.instanceTable;
|
|
2874
2994
|
if ( ! table ) return;
|
|
2875
2995
|
|
|
2996
|
+
// The emitter list names meshes, the table placements.
|
|
2876
2997
|
const hidden = new Set();
|
|
2877
2998
|
for ( let i = 0; i < table.count; i ++ ) {
|
|
2878
2999
|
|
|
2879
|
-
if ( table.isSet[ i ] && ! table.visible[ i ] ) hidden.add( i );
|
|
3000
|
+
if ( table.isSet[ i ] && ! table.visible[ i ] ) hidden.add( table.sourceMesh[ i ] );
|
|
2880
3001
|
|
|
2881
3002
|
}
|
|
2882
3003
|
|
|
@@ -3410,6 +3531,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
3410
3531
|
|
|
3411
3532
|
// Animation lifecycle → wake + refit flag
|
|
3412
3533
|
this.animationManager.wakeCallback = () => this.wake();
|
|
3534
|
+
this.animationManager.applyPoseCallback = ( pose ) => this._applyAnimationPose( pose );
|
|
3413
3535
|
this._forwardEvents( this.animationManager, [
|
|
3414
3536
|
EngineEvents.ANIMATION_STARTED,
|
|
3415
3537
|
EngineEvents.ANIMATION_PAUSED,
|
|
@@ -3418,6 +3540,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
3418
3540
|
this._addTrackedListener( this.animationManager, EngineEvents.ANIMATION_PAUSED, () => {
|
|
3419
3541
|
|
|
3420
3542
|
this._animRefitInFlight = false;
|
|
3543
|
+
this._refreshMovedEmitters();
|
|
3421
3544
|
|
|
3422
3545
|
} );
|
|
3423
3546
|
this._addTrackedListener( this.animationManager, EngineEvents.ANIMATION_STOPPED, () => {
|
|
@@ -3538,6 +3661,7 @@ export class PathTracerApp extends EventDispatcher {
|
|
|
3538
3661
|
this.animationManager.onFinished = () => {
|
|
3539
3662
|
|
|
3540
3663
|
this._animRefitInFlight = false;
|
|
3664
|
+
this._refreshMovedEmitters();
|
|
3541
3665
|
this.dispatchEvent( { type: EngineEvents.ANIMATION_FINISHED } );
|
|
3542
3666
|
|
|
3543
3667
|
};
|
|
@@ -787,10 +787,11 @@ export class AssetLoader extends EventDispatcher {
|
|
|
787
787
|
};
|
|
788
788
|
|
|
789
789
|
const pbrtStart = performance.now();
|
|
790
|
-
const { group, environment, report, warnings, meshCount, entryPath: loadedEntry, candidates,
|
|
791
|
-
parseMs, buildMs, triangleCount, placementCount, mergedShapes, skippedForBudget,
|
|
790
|
+
const { group, environment, animations, report, warnings, meshCount, entryPath: loadedEntry, candidates,
|
|
791
|
+
frames, parseMs, buildMs, triangleCount, placementCount, mergedShapes, skippedForBudget,
|
|
792
792
|
droppedNoTemplate } = await loadPBRTScene( {
|
|
793
793
|
vfs: zip, source, entryPath, plyParser, imageFromBytes, envFromBytes,
|
|
794
|
+
animation: options.animation,
|
|
794
795
|
maxTriangles: options.maxTriangles,
|
|
795
796
|
maxPlacements: options.maxPlacements,
|
|
796
797
|
mergeShapesAbove: options.mergeShapesAbove,
|
|
@@ -802,15 +803,17 @@ export class AssetLoader extends EventDispatcher {
|
|
|
802
803
|
// SceneProcessor.performanceMetrics.
|
|
803
804
|
this.lastPBRTStats = {
|
|
804
805
|
parseMs, buildMs, loaderMs: performance.now() - pbrtStart,
|
|
805
|
-
triangleCount, placementCount, mergedShapes, skippedForBudget, droppedNoTemplate, meshCount
|
|
806
|
+
triangleCount, placementCount, mergedShapes, skippedForBudget, droppedNoTemplate, meshCount,
|
|
807
|
+
frames: frames?.length ?? 1
|
|
806
808
|
};
|
|
807
809
|
|
|
808
|
-
// An archive can hold several independent scenes (
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
|
|
810
|
+
// An archive can hold several independent scenes (bistro ships three views). Only one is
|
|
811
|
+
// loaded, so name it and the alternatives rather than leave the user comparing against a
|
|
812
|
+
// reference of a different scene. A frame sequence loads whole, as its animation.
|
|
813
|
+
const loaded = new Set( frames ?? [ loadedEntry ] );
|
|
814
|
+
const others = candidates.filter( p => ! loaded.has( p ) );
|
|
815
|
+
if ( others.length > 0 ) {
|
|
812
816
|
|
|
813
|
-
const others = candidates.filter( p => p !== loadedEntry );
|
|
814
817
|
console.warn( `PBRT archive holds ${candidates.length} scenes; loaded "${loadedEntry}". Others: ${others.join( ', ' )}` );
|
|
815
818
|
this._issues?.record(
|
|
816
819
|
ISSUE_CODES.ASSET_AMBIGUOUS_ENTRY,
|
|
@@ -849,6 +852,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
849
852
|
group.name = loadedEntry || filename;
|
|
850
853
|
this.releaseTargetModel();
|
|
851
854
|
this.targetModel = group;
|
|
855
|
+
this.animations = animations ?? [];
|
|
852
856
|
|
|
853
857
|
// The light's own orientation and `scale` are already baked into the texture, so the
|
|
854
858
|
// scene is only correct at rotation 0 / intensity 1 — pinned, whatever the host's defaults.
|
|
@@ -2032,6 +2036,8 @@ export class AssetLoader extends EventDispatcher {
|
|
|
2032
2036
|
|
|
2033
2037
|
// Clone the camera to avoid modifying the original
|
|
2034
2038
|
const camera = object.clone();
|
|
2039
|
+
// The clip animates the original; this lets the copy follow it.
|
|
2040
|
+
camera.userData.__rayzeeSourceUuid = object.uuid;
|
|
2035
2041
|
|
|
2036
2042
|
// Apply world transforms — cameras may be children of
|
|
2037
2043
|
// transformed nodes, so local position/quaternion != world.
|
|
@@ -72,6 +72,7 @@ export class EmissiveTriangleBuilder {
|
|
|
72
72
|
extractEmissiveTriangles( triangleData, materials, triangleCount, table = null ) {
|
|
73
73
|
|
|
74
74
|
this.emissiveTriangles = [];
|
|
75
|
+
this._emissiveMeshes = null;
|
|
75
76
|
this.totalEmissivePower = 0;
|
|
76
77
|
this._totalTriangleCount = triangleCount;
|
|
77
78
|
|
|
@@ -172,6 +173,7 @@ export class EmissiveTriangleBuilder {
|
|
|
172
173
|
triangleIndex: i,
|
|
173
174
|
materialIndex: materialIndex,
|
|
174
175
|
meshIndex: meshIndex,
|
|
176
|
+
sideSign,
|
|
175
177
|
instanceLeaf: hasInstance ? table.tlasLeafIndex[ placement ] : - 1,
|
|
176
178
|
power: power,
|
|
177
179
|
area: area,
|
|
@@ -203,6 +205,74 @@ export class EmissiveTriangleBuilder {
|
|
|
203
205
|
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
/** Mesh indices owning at least one emissive triangle. */
|
|
209
|
+
get emissiveMeshes() {
|
|
210
|
+
|
|
211
|
+
return this._emissiveMeshes ??= new Set( this.emissiveTriangles.map( t => t.meshIndex ) );
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Re-measure every emitter at its placement's current matrix. Call buildLightBVH() after.
|
|
217
|
+
* @param {Uint32Array|object} triangleData - packed triangle records, flat or chunked
|
|
218
|
+
* @param {import('./InstanceTable.js').InstanceTable} table
|
|
219
|
+
*/
|
|
220
|
+
refreshTransforms( triangleData, table ) {
|
|
221
|
+
|
|
222
|
+
const chunked = triangleData && triangleData.chunks ? triangleData : null;
|
|
223
|
+
const chunkedF = chunked ? chunked.viewAs( Float32Array ) : null;
|
|
224
|
+
const flatF = chunked ? null : new Float32Array( triangleData.buffer, triangleData.byteOffset, triangleData.length );
|
|
225
|
+
const FLOATS_PER_TRIANGLE = TRIANGLE_DATA_LAYOUT.FLOATS_PER_TRIANGLE;
|
|
226
|
+
const m = table.world;
|
|
227
|
+
|
|
228
|
+
this.totalEmissivePower = 0;
|
|
229
|
+
|
|
230
|
+
for ( const tri of this.emissiveTriangles ) {
|
|
231
|
+
|
|
232
|
+
const f = chunked ? chunkedF.chunkFor( tri.triangleIndex ) : flatF;
|
|
233
|
+
const b = chunked ? chunked.baseOf( tri.triangleIndex ) : tri.triangleIndex * FLOATS_PER_TRIANGLE;
|
|
234
|
+
const v = [ f[ b ], f[ b + 1 ], f[ b + 2 ], f[ b + 4 ], f[ b + 5 ], f[ b + 6 ], f[ b + 8 ], f[ b + 9 ], f[ b + 10 ] ];
|
|
235
|
+
|
|
236
|
+
// As extraction resolves it.
|
|
237
|
+
const placement = table.placementRunOf?.( tri.meshIndex )?.start ?? - 1;
|
|
238
|
+
if ( placement >= 0 && table.isSet[ placement ] ) {
|
|
239
|
+
|
|
240
|
+
const o = placement * 16;
|
|
241
|
+
for ( let k = 0; k < 9; k += 3 ) {
|
|
242
|
+
|
|
243
|
+
const x = v[ k ], y = v[ k + 1 ], z = v[ k + 2 ];
|
|
244
|
+
v[ k ] = m[ o ] * x + m[ o + 4 ] * y + m[ o + 8 ] * z + m[ o + 12 ];
|
|
245
|
+
v[ k + 1 ] = m[ o + 1 ] * x + m[ o + 5 ] * y + m[ o + 9 ] * z + m[ o + 13 ];
|
|
246
|
+
v[ k + 2 ] = m[ o + 2 ] * x + m[ o + 6 ] * y + m[ o + 10 ] * z + m[ o + 14 ];
|
|
247
|
+
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const [ v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z ] = v;
|
|
253
|
+
const area = this._calculateTriangleArea( v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z );
|
|
254
|
+
const e = tri.emissive;
|
|
255
|
+
tri.power = ( 0.2126 * e.r + 0.7152 * e.g + 0.0722 * e.b ) * tri.emissiveIntensity * area;
|
|
256
|
+
tri.area = area;
|
|
257
|
+
|
|
258
|
+
const nx = ( v1y - v0y ) * ( v2z - v0z ) - ( v1z - v0z ) * ( v2y - v0y );
|
|
259
|
+
const ny = ( v1z - v0z ) * ( v2x - v0x ) - ( v1x - v0x ) * ( v2z - v0z );
|
|
260
|
+
const nz = ( v1x - v0x ) * ( v2y - v0y ) - ( v1y - v0y ) * ( v2x - v0x );
|
|
261
|
+
const nl = ( Math.sqrt( nx * nx + ny * ny + nz * nz ) || 1 ) / tri.sideSign;
|
|
262
|
+
tri.nx = nx / nl; tri.ny = ny / nl; tri.nz = nz / nl;
|
|
263
|
+
|
|
264
|
+
tri.cx = ( v0x + v1x + v2x ) / 3; tri.cy = ( v0y + v1y + v2y ) / 3; tri.cz = ( v0z + v1z + v2z ) / 3;
|
|
265
|
+
tri.bMinX = Math.min( v0x, v1x, v2x ); tri.bMinY = Math.min( v0y, v1y, v2y ); tri.bMinZ = Math.min( v0z, v1z, v2z );
|
|
266
|
+
tri.bMaxX = Math.max( v0x, v1x, v2x ); tri.bMaxY = Math.max( v0y, v1y, v2y ); tri.bMaxZ = Math.max( v0z, v1z, v2z );
|
|
267
|
+
|
|
268
|
+
this.totalEmissivePower += tri.power;
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
this._buildDataArrays();
|
|
273
|
+
|
|
274
|
+
}
|
|
275
|
+
|
|
206
276
|
/**
|
|
207
277
|
* Calculate triangle area using cross product
|
|
208
278
|
*/
|