diff --git a/src/_polyfill/CapsuleGeometry.js b/src/_polyfill/CapsuleGeometry.js index c2e655a5..8a5fc0ff 100644 --- a/src/_polyfill/CapsuleGeometry.js +++ b/src/_polyfill/CapsuleGeometry.js @@ -1,26 +1,30 @@ import { Path, LatheGeometry } from 'three' -class CapsuleGeometry extends LatheGeometry { - constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { - const path = new Path() - path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0) - path.absarc(0, length / 2, radius, 0, Math.PI * 0.5) +const CapsuleGeometry = /* @__PURE__ */ (() => { + class CapsuleGeometry extends LatheGeometry { + constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) { + const path = new Path() + path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0) + path.absarc(0, length / 2, radius, 0, Math.PI * 0.5) - super(path.getPoints(capSegments), radialSegments) + super(path.getPoints(capSegments), radialSegments) - this.type = 'CapsuleGeometry' + this.type = 'CapsuleGeometry' - this.parameters = { - radius: radius, - height: length, - capSegments: capSegments, - radialSegments: radialSegments, + this.parameters = { + radius: radius, + height: length, + capSegments: capSegments, + radialSegments: radialSegments, + } } - } - static fromJSON(data) { - return new CapsuleGeometry(data.radius, data.length, data.capSegments, data.radialSegments) + static fromJSON(data) { + return new CapsuleGeometry(data.radius, data.length, data.capSegments, data.radialSegments) + } } -} + + return CapsuleGeometry +})() export { CapsuleGeometry } diff --git a/src/_polyfill/constants.ts b/src/_polyfill/constants.ts index 1237ca1f..aabfc441 100644 --- a/src/_polyfill/constants.ts +++ b/src/_polyfill/constants.ts @@ -1,3 +1,3 @@ import { REVISION } from 'three' -export const version = parseInt(REVISION.replace(/\D+/g, '')) +export const version = /* @__PURE__ */ (() => parseInt(REVISION.replace(/\D+/g, '')))() diff --git a/src/animation/AnimationClipCreator.js b/src/animation/AnimationClipCreator.js index 2befa398..23ea4050 100644 --- a/src/animation/AnimationClipCreator.js +++ b/src/animation/AnimationClipCreator.js @@ -7,8 +7,8 @@ import { VectorKeyframeTrack, } from 'three' -class AnimationClipCreator { - static CreateRotationAnimation(period, axis = 'x') { +const AnimationClipCreator = { + CreateRotationAnimation(period, axis = 'x') { const times = [0, period], values = [0, 360] @@ -17,9 +17,9 @@ class AnimationClipCreator { const track = new NumberKeyframeTrack(trackName, times, values) return new AnimationClip(null, period, [track]) - } + }, - static CreateScaleAxisAnimation(period, axis = 'x') { + CreateScaleAxisAnimation(period, axis = 'x') { const times = [0, period], values = [0, 1] @@ -28,9 +28,9 @@ class AnimationClipCreator { const track = new NumberKeyframeTrack(trackName, times, values) return new AnimationClip(null, period, [track]) - } + }, - static CreateShakeAnimation(duration, shakeScale) { + CreateShakeAnimation(duration, shakeScale) { const times = [], values = [], tmp = new Vector3() @@ -49,9 +49,9 @@ class AnimationClipCreator { const track = new VectorKeyframeTrack(trackName, times, values) return new AnimationClip(null, duration, [track]) - } + }, - static CreatePulsationAnimation(duration, pulseScale) { + CreatePulsationAnimation(duration, pulseScale) { const times = [], values = [], tmp = new Vector3() @@ -68,9 +68,9 @@ class AnimationClipCreator { const track = new VectorKeyframeTrack(trackName, times, values) return new AnimationClip(null, duration, [track]) - } + }, - static CreateVisibilityAnimation(duration) { + CreateVisibilityAnimation(duration) { const times = [0, duration / 2, duration], values = [true, false, true] @@ -79,9 +79,9 @@ class AnimationClipCreator { const track = new BooleanKeyframeTrack(trackName, times, values) return new AnimationClip(null, duration, [track]) - } + }, - static CreateMaterialColorAnimation(duration, colors) { + CreateMaterialColorAnimation(duration, colors) { const times = [], values = [], timeStep = duration / colors.length @@ -98,7 +98,7 @@ class AnimationClipCreator { const track = new ColorKeyframeTrack(trackName, times, values) return new AnimationClip(null, duration, [track]) - } + }, } export { AnimationClipCreator } diff --git a/src/animation/CCDIKSolver.js b/src/animation/CCDIKSolver.js index 0e5650a0..75fff7e4 100644 --- a/src/animation/CCDIKSolver.js +++ b/src/animation/CCDIKSolver.js @@ -13,17 +13,17 @@ import { Vector3, } from 'three' -const _q = new Quaternion() -const _targetPos = new Vector3() -const _targetVec = new Vector3() -const _effectorPos = new Vector3() -const _effectorVec = new Vector3() -const _linkPos = new Vector3() -const _invLinkQ = new Quaternion() -const _linkScale = new Vector3() -const _axis = new Vector3() -const _vector = new Vector3() -const _matrix = new Matrix4() +const _q = /* @__PURE__ */ new Quaternion() +const _targetPos = /* @__PURE__ */ new Vector3() +const _targetVec = /* @__PURE__ */ new Vector3() +const _effectorPos = /* @__PURE__ */ new Vector3() +const _effectorVec = /* @__PURE__ */ new Vector3() +const _linkPos = /* @__PURE__ */ new Vector3() +const _invLinkQ = /* @__PURE__ */ new Quaternion() +const _linkScale = /* @__PURE__ */ new Vector3() +const _axis = /* @__PURE__ */ new Vector3() +const _vector = /* @__PURE__ */ new Vector3() +const _matrix = /* @__PURE__ */ new Matrix4() /** * CCD Algorithm diff --git a/src/animation/MMDAnimationHelper.js b/src/animation/MMDAnimationHelper.js index 8ec13284..396c7424 100644 --- a/src/animation/MMDAnimationHelper.js +++ b/src/animation/MMDAnimationHelper.js @@ -861,7 +861,7 @@ class AudioManager { } } -const _q = new Quaternion() +const _q = /* @__PURE__ */ new Quaternion() /** * Solver for Grant (Fuyo in Japanese. I just google translated because diff --git a/src/animation/MMDPhysics.js b/src/animation/MMDPhysics.js index c11992cc..22ea4ea2 100644 --- a/src/animation/MMDPhysics.js +++ b/src/animation/MMDPhysics.js @@ -1001,10 +1001,10 @@ class Constraint { } } -const _position = new Vector3() -const _quaternion = new Quaternion() -const _scale = new Vector3() -const _matrixWorldInv = new Matrix4() +const _position = /* @__PURE__ */ new Vector3() +const _quaternion = /* @__PURE__ */ new Quaternion() +const _scale = /* @__PURE__ */ new Vector3() +const _matrixWorldInv = /* @__PURE__ */ new Matrix4() class MMDPhysicsHelper extends Object3D { /** diff --git a/src/controls/ArcballControls.ts b/src/controls/ArcballControls.ts index 9a64a49c..c048168f 100644 --- a/src/controls/ArcballControls.ts +++ b/src/controls/ArcballControls.ts @@ -68,8 +68,8 @@ const _center = { //transformation matrices for gizmos and camera const _transformation: Transformation = { - camera: new Matrix4(), - gizmos: new Matrix4(), + camera: /* @__PURE__ */ new Matrix4(), + gizmos: /* @__PURE__ */ new Matrix4(), } //events diff --git a/src/controls/FirstPersonControls.ts b/src/controls/FirstPersonControls.ts index b69983f6..bc40ae9b 100644 --- a/src/controls/FirstPersonControls.ts +++ b/src/controls/FirstPersonControls.ts @@ -2,7 +2,7 @@ import { MathUtils, Spherical, Vector3, Camera } from 'three' import { EventDispatcher } from './EventDispatcher' import { StandardControlsEventMap } from './StandardControlsEventMap' -const targetPosition = new Vector3() +const targetPosition = /* @__PURE__ */ new Vector3() export class FirstPersonControls extends EventDispatcher<{}> { public object: Camera diff --git a/src/controls/OrbitControls.ts b/src/controls/OrbitControls.ts index 93d8a764..cad6a154 100644 --- a/src/controls/OrbitControls.ts +++ b/src/controls/OrbitControls.ts @@ -14,8 +14,8 @@ import { import { EventDispatcher } from './EventDispatcher' import { StandardControlsEventMap } from './StandardControlsEventMap' -const _ray = new Ray() -const _plane = new Plane() +const _ray = /* @__PURE__ */ new Ray() +const _plane = /* @__PURE__ */ new Plane() const TILT_LIMIT = Math.cos(70 * (Math.PI / 180)) // This set of controls performs orbiting, dollying (zooming), and panning. @@ -119,7 +119,6 @@ class OrbitControls extends EventDispatcher { getScale: () => number // Set the current scale (these are not used in most scenarios, however they can be useful for specific use cases) setScale: (newScale: number) => void - constructor(object: PerspectiveCamera | OrthographicCamera, domElement?: HTMLElement) { super() @@ -1096,21 +1095,21 @@ class OrbitControls extends EventDispatcher { } this.dollyOut = (dollyScale = getZoomScale()) => { - dollyOut(dollyScale) - scope.update() + dollyOut(dollyScale) + scope.update() } this.getScale = () => { - return scale; + return scale } this.setScale = (newScale) => { - setScale(newScale) - scope.update() + setScale(newScale) + scope.update() } this.getZoomScale = () => { - return getZoomScale(); + return getZoomScale() } // connect events diff --git a/src/controls/PointerLockControls.ts b/src/controls/PointerLockControls.ts index ee93891e..19d6fd0f 100644 --- a/src/controls/PointerLockControls.ts +++ b/src/controls/PointerLockControls.ts @@ -1,8 +1,8 @@ import { Euler, Camera, Vector3 } from 'three' import { EventDispatcher } from './EventDispatcher' -const _euler = new Euler(0, 0, 0, 'YXZ') -const _vector = new Vector3() +const _euler = /* @__PURE__ */ new Euler(0, 0, 0, 'YXZ') +const _vector = /* @__PURE__ */ new Vector3() const _changeEvent = { type: 'change' } const _lockEvent = { type: 'lock' } const _unlockEvent = { type: 'unlock' } @@ -12,17 +12,17 @@ export interface PointerLockControlsEventMap { /** * Fires when the user moves the mouse. */ - change: {}; + change: {} /** * Fires when the pointer lock status is "locked" (in other words: the mouse is captured). */ - lock: {}; + lock: {} /** * Fires when the pointer lock status is "unlocked" (in other words: the mouse is not captured anymore). */ - unlock: {}; + unlock: {} } class PointerLockControls extends EventDispatcher { diff --git a/src/csm/CSM.js b/src/csm/CSM.js index afa6cdee..ce5359af 100644 --- a/src/csm/CSM.js +++ b/src/csm/CSM.js @@ -2,10 +2,10 @@ import { Vector2, Vector3, DirectionalLight, MathUtils, ShaderChunk, Matrix4, Bo import { CSMFrustum } from './CSMFrustum' import { CSMShader } from './CSMShader' -const _cameraToLightMatrix = new Matrix4() -const _lightSpaceFrustum = new CSMFrustum() -const _center = new Vector3() -const _bbox = new Box3() +const _cameraToLightMatrix = /* @__PURE__ */ new Matrix4() +const _lightSpaceFrustum = /* @__PURE__ */ new CSMFrustum() +const _center = /* @__PURE__ */ new Vector3() +const _bbox = /* @__PURE__ */ new Box3() const _uniformArray = [] const _logArray = [] diff --git a/src/csm/CSMFrustum.js b/src/csm/CSMFrustum.js index ab8f7d15..3e375417 100644 --- a/src/csm/CSMFrustum.js +++ b/src/csm/CSMFrustum.js @@ -1,6 +1,6 @@ import { Vector3, Matrix4 } from 'three' -const inverseProjectionMatrix = new Matrix4() +const inverseProjectionMatrix = /* @__PURE__ */ new Matrix4() class CSMFrustum { constructor(data) { diff --git a/src/deprecated/Geometry.js b/src/deprecated/Geometry.js index d4d5afc2..c720b2a2 100644 --- a/src/deprecated/Geometry.js +++ b/src/deprecated/Geometry.js @@ -14,330 +14,300 @@ import { Vector3, } from 'three' -const _m1 = new Matrix4() -const _obj = new Object3D() -const _offset = new Vector3() +const _m1 = /* @__PURE__ */ new Matrix4() +const _obj = /* @__PURE__ */ new Object3D() +const _offset = /* @__PURE__ */ new Vector3() -class Geometry extends EventDispatcher { - static createBufferGeometryFromObject(object) { - let buffergeometry = new BufferGeometry() +const Geometry = /* @__PURE__ */ (() => { + class Geometry extends EventDispatcher { + static createBufferGeometryFromObject(object) { + let buffergeometry = new BufferGeometry() - const geometry = object.geometry + const geometry = object.geometry - if (object.isPoints || object.isLine) { - const positions = new Float32BufferAttribute(geometry.vertices.length * 3, 3) - const colors = new Float32BufferAttribute(geometry.colors.length * 3, 3) + if (object.isPoints || object.isLine) { + const positions = new Float32BufferAttribute(geometry.vertices.length * 3, 3) + const colors = new Float32BufferAttribute(geometry.colors.length * 3, 3) - buffergeometry.setAttribute('position', positions.copyVector3sArray(geometry.vertices)) - buffergeometry.setAttribute('color', colors.copyColorsArray(geometry.colors)) + buffergeometry.setAttribute('position', positions.copyVector3sArray(geometry.vertices)) + buffergeometry.setAttribute('color', colors.copyColorsArray(geometry.colors)) - if (geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length) { - const lineDistances = new Float32BufferAttribute(geometry.lineDistances.length, 1) + if (geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length) { + const lineDistances = new Float32BufferAttribute(geometry.lineDistances.length, 1) - buffergeometry.setAttribute('lineDistance', lineDistances.copyArray(geometry.lineDistances)) - } + buffergeometry.setAttribute('lineDistance', lineDistances.copyArray(geometry.lineDistances)) + } - if (geometry.boundingSphere !== null) { - buffergeometry.boundingSphere = geometry.boundingSphere.clone() - } + if (geometry.boundingSphere !== null) { + buffergeometry.boundingSphere = geometry.boundingSphere.clone() + } - if (geometry.boundingBox !== null) { - buffergeometry.boundingBox = geometry.boundingBox.clone() + if (geometry.boundingBox !== null) { + buffergeometry.boundingBox = geometry.boundingBox.clone() + } + } else if (object.isMesh) { + buffergeometry = geometry.toBufferGeometry() } - } else if (object.isMesh) { - buffergeometry = geometry.toBufferGeometry() + + return buffergeometry } - return buffergeometry - } + constructor() { + super() + this.isGeometry = true + this.uuid = MathUtils.generateUUID() - constructor() { - super() - this.isGeometry = true - this.uuid = MathUtils.generateUUID() + this.name = '' + this.type = 'Geometry' - this.name = '' - this.type = 'Geometry' + this.vertices = [] + this.colors = [] + this.faces = [] + this.faceVertexUvs = [[]] - this.vertices = [] - this.colors = [] - this.faces = [] - this.faceVertexUvs = [[]] + this.morphTargets = [] + this.morphNormals = [] - this.morphTargets = [] - this.morphNormals = [] + this.skinWeights = [] + this.skinIndices = [] - this.skinWeights = [] - this.skinIndices = [] + this.lineDistances = [] - this.lineDistances = [] + this.boundingBox = null + this.boundingSphere = null - this.boundingBox = null - this.boundingSphere = null + // update flags - // update flags - - this.elementsNeedUpdate = false - this.verticesNeedUpdate = false - this.uvsNeedUpdate = false - this.normalsNeedUpdate = false - this.colorsNeedUpdate = false - this.lineDistancesNeedUpdate = false - this.groupsNeedUpdate = false - } - - applyMatrix4(matrix) { - const normalMatrix = new Matrix3().getNormalMatrix(matrix) - - for (let i = 0, il = this.vertices.length; i < il; i++) { - const vertex = this.vertices[i] - vertex.applyMatrix4(matrix) + this.elementsNeedUpdate = false + this.verticesNeedUpdate = false + this.uvsNeedUpdate = false + this.normalsNeedUpdate = false + this.colorsNeedUpdate = false + this.lineDistancesNeedUpdate = false + this.groupsNeedUpdate = false } - for (let i = 0, il = this.faces.length; i < il; i++) { - const face = this.faces[i] - face.normal.applyMatrix3(normalMatrix).normalize() + applyMatrix4(matrix) { + const normalMatrix = new Matrix3().getNormalMatrix(matrix) - for (let j = 0, jl = face.vertexNormals.length; j < jl; j++) { - face.vertexNormals[j].applyMatrix3(normalMatrix).normalize() + for (let i = 0, il = this.vertices.length; i < il; i++) { + const vertex = this.vertices[i] + vertex.applyMatrix4(matrix) } - } - if (this.boundingBox !== null) { - this.computeBoundingBox() - } - - if (this.boundingSphere !== null) { - this.computeBoundingSphere() - } + for (let i = 0, il = this.faces.length; i < il; i++) { + const face = this.faces[i] + face.normal.applyMatrix3(normalMatrix).normalize() - this.verticesNeedUpdate = true - this.normalsNeedUpdate = true + for (let j = 0, jl = face.vertexNormals.length; j < jl; j++) { + face.vertexNormals[j].applyMatrix3(normalMatrix).normalize() + } + } - return this - } + if (this.boundingBox !== null) { + this.computeBoundingBox() + } - rotateX(angle) { - // rotate geometry around world x-axis + if (this.boundingSphere !== null) { + this.computeBoundingSphere() + } - _m1.makeRotationX(angle) + this.verticesNeedUpdate = true + this.normalsNeedUpdate = true - this.applyMatrix4(_m1) + return this + } - return this - } + rotateX(angle) { + // rotate geometry around world x-axis - rotateY(angle) { - // rotate geometry around world y-axis + _m1.makeRotationX(angle) - _m1.makeRotationY(angle) + this.applyMatrix4(_m1) - this.applyMatrix4(_m1) + return this + } - return this - } + rotateY(angle) { + // rotate geometry around world y-axis - rotateZ(angle) { - // rotate geometry around world z-axis + _m1.makeRotationY(angle) - _m1.makeRotationZ(angle) + this.applyMatrix4(_m1) - this.applyMatrix4(_m1) + return this + } - return this - } + rotateZ(angle) { + // rotate geometry around world z-axis - translate(x, y, z) { - // translate geometry + _m1.makeRotationZ(angle) - _m1.makeTranslation(x, y, z) + this.applyMatrix4(_m1) - this.applyMatrix4(_m1) + return this + } - return this - } + translate(x, y, z) { + // translate geometry - scale(x, y, z) { - // scale geometry + _m1.makeTranslation(x, y, z) - _m1.makeScale(x, y, z) + this.applyMatrix4(_m1) - this.applyMatrix4(_m1) + return this + } - return this - } + scale(x, y, z) { + // scale geometry - lookAt(vector) { - _obj.lookAt(vector) + _m1.makeScale(x, y, z) - _obj.updateMatrix() + this.applyMatrix4(_m1) - this.applyMatrix4(_obj.matrix) + return this + } - return this - } + lookAt(vector) { + _obj.lookAt(vector) - fromBufferGeometry(geometry) { - const scope = this + _obj.updateMatrix() - const index = geometry.index !== null ? geometry.index : undefined - const attributes = geometry.attributes + this.applyMatrix4(_obj.matrix) - if (attributes.position === undefined) { - console.error('THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.') return this } - const position = attributes.position - const normal = attributes.normal - const color = attributes.color - const uv = attributes.uv - const uv2 = attributes.uv2 - - if (uv2 !== undefined) this.faceVertexUvs[1] = [] + fromBufferGeometry(geometry) { + const scope = this - for (let i = 0; i < position.count; i++) { - scope.vertices.push(new Vector3().fromBufferAttribute(position, i)) + const index = geometry.index !== null ? geometry.index : undefined + const attributes = geometry.attributes - if (color !== undefined) { - scope.colors.push(new Color().fromBufferAttribute(color, i)) + if (attributes.position === undefined) { + console.error('THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.') + return this } - } - - function addFace(a, b, c, materialIndex) { - const vertexColors = - color === undefined ? [] : [scope.colors[a].clone(), scope.colors[b].clone(), scope.colors[c].clone()] - const vertexNormals = - normal === undefined - ? [] - : [ - new Vector3().fromBufferAttribute(normal, a), - new Vector3().fromBufferAttribute(normal, b), - new Vector3().fromBufferAttribute(normal, c), - ] + const position = attributes.position + const normal = attributes.normal + const color = attributes.color + const uv = attributes.uv + const uv2 = attributes.uv2 - const face = new Face3(a, b, c, vertexNormals, vertexColors, materialIndex) + if (uv2 !== undefined) this.faceVertexUvs[1] = [] - scope.faces.push(face) + for (let i = 0; i < position.count; i++) { + scope.vertices.push(new Vector3().fromBufferAttribute(position, i)) - if (uv !== undefined) { - scope.faceVertexUvs[0].push([ - new Vector2().fromBufferAttribute(uv, a), - new Vector2().fromBufferAttribute(uv, b), - new Vector2().fromBufferAttribute(uv, c), - ]) + if (color !== undefined) { + scope.colors.push(new Color().fromBufferAttribute(color, i)) + } } - if (uv2 !== undefined) { - scope.faceVertexUvs[1].push([ - new Vector2().fromBufferAttribute(uv2, a), - new Vector2().fromBufferAttribute(uv2, b), - new Vector2().fromBufferAttribute(uv2, c), - ]) + function addFace(a, b, c, materialIndex) { + const vertexColors = + color === undefined ? [] : [scope.colors[a].clone(), scope.colors[b].clone(), scope.colors[c].clone()] + + const vertexNormals = + normal === undefined + ? [] + : [ + new Vector3().fromBufferAttribute(normal, a), + new Vector3().fromBufferAttribute(normal, b), + new Vector3().fromBufferAttribute(normal, c), + ] + + const face = new Face3(a, b, c, vertexNormals, vertexColors, materialIndex) + + scope.faces.push(face) + + if (uv !== undefined) { + scope.faceVertexUvs[0].push([ + new Vector2().fromBufferAttribute(uv, a), + new Vector2().fromBufferAttribute(uv, b), + new Vector2().fromBufferAttribute(uv, c), + ]) + } + + if (uv2 !== undefined) { + scope.faceVertexUvs[1].push([ + new Vector2().fromBufferAttribute(uv2, a), + new Vector2().fromBufferAttribute(uv2, b), + new Vector2().fromBufferAttribute(uv2, c), + ]) + } } - } - const groups = geometry.groups + const groups = geometry.groups - if (groups.length > 0) { - for (let i = 0; i < groups.length; i++) { - const group = groups[i] + if (groups.length > 0) { + for (let i = 0; i < groups.length; i++) { + const group = groups[i] - const start = group.start - const count = group.count + const start = group.start + const count = group.count - for (let j = start, jl = start + count; j < jl; j += 3) { - if (index !== undefined) { - addFace(index.getX(j), index.getX(j + 1), index.getX(j + 2), group.materialIndex) - } else { - addFace(j, j + 1, j + 2, group.materialIndex) + for (let j = start, jl = start + count; j < jl; j += 3) { + if (index !== undefined) { + addFace(index.getX(j), index.getX(j + 1), index.getX(j + 2), group.materialIndex) + } else { + addFace(j, j + 1, j + 2, group.materialIndex) + } } } - } - } else { - if (index !== undefined) { - for (let i = 0; i < index.count; i += 3) { - addFace(index.getX(i), index.getX(i + 1), index.getX(i + 2)) - } } else { - for (let i = 0; i < position.count; i += 3) { - addFace(i, i + 1, i + 2) + if (index !== undefined) { + for (let i = 0; i < index.count; i += 3) { + addFace(index.getX(i), index.getX(i + 1), index.getX(i + 2)) + } + } else { + for (let i = 0; i < position.count; i += 3) { + addFace(i, i + 1, i + 2) + } } } - } - - this.computeFaceNormals() - - if (geometry.boundingBox !== null) { - this.boundingBox = geometry.boundingBox.clone() - } - if (geometry.boundingSphere !== null) { - this.boundingSphere = geometry.boundingSphere.clone() - } - - return this - } - - center() { - this.computeBoundingBox() - - this.boundingBox.getCenter(_offset).negate() - - this.translate(_offset.x, _offset.y, _offset.z) - - return this - } - - normalize() { - this.computeBoundingSphere() + this.computeFaceNormals() - const center = this.boundingSphere.center - const radius = this.boundingSphere.radius + if (geometry.boundingBox !== null) { + this.boundingBox = geometry.boundingBox.clone() + } - const s = radius === 0 ? 1 : 1.0 / radius + if (geometry.boundingSphere !== null) { + this.boundingSphere = geometry.boundingSphere.clone() + } - const matrix = new Matrix4() - matrix.set(s, 0, 0, -s * center.x, 0, s, 0, -s * center.y, 0, 0, s, -s * center.z, 0, 0, 0, 1) + return this + } - this.applyMatrix4(matrix) + center() { + this.computeBoundingBox() - return this - } + this.boundingBox.getCenter(_offset).negate() - computeFaceNormals() { - const cb = new Vector3(), - ab = new Vector3() + this.translate(_offset.x, _offset.y, _offset.z) - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + return this + } - const vA = this.vertices[face.a] - const vB = this.vertices[face.b] - const vC = this.vertices[face.c] + normalize() { + this.computeBoundingSphere() - cb.subVectors(vC, vB) - ab.subVectors(vA, vB) - cb.cross(ab) + const center = this.boundingSphere.center + const radius = this.boundingSphere.radius - cb.normalize() + const s = radius === 0 ? 1 : 1.0 / radius - face.normal.copy(cb) - } - } + const matrix = new Matrix4() + matrix.set(s, 0, 0, -s * center.x, 0, s, 0, -s * center.y, 0, 0, s, -s * center.z, 0, 0, 0, 1) - computeVertexNormals(areaWeighted = true) { - const vertices = new Array(this.vertices.length) + this.applyMatrix4(matrix) - for (let v = 0, vl = this.vertices.length; v < vl; v++) { - vertices[v] = new Vector3() + return this } - if (areaWeighted) { - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm - + computeFaceNormals() { const cb = new Vector3(), ab = new Vector3() @@ -352,549 +322,584 @@ class Geometry extends EventDispatcher { ab.subVectors(vA, vB) cb.cross(ab) - vertices[face.a].add(cb) - vertices[face.b].add(cb) - vertices[face.c].add(cb) + cb.normalize() + + face.normal.copy(cb) } - } else { - this.computeFaceNormals() + } - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + computeVertexNormals(areaWeighted = true) { + const vertices = new Array(this.vertices.length) - vertices[face.a].add(face.normal) - vertices[face.b].add(face.normal) - vertices[face.c].add(face.normal) + for (let v = 0, vl = this.vertices.length; v < vl; v++) { + vertices[v] = new Vector3() } - } - for (let v = 0, vl = this.vertices.length; v < vl; v++) { - vertices[v].normalize() - } + if (areaWeighted) { + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + const cb = new Vector3(), + ab = new Vector3() - const vertexNormals = face.vertexNormals + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - if (vertexNormals.length === 3) { - vertexNormals[0].copy(vertices[face.a]) - vertexNormals[1].copy(vertices[face.b]) - vertexNormals[2].copy(vertices[face.c]) - } else { - vertexNormals[0] = vertices[face.a].clone() - vertexNormals[1] = vertices[face.b].clone() - vertexNormals[2] = vertices[face.c].clone() - } - } + const vA = this.vertices[face.a] + const vB = this.vertices[face.b] + const vC = this.vertices[face.c] - if (this.faces.length > 0) { - this.normalsNeedUpdate = true - } - } + cb.subVectors(vC, vB) + ab.subVectors(vA, vB) + cb.cross(ab) - computeFlatVertexNormals() { - this.computeFaceNormals() + vertices[face.a].add(cb) + vertices[face.b].add(cb) + vertices[face.c].add(cb) + } + } else { + this.computeFaceNormals() - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - const vertexNormals = face.vertexNormals + vertices[face.a].add(face.normal) + vertices[face.b].add(face.normal) + vertices[face.c].add(face.normal) + } + } - if (vertexNormals.length === 3) { - vertexNormals[0].copy(face.normal) - vertexNormals[1].copy(face.normal) - vertexNormals[2].copy(face.normal) - } else { - vertexNormals[0] = face.normal.clone() - vertexNormals[1] = face.normal.clone() - vertexNormals[2] = face.normal.clone() + for (let v = 0, vl = this.vertices.length; v < vl; v++) { + vertices[v].normalize() } - } - if (this.faces.length > 0) { - this.normalsNeedUpdate = true - } - } + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - computeMorphNormals() { - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) + const vertexNormals = face.vertexNormals - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + if (vertexNormals.length === 3) { + vertexNormals[0].copy(vertices[face.a]) + vertexNormals[1].copy(vertices[face.b]) + vertexNormals[2].copy(vertices[face.c]) + } else { + vertexNormals[0] = vertices[face.a].clone() + vertexNormals[1] = vertices[face.b].clone() + vertexNormals[2] = vertices[face.c].clone() + } + } - if (!face.__originalFaceNormal) { - face.__originalFaceNormal = face.normal.clone() - } else { - face.__originalFaceNormal.copy(face.normal) + if (this.faces.length > 0) { + this.normalsNeedUpdate = true } + } - if (!face.__originalVertexNormals) face.__originalVertexNormals = [] + computeFlatVertexNormals() { + this.computeFaceNormals() + + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] + + const vertexNormals = face.vertexNormals - for (let i = 0, il = face.vertexNormals.length; i < il; i++) { - if (!face.__originalVertexNormals[i]) { - face.__originalVertexNormals[i] = face.vertexNormals[i].clone() + if (vertexNormals.length === 3) { + vertexNormals[0].copy(face.normal) + vertexNormals[1].copy(face.normal) + vertexNormals[2].copy(face.normal) } else { - face.__originalVertexNormals[i].copy(face.vertexNormals[i]) + vertexNormals[0] = face.normal.clone() + vertexNormals[1] = face.normal.clone() + vertexNormals[2] = face.normal.clone() } } - } - // use temp geometry to compute face and vertex normals for each morph + if (this.faces.length > 0) { + this.normalsNeedUpdate = true + } + } - const tmpGeo = new Geometry() - tmpGeo.faces = this.faces + computeMorphNormals() { + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) - for (let i = 0, il = this.morphTargets.length; i < il; i++) { - // create on first access + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - if (!this.morphNormals[i]) { - this.morphNormals[i] = {} - this.morphNormals[i].faceNormals = [] - this.morphNormals[i].vertexNormals = [] + if (!face.__originalFaceNormal) { + face.__originalFaceNormal = face.normal.clone() + } else { + face.__originalFaceNormal.copy(face.normal) + } - const dstNormalsFace = this.morphNormals[i].faceNormals - const dstNormalsVertex = this.morphNormals[i].vertexNormals + if (!face.__originalVertexNormals) face.__originalVertexNormals = [] - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const faceNormal = new Vector3() - const vertexNormals = { - a: new Vector3(), - b: new Vector3(), - c: new Vector3(), + for (let i = 0, il = face.vertexNormals.length; i < il; i++) { + if (!face.__originalVertexNormals[i]) { + face.__originalVertexNormals[i] = face.vertexNormals[i].clone() + } else { + face.__originalVertexNormals[i].copy(face.vertexNormals[i]) } - - dstNormalsFace.push(faceNormal) - dstNormalsVertex.push(vertexNormals) } } - const morphNormals = this.morphNormals[i] - - // set vertices to morph target + // use temp geometry to compute face and vertex normals for each morph - tmpGeo.vertices = this.morphTargets[i].vertices + const tmpGeo = new Geometry() + tmpGeo.faces = this.faces - // compute morph normals + for (let i = 0, il = this.morphTargets.length; i < il; i++) { + // create on first access - tmpGeo.computeFaceNormals() - tmpGeo.computeVertexNormals() + if (!this.morphNormals[i]) { + this.morphNormals[i] = {} + this.morphNormals[i].faceNormals = [] + this.morphNormals[i].vertexNormals = [] - // store morph normals + const dstNormalsFace = this.morphNormals[i].faceNormals + const dstNormalsVertex = this.morphNormals[i].vertexNormals - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const faceNormal = new Vector3() + const vertexNormals = { + a: new Vector3(), + b: new Vector3(), + c: new Vector3(), + } - const faceNormal = morphNormals.faceNormals[f] - const vertexNormals = morphNormals.vertexNormals[f] + dstNormalsFace.push(faceNormal) + dstNormalsVertex.push(vertexNormals) + } + } - faceNormal.copy(face.normal) + const morphNormals = this.morphNormals[i] - vertexNormals.a.copy(face.vertexNormals[0]) - vertexNormals.b.copy(face.vertexNormals[1]) - vertexNormals.c.copy(face.vertexNormals[2]) - } - } + // set vertices to morph target - // restore original normals + tmpGeo.vertices = this.morphTargets[i].vertices - for (let f = 0, fl = this.faces.length; f < fl; f++) { - const face = this.faces[f] + // compute morph normals - face.normal = face.__originalFaceNormal - face.vertexNormals = face.__originalVertexNormals - } - } + tmpGeo.computeFaceNormals() + tmpGeo.computeVertexNormals() - computeBoundingBox() { - if (this.boundingBox === null) { - this.boundingBox = new Box3() - } - - this.boundingBox.setFromPoints(this.vertices) - } + // store morph normals - computeBoundingSphere() { - if (this.boundingSphere === null) { - this.boundingSphere = new Sphere() - } + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - this.boundingSphere.setFromPoints(this.vertices) - } + const faceNormal = morphNormals.faceNormals[f] + const vertexNormals = morphNormals.vertexNormals[f] - merge(geometry, matrix, materialIndexOffset = 0) { - if (!(geometry && geometry.isGeometry)) { - console.error('THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry) - return - } + faceNormal.copy(face.normal) - let normalMatrix - const vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - colors1 = this.colors, - colors2 = geometry.colors - - if (matrix !== undefined) { - normalMatrix = new Matrix3().getNormalMatrix(matrix) - } + vertexNormals.a.copy(face.vertexNormals[0]) + vertexNormals.b.copy(face.vertexNormals[1]) + vertexNormals.c.copy(face.vertexNormals[2]) + } + } - // vertices + // restore original normals - for (let i = 0, il = vertices2.length; i < il; i++) { - const vertex = vertices2[i] + for (let f = 0, fl = this.faces.length; f < fl; f++) { + const face = this.faces[f] - const vertexCopy = vertex.clone() + face.normal = face.__originalFaceNormal + face.vertexNormals = face.__originalVertexNormals + } + } - if (matrix !== undefined) vertexCopy.applyMatrix4(matrix) + computeBoundingBox() { + if (this.boundingBox === null) { + this.boundingBox = new Box3() + } - vertices1.push(vertexCopy) + this.boundingBox.setFromPoints(this.vertices) } - // colors + computeBoundingSphere() { + if (this.boundingSphere === null) { + this.boundingSphere = new Sphere() + } - for (let i = 0, il = colors2.length; i < il; i++) { - colors1.push(colors2[i].clone()) + this.boundingSphere.setFromPoints(this.vertices) } - // faces + merge(geometry, matrix, materialIndexOffset = 0) { + if (!(geometry && geometry.isGeometry)) { + console.error('THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry) + return + } - for (let i = 0, il = faces2.length; i < il; i++) { - const face = faces2[i] - let normal, color - const faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors + let normalMatrix + const vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + colors1 = this.colors, + colors2 = geometry.colors + + if (matrix !== undefined) { + normalMatrix = new Matrix3().getNormalMatrix(matrix) + } - const faceCopy = new Face3(face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset) - faceCopy.normal.copy(face.normal) + // vertices - if (normalMatrix !== undefined) { - faceCopy.normal.applyMatrix3(normalMatrix).normalize() - } + for (let i = 0, il = vertices2.length; i < il; i++) { + const vertex = vertices2[i] - for (let j = 0, jl = faceVertexNormals.length; j < jl; j++) { - normal = faceVertexNormals[j].clone() + const vertexCopy = vertex.clone() - if (normalMatrix !== undefined) { - normal.applyMatrix3(normalMatrix).normalize() - } + if (matrix !== undefined) vertexCopy.applyMatrix4(matrix) - faceCopy.vertexNormals.push(normal) + vertices1.push(vertexCopy) } - faceCopy.color.copy(face.color) + // colors - for (let j = 0, jl = faceVertexColors.length; j < jl; j++) { - color = faceVertexColors[j] - faceCopy.vertexColors.push(color.clone()) + for (let i = 0, il = colors2.length; i < il; i++) { + colors1.push(colors2[i].clone()) } - faceCopy.materialIndex = face.materialIndex + materialIndexOffset + // faces - faces1.push(faceCopy) - } + for (let i = 0, il = faces2.length; i < il; i++) { + const face = faces2[i] + let normal, color + const faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors - // uvs + const faceCopy = new Face3(face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset) + faceCopy.normal.copy(face.normal) - for (let i = 0, il = geometry.faceVertexUvs.length; i < il; i++) { - const faceVertexUvs2 = geometry.faceVertexUvs[i] + if (normalMatrix !== undefined) { + faceCopy.normal.applyMatrix3(normalMatrix).normalize() + } - if (this.faceVertexUvs[i] === undefined) this.faceVertexUvs[i] = [] + for (let j = 0, jl = faceVertexNormals.length; j < jl; j++) { + normal = faceVertexNormals[j].clone() - for (let j = 0, jl = faceVertexUvs2.length; j < jl; j++) { - const uvs2 = faceVertexUvs2[j], - uvsCopy = [] + if (normalMatrix !== undefined) { + normal.applyMatrix3(normalMatrix).normalize() + } - for (let k = 0, kl = uvs2.length; k < kl; k++) { - uvsCopy.push(uvs2[k].clone()) + faceCopy.vertexNormals.push(normal) } - this.faceVertexUvs[i].push(uvsCopy) - } - } - } + faceCopy.color.copy(face.color) - mergeMesh(mesh) { - if (!(mesh && mesh.isMesh)) { - console.error('THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh) - return - } + for (let j = 0, jl = faceVertexColors.length; j < jl; j++) { + color = faceVertexColors[j] + faceCopy.vertexColors.push(color.clone()) + } - if (mesh.matrixAutoUpdate) mesh.updateMatrix() + faceCopy.materialIndex = face.materialIndex + materialIndexOffset - this.merge(mesh.geometry, mesh.matrix) - } + faces1.push(faceCopy) + } - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ + // uvs - mergeVertices(precisionPoints = 4) { - const verticesMap = {} // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - const unique = [], - changes = [] + for (let i = 0, il = geometry.faceVertexUvs.length; i < il; i++) { + const faceVertexUvs2 = geometry.faceVertexUvs[i] - const precision = Math.pow(10, precisionPoints) + if (this.faceVertexUvs[i] === undefined) this.faceVertexUvs[i] = [] - for (let i = 0, il = this.vertices.length; i < il; i++) { - const v = this.vertices[i] - const key = `${Math.round(v.x * precision)}_${Math.round(v.y * precision)}_${Math.round(v.z * precision)}` + for (let j = 0, jl = faceVertexUvs2.length; j < jl; j++) { + const uvs2 = faceVertexUvs2[j], + uvsCopy = [] - if (verticesMap[key] === undefined) { - verticesMap[key] = i - unique.push(this.vertices[i]) - changes[i] = unique.length - 1 - } else { - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[i] = changes[verticesMap[key]] + for (let k = 0, kl = uvs2.length; k < kl; k++) { + uvsCopy.push(uvs2[k].clone()) + } + + this.faceVertexUvs[i].push(uvsCopy) + } } } - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - const faceIndicesToRemove = [] - - for (let i = 0, il = this.faces.length; i < il; i++) { - const face = this.faces[i] + mergeMesh(mesh) { + if (!(mesh && mesh.isMesh)) { + console.error('THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh) + return + } - face.a = changes[face.a] - face.b = changes[face.b] - face.c = changes[face.c] + if (mesh.matrixAutoUpdate) mesh.updateMatrix() - const indices = [face.a, face.b, face.c] + this.merge(mesh.geometry, mesh.matrix) + } - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for (let n = 0; n < 3; n++) { - if (indices[n] === indices[(n + 1) % 3]) { - faceIndicesToRemove.push(i) - break + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices(precisionPoints = 4) { + const verticesMap = {} // Hashmap for looking up vertices by position coordinates (and making sure they are unique) + const unique = [], + changes = [] + + const precision = Math.pow(10, precisionPoints) + + for (let i = 0, il = this.vertices.length; i < il; i++) { + const v = this.vertices[i] + const key = `${Math.round(v.x * precision)}_${Math.round(v.y * precision)}_${Math.round(v.z * precision)}` + + if (verticesMap[key] === undefined) { + verticesMap[key] = i + unique.push(this.vertices[i]) + changes[i] = unique.length - 1 + } else { + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[i] = changes[verticesMap[key]] } } - } - for (let i = faceIndicesToRemove.length - 1; i >= 0; i--) { - const idx = faceIndicesToRemove[i] + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + const faceIndicesToRemove = [] - this.faces.splice(idx, 1) + for (let i = 0, il = this.faces.length; i < il; i++) { + const face = this.faces[i] - for (let j = 0, jl = this.faceVertexUvs.length; j < jl; j++) { - this.faceVertexUvs[j].splice(idx, 1) - } - } + face.a = changes[face.a] + face.b = changes[face.b] + face.c = changes[face.c] - // Use unique set of vertices + const indices = [face.a, face.b, face.c] - const diff = this.vertices.length - unique.length - this.vertices = unique - return diff - } - - setFromPoints(points) { - this.vertices = [] + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for (let n = 0; n < 3; n++) { + if (indices[n] === indices[(n + 1) % 3]) { + faceIndicesToRemove.push(i) + break + } + } + } - for (let i = 0, l = points.length; i < l; i++) { - const point = points[i] - this.vertices.push(new Vector3(point.x, point.y, point.z || 0)) - } + for (let i = faceIndicesToRemove.length - 1; i >= 0; i--) { + const idx = faceIndicesToRemove[i] - return this - } + this.faces.splice(idx, 1) - sortFacesByMaterialIndex() { - const faces = this.faces - const length = faces.length + for (let j = 0, jl = this.faceVertexUvs.length; j < jl; j++) { + this.faceVertexUvs[j].splice(idx, 1) + } + } - // tag faces + // Use unique set of vertices - for (let i = 0; i < length; i++) { - faces[i]._id = i + const diff = this.vertices.length - unique.length + this.vertices = unique + return diff } - // sort faces + setFromPoints(points) { + this.vertices = [] - function materialIndexSort(a, b) { - return a.materialIndex - b.materialIndex - } + for (let i = 0, l = points.length; i < l; i++) { + const point = points[i] + this.vertices.push(new Vector3(point.x, point.y, point.z || 0)) + } - faces.sort(materialIndexSort) + return this + } - // sort uvs + sortFacesByMaterialIndex() { + const faces = this.faces + const length = faces.length - const uvs1 = this.faceVertexUvs[0] - const uvs2 = this.faceVertexUvs[1] + // tag faces - let newUvs1, newUvs2 + for (let i = 0; i < length; i++) { + faces[i]._id = i + } - if (uvs1 && uvs1.length === length) newUvs1 = [] - if (uvs2 && uvs2.length === length) newUvs2 = [] + // sort faces - for (let i = 0; i < length; i++) { - const id = faces[i]._id + function materialIndexSort(a, b) { + return a.materialIndex - b.materialIndex + } - if (newUvs1) newUvs1.push(uvs1[id]) - if (newUvs2) newUvs2.push(uvs2[id]) - } + faces.sort(materialIndexSort) - if (newUvs1) this.faceVertexUvs[0] = newUvs1 - if (newUvs2) this.faceVertexUvs[1] = newUvs2 - } + // sort uvs - toJSON() { - const data = { - metadata: { - version: 4.5, - type: 'Geometry', - generator: 'Geometry.toJSON', - }, - } + const uvs1 = this.faceVertexUvs[0] + const uvs2 = this.faceVertexUvs[1] - // standard Geometry serialization + let newUvs1, newUvs2 - data.uuid = this.uuid - data.type = this.type - if (this.name !== '') data.name = this.name + if (uvs1 && uvs1.length === length) newUvs1 = [] + if (uvs2 && uvs2.length === length) newUvs2 = [] - if (this.parameters !== undefined) { - const parameters = this.parameters + for (let i = 0; i < length; i++) { + const id = faces[i]._id - for (let key in parameters) { - if (parameters[key] !== undefined) data[key] = parameters[key] + if (newUvs1) newUvs1.push(uvs1[id]) + if (newUvs2) newUvs2.push(uvs2[id]) } - return data + if (newUvs1) this.faceVertexUvs[0] = newUvs1 + if (newUvs2) this.faceVertexUvs[1] = newUvs2 } - const vertices = [] + toJSON() { + const data = { + metadata: { + version: 4.5, + type: 'Geometry', + generator: 'Geometry.toJSON', + }, + } - for (let i = 0; i < this.vertices.length; i++) { - const vertex = this.vertices[i] - vertices.push(vertex.x, vertex.y, vertex.z) - } + // standard Geometry serialization - const faces = [] - const normals = [] - const normalsHash = {} - const colors = [] - const colorsHash = {} - const uvs = [] - const uvsHash = {} + data.uuid = this.uuid + data.type = this.type + if (this.name !== '') data.name = this.name - for (let i = 0; i < this.faces.length; i++) { - const face = this.faces[i] + if (this.parameters !== undefined) { + const parameters = this.parameters - const hasMaterial = true - const hasFaceUv = false // deprecated - const hasFaceVertexUv = this.faceVertexUvs[0][i] !== undefined - const hasFaceNormal = face.normal.length() > 0 - const hasFaceVertexNormal = face.vertexNormals.length > 0 - const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1 - const hasFaceVertexColor = face.vertexColors.length > 0 + for (let key in parameters) { + if (parameters[key] !== undefined) data[key] = parameters[key] + } - let faceType = 0 + return data + } - faceType = setBit(faceType, 0, 0) // isQuad - faceType = setBit(faceType, 1, hasMaterial) - faceType = setBit(faceType, 2, hasFaceUv) - faceType = setBit(faceType, 3, hasFaceVertexUv) - faceType = setBit(faceType, 4, hasFaceNormal) - faceType = setBit(faceType, 5, hasFaceVertexNormal) - faceType = setBit(faceType, 6, hasFaceColor) - faceType = setBit(faceType, 7, hasFaceVertexColor) + const vertices = [] - faces.push(faceType) - faces.push(face.a, face.b, face.c) - faces.push(face.materialIndex) + for (let i = 0; i < this.vertices.length; i++) { + const vertex = this.vertices[i] + vertices.push(vertex.x, vertex.y, vertex.z) + } - if (hasFaceVertexUv) { - const faceVertexUvs = this.faceVertexUvs[0][i] + const faces = [] + const normals = [] + const normalsHash = {} + const colors = [] + const colorsHash = {} + const uvs = [] + const uvsHash = {} + + for (let i = 0; i < this.faces.length; i++) { + const face = this.faces[i] + + const hasMaterial = true + const hasFaceUv = false // deprecated + const hasFaceVertexUv = this.faceVertexUvs[0][i] !== undefined + const hasFaceNormal = face.normal.length() > 0 + const hasFaceVertexNormal = face.vertexNormals.length > 0 + const hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1 + const hasFaceVertexColor = face.vertexColors.length > 0 + + let faceType = 0 + + faceType = setBit(faceType, 0, 0) // isQuad + faceType = setBit(faceType, 1, hasMaterial) + faceType = setBit(faceType, 2, hasFaceUv) + faceType = setBit(faceType, 3, hasFaceVertexUv) + faceType = setBit(faceType, 4, hasFaceNormal) + faceType = setBit(faceType, 5, hasFaceVertexNormal) + faceType = setBit(faceType, 6, hasFaceColor) + faceType = setBit(faceType, 7, hasFaceVertexColor) + + faces.push(faceType) + faces.push(face.a, face.b, face.c) + faces.push(face.materialIndex) + + if (hasFaceVertexUv) { + const faceVertexUvs = this.faceVertexUvs[0][i] + + faces.push(getUvIndex(faceVertexUvs[0]), getUvIndex(faceVertexUvs[1]), getUvIndex(faceVertexUvs[2])) + } - faces.push(getUvIndex(faceVertexUvs[0]), getUvIndex(faceVertexUvs[1]), getUvIndex(faceVertexUvs[2])) - } + if (hasFaceNormal) { + faces.push(getNormalIndex(face.normal)) + } - if (hasFaceNormal) { - faces.push(getNormalIndex(face.normal)) - } + if (hasFaceVertexNormal) { + const vertexNormals = face.vertexNormals - if (hasFaceVertexNormal) { - const vertexNormals = face.vertexNormals + faces.push( + getNormalIndex(vertexNormals[0]), + getNormalIndex(vertexNormals[1]), + getNormalIndex(vertexNormals[2]), + ) + } - faces.push(getNormalIndex(vertexNormals[0]), getNormalIndex(vertexNormals[1]), getNormalIndex(vertexNormals[2])) - } + if (hasFaceColor) { + faces.push(getColorIndex(face.color)) + } - if (hasFaceColor) { - faces.push(getColorIndex(face.color)) - } + if (hasFaceVertexColor) { + const vertexColors = face.vertexColors - if (hasFaceVertexColor) { - const vertexColors = face.vertexColors + faces.push(getColorIndex(vertexColors[0]), getColorIndex(vertexColors[1]), getColorIndex(vertexColors[2])) + } + } - faces.push(getColorIndex(vertexColors[0]), getColorIndex(vertexColors[1]), getColorIndex(vertexColors[2])) + function setBit(value, position, enabled) { + return enabled ? value | (1 << position) : value & ~(1 << position) } - } - function setBit(value, position, enabled) { - return enabled ? value | (1 << position) : value & ~(1 << position) - } + function getNormalIndex(normal) { + const hash = normal.x.toString() + normal.y.toString() + normal.z.toString() + + if (normalsHash[hash] !== undefined) { + return normalsHash[hash] + } - function getNormalIndex(normal) { - const hash = normal.x.toString() + normal.y.toString() + normal.z.toString() + normalsHash[hash] = normals.length / 3 + normals.push(normal.x, normal.y, normal.z) - if (normalsHash[hash] !== undefined) { return normalsHash[hash] } - normalsHash[hash] = normals.length / 3 - normals.push(normal.x, normal.y, normal.z) + function getColorIndex(color) { + const hash = color.r.toString() + color.g.toString() + color.b.toString() - return normalsHash[hash] - } + if (colorsHash[hash] !== undefined) { + return colorsHash[hash] + } - function getColorIndex(color) { - const hash = color.r.toString() + color.g.toString() + color.b.toString() + colorsHash[hash] = colors.length + colors.push(color.getHex()) - if (colorsHash[hash] !== undefined) { return colorsHash[hash] } - colorsHash[hash] = colors.length - colors.push(color.getHex()) + function getUvIndex(uv) { + const hash = uv.x.toString() + uv.y.toString() - return colorsHash[hash] - } + if (uvsHash[hash] !== undefined) { + return uvsHash[hash] + } - function getUvIndex(uv) { - const hash = uv.x.toString() + uv.y.toString() + uvsHash[hash] = uvs.length / 2 + uvs.push(uv.x, uv.y) - if (uvsHash[hash] !== undefined) { return uvsHash[hash] } - uvsHash[hash] = uvs.length / 2 - uvs.push(uv.x, uv.y) - - return uvsHash[hash] - } - - data.data = {} + data.data = {} - data.data.vertices = vertices - data.data.normals = normals - if (colors.length > 0) data.data.colors = colors - if (uvs.length > 0) data.data.uvs = [uvs] // temporal backward compatibility - data.data.faces = faces + data.data.vertices = vertices + data.data.normals = normals + if (colors.length > 0) data.data.colors = colors + if (uvs.length > 0) data.data.uvs = [uvs] // temporal backward compatibility + data.data.faces = faces - return data - } + return data + } - clone() { - /* + clone() { + /* // Handle primitives const parameters = this.parameters; @@ -918,290 +923,293 @@ class Geometry extends EventDispatcher { return new this.constructor().copy( this ); */ - return new Geometry().copy(this) - } + return new Geometry().copy(this) + } - copy(source) { - // reset + copy(source) { + // reset - this.vertices = [] - this.colors = [] - this.faces = [] - this.faceVertexUvs = [[]] - this.morphTargets = [] - this.morphNormals = [] - this.skinWeights = [] - this.skinIndices = [] - this.lineDistances = [] - this.boundingBox = null - this.boundingSphere = null + this.vertices = [] + this.colors = [] + this.faces = [] + this.faceVertexUvs = [[]] + this.morphTargets = [] + this.morphNormals = [] + this.skinWeights = [] + this.skinIndices = [] + this.lineDistances = [] + this.boundingBox = null + this.boundingSphere = null - // name + // name - this.name = source.name + this.name = source.name - // vertices + // vertices - const vertices = source.vertices + const vertices = source.vertices - for (let i = 0, il = vertices.length; i < il; i++) { - this.vertices.push(vertices[i].clone()) - } + for (let i = 0, il = vertices.length; i < il; i++) { + this.vertices.push(vertices[i].clone()) + } - // colors + // colors - const colors = source.colors + const colors = source.colors - for (let i = 0, il = colors.length; i < il; i++) { - this.colors.push(colors[i].clone()) - } + for (let i = 0, il = colors.length; i < il; i++) { + this.colors.push(colors[i].clone()) + } - // faces + // faces - const faces = source.faces + const faces = source.faces - for (let i = 0, il = faces.length; i < il; i++) { - this.faces.push(faces[i].clone()) - } + for (let i = 0, il = faces.length; i < il; i++) { + this.faces.push(faces[i].clone()) + } - // face vertex uvs + // face vertex uvs - for (let i = 0, il = source.faceVertexUvs.length; i < il; i++) { - const faceVertexUvs = source.faceVertexUvs[i] + for (let i = 0, il = source.faceVertexUvs.length; i < il; i++) { + const faceVertexUvs = source.faceVertexUvs[i] - if (this.faceVertexUvs[i] === undefined) { - this.faceVertexUvs[i] = [] - } + if (this.faceVertexUvs[i] === undefined) { + this.faceVertexUvs[i] = [] + } - for (let j = 0, jl = faceVertexUvs.length; j < jl; j++) { - const uvs = faceVertexUvs[j], - uvsCopy = [] + for (let j = 0, jl = faceVertexUvs.length; j < jl; j++) { + const uvs = faceVertexUvs[j], + uvsCopy = [] - for (let k = 0, kl = uvs.length; k < kl; k++) { - const uv = uvs[k] + for (let k = 0, kl = uvs.length; k < kl; k++) { + const uv = uvs[k] - uvsCopy.push(uv.clone()) - } + uvsCopy.push(uv.clone()) + } - this.faceVertexUvs[i].push(uvsCopy) + this.faceVertexUvs[i].push(uvsCopy) + } } - } - // morph targets + // morph targets - const morphTargets = source.morphTargets + const morphTargets = source.morphTargets - for (let i = 0, il = morphTargets.length; i < il; i++) { - const morphTarget = {} - morphTarget.name = morphTargets[i].name + for (let i = 0, il = morphTargets.length; i < il; i++) { + const morphTarget = {} + morphTarget.name = morphTargets[i].name - // vertices + // vertices - if (morphTargets[i].vertices !== undefined) { - morphTarget.vertices = [] + if (morphTargets[i].vertices !== undefined) { + morphTarget.vertices = [] - for (let j = 0, jl = morphTargets[i].vertices.length; j < jl; j++) { - morphTarget.vertices.push(morphTargets[i].vertices[j].clone()) + for (let j = 0, jl = morphTargets[i].vertices.length; j < jl; j++) { + morphTarget.vertices.push(morphTargets[i].vertices[j].clone()) + } } - } - // normals + // normals - if (morphTargets[i].normals !== undefined) { - morphTarget.normals = [] + if (morphTargets[i].normals !== undefined) { + morphTarget.normals = [] - for (let j = 0, jl = morphTargets[i].normals.length; j < jl; j++) { - morphTarget.normals.push(morphTargets[i].normals[j].clone()) + for (let j = 0, jl = morphTargets[i].normals.length; j < jl; j++) { + morphTarget.normals.push(morphTargets[i].normals[j].clone()) + } } - } - this.morphTargets.push(morphTarget) - } + this.morphTargets.push(morphTarget) + } - // morph normals + // morph normals - const morphNormals = source.morphNormals + const morphNormals = source.morphNormals - for (let i = 0, il = morphNormals.length; i < il; i++) { - const morphNormal = {} + for (let i = 0, il = morphNormals.length; i < il; i++) { + const morphNormal = {} - // vertex normals + // vertex normals - if (morphNormals[i].vertexNormals !== undefined) { - morphNormal.vertexNormals = [] + if (morphNormals[i].vertexNormals !== undefined) { + morphNormal.vertexNormals = [] - for (let j = 0, jl = morphNormals[i].vertexNormals.length; j < jl; j++) { - const srcVertexNormal = morphNormals[i].vertexNormals[j] - const destVertexNormal = {} + for (let j = 0, jl = morphNormals[i].vertexNormals.length; j < jl; j++) { + const srcVertexNormal = morphNormals[i].vertexNormals[j] + const destVertexNormal = {} - destVertexNormal.a = srcVertexNormal.a.clone() - destVertexNormal.b = srcVertexNormal.b.clone() - destVertexNormal.c = srcVertexNormal.c.clone() + destVertexNormal.a = srcVertexNormal.a.clone() + destVertexNormal.b = srcVertexNormal.b.clone() + destVertexNormal.c = srcVertexNormal.c.clone() - morphNormal.vertexNormals.push(destVertexNormal) + morphNormal.vertexNormals.push(destVertexNormal) + } } - } - // face normals + // face normals - if (morphNormals[i].faceNormals !== undefined) { - morphNormal.faceNormals = [] + if (morphNormals[i].faceNormals !== undefined) { + morphNormal.faceNormals = [] - for (let j = 0, jl = morphNormals[i].faceNormals.length; j < jl; j++) { - morphNormal.faceNormals.push(morphNormals[i].faceNormals[j].clone()) + for (let j = 0, jl = morphNormals[i].faceNormals.length; j < jl; j++) { + morphNormal.faceNormals.push(morphNormals[i].faceNormals[j].clone()) + } } + + this.morphNormals.push(morphNormal) } - this.morphNormals.push(morphNormal) - } + // skin weights - // skin weights + const skinWeights = source.skinWeights - const skinWeights = source.skinWeights + for (let i = 0, il = skinWeights.length; i < il; i++) { + this.skinWeights.push(skinWeights[i].clone()) + } - for (let i = 0, il = skinWeights.length; i < il; i++) { - this.skinWeights.push(skinWeights[i].clone()) - } + // skin indices - // skin indices + const skinIndices = source.skinIndices - const skinIndices = source.skinIndices + for (let i = 0, il = skinIndices.length; i < il; i++) { + this.skinIndices.push(skinIndices[i].clone()) + } - for (let i = 0, il = skinIndices.length; i < il; i++) { - this.skinIndices.push(skinIndices[i].clone()) - } + // line distances - // line distances + const lineDistances = source.lineDistances - const lineDistances = source.lineDistances + for (let i = 0, il = lineDistances.length; i < il; i++) { + this.lineDistances.push(lineDistances[i]) + } - for (let i = 0, il = lineDistances.length; i < il; i++) { - this.lineDistances.push(lineDistances[i]) - } + // bounding box - // bounding box + const boundingBox = source.boundingBox - const boundingBox = source.boundingBox + if (boundingBox !== null) { + this.boundingBox = boundingBox.clone() + } - if (boundingBox !== null) { - this.boundingBox = boundingBox.clone() - } + // bounding sphere - // bounding sphere + const boundingSphere = source.boundingSphere - const boundingSphere = source.boundingSphere + if (boundingSphere !== null) { + this.boundingSphere = boundingSphere.clone() + } - if (boundingSphere !== null) { - this.boundingSphere = boundingSphere.clone() - } + // update flags - // update flags + this.elementsNeedUpdate = source.elementsNeedUpdate + this.verticesNeedUpdate = source.verticesNeedUpdate + this.uvsNeedUpdate = source.uvsNeedUpdate + this.normalsNeedUpdate = source.normalsNeedUpdate + this.colorsNeedUpdate = source.colorsNeedUpdate + this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate + this.groupsNeedUpdate = source.groupsNeedUpdate - this.elementsNeedUpdate = source.elementsNeedUpdate - this.verticesNeedUpdate = source.verticesNeedUpdate - this.uvsNeedUpdate = source.uvsNeedUpdate - this.normalsNeedUpdate = source.normalsNeedUpdate - this.colorsNeedUpdate = source.colorsNeedUpdate - this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate - this.groupsNeedUpdate = source.groupsNeedUpdate + return this + } - return this - } + toBufferGeometry() { + const geometry = new DirectGeometry().fromGeometry(this) - toBufferGeometry() { - const geometry = new DirectGeometry().fromGeometry(this) + const buffergeometry = new BufferGeometry() - const buffergeometry = new BufferGeometry() + const positions = new Float32Array(geometry.vertices.length * 3) + buffergeometry.setAttribute('position', new BufferAttribute(positions, 3).copyVector3sArray(geometry.vertices)) - const positions = new Float32Array(geometry.vertices.length * 3) - buffergeometry.setAttribute('position', new BufferAttribute(positions, 3).copyVector3sArray(geometry.vertices)) + if (geometry.normals.length > 0) { + const normals = new Float32Array(geometry.normals.length * 3) + buffergeometry.setAttribute('normal', new BufferAttribute(normals, 3).copyVector3sArray(geometry.normals)) + } - if (geometry.normals.length > 0) { - const normals = new Float32Array(geometry.normals.length * 3) - buffergeometry.setAttribute('normal', new BufferAttribute(normals, 3).copyVector3sArray(geometry.normals)) - } + if (geometry.colors.length > 0) { + const colors = new Float32Array(geometry.colors.length * 3) + buffergeometry.setAttribute('color', new BufferAttribute(colors, 3).copyColorsArray(geometry.colors)) + } - if (geometry.colors.length > 0) { - const colors = new Float32Array(geometry.colors.length * 3) - buffergeometry.setAttribute('color', new BufferAttribute(colors, 3).copyColorsArray(geometry.colors)) - } + if (geometry.uvs.length > 0) { + const uvs = new Float32Array(geometry.uvs.length * 2) + buffergeometry.setAttribute('uv', new BufferAttribute(uvs, 2).copyVector2sArray(geometry.uvs)) + } - if (geometry.uvs.length > 0) { - const uvs = new Float32Array(geometry.uvs.length * 2) - buffergeometry.setAttribute('uv', new BufferAttribute(uvs, 2).copyVector2sArray(geometry.uvs)) - } + if (geometry.uvs2.length > 0) { + const uvs2 = new Float32Array(geometry.uvs2.length * 2) + buffergeometry.setAttribute('uv2', new BufferAttribute(uvs2, 2).copyVector2sArray(geometry.uvs2)) + } - if (geometry.uvs2.length > 0) { - const uvs2 = new Float32Array(geometry.uvs2.length * 2) - buffergeometry.setAttribute('uv2', new BufferAttribute(uvs2, 2).copyVector2sArray(geometry.uvs2)) - } + // groups - // groups + buffergeometry.groups = geometry.groups - buffergeometry.groups = geometry.groups + // morphs - // morphs + for (let name in geometry.morphTargets) { + const array = [] + const morphTargets = geometry.morphTargets[name] - for (let name in geometry.morphTargets) { - const array = [] - const morphTargets = geometry.morphTargets[name] + for (let i = 0, l = morphTargets.length; i < l; i++) { + const morphTarget = morphTargets[i] - for (let i = 0, l = morphTargets.length; i < l; i++) { - const morphTarget = morphTargets[i] + const attribute = new Float32BufferAttribute(morphTarget.data.length * 3, 3) + attribute.name = morphTarget.name - const attribute = new Float32BufferAttribute(morphTarget.data.length * 3, 3) - attribute.name = morphTarget.name + array.push(attribute.copyVector3sArray(morphTarget.data)) + } - array.push(attribute.copyVector3sArray(morphTarget.data)) + buffergeometry.morphAttributes[name] = array } - buffergeometry.morphAttributes[name] = array - } + // skinning + + if (geometry.skinIndices.length > 0) { + const skinIndices = new Float32BufferAttribute(geometry.skinIndices.length * 4, 4) + buffergeometry.setAttribute('skinIndex', skinIndices.copyVector4sArray(geometry.skinIndices)) + } - // skinning + if (geometry.skinWeights.length > 0) { + const skinWeights = new Float32BufferAttribute(geometry.skinWeights.length * 4, 4) + buffergeometry.setAttribute('skinWeight', skinWeights.copyVector4sArray(geometry.skinWeights)) + } - if (geometry.skinIndices.length > 0) { - const skinIndices = new Float32BufferAttribute(geometry.skinIndices.length * 4, 4) - buffergeometry.setAttribute('skinIndex', skinIndices.copyVector4sArray(geometry.skinIndices)) - } + // - if (geometry.skinWeights.length > 0) { - const skinWeights = new Float32BufferAttribute(geometry.skinWeights.length * 4, 4) - buffergeometry.setAttribute('skinWeight', skinWeights.copyVector4sArray(geometry.skinWeights)) - } + if (geometry.boundingSphere !== null) { + buffergeometry.boundingSphere = geometry.boundingSphere.clone() + } - // + if (geometry.boundingBox !== null) { + buffergeometry.boundingBox = geometry.boundingBox.clone() + } - if (geometry.boundingSphere !== null) { - buffergeometry.boundingSphere = geometry.boundingSphere.clone() + return buffergeometry } - if (geometry.boundingBox !== null) { - buffergeometry.boundingBox = geometry.boundingBox.clone() + computeTangents() { + console.error('THREE.Geometry: .computeTangents() has been removed.') } - return buffergeometry - } - - computeTangents() { - console.error('THREE.Geometry: .computeTangents() has been removed.') - } + computeLineDistances() { + console.error( + 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.', + ) + } - computeLineDistances() { - console.error( - 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.', - ) - } + applyMatrix(matrix) { + console.warn('THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().') + return this.applyMatrix4(matrix) + } - applyMatrix(matrix) { - console.warn('THREE.Geometry: .applyMatrix() has been renamed to .applyMatrix4().') - return this.applyMatrix4(matrix) + dispose() { + this.dispatchEvent({ type: 'dispose' }) + } } - dispose() { - this.dispatchEvent({ type: 'dispose' }) - } -} + return Geometry +})() class DirectGeometry { constructor() { diff --git a/src/effects/AnaglyphEffect.js b/src/effects/AnaglyphEffect.js index fdbdc8c3..7713e65e 100644 --- a/src/effects/AnaglyphEffect.js +++ b/src/effects/AnaglyphEffect.js @@ -10,8 +10,8 @@ import { ShaderMaterial, StereoCamera, WebGLRenderTarget, - REVISION, } from 'three' +import { version } from '../_polyfill/constants' class AnaglyphEffect { constructor(renderer, width = 512, height = 512) { @@ -96,7 +96,7 @@ class AnaglyphEffect { ' max( colorL.a, colorR.a ) );', ' #include ', - ` #include <${parseInt(REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, + ` #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, '}', ].join('\n'), diff --git a/src/effects/OutlineEffect.js b/src/effects/OutlineEffect.js index 2999048c..581b2349 100644 --- a/src/effects/OutlineEffect.js +++ b/src/effects/OutlineEffect.js @@ -1,4 +1,5 @@ -import { BackSide, Color, ShaderMaterial, UniformsLib, UniformsUtils, REVISION } from 'three' +import { BackSide, Color, ShaderMaterial, UniformsLib, UniformsUtils } from 'three' +import { version } from '../_polyfill/constants' /** * Reference: https://en.wikipedia.org/wiki/Cel_shading @@ -157,7 +158,7 @@ class OutlineEffect { ' gl_FragColor = vec4( outlineColor, outlineAlpha );', ' #include ', - ` #include <${parseInt(REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, + ` #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, ' #include ', ' #include ', diff --git a/src/effects/ParallaxBarrierEffect.js b/src/effects/ParallaxBarrierEffect.js index 00ae33f4..8cee4954 100644 --- a/src/effects/ParallaxBarrierEffect.js +++ b/src/effects/ParallaxBarrierEffect.js @@ -9,8 +9,8 @@ import { ShaderMaterial, StereoCamera, WebGLRenderTarget, - REVISION, } from 'three' +import { version } from '../_polyfill/constants' class ParallaxBarrierEffect { constructor(renderer) { @@ -62,7 +62,7 @@ class ParallaxBarrierEffect { ' }', ' #include ', - ` #include <${parseInt(REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, + ` #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>`, '}', ].join('\n'), diff --git a/src/exporters/DRACOExporter.d.ts b/src/exporters/DRACOExporter.d.ts new file mode 100644 index 00000000..01a57ca5 --- /dev/null +++ b/src/exporters/DRACOExporter.d.ts @@ -0,0 +1,36 @@ +import * as THREE from 'three' + +declare class DRACOExporter { + // Encoder methods + + public static MESH_EDGEBREAKER_ENCODING: number + public static MESH_SEQUENTIAL_ENCODING: number + + // Geometry type + + public static POINT_CLOUD: number + public static TRIANGULAR_MESH: number + + // Attribute type + public static INVALID: number + public static POSITION: number + public static NORMAL: number + public static COLOR: number + public static TEX_COORD: number + public static GENERIC: number + + public parse( + object: THREE.Mesh | THREE.Points, + options?: { + decodeSpeed?: number + encodeSpeed?: number + encoderMethod?: number + quantization?: [number, number, number, number, number] + exportUvs?: boolean + exportNormals?: boolean + exportColor?: boolean + }, + ): Int8Array +} + +export { DRACOExporter } diff --git a/src/exporters/DRACOExporter.js b/src/exporters/DRACOExporter.js new file mode 100644 index 00000000..e698fc2f --- /dev/null +++ b/src/exporters/DRACOExporter.js @@ -0,0 +1,216 @@ +import { BufferGeometry, Mesh, Points } from 'three' + +/** + * Export draco compressed files from threejs geometry objects. + * + * Draco files are compressed and usually are smaller than conventional 3D file formats. + * + * The exporter receives a options object containing + * - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality) + * - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality) + * - encoderMethod + * - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC) + * - exportUvs + * - exportNormals + */ + +const DRACOExporter = /* @__PURE__ */ (() => { + class DRACOExporter { + // Encoder methods + + static MESH_EDGEBREAKER_ENCODING = 1 + static MESH_SEQUENTIAL_ENCODING = 0 + + // Geometry type + + static POINT_CLOUD = 0 + static TRIANGULAR_MESH = 1 + + // Attribute type + static INVALID = -1 + static POSITION = 0 + static NORMAL = 1 + static COLOR = 2 + static TEX_COORD = 3 + static GENERIC = 4 + + parse( + object, + options = { + decodeSpeed: 5, + encodeSpeed: 5, + encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING, + quantization: [16, 8, 8, 8, 8], + exportUvs: true, + exportNormals: true, + exportColor: false, + }, + ) { + if (object instanceof BufferGeometry && object.isBufferGeometry) { + throw new Error('DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.') + } + + if (DracoEncoderModule === undefined) { + throw new Error('THREE.DRACOExporter: required the draco_encoder to work.') + } + + const geometry = object.geometry + + const dracoEncoder = DracoEncoderModule() + const encoder = new dracoEncoder.Encoder() + let builder + let dracoObject + + if (!geometry.isBufferGeometry) { + throw new Error( + 'THREE.DRACOExporter.parse(geometry, options): geometry is not a THREE.BufferGeometry instance.', + ) + } + + if (object instanceof Mesh && object.isMesh) { + builder = new dracoEncoder.MeshBuilder() + dracoObject = new dracoEncoder.Mesh() + + const vertices = geometry.getAttribute('position') + // @ts-ignore + builder.AddFloatAttributeToMesh( + dracoObject, + dracoEncoder.POSITION, + vertices.count, + vertices.itemSize, + vertices.array, + ) + + const faces = geometry.getIndex() + + if (faces !== null) { + builder.AddFacesToMesh(dracoObject, faces.count / 3, faces.array) + } else { + const faces = new (vertices.count > 65535 ? Uint32Array : Uint16Array)(vertices.count) + + for (let i = 0; i < faces.length; i++) { + faces[i] = i + } + + builder.AddFacesToMesh(dracoObject, vertices.count, faces) + } + + if (options.exportNormals) { + const normals = geometry.getAttribute('normal') + + if (normals !== undefined) { + // @ts-ignore + builder.AddFloatAttributeToMesh( + dracoObject, + dracoEncoder.NORMAL, + normals.count, + normals.itemSize, + normals.array, + ) + } + } + + if (options.exportUvs) { + const uvs = geometry.getAttribute('uv') + + if (uvs !== undefined) { + // @ts-ignore + builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array) + } + } + + if (options.exportColor) { + const colors = geometry.getAttribute('color') + + if (colors !== undefined) { + // @ts-ignore + builder.AddFloatAttributeToMesh( + dracoObject, + dracoEncoder.COLOR, + colors.count, + colors.itemSize, + colors.array, + ) + } + } + } else if (object instanceof Points && object.isPoints) { + // @ts-ignore + builder = new dracoEncoder.PointCloudBuilder() + // @ts-ignore + dracoObject = new dracoEncoder.PointCloud() + + const vertices = geometry.getAttribute('position') + builder.AddFloatAttribute(dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array) + + if (options.exportColor) { + const colors = geometry.getAttribute('color') + + if (colors !== undefined) { + builder.AddFloatAttribute(dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array) + } + } + } else { + throw new Error('DRACOExporter: Unsupported object type.') + } + + //Compress using draco encoder + + const encodedData = new dracoEncoder.DracoInt8Array() + + //Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression). + + const encodeSpeed = options.encodeSpeed !== undefined ? options.encodeSpeed : 5 + const decodeSpeed = options.decodeSpeed !== undefined ? options.decodeSpeed : 5 + + encoder.SetSpeedOptions(encodeSpeed, decodeSpeed) + + // Sets the desired encoding method for a given geometry. + + if (options.encoderMethod !== undefined) { + encoder.SetEncodingMethod(options.encoderMethod) + } + + // Sets the quantization (number of bits used to represent) compression options for a named attribute. + // The attribute values will be quantized in a box defined by the maximum extent of the attribute values. + if (options.quantization !== undefined) { + for (let i = 0; i < 5; i++) { + if (options.quantization[i] !== undefined) { + encoder.SetAttributeQuantization(i, options.quantization[i]) + } + } + } + + let length + + if (object instanceof Mesh && object.isMesh) { + length = encoder.EncodeMeshToDracoBuffer(dracoObject, encodedData) + } else { + // @ts-ignore + length = encoder.EncodePointCloudToDracoBuffer(dracoObject, true, encodedData) + } + + dracoEncoder.destroy(dracoObject) + + if (length === 0) { + throw new Error('THREE.DRACOExporter: Draco encoding failed.') + } + + //Copy encoded data to buffer. + const outputData = new Int8Array(new ArrayBuffer(length)) + + for (let i = 0; i < length; i++) { + outputData[i] = encodedData.GetValue(i) + } + + dracoEncoder.destroy(encodedData) + dracoEncoder.destroy(encoder) + dracoEncoder.destroy(builder) + + return outputData + } + } + + return DRACOExporter +})() + +export { DRACOExporter } diff --git a/src/exporters/DRACOExporter.ts b/src/exporters/DRACOExporter.ts deleted file mode 100644 index 474d6097..00000000 --- a/src/exporters/DRACOExporter.ts +++ /dev/null @@ -1,207 +0,0 @@ -import type { EncoderModule } from 'draco3d' -import { BufferGeometry, Mesh, Points } from 'three' - -/** - * Export draco compressed files from threejs geometry objects. - * - * Draco files are compressed and usually are smaller than conventional 3D file formats. - * - * The exporter receives a options object containing - * - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality) - * - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality) - * - encoderMethod - * - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC) - * - exportUvs - * - exportNormals - */ - -declare const DracoEncoderModule: () => EncoderModule - -class DRACOExporter { - // Encoder methods - - public static MESH_EDGEBREAKER_ENCODING = 1 - public static MESH_SEQUENTIAL_ENCODING = 0 - - // Geometry type - - public static POINT_CLOUD = 0 - public static TRIANGULAR_MESH = 1 - - // Attribute type - public static INVALID = -1 - public static POSITION = 0 - public static NORMAL = 1 - public static COLOR = 2 - public static TEX_COORD = 3 - public static GENERIC = 4 - - public parse( - object: Mesh | Points, - options = { - decodeSpeed: 5, - encodeSpeed: 5, - encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING, - quantization: [16, 8, 8, 8, 8], - exportUvs: true, - exportNormals: true, - exportColor: false, - }, - ): Int8Array { - if (object instanceof BufferGeometry && object.isBufferGeometry) { - throw new Error('DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.') - } - - if (DracoEncoderModule === undefined) { - throw new Error('THREE.DRACOExporter: required the draco_encoder to work.') - } - - const geometry = object.geometry - - const dracoEncoder = DracoEncoderModule() - const encoder = new dracoEncoder.Encoder() - let builder - let dracoObject - - if (!geometry.isBufferGeometry) { - throw new Error('THREE.DRACOExporter.parse(geometry, options): geometry is not a THREE.BufferGeometry instance.') - } - - if (object instanceof Mesh && object.isMesh) { - builder = new dracoEncoder.MeshBuilder() - dracoObject = new dracoEncoder.Mesh() - - const vertices = geometry.getAttribute('position') - // @ts-ignore - builder.AddFloatAttributeToMesh( - dracoObject, - dracoEncoder.POSITION, - vertices.count, - vertices.itemSize, - vertices.array, - ) - - const faces = geometry.getIndex() - - if (faces !== null) { - builder.AddFacesToMesh(dracoObject, faces.count / 3, faces.array as Uint16Array | Uint32Array) - } else { - const faces = new (vertices.count > 65535 ? Uint32Array : Uint16Array)(vertices.count) - - for (let i = 0; i < faces.length; i++) { - faces[i] = i - } - - builder.AddFacesToMesh(dracoObject, vertices.count, faces) - } - - if (options.exportNormals) { - const normals = geometry.getAttribute('normal') - - if (normals !== undefined) { - // @ts-ignore - builder.AddFloatAttributeToMesh( - dracoObject, - dracoEncoder.NORMAL, - normals.count, - normals.itemSize, - normals.array, - ) - } - } - - if (options.exportUvs) { - const uvs = geometry.getAttribute('uv') - - if (uvs !== undefined) { - // @ts-ignore - builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array) - } - } - - if (options.exportColor) { - const colors = geometry.getAttribute('color') - - if (colors !== undefined) { - // @ts-ignore - builder.AddFloatAttributeToMesh(dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array) - } - } - } else if (object instanceof Points && object.isPoints) { - // @ts-ignore - builder = new dracoEncoder.PointCloudBuilder() - // @ts-ignore - dracoObject = new dracoEncoder.PointCloud() - - const vertices = geometry.getAttribute('position') - builder.AddFloatAttribute(dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array) - - if (options.exportColor) { - const colors = geometry.getAttribute('color') - - if (colors !== undefined) { - builder.AddFloatAttribute(dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array) - } - } - } else { - throw new Error('DRACOExporter: Unsupported object type.') - } - - //Compress using draco encoder - - const encodedData = new dracoEncoder.DracoInt8Array() - - //Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression). - - const encodeSpeed = options.encodeSpeed !== undefined ? options.encodeSpeed : 5 - const decodeSpeed = options.decodeSpeed !== undefined ? options.decodeSpeed : 5 - - encoder.SetSpeedOptions(encodeSpeed, decodeSpeed) - - // Sets the desired encoding method for a given geometry. - - if (options.encoderMethod !== undefined) { - encoder.SetEncodingMethod(options.encoderMethod) - } - - // Sets the quantization (number of bits used to represent) compression options for a named attribute. - // The attribute values will be quantized in a box defined by the maximum extent of the attribute values. - if (options.quantization !== undefined) { - for (let i = 0; i < 5; i++) { - if (options.quantization[i] !== undefined) { - encoder.SetAttributeQuantization(i, options.quantization[i]) - } - } - } - - let length - - if (object instanceof Mesh && object.isMesh) { - length = encoder.EncodeMeshToDracoBuffer(dracoObject, encodedData) - } else { - // @ts-ignore - length = encoder.EncodePointCloudToDracoBuffer(dracoObject, true, encodedData) - } - - dracoEncoder.destroy(dracoObject) - - if (length === 0) { - throw new Error('THREE.DRACOExporter: Draco encoding failed.') - } - - //Copy encoded data to buffer. - const outputData = new Int8Array(new ArrayBuffer(length)) - - for (let i = 0; i < length; i++) { - outputData[i] = encodedData.GetValue(i) - } - - dracoEncoder.destroy(encodedData) - dracoEncoder.destroy(encoder) - dracoEncoder.destroy(builder) - - return outputData - } -} - -export { DRACOExporter } diff --git a/src/exporters/GLTFExporter.js b/src/exporters/GLTFExporter.js index 143a42d5..b2c9736b 100644 --- a/src/exporters/GLTFExporter.js +++ b/src/exporters/GLTFExporter.js @@ -129,259 +129,263 @@ const KHR_mesh_quantization_ExtraAttrTypes = { TEXCOORD: ['byte', 'byte normalized', 'unsigned byte', 'short', 'short normalized', 'unsigned short'], } -class GLTFExporter { - /** - * Static utility functions - */ - static Utils = { - insertKeyframe: function (track, time) { - const tolerance = 0.001 // 1ms - const valueSize = track.getValueSize() - - const times = new track.TimeBufferType(track.times.length + 1) - const values = new track.ValueBufferType(track.values.length + valueSize) - const interpolant = track.createInterpolant(new track.ValueBufferType(valueSize)) +const GLTFExporter = /* @__PURE__ */ (() => { + class GLTFExporter { + /** + * Static utility functions + */ + static Utils = { + insertKeyframe: function (track, time) { + const tolerance = 0.001 // 1ms + const valueSize = track.getValueSize() + + const times = new track.TimeBufferType(track.times.length + 1) + const values = new track.ValueBufferType(track.values.length + valueSize) + const interpolant = track.createInterpolant(new track.ValueBufferType(valueSize)) + + let index + + if (track.times.length === 0) { + times[0] = time + + for (let i = 0; i < valueSize; i++) { + values[i] = 0 + } - let index + index = 0 + } else if (time < track.times[0]) { + if (Math.abs(track.times[0] - time) < tolerance) return 0 - if (track.times.length === 0) { - times[0] = time + times[0] = time + times.set(track.times, 1) - for (let i = 0; i < valueSize; i++) { - values[i] = 0 - } + values.set(interpolant.evaluate(time), 0) + values.set(track.values, valueSize) - index = 0 - } else if (time < track.times[0]) { - if (Math.abs(track.times[0] - time) < tolerance) return 0 + index = 0 + } else if (time > track.times[track.times.length - 1]) { + if (Math.abs(track.times[track.times.length - 1] - time) < tolerance) { + return track.times.length - 1 + } - times[0] = time - times.set(track.times, 1) + times[times.length - 1] = time + times.set(track.times, 0) - values.set(interpolant.evaluate(time), 0) - values.set(track.values, valueSize) + values.set(track.values, 0) + values.set(interpolant.evaluate(time), track.values.length) - index = 0 - } else if (time > track.times[track.times.length - 1]) { - if (Math.abs(track.times[track.times.length - 1] - time) < tolerance) { - return track.times.length - 1 - } + index = times.length - 1 + } else { + for (let i = 0; i < track.times.length; i++) { + if (Math.abs(track.times[i] - time) < tolerance) return i - times[times.length - 1] = time - times.set(track.times, 0) + if (track.times[i] < time && track.times[i + 1] > time) { + times.set(track.times.slice(0, i + 1), 0) + times[i + 1] = time + times.set(track.times.slice(i + 1), i + 2) - values.set(track.values, 0) - values.set(interpolant.evaluate(time), track.values.length) + values.set(track.values.slice(0, (i + 1) * valueSize), 0) + values.set(interpolant.evaluate(time), (i + 1) * valueSize) + values.set(track.values.slice((i + 1) * valueSize), (i + 2) * valueSize) - index = times.length - 1 - } else { - for (let i = 0; i < track.times.length; i++) { - if (Math.abs(track.times[i] - time) < tolerance) return i + index = i + 1 - if (track.times[i] < time && track.times[i + 1] > time) { - times.set(track.times.slice(0, i + 1), 0) - times[i + 1] = time - times.set(track.times.slice(i + 1), i + 2) + break + } + } + } - values.set(track.values.slice(0, (i + 1) * valueSize), 0) - values.set(interpolant.evaluate(time), (i + 1) * valueSize) - values.set(track.values.slice((i + 1) * valueSize), (i + 2) * valueSize) + track.times = times + track.values = values - index = i + 1 + return index + }, - break + mergeMorphTargetTracks: function (clip, root) { + const tracks = [] + const mergedTracks = {} + const sourceTracks = clip.tracks + + for (let i = 0; i < sourceTracks.length; ++i) { + let sourceTrack = sourceTracks[i] + const sourceTrackBinding = PropertyBinding.parseTrackName(sourceTrack.name) + const sourceTrackNode = PropertyBinding.findNode(root, sourceTrackBinding.nodeName) + + if ( + sourceTrackBinding.propertyName !== 'morphTargetInfluences' || + sourceTrackBinding.propertyIndex === undefined + ) { + // Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is. + tracks.push(sourceTrack) + continue } - } - } - track.times = times - track.values = values + if ( + sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodDiscrete && + sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodLinear + ) { + if (sourceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { + // This should never happen, because glTF morph target animations + // affect all targets already. + throw new Error('THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.') + } - return index - }, - - mergeMorphTargetTracks: function (clip, root) { - const tracks = [] - const mergedTracks = {} - const sourceTracks = clip.tracks - - for (let i = 0; i < sourceTracks.length; ++i) { - let sourceTrack = sourceTracks[i] - const sourceTrackBinding = PropertyBinding.parseTrackName(sourceTrack.name) - const sourceTrackNode = PropertyBinding.findNode(root, sourceTrackBinding.nodeName) - - if ( - sourceTrackBinding.propertyName !== 'morphTargetInfluences' || - sourceTrackBinding.propertyIndex === undefined - ) { - // Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is. - tracks.push(sourceTrack) - continue - } + console.warn('THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.') - if ( - sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodDiscrete && - sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodLinear - ) { - if (sourceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline) { - // This should never happen, because glTF morph target animations - // affect all targets already. - throw new Error('THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.') + sourceTrack = sourceTrack.clone() + sourceTrack.setInterpolation(InterpolateLinear) } - console.warn('THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.') - - sourceTrack = sourceTrack.clone() - sourceTrack.setInterpolation(InterpolateLinear) - } + const targetCount = sourceTrackNode.morphTargetInfluences.length + const targetIndex = sourceTrackNode.morphTargetDictionary[sourceTrackBinding.propertyIndex] - const targetCount = sourceTrackNode.morphTargetInfluences.length - const targetIndex = sourceTrackNode.morphTargetDictionary[sourceTrackBinding.propertyIndex] + if (targetIndex === undefined) { + throw new Error('THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding.propertyIndex) + } - if (targetIndex === undefined) { - throw new Error('THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding.propertyIndex) - } + let mergedTrack - let mergedTrack + // If this is the first time we've seen this object, create a new + // track to store merged keyframe data for each morph target. + if (mergedTracks[sourceTrackNode.uuid] === undefined) { + mergedTrack = sourceTrack.clone() - // If this is the first time we've seen this object, create a new - // track to store merged keyframe data for each morph target. - if (mergedTracks[sourceTrackNode.uuid] === undefined) { - mergedTrack = sourceTrack.clone() + const values = new mergedTrack.ValueBufferType(targetCount * mergedTrack.times.length) - const values = new mergedTrack.ValueBufferType(targetCount * mergedTrack.times.length) + for (let j = 0; j < mergedTrack.times.length; j++) { + values[j * targetCount + targetIndex] = mergedTrack.values[j] + } - for (let j = 0; j < mergedTrack.times.length; j++) { - values[j * targetCount + targetIndex] = mergedTrack.values[j] - } + // We need to take into consideration the intended target node + // of our original un-merged morphTarget animation. + mergedTrack.name = (sourceTrackBinding.nodeName || '') + '.morphTargetInfluences' + mergedTrack.values = values - // We need to take into consideration the intended target node - // of our original un-merged morphTarget animation. - mergedTrack.name = (sourceTrackBinding.nodeName || '') + '.morphTargetInfluences' - mergedTrack.values = values + mergedTracks[sourceTrackNode.uuid] = mergedTrack + tracks.push(mergedTrack) - mergedTracks[sourceTrackNode.uuid] = mergedTrack - tracks.push(mergedTrack) + continue + } - continue - } + const sourceInterpolant = sourceTrack.createInterpolant(new sourceTrack.ValueBufferType(1)) - const sourceInterpolant = sourceTrack.createInterpolant(new sourceTrack.ValueBufferType(1)) + mergedTrack = mergedTracks[sourceTrackNode.uuid] - mergedTrack = mergedTracks[sourceTrackNode.uuid] + // For every existing keyframe of the merged track, write a (possibly + // interpolated) value from the source track. + for (let j = 0; j < mergedTrack.times.length; j++) { + mergedTrack.values[j * targetCount + targetIndex] = sourceInterpolant.evaluate(mergedTrack.times[j]) + } - // For every existing keyframe of the merged track, write a (possibly - // interpolated) value from the source track. - for (let j = 0; j < mergedTrack.times.length; j++) { - mergedTrack.values[j * targetCount + targetIndex] = sourceInterpolant.evaluate(mergedTrack.times[j]) + // For every existing keyframe of the source track, write a (possibly + // new) keyframe to the merged track. Values from the previous loop may + // be written again, but keyframes are de-duplicated. + for (let j = 0; j < sourceTrack.times.length; j++) { + const keyframeIndex = this.insertKeyframe(mergedTrack, sourceTrack.times[j]) + mergedTrack.values[keyframeIndex * targetCount + targetIndex] = sourceTrack.values[j] + } } - // For every existing keyframe of the source track, write a (possibly - // new) keyframe to the merged track. Values from the previous loop may - // be written again, but keyframes are de-duplicated. - for (let j = 0; j < sourceTrack.times.length; j++) { - const keyframeIndex = this.insertKeyframe(mergedTrack, sourceTrack.times[j]) - mergedTrack.values[keyframeIndex * targetCount + targetIndex] = sourceTrack.values[j] - } - } + clip.tracks = tracks - clip.tracks = tracks + return clip + }, + } - return clip - }, - } + constructor() { + this.pluginCallbacks = [] - constructor() { - this.pluginCallbacks = [] + this.register(function (writer) { + return new GLTFLightExtension(writer) + }) - this.register(function (writer) { - return new GLTFLightExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsUnlitExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsUnlitExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsTransmissionExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsTransmissionExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsVolumeExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsVolumeExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsIorExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsIorExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsSpecularExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsSpecularExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsClearcoatExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsClearcoatExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsIridescenceExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsIridescenceExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsSheenExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsSheenExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsAnisotropyExtension(writer) + }) - this.register(function (writer) { - return new GLTFMaterialsAnisotropyExtension(writer) - }) + this.register(function (writer) { + return new GLTFMaterialsEmissiveStrengthExtension(writer) + }) + } - this.register(function (writer) { - return new GLTFMaterialsEmissiveStrengthExtension(writer) - }) - } + register(callback) { + if (this.pluginCallbacks.indexOf(callback) === -1) { + this.pluginCallbacks.push(callback) + } - register(callback) { - if (this.pluginCallbacks.indexOf(callback) === -1) { - this.pluginCallbacks.push(callback) + return this } - return this - } + unregister(callback) { + if (this.pluginCallbacks.indexOf(callback) !== -1) { + this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1) + } - unregister(callback) { - if (this.pluginCallbacks.indexOf(callback) !== -1) { - this.pluginCallbacks.splice(this.pluginCallbacks.indexOf(callback), 1) + return this } - return this - } + /** + * Parse scenes and generate GLTF output + * @param {Scene or [THREE.Scenes]} input Scene or Array of THREE.Scenes + * @param {Function} onDone Callback on completed + * @param {Function} onError Callback on errors + * @param {Object} options options + */ + parse(input, onDone, onError, options) { + const writer = new GLTFWriter() + const plugins = [] - /** - * Parse scenes and generate GLTF output - * @param {Scene or [THREE.Scenes]} input Scene or Array of THREE.Scenes - * @param {Function} onDone Callback on completed - * @param {Function} onError Callback on errors - * @param {Object} options options - */ - parse(input, onDone, onError, options) { - const writer = new GLTFWriter() - const plugins = [] + for (let i = 0, il = this.pluginCallbacks.length; i < il; i++) { + plugins.push(this.pluginCallbacks[i](writer)) + } - for (let i = 0, il = this.pluginCallbacks.length; i < il; i++) { - plugins.push(this.pluginCallbacks[i](writer)) + writer.setPlugins(plugins) + writer.write(input, onDone, options).catch(onError) } - writer.setPlugins(plugins) - writer.write(input, onDone, options).catch(onError) - } - - parseAsync(input, options) { - const scope = this + parseAsync(input, options) { + const scope = this - return new Promise(function (resolve, reject) { - scope.parse(input, resolve, reject, options) - }) + return new Promise(function (resolve, reject) { + scope.parse(input, resolve, reject, options) + }) + } } -} + + return GLTFExporter +})() //------------------------------------------------------------------------------ // Constants @@ -441,7 +445,7 @@ const PATH_PROPERTIES = { morphTargetInfluences: 'weights', } -const DEFAULT_SPECULAR_COLOR = new Color() +const DEFAULT_SPECULAR_COLOR = /* @__PURE__ */ new Color() // GLB constants // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification diff --git a/src/geometries/LightningStrike.js b/src/geometries/LightningStrike.js index 36cb6bc0..fe434a24 100644 --- a/src/geometries/LightningStrike.js +++ b/src/geometries/LightningStrike.js @@ -109,811 +109,821 @@ import { SimplexNoise } from '../math/SimplexNoise' * */ -class LightningStrike extends BufferGeometry { - // Ray states - static RAY_INITIALIZED = 0 - static RAY_UNBORN = 1 - static RAY_PROPAGATING = 2 - static RAY_STEADY = 3 - static RAY_VANISHING = 4 - static RAY_EXTINGUISHED = 5 +const LightningStrike = /* @__PURE__ */ (() => { + class LightningStrike extends BufferGeometry { + // Ray states + static RAY_INITIALIZED = 0 + static RAY_UNBORN = 1 + static RAY_PROPAGATING = 2 + static RAY_STEADY = 3 + static RAY_VANISHING = 4 + static RAY_EXTINGUISHED = 5 - static COS30DEG = Math.cos((30 * Math.PI) / 180) - static SIN30DEG = Math.sin((30 * Math.PI) / 180) + static COS30DEG = Math.cos((30 * Math.PI) / 180) + static SIN30DEG = Math.sin((30 * Math.PI) / 180) - constructor(rayParameters = {}) { - super() + constructor(rayParameters = {}) { + super() - this.isLightningStrike = true + this.isLightningStrike = true - this.type = 'LightningStrike' + this.type = 'LightningStrike' - // Set parameters, and set undefined parameters to default values - this.init(LightningStrike.copyParameters(rayParameters, rayParameters)) + // Set parameters, and set undefined parameters to default values + this.init(LightningStrike.copyParameters(rayParameters, rayParameters)) - // Creates and populates the mesh - this.createMesh() - } - - static createRandomGenerator() { - const numSeeds = 2053 - const seeds = [] - - for (let i = 0; i < numSeeds; i++) { - seeds.push(Math.random()) + // Creates and populates the mesh + this.createMesh() } - const generator = { - currentSeed: 0, + static createRandomGenerator() { + const numSeeds = 2053 + const seeds = [] - random: function () { - const value = seeds[generator.currentSeed] + for (let i = 0; i < numSeeds; i++) { + seeds.push(Math.random()) + } - generator.currentSeed = (generator.currentSeed + 1) % numSeeds + const generator = { + currentSeed: 0, - return value - }, + random: function () { + const value = seeds[generator.currentSeed] - getSeed: function () { - return generator.currentSeed / numSeeds - }, + generator.currentSeed = (generator.currentSeed + 1) % numSeeds - setSeed: function (seed) { - generator.currentSeed = Math.floor(seed * numSeeds) % numSeeds - }, - } + return value + }, - return generator - } + getSeed: function () { + return generator.currentSeed / numSeeds + }, - static copyParameters(dest = {}, source = {}) { - const vecCopy = function (v) { - if (source === dest) { - return v - } else { - return v.clone() + setSeed: function (seed) { + generator.currentSeed = Math.floor(seed * numSeeds) % numSeeds + }, } - } - ;(dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy(source.sourceOffset) : new Vector3(0, 100, 0)), - (dest.destOffset = source.destOffset !== undefined ? vecCopy(source.destOffset) : new Vector3(0, 0, 0)), - (dest.timeScale = source.timeScale !== undefined ? source.timeScale : 1), - (dest.roughness = source.roughness !== undefined ? source.roughness : 0.9), - (dest.straightness = source.straightness !== undefined ? source.straightness : 0.7), - (dest.up0 = source.up0 !== undefined ? vecCopy(source.up0) : new Vector3(0, 0, 1)) - ;(dest.up1 = source.up1 !== undefined ? vecCopy(source.up1) : new Vector3(0, 0, 1)), - (dest.radius0 = source.radius0 !== undefined ? source.radius0 : 1), - (dest.radius1 = source.radius1 !== undefined ? source.radius1 : 1), - (dest.radius0Factor = source.radius0Factor !== undefined ? source.radius0Factor : 0.5), - (dest.radius1Factor = source.radius1Factor !== undefined ? source.radius1Factor : 0.2), - (dest.minRadius = source.minRadius !== undefined ? source.minRadius : 0.2), - // These parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly: - - (dest.isEternal = - source.isEternal !== undefined - ? source.isEternal - : source.birthTime === undefined || source.deathTime === undefined), - (dest.birthTime = source.birthTime), - (dest.deathTime = source.deathTime), - (dest.propagationTimeFactor = source.propagationTimeFactor !== undefined ? source.propagationTimeFactor : 0.1), - (dest.vanishingTimeFactor = source.vanishingTimeFactor !== undefined ? source.vanishingTimeFactor : 0.9), - (dest.subrayPeriod = source.subrayPeriod !== undefined ? source.subrayPeriod : 4), - (dest.subrayDutyCycle = source.subrayDutyCycle !== undefined ? source.subrayDutyCycle : 0.6) - - // These parameters cannot change after lightning creation: - - dest.maxIterations = source.maxIterations !== undefined ? source.maxIterations : 9 - dest.isStatic = source.isStatic !== undefined ? source.isStatic : false - dest.ramification = source.ramification !== undefined ? source.ramification : 5 - dest.maxSubrayRecursion = source.maxSubrayRecursion !== undefined ? source.maxSubrayRecursion : 3 - dest.recursionProbability = source.recursionProbability !== undefined ? source.recursionProbability : 0.6 - dest.generateUVs = source.generateUVs !== undefined ? source.generateUVs : false - ;(dest.randomGenerator = source.randomGenerator), - (dest.noiseSeed = source.noiseSeed), - (dest.onDecideSubrayCreation = source.onDecideSubrayCreation), - (dest.onSubrayCreation = source.onSubrayCreation) - - return dest - } + return generator + } - update(time) { - if (this.isStatic) return + static copyParameters(dest = {}, source = {}) { + const vecCopy = function (v) { + if (source === dest) { + return v + } else { + return v.clone() + } + } - if ( - this.rayParameters.isEternal || - (this.rayParameters.birthTime <= time && time <= this.rayParameters.deathTime) - ) { - this.updateMesh(time) + ;(dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy(source.sourceOffset) : new Vector3(0, 100, 0)), + (dest.destOffset = source.destOffset !== undefined ? vecCopy(source.destOffset) : new Vector3(0, 0, 0)), + (dest.timeScale = source.timeScale !== undefined ? source.timeScale : 1), + (dest.roughness = source.roughness !== undefined ? source.roughness : 0.9), + (dest.straightness = source.straightness !== undefined ? source.straightness : 0.7), + (dest.up0 = source.up0 !== undefined ? vecCopy(source.up0) : new Vector3(0, 0, 1)) + ;(dest.up1 = source.up1 !== undefined ? vecCopy(source.up1) : new Vector3(0, 0, 1)), + (dest.radius0 = source.radius0 !== undefined ? source.radius0 : 1), + (dest.radius1 = source.radius1 !== undefined ? source.radius1 : 1), + (dest.radius0Factor = source.radius0Factor !== undefined ? source.radius0Factor : 0.5), + (dest.radius1Factor = source.radius1Factor !== undefined ? source.radius1Factor : 0.2), + (dest.minRadius = source.minRadius !== undefined ? source.minRadius : 0.2), + // These parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly: + + (dest.isEternal = + source.isEternal !== undefined + ? source.isEternal + : source.birthTime === undefined || source.deathTime === undefined), + (dest.birthTime = source.birthTime), + (dest.deathTime = source.deathTime), + (dest.propagationTimeFactor = source.propagationTimeFactor !== undefined ? source.propagationTimeFactor : 0.1), + (dest.vanishingTimeFactor = source.vanishingTimeFactor !== undefined ? source.vanishingTimeFactor : 0.9), + (dest.subrayPeriod = source.subrayPeriod !== undefined ? source.subrayPeriod : 4), + (dest.subrayDutyCycle = source.subrayDutyCycle !== undefined ? source.subrayDutyCycle : 0.6) + + // These parameters cannot change after lightning creation: + + dest.maxIterations = source.maxIterations !== undefined ? source.maxIterations : 9 + dest.isStatic = source.isStatic !== undefined ? source.isStatic : false + dest.ramification = source.ramification !== undefined ? source.ramification : 5 + dest.maxSubrayRecursion = source.maxSubrayRecursion !== undefined ? source.maxSubrayRecursion : 3 + dest.recursionProbability = source.recursionProbability !== undefined ? source.recursionProbability : 0.6 + dest.generateUVs = source.generateUVs !== undefined ? source.generateUVs : false + ;(dest.randomGenerator = source.randomGenerator), + (dest.noiseSeed = source.noiseSeed), + (dest.onDecideSubrayCreation = source.onDecideSubrayCreation), + (dest.onSubrayCreation = source.onSubrayCreation) + + return dest + } - if (time < this.subrays[0].endPropagationTime) { - this.state = LightningStrike.RAY_PROPAGATING - } else if (time > this.subrays[0].beginVanishingTime) { - this.state = LightningStrike.RAY_VANISHING - } else { - this.state = LightningStrike.RAY_STEADY - } + update(time) { + if (this.isStatic) return - this.visible = true - } else { - this.visible = false + if ( + this.rayParameters.isEternal || + (this.rayParameters.birthTime <= time && time <= this.rayParameters.deathTime) + ) { + this.updateMesh(time) + + if (time < this.subrays[0].endPropagationTime) { + this.state = LightningStrike.RAY_PROPAGATING + } else if (time > this.subrays[0].beginVanishingTime) { + this.state = LightningStrike.RAY_VANISHING + } else { + this.state = LightningStrike.RAY_STEADY + } - if (time < this.rayParameters.birthTime) { - this.state = LightningStrike.RAY_UNBORN + this.visible = true } else { - this.state = LightningStrike.RAY_EXTINGUISHED + this.visible = false + + if (time < this.rayParameters.birthTime) { + this.state = LightningStrike.RAY_UNBORN + } else { + this.state = LightningStrike.RAY_EXTINGUISHED + } } } - } - init(rayParameters) { - // Init all the state from the parameters - - this.rayParameters = rayParameters - - // These parameters cannot change after lightning creation: - - this.maxIterations = rayParameters.maxIterations !== undefined ? Math.floor(rayParameters.maxIterations) : 9 - rayParameters.maxIterations = this.maxIterations - this.isStatic = rayParameters.isStatic !== undefined ? rayParameters.isStatic : false - rayParameters.isStatic = this.isStatic - this.ramification = rayParameters.ramification !== undefined ? Math.floor(rayParameters.ramification) : 5 - rayParameters.ramification = this.ramification - this.maxSubrayRecursion = - rayParameters.maxSubrayRecursion !== undefined ? Math.floor(rayParameters.maxSubrayRecursion) : 3 - rayParameters.maxSubrayRecursion = this.maxSubrayRecursion - this.recursionProbability = - rayParameters.recursionProbability !== undefined ? rayParameters.recursionProbability : 0.6 - rayParameters.recursionProbability = this.recursionProbability - this.generateUVs = rayParameters.generateUVs !== undefined ? rayParameters.generateUVs : false - rayParameters.generateUVs = this.generateUVs - - // Random generator - if (rayParameters.randomGenerator !== undefined) { - this.randomGenerator = rayParameters.randomGenerator - this.seedGenerator = rayParameters.randomGenerator - - if (rayParameters.noiseSeed !== undefined) { - this.seedGenerator.setSeed(rayParameters.noiseSeed) + init(rayParameters) { + // Init all the state from the parameters + + this.rayParameters = rayParameters + + // These parameters cannot change after lightning creation: + + this.maxIterations = rayParameters.maxIterations !== undefined ? Math.floor(rayParameters.maxIterations) : 9 + rayParameters.maxIterations = this.maxIterations + this.isStatic = rayParameters.isStatic !== undefined ? rayParameters.isStatic : false + rayParameters.isStatic = this.isStatic + this.ramification = rayParameters.ramification !== undefined ? Math.floor(rayParameters.ramification) : 5 + rayParameters.ramification = this.ramification + this.maxSubrayRecursion = + rayParameters.maxSubrayRecursion !== undefined ? Math.floor(rayParameters.maxSubrayRecursion) : 3 + rayParameters.maxSubrayRecursion = this.maxSubrayRecursion + this.recursionProbability = + rayParameters.recursionProbability !== undefined ? rayParameters.recursionProbability : 0.6 + rayParameters.recursionProbability = this.recursionProbability + this.generateUVs = rayParameters.generateUVs !== undefined ? rayParameters.generateUVs : false + rayParameters.generateUVs = this.generateUVs + + // Random generator + if (rayParameters.randomGenerator !== undefined) { + this.randomGenerator = rayParameters.randomGenerator + this.seedGenerator = rayParameters.randomGenerator + + if (rayParameters.noiseSeed !== undefined) { + this.seedGenerator.setSeed(rayParameters.noiseSeed) + } + } else { + this.randomGenerator = LightningStrike.createRandomGenerator() + this.seedGenerator = Math } - } else { - this.randomGenerator = LightningStrike.createRandomGenerator() - this.seedGenerator = Math - } - // Ray creation callbacks - if (rayParameters.onDecideSubrayCreation !== undefined) { - this.onDecideSubrayCreation = rayParameters.onDecideSubrayCreation - } else { - this.createDefaultSubrayCreationCallbacks() + // Ray creation callbacks + if (rayParameters.onDecideSubrayCreation !== undefined) { + this.onDecideSubrayCreation = rayParameters.onDecideSubrayCreation + } else { + this.createDefaultSubrayCreationCallbacks() - if (rayParameters.onSubrayCreation !== undefined) { - this.onSubrayCreation = rayParameters.onSubrayCreation + if (rayParameters.onSubrayCreation !== undefined) { + this.onSubrayCreation = rayParameters.onSubrayCreation + } } - } - // Internal state + // Internal state - this.state = LightningStrike.RAY_INITIALIZED + this.state = LightningStrike.RAY_INITIALIZED - this.maxSubrays = Math.ceil(1 + Math.pow(this.ramification, Math.max(0, this.maxSubrayRecursion - 1))) - rayParameters.maxSubrays = this.maxSubrays + this.maxSubrays = Math.ceil(1 + Math.pow(this.ramification, Math.max(0, this.maxSubrayRecursion - 1))) + rayParameters.maxSubrays = this.maxSubrays - this.maxRaySegments = 2 * (1 << this.maxIterations) + this.maxRaySegments = 2 * (1 << this.maxIterations) - this.subrays = [] + this.subrays = [] - for (let i = 0; i < this.maxSubrays; i++) { - this.subrays.push(this.createSubray()) - } + for (let i = 0; i < this.maxSubrays; i++) { + this.subrays.push(this.createSubray()) + } + + this.raySegments = [] - this.raySegments = [] + for (let i = 0; i < this.maxRaySegments; i++) { + this.raySegments.push(this.createSegment()) + } - for (let i = 0; i < this.maxRaySegments; i++) { - this.raySegments.push(this.createSegment()) + this.time = 0 + this.timeFraction = 0 + this.currentSegmentCallback = null + this.currentCreateTriangleVertices = this.generateUVs + ? this.createTriangleVerticesWithUVs + : this.createTriangleVerticesWithoutUVs + this.numSubrays = 0 + this.currentSubray = null + this.currentSegmentIndex = 0 + this.isInitialSegment = false + this.subrayProbability = 0 + + this.currentVertex = 0 + this.currentIndex = 0 + this.currentCoordinate = 0 + this.currentUVCoordinate = 0 + this.vertices = null + this.uvs = null + this.indices = null + this.positionAttribute = null + this.uvsAttribute = null + + this.simplexX = new SimplexNoise(this.seedGenerator) + this.simplexY = new SimplexNoise(this.seedGenerator) + this.simplexZ = new SimplexNoise(this.seedGenerator) + + // Temp vectors + this.forwards = new Vector3() + this.forwardsFill = new Vector3() + this.side = new Vector3() + this.down = new Vector3() + this.middlePos = new Vector3() + this.middleLinPos = new Vector3() + this.newPos = new Vector3() + this.vPos = new Vector3() + this.cross1 = new Vector3() } - this.time = 0 - this.timeFraction = 0 - this.currentSegmentCallback = null - this.currentCreateTriangleVertices = this.generateUVs - ? this.createTriangleVerticesWithUVs - : this.createTriangleVerticesWithoutUVs - this.numSubrays = 0 - this.currentSubray = null - this.currentSegmentIndex = 0 - this.isInitialSegment = false - this.subrayProbability = 0 - - this.currentVertex = 0 - this.currentIndex = 0 - this.currentCoordinate = 0 - this.currentUVCoordinate = 0 - this.vertices = null - this.uvs = null - this.indices = null - this.positionAttribute = null - this.uvsAttribute = null - - this.simplexX = new SimplexNoise(this.seedGenerator) - this.simplexY = new SimplexNoise(this.seedGenerator) - this.simplexZ = new SimplexNoise(this.seedGenerator) - - // Temp vectors - this.forwards = new Vector3() - this.forwardsFill = new Vector3() - this.side = new Vector3() - this.down = new Vector3() - this.middlePos = new Vector3() - this.middleLinPos = new Vector3() - this.newPos = new Vector3() - this.vPos = new Vector3() - this.cross1 = new Vector3() - } + createMesh() { + const maxDrawableSegmentsPerSubRay = 1 << this.maxIterations - createMesh() { - const maxDrawableSegmentsPerSubRay = 1 << this.maxIterations + const maxVerts = 3 * (maxDrawableSegmentsPerSubRay + 1) * this.maxSubrays + const maxIndices = 18 * maxDrawableSegmentsPerSubRay * this.maxSubrays - const maxVerts = 3 * (maxDrawableSegmentsPerSubRay + 1) * this.maxSubrays - const maxIndices = 18 * maxDrawableSegmentsPerSubRay * this.maxSubrays + this.vertices = new Float32Array(maxVerts * 3) + this.indices = new Uint32Array(maxIndices) - this.vertices = new Float32Array(maxVerts * 3) - this.indices = new Uint32Array(maxIndices) + if (this.generateUVs) { + this.uvs = new Float32Array(maxVerts * 2) + } - if (this.generateUVs) { - this.uvs = new Float32Array(maxVerts * 2) - } + // Populate the mesh + this.fillMesh(0) - // Populate the mesh - this.fillMesh(0) + this.setIndex(new Uint32BufferAttribute(this.indices, 1)) - this.setIndex(new Uint32BufferAttribute(this.indices, 1)) + this.positionAttribute = new Float32BufferAttribute(this.vertices, 3) + this.setAttribute('position', this.positionAttribute) - this.positionAttribute = new Float32BufferAttribute(this.vertices, 3) - this.setAttribute('position', this.positionAttribute) + if (this.generateUVs) { + this.uvsAttribute = new Float32BufferAttribute(new Float32Array(this.uvs), 2) + this.setAttribute('uv', this.uvsAttribute) + } - if (this.generateUVs) { - this.uvsAttribute = new Float32BufferAttribute(new Float32Array(this.uvs), 2) - this.setAttribute('uv', this.uvsAttribute) - } + if (!this.isStatic) { + this.index.usage = DynamicDrawUsage + this.positionAttribute.usage = DynamicDrawUsage - if (!this.isStatic) { - this.index.usage = DynamicDrawUsage - this.positionAttribute.usage = DynamicDrawUsage + if (this.generateUVs) { + this.uvsAttribute.usage = DynamicDrawUsage + } + } + + // Store buffers for later modification + this.vertices = this.positionAttribute.array + this.indices = this.index.array if (this.generateUVs) { - this.uvsAttribute.usage = DynamicDrawUsage + this.uvs = this.uvsAttribute.array } } - // Store buffers for later modification - this.vertices = this.positionAttribute.array - this.indices = this.index.array + updateMesh(time) { + this.fillMesh(time) - if (this.generateUVs) { - this.uvs = this.uvsAttribute.array - } - } + this.drawRange.count = this.currentIndex - updateMesh(time) { - this.fillMesh(time) + this.index.needsUpdate = true - this.drawRange.count = this.currentIndex + this.positionAttribute.needsUpdate = true - this.index.needsUpdate = true + if (this.generateUVs) { + this.uvsAttribute.needsUpdate = true + } + } - this.positionAttribute.needsUpdate = true + fillMesh(time) { + const scope = this - if (this.generateUVs) { - this.uvsAttribute.needsUpdate = true - } - } + this.currentVertex = 0 + this.currentIndex = 0 + this.currentCoordinate = 0 + this.currentUVCoordinate = 0 - fillMesh(time) { - const scope = this + this.fractalRay(time, function fillVertices(segment) { + const subray = scope.currentSubray - this.currentVertex = 0 - this.currentIndex = 0 - this.currentCoordinate = 0 - this.currentUVCoordinate = 0 + if (time < subray.birthTime) { + //&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) { - this.fractalRay(time, function fillVertices(segment) { - const subray = scope.currentSubray + return + } else if (this.rayParameters.isEternal && scope.currentSubray.recursion == 0) { + // Eternal rays don't propagate nor vanish, but its subrays do - if (time < subray.birthTime) { - //&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) { + scope.createPrism(segment) - return - } else if (this.rayParameters.isEternal && scope.currentSubray.recursion == 0) { - // Eternal rays don't propagate nor vanish, but its subrays do + scope.onDecideSubrayCreation(segment, scope) + } else if (time < subray.endPropagationTime) { + if (scope.timeFraction >= segment.fraction0 * subray.propagationTimeFactor) { + // Ray propagation has arrived to this segment - scope.createPrism(segment) + scope.createPrism(segment) - scope.onDecideSubrayCreation(segment, scope) - } else if (time < subray.endPropagationTime) { - if (scope.timeFraction >= segment.fraction0 * subray.propagationTimeFactor) { - // Ray propagation has arrived to this segment + scope.onDecideSubrayCreation(segment, scope) + } + } else if (time < subray.beginVanishingTime) { + // Ray is steady (nor propagating nor vanishing) scope.createPrism(segment) + scope.onDecideSubrayCreation(segment, scope) + } else { + if (scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1 * (1 - subray.vanishingTimeFactor)) { + // Segment has not yet vanished + + scope.createPrism(segment) + } + scope.onDecideSubrayCreation(segment, scope) } - } else if (time < subray.beginVanishingTime) { - // Ray is steady (nor propagating nor vanishing) + }) + } - scope.createPrism(segment) + addNewSubray(/*rayParameters*/) { + return this.subrays[this.numSubrays++] + } - scope.onDecideSubrayCreation(segment, scope) - } else { - if (scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1 * (1 - subray.vanishingTimeFactor)) { - // Segment has not yet vanished + initSubray(subray, rayParameters) { + subray.pos0.copy(rayParameters.sourceOffset) + subray.pos1.copy(rayParameters.destOffset) + subray.up0.copy(rayParameters.up0) + subray.up1.copy(rayParameters.up1) + subray.radius0 = rayParameters.radius0 + subray.radius1 = rayParameters.radius1 + subray.birthTime = rayParameters.birthTime + subray.deathTime = rayParameters.deathTime + subray.timeScale = rayParameters.timeScale + subray.roughness = rayParameters.roughness + subray.straightness = rayParameters.straightness + subray.propagationTimeFactor = rayParameters.propagationTimeFactor + subray.vanishingTimeFactor = rayParameters.vanishingTimeFactor + + subray.maxIterations = this.maxIterations + subray.seed = rayParameters.noiseSeed !== undefined ? rayParameters.noiseSeed : 0 + subray.recursion = 0 + } - scope.createPrism(segment) - } + fractalRay(time, segmentCallback) { + this.time = time + this.currentSegmentCallback = segmentCallback + this.numSubrays = 0 - scope.onDecideSubrayCreation(segment, scope) - } - }) - } + // Add the top level subray + this.initSubray(this.addNewSubray(), this.rayParameters) - addNewSubray(/*rayParameters*/) { - return this.subrays[this.numSubrays++] - } + // Process all subrays that are being generated until consuming all of them + for (let subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex++) { + const subray = this.subrays[subrayIndex] + this.currentSubray = subray - initSubray(subray, rayParameters) { - subray.pos0.copy(rayParameters.sourceOffset) - subray.pos1.copy(rayParameters.destOffset) - subray.up0.copy(rayParameters.up0) - subray.up1.copy(rayParameters.up1) - subray.radius0 = rayParameters.radius0 - subray.radius1 = rayParameters.radius1 - subray.birthTime = rayParameters.birthTime - subray.deathTime = rayParameters.deathTime - subray.timeScale = rayParameters.timeScale - subray.roughness = rayParameters.roughness - subray.straightness = rayParameters.straightness - subray.propagationTimeFactor = rayParameters.propagationTimeFactor - subray.vanishingTimeFactor = rayParameters.vanishingTimeFactor - - subray.maxIterations = this.maxIterations - subray.seed = rayParameters.noiseSeed !== undefined ? rayParameters.noiseSeed : 0 - subray.recursion = 0 - } + this.randomGenerator.setSeed(subray.seed) - fractalRay(time, segmentCallback) { - this.time = time - this.currentSegmentCallback = segmentCallback - this.numSubrays = 0 + subray.endPropagationTime = MathUtils.lerp(subray.birthTime, subray.deathTime, subray.propagationTimeFactor) + subray.beginVanishingTime = MathUtils.lerp(subray.deathTime, subray.birthTime, 1 - subray.vanishingTimeFactor) - // Add the top level subray - this.initSubray(this.addNewSubray(), this.rayParameters) + const random1 = this.randomGenerator.random + subray.linPos0.set(random1(), random1(), random1()).multiplyScalar(1000) + subray.linPos1.set(random1(), random1(), random1()).multiplyScalar(1000) - // Process all subrays that are being generated until consuming all of them - for (let subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex++) { - const subray = this.subrays[subrayIndex] - this.currentSubray = subray + this.timeFraction = (time - subray.birthTime) / (subray.deathTime - subray.birthTime) - this.randomGenerator.setSeed(subray.seed) + this.currentSegmentIndex = 0 + this.isInitialSegment = true - subray.endPropagationTime = MathUtils.lerp(subray.birthTime, subray.deathTime, subray.propagationTimeFactor) - subray.beginVanishingTime = MathUtils.lerp(subray.deathTime, subray.birthTime, 1 - subray.vanishingTimeFactor) + const segment = this.getNewSegment() + segment.iteration = 0 + segment.pos0.copy(subray.pos0) + segment.pos1.copy(subray.pos1) + segment.linPos0.copy(subray.linPos0) + segment.linPos1.copy(subray.linPos1) + segment.up0.copy(subray.up0) + segment.up1.copy(subray.up1) + segment.radius0 = subray.radius0 + segment.radius1 = subray.radius1 + segment.fraction0 = 0 + segment.fraction1 = 1 + segment.positionVariationFactor = 1 - subray.straightness - const random1 = this.randomGenerator.random - subray.linPos0.set(random1(), random1(), random1()).multiplyScalar(1000) - subray.linPos1.set(random1(), random1(), random1()).multiplyScalar(1000) + this.subrayProbability = + (this.ramification * Math.pow(this.recursionProbability, subray.recursion)) / (1 << subray.maxIterations) - this.timeFraction = (time - subray.birthTime) / (subray.deathTime - subray.birthTime) + this.fractalRayRecursive(segment) + } - this.currentSegmentIndex = 0 - this.isInitialSegment = true - - const segment = this.getNewSegment() - segment.iteration = 0 - segment.pos0.copy(subray.pos0) - segment.pos1.copy(subray.pos1) - segment.linPos0.copy(subray.linPos0) - segment.linPos1.copy(subray.linPos1) - segment.up0.copy(subray.up0) - segment.up1.copy(subray.up1) - segment.radius0 = subray.radius0 - segment.radius1 = subray.radius1 - segment.fraction0 = 0 - segment.fraction1 = 1 - segment.positionVariationFactor = 1 - subray.straightness - - this.subrayProbability = - (this.ramification * Math.pow(this.recursionProbability, subray.recursion)) / (1 << subray.maxIterations) - - this.fractalRayRecursive(segment) + this.currentSegmentCallback = null + this.currentSubray = null } - this.currentSegmentCallback = null - this.currentSubray = null - } + fractalRayRecursive(segment) { + // Leave recursion condition + if (segment.iteration >= this.currentSubray.maxIterations) { + this.currentSegmentCallback(segment) - fractalRayRecursive(segment) { - // Leave recursion condition - if (segment.iteration >= this.currentSubray.maxIterations) { - this.currentSegmentCallback(segment) + return + } - return - } + // Interpolation + this.forwards.subVectors(segment.pos1, segment.pos0) + let lForwards = this.forwards.length() - // Interpolation - this.forwards.subVectors(segment.pos1, segment.pos0) - let lForwards = this.forwards.length() + if (lForwards < 0.000001) { + this.forwards.set(0, 0, 0.01) + lForwards = this.forwards.length() + } - if (lForwards < 0.000001) { - this.forwards.set(0, 0, 0.01) - lForwards = this.forwards.length() + const middleRadius = (segment.radius0 + segment.radius1) * 0.5 + const middleFraction = (segment.fraction0 + segment.fraction1) * 0.5 + + const timeDimension = this.time * this.currentSubray.timeScale * Math.pow(2, segment.iteration) + + this.middlePos.lerpVectors(segment.pos0, segment.pos1, 0.5) + this.middleLinPos.lerpVectors(segment.linPos0, segment.linPos1, 0.5) + const p = this.middleLinPos + + // Noise + this.newPos.set( + this.simplexX.noise4d(p.x, p.y, p.z, timeDimension), + this.simplexY.noise4d(p.x, p.y, p.z, timeDimension), + this.simplexZ.noise4d(p.x, p.y, p.z, timeDimension), + ) + + this.newPos.multiplyScalar(segment.positionVariationFactor * lForwards) + this.newPos.add(this.middlePos) + + // Recursion + + const newSegment1 = this.getNewSegment() + newSegment1.pos0.copy(segment.pos0) + newSegment1.pos1.copy(this.newPos) + newSegment1.linPos0.copy(segment.linPos0) + newSegment1.linPos1.copy(this.middleLinPos) + newSegment1.up0.copy(segment.up0) + newSegment1.up1.copy(segment.up1) + newSegment1.radius0 = segment.radius0 + newSegment1.radius1 = middleRadius + newSegment1.fraction0 = segment.fraction0 + newSegment1.fraction1 = middleFraction + newSegment1.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness + newSegment1.iteration = segment.iteration + 1 + + const newSegment2 = this.getNewSegment() + newSegment2.pos0.copy(this.newPos) + newSegment2.pos1.copy(segment.pos1) + newSegment2.linPos0.copy(this.middleLinPos) + newSegment2.linPos1.copy(segment.linPos1) + this.cross1.crossVectors(segment.up0, this.forwards.normalize()) + newSegment2.up0.crossVectors(this.forwards, this.cross1).normalize() + newSegment2.up1.copy(segment.up1) + newSegment2.radius0 = middleRadius + newSegment2.radius1 = segment.radius1 + newSegment2.fraction0 = middleFraction + newSegment2.fraction1 = segment.fraction1 + newSegment2.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness + newSegment2.iteration = segment.iteration + 1 + + this.fractalRayRecursive(newSegment1) + + this.fractalRayRecursive(newSegment2) } - const middleRadius = (segment.radius0 + segment.radius1) * 0.5 - const middleFraction = (segment.fraction0 + segment.fraction1) * 0.5 - - const timeDimension = this.time * this.currentSubray.timeScale * Math.pow(2, segment.iteration) - - this.middlePos.lerpVectors(segment.pos0, segment.pos1, 0.5) - this.middleLinPos.lerpVectors(segment.linPos0, segment.linPos1, 0.5) - const p = this.middleLinPos - - // Noise - this.newPos.set( - this.simplexX.noise4d(p.x, p.y, p.z, timeDimension), - this.simplexY.noise4d(p.x, p.y, p.z, timeDimension), - this.simplexZ.noise4d(p.x, p.y, p.z, timeDimension), - ) - - this.newPos.multiplyScalar(segment.positionVariationFactor * lForwards) - this.newPos.add(this.middlePos) - - // Recursion - - const newSegment1 = this.getNewSegment() - newSegment1.pos0.copy(segment.pos0) - newSegment1.pos1.copy(this.newPos) - newSegment1.linPos0.copy(segment.linPos0) - newSegment1.linPos1.copy(this.middleLinPos) - newSegment1.up0.copy(segment.up0) - newSegment1.up1.copy(segment.up1) - newSegment1.radius0 = segment.radius0 - newSegment1.radius1 = middleRadius - newSegment1.fraction0 = segment.fraction0 - newSegment1.fraction1 = middleFraction - newSegment1.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness - newSegment1.iteration = segment.iteration + 1 - - const newSegment2 = this.getNewSegment() - newSegment2.pos0.copy(this.newPos) - newSegment2.pos1.copy(segment.pos1) - newSegment2.linPos0.copy(this.middleLinPos) - newSegment2.linPos1.copy(segment.linPos1) - this.cross1.crossVectors(segment.up0, this.forwards.normalize()) - newSegment2.up0.crossVectors(this.forwards, this.cross1).normalize() - newSegment2.up1.copy(segment.up1) - newSegment2.radius0 = middleRadius - newSegment2.radius1 = segment.radius1 - newSegment2.fraction0 = middleFraction - newSegment2.fraction1 = segment.fraction1 - newSegment2.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness - newSegment2.iteration = segment.iteration + 1 - - this.fractalRayRecursive(newSegment1) - - this.fractalRayRecursive(newSegment2) - } - - createPrism(segment) { - // Creates one triangular prism and its vertices at the segment + createPrism(segment) { + // Creates one triangular prism and its vertices at the segment - this.forwardsFill.subVectors(segment.pos1, segment.pos0).normalize() + this.forwardsFill.subVectors(segment.pos1, segment.pos0).normalize() - if (this.isInitialSegment) { - this.currentCreateTriangleVertices(segment.pos0, segment.up0, this.forwardsFill, segment.radius0, 0) + if (this.isInitialSegment) { + this.currentCreateTriangleVertices(segment.pos0, segment.up0, this.forwardsFill, segment.radius0, 0) - this.isInitialSegment = false - } + this.isInitialSegment = false + } - this.currentCreateTriangleVertices(segment.pos1, segment.up0, this.forwardsFill, segment.radius1, segment.fraction1) + this.currentCreateTriangleVertices( + segment.pos1, + segment.up0, + this.forwardsFill, + segment.radius1, + segment.fraction1, + ) - this.createPrismFaces() - } + this.createPrismFaces() + } - createTriangleVerticesWithoutUVs(pos, up, forwards, radius) { - // Create an equilateral triangle (only vertices) + createTriangleVerticesWithoutUVs(pos, up, forwards, radius) { + // Create an equilateral triangle (only vertices) - this.side.crossVectors(up, forwards).multiplyScalar(radius * LightningStrike.COS30DEG) - this.down.copy(up).multiplyScalar(-radius * LightningStrike.SIN30DEG) + this.side.crossVectors(up, forwards).multiplyScalar(radius * LightningStrike.COS30DEG) + this.down.copy(up).multiplyScalar(-radius * LightningStrike.SIN30DEG) - const p = this.vPos - const v = this.vertices + const p = this.vPos + const v = this.vertices - p.copy(pos).sub(this.side).add(this.down) + p.copy(pos).sub(this.side).add(this.down) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - p.copy(pos).add(this.side).add(this.down) + p.copy(pos).add(this.side).add(this.down) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - p.copy(up).multiplyScalar(radius).add(pos) + p.copy(up).multiplyScalar(radius).add(pos) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - this.currentVertex += 3 - } + this.currentVertex += 3 + } - createTriangleVerticesWithUVs(pos, up, forwards, radius, u) { - // Create an equilateral triangle (only vertices) + createTriangleVerticesWithUVs(pos, up, forwards, radius, u) { + // Create an equilateral triangle (only vertices) - this.side.crossVectors(up, forwards).multiplyScalar(radius * LightningStrike.COS30DEG) - this.down.copy(up).multiplyScalar(-radius * LightningStrike.SIN30DEG) + this.side.crossVectors(up, forwards).multiplyScalar(radius * LightningStrike.COS30DEG) + this.down.copy(up).multiplyScalar(-radius * LightningStrike.SIN30DEG) - const p = this.vPos - const v = this.vertices - const uv = this.uvs + const p = this.vPos + const v = this.vertices + const uv = this.uvs - p.copy(pos).sub(this.side).add(this.down) + p.copy(pos).sub(this.side).add(this.down) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - uv[this.currentUVCoordinate++] = u - uv[this.currentUVCoordinate++] = 0 + uv[this.currentUVCoordinate++] = u + uv[this.currentUVCoordinate++] = 0 - p.copy(pos).add(this.side).add(this.down) + p.copy(pos).add(this.side).add(this.down) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - uv[this.currentUVCoordinate++] = u - uv[this.currentUVCoordinate++] = 0.5 + uv[this.currentUVCoordinate++] = u + uv[this.currentUVCoordinate++] = 0.5 - p.copy(up).multiplyScalar(radius).add(pos) + p.copy(up).multiplyScalar(radius).add(pos) - v[this.currentCoordinate++] = p.x - v[this.currentCoordinate++] = p.y - v[this.currentCoordinate++] = p.z + v[this.currentCoordinate++] = p.x + v[this.currentCoordinate++] = p.y + v[this.currentCoordinate++] = p.z - uv[this.currentUVCoordinate++] = u - uv[this.currentUVCoordinate++] = 1 + uv[this.currentUVCoordinate++] = u + uv[this.currentUVCoordinate++] = 1 - this.currentVertex += 3 - } + this.currentVertex += 3 + } - createPrismFaces(vertex /*, index*/) { - const indices = this.indices - vertex = this.currentVertex - 6 - - indices[this.currentIndex++] = vertex + 1 - indices[this.currentIndex++] = vertex + 2 - indices[this.currentIndex++] = vertex + 5 - indices[this.currentIndex++] = vertex + 1 - indices[this.currentIndex++] = vertex + 5 - indices[this.currentIndex++] = vertex + 4 - indices[this.currentIndex++] = vertex + 0 - indices[this.currentIndex++] = vertex + 1 - indices[this.currentIndex++] = vertex + 4 - indices[this.currentIndex++] = vertex + 0 - indices[this.currentIndex++] = vertex + 4 - indices[this.currentIndex++] = vertex + 3 - indices[this.currentIndex++] = vertex + 2 - indices[this.currentIndex++] = vertex + 0 - indices[this.currentIndex++] = vertex + 3 - indices[this.currentIndex++] = vertex + 2 - indices[this.currentIndex++] = vertex + 3 - indices[this.currentIndex++] = vertex + 5 - } + createPrismFaces(vertex /*, index*/) { + const indices = this.indices + vertex = this.currentVertex - 6 + + indices[this.currentIndex++] = vertex + 1 + indices[this.currentIndex++] = vertex + 2 + indices[this.currentIndex++] = vertex + 5 + indices[this.currentIndex++] = vertex + 1 + indices[this.currentIndex++] = vertex + 5 + indices[this.currentIndex++] = vertex + 4 + indices[this.currentIndex++] = vertex + 0 + indices[this.currentIndex++] = vertex + 1 + indices[this.currentIndex++] = vertex + 4 + indices[this.currentIndex++] = vertex + 0 + indices[this.currentIndex++] = vertex + 4 + indices[this.currentIndex++] = vertex + 3 + indices[this.currentIndex++] = vertex + 2 + indices[this.currentIndex++] = vertex + 0 + indices[this.currentIndex++] = vertex + 3 + indices[this.currentIndex++] = vertex + 2 + indices[this.currentIndex++] = vertex + 3 + indices[this.currentIndex++] = vertex + 5 + } - createDefaultSubrayCreationCallbacks() { - const random1 = this.randomGenerator.random + createDefaultSubrayCreationCallbacks() { + const random1 = this.randomGenerator.random - this.onDecideSubrayCreation = function (segment, lightningStrike) { - // Decide subrays creation at parent (sub)ray segment + this.onDecideSubrayCreation = function (segment, lightningStrike) { + // Decide subrays creation at parent (sub)ray segment - const subray = lightningStrike.currentSubray + const subray = lightningStrike.currentSubray - const period = lightningStrike.rayParameters.subrayPeriod - const dutyCycle = lightningStrike.rayParameters.subrayDutyCycle + const period = lightningStrike.rayParameters.subrayPeriod + const dutyCycle = lightningStrike.rayParameters.subrayDutyCycle - const phase0 = - lightningStrike.rayParameters.isEternal && subray.recursion == 0 - ? -random1() * period - : MathUtils.lerp(subray.birthTime, subray.endPropagationTime, segment.fraction0) - random1() * period + const phase0 = + lightningStrike.rayParameters.isEternal && subray.recursion == 0 + ? -random1() * period + : MathUtils.lerp(subray.birthTime, subray.endPropagationTime, segment.fraction0) - random1() * period - const phase = lightningStrike.time - phase0 - const currentCycle = Math.floor(phase / period) + const phase = lightningStrike.time - phase0 + const currentCycle = Math.floor(phase / period) - const childSubraySeed = random1() * (currentCycle + 1) + const childSubraySeed = random1() * (currentCycle + 1) - const isActive = phase % period <= dutyCycle * period + const isActive = phase % period <= dutyCycle * period - let probability = 0 + let probability = 0 - if (isActive) { - probability = lightningStrike.subrayProbability - // Distribution test: probability *= segment.fraction0 > 0.5 && segment.fraction0 < 0.9 ? 1 / 0.4 : 0; - } + if (isActive) { + probability = lightningStrike.subrayProbability + // Distribution test: probability *= segment.fraction0 > 0.5 && segment.fraction0 < 0.9 ? 1 / 0.4 : 0; + } - if ( - subray.recursion < lightningStrike.maxSubrayRecursion && - lightningStrike.numSubrays < lightningStrike.maxSubrays && - random1() < probability - ) { - const childSubray = lightningStrike.addNewSubray() - - const parentSeed = lightningStrike.randomGenerator.getSeed() - childSubray.seed = childSubraySeed - lightningStrike.randomGenerator.setSeed(childSubraySeed) - - childSubray.recursion = subray.recursion + 1 - childSubray.maxIterations = Math.max(1, subray.maxIterations - 1) - - childSubray.linPos0.set(random1(), random1(), random1()).multiplyScalar(1000) - childSubray.linPos1.set(random1(), random1(), random1()).multiplyScalar(1000) - childSubray.up0.copy(subray.up0) - childSubray.up1.copy(subray.up1) - childSubray.radius0 = segment.radius0 * lightningStrike.rayParameters.radius0Factor - childSubray.radius1 = Math.min( - lightningStrike.rayParameters.minRadius, - segment.radius1 * lightningStrike.rayParameters.radius1Factor, - ) - - childSubray.birthTime = phase0 + currentCycle * period - childSubray.deathTime = childSubray.birthTime + period * dutyCycle - - if (!lightningStrike.rayParameters.isEternal && subray.recursion == 0) { - childSubray.birthTime = Math.max(childSubray.birthTime, subray.birthTime) - childSubray.deathTime = Math.min(childSubray.deathTime, subray.deathTime) + if ( + subray.recursion < lightningStrike.maxSubrayRecursion && + lightningStrike.numSubrays < lightningStrike.maxSubrays && + random1() < probability + ) { + const childSubray = lightningStrike.addNewSubray() + + const parentSeed = lightningStrike.randomGenerator.getSeed() + childSubray.seed = childSubraySeed + lightningStrike.randomGenerator.setSeed(childSubraySeed) + + childSubray.recursion = subray.recursion + 1 + childSubray.maxIterations = Math.max(1, subray.maxIterations - 1) + + childSubray.linPos0.set(random1(), random1(), random1()).multiplyScalar(1000) + childSubray.linPos1.set(random1(), random1(), random1()).multiplyScalar(1000) + childSubray.up0.copy(subray.up0) + childSubray.up1.copy(subray.up1) + childSubray.radius0 = segment.radius0 * lightningStrike.rayParameters.radius0Factor + childSubray.radius1 = Math.min( + lightningStrike.rayParameters.minRadius, + segment.radius1 * lightningStrike.rayParameters.radius1Factor, + ) + + childSubray.birthTime = phase0 + currentCycle * period + childSubray.deathTime = childSubray.birthTime + period * dutyCycle + + if (!lightningStrike.rayParameters.isEternal && subray.recursion == 0) { + childSubray.birthTime = Math.max(childSubray.birthTime, subray.birthTime) + childSubray.deathTime = Math.min(childSubray.deathTime, subray.deathTime) + } + + childSubray.timeScale = subray.timeScale * 2 + childSubray.roughness = subray.roughness + childSubray.straightness = subray.straightness + childSubray.propagationTimeFactor = subray.propagationTimeFactor + childSubray.vanishingTimeFactor = subray.vanishingTimeFactor + + lightningStrike.onSubrayCreation(segment, subray, childSubray, lightningStrike) + + lightningStrike.randomGenerator.setSeed(parentSeed) } + } - childSubray.timeScale = subray.timeScale * 2 - childSubray.roughness = subray.roughness - childSubray.straightness = subray.straightness - childSubray.propagationTimeFactor = subray.propagationTimeFactor - childSubray.vanishingTimeFactor = subray.vanishingTimeFactor + const vec1Pos = new Vector3() + const vec2Forward = new Vector3() + const vec3Side = new Vector3() + const vec4Up = new Vector3() - lightningStrike.onSubrayCreation(segment, subray, childSubray, lightningStrike) + this.onSubrayCreation = function (segment, parentSubray, childSubray, lightningStrike) { + // Decide childSubray origin and destination positions (pos0 and pos1) and possibly other properties of childSubray - lightningStrike.randomGenerator.setSeed(parentSeed) + // Just use the default cone position generator + lightningStrike.subrayCylinderPosition(segment, parentSubray, childSubray, 0.5, 0.6, 0.2) } - } - const vec1Pos = new Vector3() - const vec2Forward = new Vector3() - const vec3Side = new Vector3() - const vec4Up = new Vector3() - - this.onSubrayCreation = function (segment, parentSubray, childSubray, lightningStrike) { - // Decide childSubray origin and destination positions (pos0 and pos1) and possibly other properties of childSubray - - // Just use the default cone position generator - lightningStrike.subrayCylinderPosition(segment, parentSubray, childSubray, 0.5, 0.6, 0.2) - } + this.subrayConePosition = function ( + segment, + parentSubray, + childSubray, + heightFactor, + sideWidthFactor, + minSideWidthFactor, + ) { + // Sets childSubray pos0 and pos1 in a cone + + childSubray.pos0.copy(segment.pos0) + + vec1Pos.subVectors(parentSubray.pos1, parentSubray.pos0) + vec2Forward.copy(vec1Pos).normalize() + vec1Pos.multiplyScalar(segment.fraction0 + (1 - segment.fraction0) * (random1() * heightFactor)) + const length = vec1Pos.length() + vec3Side.crossVectors(parentSubray.up0, vec2Forward) + const angle = 2 * Math.PI * random1() + vec3Side.multiplyScalar(Math.cos(angle)) + vec4Up.copy(parentSubray.up0).multiplyScalar(Math.sin(angle)) + + childSubray.pos1 + .copy(vec3Side) + .add(vec4Up) + .multiplyScalar(length * sideWidthFactor * (minSideWidthFactor + random1() * (1 - minSideWidthFactor))) + .add(vec1Pos) + .add(parentSubray.pos0) + } - this.subrayConePosition = function ( - segment, - parentSubray, - childSubray, - heightFactor, - sideWidthFactor, - minSideWidthFactor, - ) { - // Sets childSubray pos0 and pos1 in a cone - - childSubray.pos0.copy(segment.pos0) - - vec1Pos.subVectors(parentSubray.pos1, parentSubray.pos0) - vec2Forward.copy(vec1Pos).normalize() - vec1Pos.multiplyScalar(segment.fraction0 + (1 - segment.fraction0) * (random1() * heightFactor)) - const length = vec1Pos.length() - vec3Side.crossVectors(parentSubray.up0, vec2Forward) - const angle = 2 * Math.PI * random1() - vec3Side.multiplyScalar(Math.cos(angle)) - vec4Up.copy(parentSubray.up0).multiplyScalar(Math.sin(angle)) - - childSubray.pos1 - .copy(vec3Side) - .add(vec4Up) - .multiplyScalar(length * sideWidthFactor * (minSideWidthFactor + random1() * (1 - minSideWidthFactor))) - .add(vec1Pos) - .add(parentSubray.pos0) + this.subrayCylinderPosition = function ( + segment, + parentSubray, + childSubray, + heightFactor, + sideWidthFactor, + minSideWidthFactor, + ) { + // Sets childSubray pos0 and pos1 in a cylinder + + childSubray.pos0.copy(segment.pos0) + + vec1Pos.subVectors(parentSubray.pos1, parentSubray.pos0) + vec2Forward.copy(vec1Pos).normalize() + vec1Pos.multiplyScalar(segment.fraction0 + (1 - segment.fraction0) * ((2 * random1() - 1) * heightFactor)) + const length = vec1Pos.length() + vec3Side.crossVectors(parentSubray.up0, vec2Forward) + const angle = 2 * Math.PI * random1() + vec3Side.multiplyScalar(Math.cos(angle)) + vec4Up.copy(parentSubray.up0).multiplyScalar(Math.sin(angle)) + + childSubray.pos1 + .copy(vec3Side) + .add(vec4Up) + .multiplyScalar(length * sideWidthFactor * (minSideWidthFactor + random1() * (1 - minSideWidthFactor))) + .add(vec1Pos) + .add(parentSubray.pos0) + } } - this.subrayCylinderPosition = function ( - segment, - parentSubray, - childSubray, - heightFactor, - sideWidthFactor, - minSideWidthFactor, - ) { - // Sets childSubray pos0 and pos1 in a cylinder - - childSubray.pos0.copy(segment.pos0) - - vec1Pos.subVectors(parentSubray.pos1, parentSubray.pos0) - vec2Forward.copy(vec1Pos).normalize() - vec1Pos.multiplyScalar(segment.fraction0 + (1 - segment.fraction0) * ((2 * random1() - 1) * heightFactor)) - const length = vec1Pos.length() - vec3Side.crossVectors(parentSubray.up0, vec2Forward) - const angle = 2 * Math.PI * random1() - vec3Side.multiplyScalar(Math.cos(angle)) - vec4Up.copy(parentSubray.up0).multiplyScalar(Math.sin(angle)) - - childSubray.pos1 - .copy(vec3Side) - .add(vec4Up) - .multiplyScalar(length * sideWidthFactor * (minSideWidthFactor + random1() * (1 - minSideWidthFactor))) - .add(vec1Pos) - .add(parentSubray.pos0) + createSubray() { + return { + seed: 0, + maxIterations: 0, + recursion: 0, + pos0: new Vector3(), + pos1: new Vector3(), + linPos0: new Vector3(), + linPos1: new Vector3(), + up0: new Vector3(), + up1: new Vector3(), + radius0: 0, + radius1: 0, + birthTime: 0, + deathTime: 0, + timeScale: 0, + roughness: 0, + straightness: 0, + propagationTimeFactor: 0, + vanishingTimeFactor: 0, + endPropagationTime: 0, + beginVanishingTime: 0, + } } - } - createSubray() { - return { - seed: 0, - maxIterations: 0, - recursion: 0, - pos0: new Vector3(), - pos1: new Vector3(), - linPos0: new Vector3(), - linPos1: new Vector3(), - up0: new Vector3(), - up1: new Vector3(), - radius0: 0, - radius1: 0, - birthTime: 0, - deathTime: 0, - timeScale: 0, - roughness: 0, - straightness: 0, - propagationTimeFactor: 0, - vanishingTimeFactor: 0, - endPropagationTime: 0, - beginVanishingTime: 0, + createSegment() { + return { + iteration: 0, + pos0: new Vector3(), + pos1: new Vector3(), + linPos0: new Vector3(), + linPos1: new Vector3(), + up0: new Vector3(), + up1: new Vector3(), + radius0: 0, + radius1: 0, + fraction0: 0, + fraction1: 0, + positionVariationFactor: 0, + } } - } - createSegment() { - return { - iteration: 0, - pos0: new Vector3(), - pos1: new Vector3(), - linPos0: new Vector3(), - linPos1: new Vector3(), - up0: new Vector3(), - up1: new Vector3(), - radius0: 0, - radius1: 0, - fraction0: 0, - fraction1: 0, - positionVariationFactor: 0, + getNewSegment() { + return this.raySegments[this.currentSegmentIndex++] } - } - getNewSegment() { - return this.raySegments[this.currentSegmentIndex++] - } + copy(source) { + super.copy(source) - copy(source) { - super.copy(source) + this.init(LightningStrike.copyParameters({}, source.rayParameters)) - this.init(LightningStrike.copyParameters({}, source.rayParameters)) + return this + } - return this + clone() { + return new this.constructor(LightningStrike.copyParameters({}, this.rayParameters)) + } } - clone() { - return new this.constructor(LightningStrike.copyParameters({}, this.rayParameters)) - } -} + return LightningStrike +})() export { LightningStrike } diff --git a/src/geometries/RoundedBoxGeometry.js b/src/geometries/RoundedBoxGeometry.js index 80c5f35b..3a10f247 100644 --- a/src/geometries/RoundedBoxGeometry.js +++ b/src/geometries/RoundedBoxGeometry.js @@ -1,6 +1,6 @@ import { BoxGeometry, Vector3 } from 'three' -const tempNormal = new Vector3() +const tempNormal = /* @__PURE__ */ new Vector3() function getUv(faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength) { const totArcLength = (2 * Math.PI * radius) / 4 diff --git a/src/helpers/RaycasterHelper.ts b/src/helpers/RaycasterHelper.ts index f5f57644..ed093375 100644 --- a/src/helpers/RaycasterHelper.ts +++ b/src/helpers/RaycasterHelper.ts @@ -18,8 +18,8 @@ import { Vector3, } from 'three' -const _o = new Object3D() -const _v = new Vector3() +const _o = /* @__PURE__ */ new Object3D() +const _v = /* @__PURE__ */ new Vector3() class RaycasterHelper extends Object3D { raycaster: Raycaster diff --git a/src/helpers/VertexNormalsHelper.js b/src/helpers/VertexNormalsHelper.js index 18e8addb..b586a130 100644 --- a/src/helpers/VertexNormalsHelper.js +++ b/src/helpers/VertexNormalsHelper.js @@ -1,8 +1,8 @@ import { BufferGeometry, Float32BufferAttribute, LineSegments, LineBasicMaterial, Matrix3, Vector3 } from 'three' -const _v1 = new Vector3() -const _v2 = new Vector3() -const _normalMatrix = new Matrix3() +const _v1 = /* @__PURE__ */ new Vector3() +const _v2 = /* @__PURE__ */ new Vector3() +const _normalMatrix = /* @__PURE__ */ new Matrix3() class VertexNormalsHelper extends LineSegments { constructor(object, size = 1, color = 0xff0000) { diff --git a/src/helpers/VertexTangentsHelper.js b/src/helpers/VertexTangentsHelper.js index 42d50ee5..a9af184a 100644 --- a/src/helpers/VertexTangentsHelper.js +++ b/src/helpers/VertexTangentsHelper.js @@ -1,7 +1,7 @@ import { BufferGeometry, Float32BufferAttribute, LineSegments, LineBasicMaterial, Vector3 } from 'three' -const _v1 = new Vector3() -const _v2 = new Vector3() +const _v1 = /* @__PURE__ */ new Vector3() +const _v2 = /* @__PURE__ */ new Vector3() class VertexTangentsHelper extends LineSegments { constructor(object, size = 1, color = 0x00ffff) { diff --git a/src/interactive/InteractiveGroup.js b/src/interactive/InteractiveGroup.js index 704f4ffb..263e979b 100644 --- a/src/interactive/InteractiveGroup.js +++ b/src/interactive/InteractiveGroup.js @@ -1,6 +1,6 @@ import { Group, Matrix4, Raycaster, Vector2 } from 'three' -const _pointer = new Vector2() +const _pointer = /* @__PURE__ */ new Vector2() const _event = { type: '', data: _pointer } class InteractiveGroup extends Group { diff --git a/src/interactive/SelectionBox.js b/src/interactive/SelectionBox.js index 4db0205e..dda5dd6e 100644 --- a/src/interactive/SelectionBox.js +++ b/src/interactive/SelectionBox.js @@ -1,24 +1,24 @@ import { Frustum, Vector3 } from 'three' -const frustum = new Frustum() -const center = new Vector3() +const frustum = /* @__PURE__ */ new Frustum() +const center = /* @__PURE__ */ new Vector3() -const tmpPoint = new Vector3() +const tmpPoint = /* @__PURE__ */ new Vector3() -const vecNear = new Vector3() -const vecTopLeft = new Vector3() -const vecTopRight = new Vector3() -const vecDownRight = new Vector3() -const vecDownLeft = new Vector3() +const vecNear = /* @__PURE__ */ new Vector3() +const vecTopLeft = /* @__PURE__ */ new Vector3() +const vecTopRight = /* @__PURE__ */ new Vector3() +const vecDownRight = /* @__PURE__ */ new Vector3() +const vecDownLeft = /* @__PURE__ */ new Vector3() -const vecFarTopLeft = new Vector3() -const vecFarTopRight = new Vector3() -const vecFarDownRight = new Vector3() -const vecFarDownLeft = new Vector3() +const vecFarTopLeft = /* @__PURE__ */ new Vector3() +const vecFarTopRight = /* @__PURE__ */ new Vector3() +const vecFarDownRight = /* @__PURE__ */ new Vector3() +const vecFarDownLeft = /* @__PURE__ */ new Vector3() -const vectemp1 = new Vector3() -const vectemp2 = new Vector3() -const vectemp3 = new Vector3() +const vectemp1 = /* @__PURE__ */ new Vector3() +const vectemp2 = /* @__PURE__ */ new Vector3() +const vectemp3 = /* @__PURE__ */ new Vector3() class SelectionBox { constructor(camera, scene, deep) { diff --git a/src/libs/chevrotain.js b/src/libs/chevrotain.js index 8e23804d..4d8c0e48 100644 --- a/src/libs/chevrotain.js +++ b/src/libs/chevrotain.js @@ -1,4 +1,4 @@ -const { CstParser, Lexer, createToken } = (() => { +const { CstParser, Lexer, createToken } = /* @__PURE__ */ (() => { /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global diff --git a/src/libs/lottie.js b/src/libs/lottie.js index 2c2d699d..e6e503df 100644 --- a/src/libs/lottie.js +++ b/src/libs/lottie.js @@ -1,4 +1,4 @@ -const lottie = (() => { +const lottie = /* @__PURE__ */ (() => { if (typeof navigator === 'undefined' || typeof document === 'undefined') return {} const svgNS = 'http://www.w3.org/2000/svg' diff --git a/src/libs/opentype.js b/src/libs/opentype.js index ecb8c687..d8aa0d04 100644 --- a/src/libs/opentype.js +++ b/src/libs/opentype.js @@ -1,4 +1,4 @@ -const { parseBuffer } = (() => { +const { parseBuffer } = /* @__PURE__ */ (() => { /** * https://opentype.js.org v1.3.4 | (c) Frederik De Bleser and other contributors | MIT License | Uses tiny-inflate by Devon Govett and string.prototype.codepointat polyfill by Mathias Bynens */ diff --git a/src/lights/LightProbeGenerator.js b/src/lights/LightProbeGenerator.js index 216438e4..bd38bf06 100644 --- a/src/lights/LightProbeGenerator.js +++ b/src/lights/LightProbeGenerator.js @@ -1,8 +1,8 @@ import { Color, LightProbe, SphericalHarmonics3, Vector3 } from 'three' -class LightProbeGenerator { +const LightProbeGenerator = { // https://www.ppsloan.org/publications/StupidSH36.pdf - static fromCubeTexture(cubeTexture) { + fromCubeTexture(cubeTexture) { let totalWeight = 0 const coord = new Vector3() @@ -122,9 +122,9 @@ class LightProbeGenerator { } return new LightProbe(sh) - } + }, - static fromCubeRenderTarget(renderer, cubeRenderTarget) { + fromCubeRenderTarget(renderer, cubeRenderTarget) { // The renderTarget must be set to RGBA in order to make readRenderTargetPixels works let totalWeight = 0 @@ -229,7 +229,7 @@ class LightProbeGenerator { } return new LightProbe(sh) - } + }, } export { LightProbeGenerator } diff --git a/src/lights/RectAreaLightUniformsLib.js b/src/lights/RectAreaLightUniformsLib.js index c14634f1..3f495939 100644 --- a/src/lights/RectAreaLightUniformsLib.js +++ b/src/lights/RectAreaLightUniformsLib.js @@ -28,8 +28,8 @@ import { // by Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt // code: https://github.com/selfshadow/ltc_code/ -class RectAreaLightUniformsLib { - static init() { +const RectAreaLightUniformsLib = { + init() { // source: https://github.com/selfshadow/ltc_code/tree/master/fit/results/ltc.js const LTC_MAT_1 = [ @@ -32882,7 +32882,7 @@ class RectAreaLightUniformsLib { UniformsLib.LTC_HALF_1.needsUpdate = true UniformsLib.LTC_HALF_2.needsUpdate = true - } + }, } export { RectAreaLightUniformsLib } diff --git a/src/lines/LineMaterial.js b/src/lines/LineMaterial.js index 3c4959de..8e54c9e8 100644 --- a/src/lines/LineMaterial.js +++ b/src/lines/LineMaterial.js @@ -10,7 +10,8 @@ * } */ -import { ShaderMaterial, UniformsLib, UniformsUtils, Vector2, REVISION } from 'three' +import { ShaderMaterial, UniformsLib, UniformsUtils, Vector2 } from 'three' +import { version } from '../_polyfill/constants' class LineMaterial extends ShaderMaterial { constructor(parameters) { @@ -437,7 +438,7 @@ class LineMaterial extends ShaderMaterial { gl_FragColor = diffuseColor; #include - #include <${parseInt(REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> + #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> #include #include diff --git a/src/lines/LineSegments2.js b/src/lines/LineSegments2.js index b514fe81..461e240a 100644 --- a/src/lines/LineSegments2.js +++ b/src/lines/LineSegments2.js @@ -14,23 +14,23 @@ import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry' import { LineMaterial } from '../lines/LineMaterial' import { UV1 } from '../_polyfill/uv1' -const _viewport = new Vector4(); +const _viewport = /* @__PURE__ */ new Vector4() -const _start = new Vector3() -const _end = new Vector3() +const _start = /* @__PURE__ */ new Vector3() +const _end = /* @__PURE__ */ new Vector3() -const _start4 = new Vector4() -const _end4 = new Vector4() +const _start4 = /* @__PURE__ */ new Vector4() +const _end4 = /* @__PURE__ */ new Vector4() -const _ssOrigin = new Vector4() -const _ssOrigin3 = new Vector3() -const _mvMatrix = new Matrix4() -const _line = new Line3() -const _closestPoint = new Vector3() +const _ssOrigin = /* @__PURE__ */ new Vector4() +const _ssOrigin3 = /* @__PURE__ */ new Vector3() +const _mvMatrix = /* @__PURE__ */ new Matrix4() +const _line = /* @__PURE__ */ new Line3() +const _closestPoint = /* @__PURE__ */ new Vector3() -const _box = new Box3() -const _sphere = new Sphere() -const _clipToWorldVector = new Vector4() +const _box = /* @__PURE__ */ new Box3() +const _sphere = /* @__PURE__ */ new Sphere() +const _clipToWorldVector = /* @__PURE__ */ new Vector4() let _ray, _lineWidth @@ -51,18 +51,17 @@ function getWorldSpaceHalfWidth(camera, distance, resolution) { } function raycastWorldUnits(lineSegments, intersects) { - - const matrixWorld = lineSegments.matrixWorld; - const geometry = lineSegments.geometry; - const instanceStart = geometry.attributes.instanceStart; - const instanceEnd = geometry.attributes.instanceEnd; - const segmentCount = Math.min(geometry.instanceCount, instanceStart.count); + const matrixWorld = lineSegments.matrixWorld + const geometry = lineSegments.geometry + const instanceStart = geometry.attributes.instanceStart + const instanceEnd = geometry.attributes.instanceEnd + const segmentCount = Math.min(geometry.instanceCount, instanceStart.count) for (let i = 0, l = segmentCount; i < l; i++) { _line.start.fromBufferAttribute(instanceStart, i) _line.end.fromBufferAttribute(instanceEnd, i) - _line.applyMatrix4(matrixWorld); + _line.applyMatrix4(matrixWorld) const pointOnLine = new Vector3() const point = new Vector3() @@ -94,7 +93,7 @@ function raycastScreenSpace(lineSegments, camera, intersects) { const geometry = lineSegments.geometry const instanceStart = geometry.attributes.instanceStart const instanceEnd = geometry.attributes.instanceEnd - const segmentCount = Math.min(geometry.instanceCount, instanceStart.count); + const segmentCount = Math.min(geometry.instanceCount, instanceStart.count) const near = -camera.near @@ -312,16 +311,12 @@ class LineSegments2 extends Mesh { } onBeforeRender(renderer) { - - const uniforms = this.material.uniforms; + const uniforms = this.material.uniforms if (uniforms && uniforms.resolution) { - - renderer.getViewport(_viewport); - this.material.uniforms.resolution.value.set(_viewport.z, _viewport.w); - + renderer.getViewport(_viewport) + this.material.uniforms.resolution.value.set(_viewport.z, _viewport.w) } - } } diff --git a/src/lines/LineSegmentsGeometry.js b/src/lines/LineSegmentsGeometry.js index 967b2fcb..5b1fb7bb 100644 --- a/src/lines/LineSegmentsGeometry.js +++ b/src/lines/LineSegmentsGeometry.js @@ -9,8 +9,8 @@ import { WireframeGeometry, } from 'three' -const _box = new Box3() -const _vector = new Vector3() +const _box = /* @__PURE__ */ new Box3() +const _vector = /* @__PURE__ */ new Vector3() class LineSegmentsGeometry extends InstancedBufferGeometry { constructor() { diff --git a/src/lines/Wireframe.js b/src/lines/Wireframe.js index 5c3bfe87..5d97668b 100644 --- a/src/lines/Wireframe.js +++ b/src/lines/Wireframe.js @@ -2,9 +2,9 @@ import { InstancedInterleavedBuffer, InterleavedBufferAttribute, Mesh, Vector3, import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry' import { LineMaterial } from '../lines/LineMaterial' -const _start = new Vector3() -const _end = new Vector3() -const _viewport = new Vector4(); +const _start = /* @__PURE__ */ new Vector3() +const _end = /* @__PURE__ */ new Vector3() +const _viewport = /* @__PURE__ */ new Vector4() class Wireframe extends Mesh { constructor(geometry = new LineSegmentsGeometry(), material = new LineMaterial({ color: Math.random() * 0xffffff })) { @@ -41,16 +41,12 @@ class Wireframe extends Mesh { } onBeforeRender(renderer) { - - const uniforms = this.material.uniforms; + const uniforms = this.material.uniforms if (uniforms && uniforms.resolution) { - - renderer.getViewport(_viewport); - this.material.uniforms.resolution.value.set(_viewport.z, _viewport.w); - + renderer.getViewport(_viewport) + this.material.uniforms.resolution.value.set(_viewport.z, _viewport.w) } - } } diff --git a/src/loaders/BasisTextureLoader.js b/src/loaders/BasisTextureLoader.js index f568464c..6d2babf6 100644 --- a/src/loaders/BasisTextureLoader.js +++ b/src/loaders/BasisTextureLoader.js @@ -32,135 +32,174 @@ import { const _taskCache = new WeakMap() -class BasisTextureLoader extends Loader { - /* CONSTANTS */ +const BasisTextureLoader = /* @__PURE__ */ (() => { + class BasisTextureLoader extends Loader { + /* CONSTANTS */ - static BasisFormat = { - ETC1S: 0, - UASTC_4x4: 1, - } + static BasisFormat = { + ETC1S: 0, + UASTC_4x4: 1, + } - static TranscoderFormat = { - ETC1: 0, - ETC2: 1, - BC1: 2, - BC3: 3, - BC4: 4, - BC5: 5, - BC7_M6_OPAQUE_ONLY: 6, - BC7_M5: 7, - PVRTC1_4_RGB: 8, - PVRTC1_4_RGBA: 9, - ASTC_4x4: 10, - ATC_RGB: 11, - ATC_RGBA_INTERPOLATED_ALPHA: 12, - RGBA32: 13, - RGB565: 14, - BGR565: 15, - RGBA4444: 16, - } + static TranscoderFormat = { + ETC1: 0, + ETC2: 1, + BC1: 2, + BC3: 3, + BC4: 4, + BC5: 5, + BC7_M6_OPAQUE_ONLY: 6, + BC7_M5: 7, + PVRTC1_4_RGB: 8, + PVRTC1_4_RGBA: 9, + ASTC_4x4: 10, + ATC_RGB: 11, + ATC_RGBA_INTERPOLATED_ALPHA: 12, + RGBA32: 13, + RGB565: 14, + BGR565: 15, + RGBA4444: 16, + } - static EngineFormat = { - RGBAFormat: RGBAFormat, - RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format, - RGBA_BPTC_Format: RGBA_BPTC_Format, - RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format, - RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format, - RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format, - RGB_ETC1_Format: RGB_ETC1_Format, - RGB_ETC2_Format: RGB_ETC2_Format, - RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format, - RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format, - } + static EngineFormat = { + RGBAFormat: RGBAFormat, + RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format, + RGBA_BPTC_Format: RGBA_BPTC_Format, + RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format, + RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format, + RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format, + RGB_ETC1_Format: RGB_ETC1_Format, + RGB_ETC2_Format: RGB_ETC2_Format, + RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format, + RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format, + } - /* WEB WORKER */ + /* WEB WORKER */ - static BasisWorker = function () { - let config - let transcoderPending - let BasisModule + static BasisWorker = function () { + let config + let transcoderPending + let BasisModule - const EngineFormat = _EngineFormat - const TranscoderFormat = _TranscoderFormat - const BasisFormat = _BasisFormat + const EngineFormat = _EngineFormat + const TranscoderFormat = _TranscoderFormat + const BasisFormat = _BasisFormat - onmessage = function (e) { - const message = e.data + onmessage = function (e) { + const message = e.data - switch (message.type) { - case 'init': - config = message.config - init(message.transcoderBinary) - break + switch (message.type) { + case 'init': + config = message.config + init(message.transcoderBinary) + break - case 'transcode': - transcoderPending.then(() => { - try { - const { width, height, hasAlpha, mipmaps, format } = message.taskConfig.lowLevel - ? transcodeLowLevel(message.taskConfig) - : transcode(message.buffers[0]) + case 'transcode': + transcoderPending.then(() => { + try { + const { width, height, hasAlpha, mipmaps, format } = message.taskConfig.lowLevel + ? transcodeLowLevel(message.taskConfig) + : transcode(message.buffers[0]) - const buffers = [] + const buffers = [] - for (let i = 0; i < mipmaps.length; ++i) { - buffers.push(mipmaps[i].data.buffer) - } + for (let i = 0; i < mipmaps.length; ++i) { + buffers.push(mipmaps[i].data.buffer) + } - self.postMessage({ type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers) - } catch (error) { - console.error(error) + self.postMessage( + { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, + buffers, + ) + } catch (error) { + console.error(error) - self.postMessage({ type: 'error', id: message.id, error: error.message }) - } - }) - break + self.postMessage({ type: 'error', id: message.id, error: error.message }) + } + }) + break + } } - } - function init(wasmBinary) { - transcoderPending = new Promise((resolve) => { - BasisModule = { wasmBinary, onRuntimeInitialized: resolve } - BASIS(BasisModule) - }).then(() => { - BasisModule.initializeBasis() - }) - } + function init(wasmBinary) { + transcoderPending = new Promise((resolve) => { + BasisModule = { wasmBinary, onRuntimeInitialized: resolve } + BASIS(BasisModule) + }).then(() => { + BasisModule.initializeBasis() + }) + } + + function transcodeLowLevel(taskConfig) { + const { basisFormat, width, height, hasAlpha } = taskConfig + + const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) + + const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat) - function transcodeLowLevel(taskConfig) { - const { basisFormat, width, height, hasAlpha } = taskConfig + assert(BasisModule.isFormatSupported(transcoderFormat), 'THREE.BasisTextureLoader: Unsupported format.') - const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) + const mipmaps = [] - const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat) + if (basisFormat === BasisFormat.ETC1S) { + const transcoder = new BasisModule.LowLevelETC1SImageTranscoder() - assert(BasisModule.isFormatSupported(transcoderFormat), 'THREE.BasisTextureLoader: Unsupported format.') + const { endpointCount, endpointsData, selectorCount, selectorsData, tablesData } = taskConfig.globalData - const mipmaps = [] + try { + let ok - if (basisFormat === BasisFormat.ETC1S) { - const transcoder = new BasisModule.LowLevelETC1SImageTranscoder() + ok = transcoder.decodePalettes(endpointCount, endpointsData, selectorCount, selectorsData) - const { endpointCount, endpointsData, selectorCount, selectorsData, tablesData } = taskConfig.globalData + assert(ok, 'THREE.BasisTextureLoader: decodePalettes() failed.') - try { - let ok + ok = transcoder.decodeTables(tablesData) - ok = transcoder.decodePalettes(endpointCount, endpointsData, selectorCount, selectorsData) + assert(ok, 'THREE.BasisTextureLoader: decodeTables() failed.') - assert(ok, 'THREE.BasisTextureLoader: decodePalettes() failed.') + for (let i = 0; i < taskConfig.levels.length; i++) { + const level = taskConfig.levels[i] + const imageDesc = taskConfig.globalData.imageDescs[i] - ok = transcoder.decodeTables(tablesData) + const dstByteLength = getTranscodedImageByteLength(transcoderFormat, level.width, level.height) + const dst = new Uint8Array(dstByteLength) - assert(ok, 'THREE.BasisTextureLoader: decodeTables() failed.') + ok = transcoder.transcodeImage( + transcoderFormat, + dst, + dstByteLength / blockByteLength, + level.data, + getWidthInBlocks(transcoderFormat, level.width), + getHeightInBlocks(transcoderFormat, level.height), + level.width, + level.height, + level.index, + imageDesc.rgbSliceByteOffset, + imageDesc.rgbSliceByteLength, + imageDesc.alphaSliceByteOffset, + imageDesc.alphaSliceByteLength, + imageDesc.imageFlags, + hasAlpha, + false, + 0, + 0, + ) + assert(ok, 'THREE.BasisTextureLoader: transcodeImage() failed for level ' + level.index + '.') + + mipmaps.push({ data: dst, width: level.width, height: level.height }) + } + } finally { + transcoder.delete() + } + } else { for (let i = 0; i < taskConfig.levels.length; i++) { const level = taskConfig.levels[i] - const imageDesc = taskConfig.globalData.imageDescs[i] const dstByteLength = getTranscodedImageByteLength(transcoderFormat, level.width, level.height) const dst = new Uint8Array(dstByteLength) - ok = transcoder.transcodeImage( + const ok = BasisModule.transcodeUASTCImage( transcoderFormat, dst, dstByteLength / blockByteLength, @@ -170,487 +209,455 @@ class BasisTextureLoader extends Loader { level.width, level.height, level.index, - imageDesc.rgbSliceByteOffset, - imageDesc.rgbSliceByteLength, - imageDesc.alphaSliceByteOffset, - imageDesc.alphaSliceByteLength, - imageDesc.imageFlags, + 0, + level.data.byteLength, + 0, hasAlpha, false, 0, 0, + -1, + -1, ) - assert(ok, 'THREE.BasisTextureLoader: transcodeImage() failed for level ' + level.index + '.') + assert(ok, 'THREE.BasisTextureLoader: transcodeUASTCImage() failed for level ' + level.index + '.') mipmaps.push({ data: dst, width: level.width, height: level.height }) } - } finally { - transcoder.delete() - } - } else { - for (let i = 0; i < taskConfig.levels.length; i++) { - const level = taskConfig.levels[i] - - const dstByteLength = getTranscodedImageByteLength(transcoderFormat, level.width, level.height) - const dst = new Uint8Array(dstByteLength) - - const ok = BasisModule.transcodeUASTCImage( - transcoderFormat, - dst, - dstByteLength / blockByteLength, - level.data, - getWidthInBlocks(transcoderFormat, level.width), - getHeightInBlocks(transcoderFormat, level.height), - level.width, - level.height, - level.index, - 0, - level.data.byteLength, - 0, - hasAlpha, - false, - 0, - 0, - -1, - -1, - ) - - assert(ok, 'THREE.BasisTextureLoader: transcodeUASTCImage() failed for level ' + level.index + '.') - - mipmaps.push({ data: dst, width: level.width, height: level.height }) } + + return { width, height, hasAlpha, mipmaps, format: engineFormat } } - return { width, height, hasAlpha, mipmaps, format: engineFormat } - } + function transcode(buffer) { + const basisFile = new BasisModule.BasisFile(new Uint8Array(buffer)) - function transcode(buffer) { - const basisFile = new BasisModule.BasisFile(new Uint8Array(buffer)) + const basisFormat = basisFile.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S + const width = basisFile.getImageWidth(0, 0) + const height = basisFile.getImageHeight(0, 0) + const levels = basisFile.getNumLevels(0) + const hasAlpha = basisFile.getHasAlpha() - const basisFormat = basisFile.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S - const width = basisFile.getImageWidth(0, 0) - const height = basisFile.getImageHeight(0, 0) - const levels = basisFile.getNumLevels(0) - const hasAlpha = basisFile.getHasAlpha() + function cleanup() { + basisFile.close() + basisFile.delete() + } - function cleanup() { - basisFile.close() - basisFile.delete() - } + const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) - const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) + if (!width || !height || !levels) { + cleanup() + throw new Error('THREE.BasisTextureLoader: Invalid texture') + } - if (!width || !height || !levels) { - cleanup() - throw new Error('THREE.BasisTextureLoader: Invalid texture') - } + if (!basisFile.startTranscoding()) { + cleanup() + throw new Error('THREE.BasisTextureLoader: .startTranscoding failed') + } - if (!basisFile.startTranscoding()) { - cleanup() - throw new Error('THREE.BasisTextureLoader: .startTranscoding failed') - } + const mipmaps = [] - const mipmaps = [] + for (let mip = 0; mip < levels; mip++) { + const mipWidth = basisFile.getImageWidth(0, mip) + const mipHeight = basisFile.getImageHeight(0, mip) + const dst = new Uint8Array(basisFile.getImageTranscodedSizeInBytes(0, mip, transcoderFormat)) - for (let mip = 0; mip < levels; mip++) { - const mipWidth = basisFile.getImageWidth(0, mip) - const mipHeight = basisFile.getImageHeight(0, mip) - const dst = new Uint8Array(basisFile.getImageTranscodedSizeInBytes(0, mip, transcoderFormat)) + const status = basisFile.transcodeImage(dst, 0, mip, transcoderFormat, 0, hasAlpha) - const status = basisFile.transcodeImage(dst, 0, mip, transcoderFormat, 0, hasAlpha) + if (!status) { + cleanup() + throw new Error('THREE.BasisTextureLoader: .transcodeImage failed.') + } - if (!status) { - cleanup() - throw new Error('THREE.BasisTextureLoader: .transcodeImage failed.') + mipmaps.push({ data: dst, width: mipWidth, height: mipHeight }) } - mipmaps.push({ data: dst, width: mipWidth, height: mipHeight }) + cleanup() + + return { width, height, hasAlpha, mipmaps, format: engineFormat } } - cleanup() + // + + // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC), + // device capabilities, and texture dimensions. The list below ranks the formats separately + // for ETC1S and UASTC. + // + // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at + // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently + // chooses RGBA32 only as a last resort and does not expose that option to the caller. + const FORMAT_OPTIONS = [ + { + if: 'astcSupported', + basisFormat: [BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4], + engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format], + priorityETC1S: Infinity, + priorityUASTC: 1, + needsPowerOfTwo: false, + }, + { + if: 'bptcSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5], + engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format], + priorityETC1S: 3, + priorityUASTC: 2, + needsPowerOfTwo: false, + }, + { + if: 'dxtSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3], + engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format], + priorityETC1S: 4, + priorityUASTC: 5, + needsPowerOfTwo: false, + }, + { + if: 'etc2Supported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2], + engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format], + priorityETC1S: 1, + priorityUASTC: 3, + needsPowerOfTwo: false, + }, + { + if: 'etc1Supported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC1], + engineFormat: [EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format], + priorityETC1S: 2, + priorityUASTC: 4, + needsPowerOfTwo: false, + }, + { + if: 'pvrtcSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA], + engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format], + priorityETC1S: 5, + priorityUASTC: 6, + needsPowerOfTwo: true, + }, + ] + + const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { + return a.priorityETC1S - b.priorityETC1S + }) + const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { + return a.priorityUASTC - b.priorityUASTC + }) - return { width, height, hasAlpha, mipmaps, format: engineFormat } - } + function getTranscoderFormat(basisFormat, width, height, hasAlpha) { + let transcoderFormat + let engineFormat - // - - // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC), - // device capabilities, and texture dimensions. The list below ranks the formats separately - // for ETC1S and UASTC. - // - // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at - // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently - // chooses RGBA32 only as a last resort and does not expose that option to the caller. - const FORMAT_OPTIONS = [ - { - if: 'astcSupported', - basisFormat: [BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4], - engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format], - priorityETC1S: Infinity, - priorityUASTC: 1, - needsPowerOfTwo: false, - }, - { - if: 'bptcSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5], - engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format], - priorityETC1S: 3, - priorityUASTC: 2, - needsPowerOfTwo: false, - }, - { - if: 'dxtSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3], - engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format], - priorityETC1S: 4, - priorityUASTC: 5, - needsPowerOfTwo: false, - }, - { - if: 'etc2Supported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2], - engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format], - priorityETC1S: 1, - priorityUASTC: 3, - needsPowerOfTwo: false, - }, - { - if: 'etc1Supported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC1], - engineFormat: [EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format], - priorityETC1S: 2, - priorityUASTC: 4, - needsPowerOfTwo: false, - }, - { - if: 'pvrtcSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA], - engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format], - priorityETC1S: 5, - priorityUASTC: 6, - needsPowerOfTwo: true, - }, - ] - - const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { - return a.priorityETC1S - b.priorityETC1S - }) - const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { - return a.priorityUASTC - b.priorityUASTC - }) - - function getTranscoderFormat(basisFormat, width, height, hasAlpha) { - let transcoderFormat - let engineFormat - - const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS - - for (let i = 0; i < options.length; i++) { - const opt = options[i] - - if (!config[opt.if]) continue - if (!opt.basisFormat.includes(basisFormat)) continue - if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue - - transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0] - engineFormat = opt.engineFormat[hasAlpha ? 1 : 0] + const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS - return { transcoderFormat, engineFormat } - } - - console.warn('THREE.BasisTextureLoader: No suitable compressed texture format found. Decoding to RGBA32.') + for (let i = 0; i < options.length; i++) { + const opt = options[i] - transcoderFormat = TranscoderFormat.RGBA32 - engineFormat = EngineFormat.RGBAFormat + if (!config[opt.if]) continue + if (!opt.basisFormat.includes(basisFormat)) continue + if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue - return { transcoderFormat, engineFormat } - } + transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0] + engineFormat = opt.engineFormat[hasAlpha ? 1 : 0] - function assert(ok, message) { - if (!ok) throw new Error(message) - } + return { transcoderFormat, engineFormat } + } - function getWidthInBlocks(transcoderFormat, width) { - return Math.ceil(width / BasisModule.getFormatBlockWidth(transcoderFormat)) - } + console.warn('THREE.BasisTextureLoader: No suitable compressed texture format found. Decoding to RGBA32.') - function getHeightInBlocks(transcoderFormat, height) { - return Math.ceil(height / BasisModule.getFormatBlockHeight(transcoderFormat)) - } + transcoderFormat = TranscoderFormat.RGBA32 + engineFormat = EngineFormat.RGBAFormat - function getTranscodedImageByteLength(transcoderFormat, width, height) { - const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat) + return { transcoderFormat, engineFormat } + } - if (BasisModule.formatIsUncompressed(transcoderFormat)) { - return width * height * blockByteLength + function assert(ok, message) { + if (!ok) throw new Error(message) } - if (transcoderFormat === TranscoderFormat.PVRTC1_4_RGB || transcoderFormat === TranscoderFormat.PVRTC1_4_RGBA) { - // GL requires extra padding for very small textures: - // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt - const paddedWidth = (width + 3) & ~3 - const paddedHeight = (height + 3) & ~3 + function getWidthInBlocks(transcoderFormat, width) { + return Math.ceil(width / BasisModule.getFormatBlockWidth(transcoderFormat)) + } - return (Math.max(8, paddedWidth) * Math.max(8, paddedHeight) * 4 + 7) / 8 + function getHeightInBlocks(transcoderFormat, height) { + return Math.ceil(height / BasisModule.getFormatBlockHeight(transcoderFormat)) } - return getWidthInBlocks(transcoderFormat, width) * getHeightInBlocks(transcoderFormat, height) * blockByteLength - } + function getTranscodedImageByteLength(transcoderFormat, width, height) { + const blockByteLength = BasisModule.getBytesPerBlockOrPixel(transcoderFormat) - function isPowerOfTwo(value) { - if (value <= 2) return true + if (BasisModule.formatIsUncompressed(transcoderFormat)) { + return width * height * blockByteLength + } - return (value & (value - 1)) === 0 && value !== 0 - } - } + if (transcoderFormat === TranscoderFormat.PVRTC1_4_RGB || transcoderFormat === TranscoderFormat.PVRTC1_4_RGBA) { + // GL requires extra padding for very small textures: + // https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt + const paddedWidth = (width + 3) & ~3 + const paddedHeight = (height + 3) & ~3 - constructor(manager) { - super(manager) + return (Math.max(8, paddedWidth) * Math.max(8, paddedHeight) * 4 + 7) / 8 + } - this.transcoderPath = '' - this.transcoderBinary = null - this.transcoderPending = null + return getWidthInBlocks(transcoderFormat, width) * getHeightInBlocks(transcoderFormat, height) * blockByteLength + } - this.workerLimit = 4 - this.workerPool = [] - this.workerNextTaskID = 1 - this.workerSourceURL = '' - this.workerConfig = null - } + function isPowerOfTwo(value) { + if (value <= 2) return true - setTranscoderPath(path) { - this.transcoderPath = path + return (value & (value - 1)) === 0 && value !== 0 + } + } - return this - } + constructor(manager) { + super(manager) - setWorkerLimit(workerLimit) { - this.workerLimit = workerLimit + this.transcoderPath = '' + this.transcoderBinary = null + this.transcoderPending = null - return this - } + this.workerLimit = 4 + this.workerPool = [] + this.workerNextTaskID = 1 + this.workerSourceURL = '' + this.workerConfig = null + } - detectSupport(renderer) { - this.workerConfig = { - astcSupported: renderer.extensions.has('WEBGL_compressed_texture_astc'), - etc1Supported: renderer.extensions.has('WEBGL_compressed_texture_etc1'), - etc2Supported: renderer.extensions.has('WEBGL_compressed_texture_etc'), - dxtSupported: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), - bptcSupported: renderer.extensions.has('EXT_texture_compression_bptc'), - pvrtcSupported: - renderer.extensions.has('WEBGL_compressed_texture_pvrtc') || - renderer.extensions.has('WEBKIT_WEBGL_compressed_texture_pvrtc'), + setTranscoderPath(path) { + this.transcoderPath = path + + return this } - return this - } + setWorkerLimit(workerLimit) { + this.workerLimit = workerLimit - load(url, onLoad, onProgress, onError) { - const loader = new FileLoader(this.manager) + return this + } - loader.setResponseType('arraybuffer') - loader.setWithCredentials(this.withCredentials) + detectSupport(renderer) { + this.workerConfig = { + astcSupported: renderer.extensions.has('WEBGL_compressed_texture_astc'), + etc1Supported: renderer.extensions.has('WEBGL_compressed_texture_etc1'), + etc2Supported: renderer.extensions.has('WEBGL_compressed_texture_etc'), + dxtSupported: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), + bptcSupported: renderer.extensions.has('EXT_texture_compression_bptc'), + pvrtcSupported: + renderer.extensions.has('WEBGL_compressed_texture_pvrtc') || + renderer.extensions.has('WEBKIT_WEBGL_compressed_texture_pvrtc'), + } - const texture = new CompressedTexture() + return this + } - loader.load( - url, - (buffer) => { - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if (_taskCache.has(buffer)) { - const cachedTask = _taskCache.get(buffer) + load(url, onLoad, onProgress, onError) { + const loader = new FileLoader(this.manager) - return cachedTask.promise.then(onLoad).catch(onError) - } + loader.setResponseType('arraybuffer') + loader.setWithCredentials(this.withCredentials) - this._createTexture([buffer]) - .then(function (_texture) { - texture.copy(_texture) - texture.needsUpdate = true + const texture = new CompressedTexture() - if (onLoad) onLoad(texture) - }) - .catch(onError) - }, - onProgress, - onError, - ) + loader.load( + url, + (buffer) => { + // Check for an existing task using this buffer. A transferred buffer cannot be transferred + // again from this thread. + if (_taskCache.has(buffer)) { + const cachedTask = _taskCache.get(buffer) - return texture - } + return cachedTask.promise.then(onLoad).catch(onError) + } - /** Low-level transcoding API, exposed for use by KTX2Loader. */ - parseInternalAsync(options) { - const { levels } = options + this._createTexture([buffer]) + .then(function (_texture) { + texture.copy(_texture) + texture.needsUpdate = true - const buffers = new Set() + if (onLoad) onLoad(texture) + }) + .catch(onError) + }, + onProgress, + onError, + ) - for (let i = 0; i < levels.length; i++) { - buffers.add(levels[i].data.buffer) + return texture } - return this._createTexture(Array.from(buffers), { ...options, lowLevel: true }) - } + /** Low-level transcoding API, exposed for use by KTX2Loader. */ + parseInternalAsync(options) { + const { levels } = options - /** - * @param {ArrayBuffer[]} buffers - * @param {object?} config - * @return {Promise} - */ - _createTexture(buffers, config = {}) { - let worker - let taskID + const buffers = new Set() - const taskConfig = config - let taskCost = 0 + for (let i = 0; i < levels.length; i++) { + buffers.add(levels[i].data.buffer) + } - for (let i = 0; i < buffers.length; i++) { - taskCost += buffers[i].byteLength + return this._createTexture(Array.from(buffers), { ...options, lowLevel: true }) } - const texturePending = this._allocateWorker(taskCost) - .then((_worker) => { - worker = _worker - taskID = this.workerNextTaskID++ + /** + * @param {ArrayBuffer[]} buffers + * @param {object?} config + * @return {Promise} + */ + _createTexture(buffers, config = {}) { + let worker + let taskID - return new Promise((resolve, reject) => { - worker._callbacks[taskID] = { resolve, reject } + const taskConfig = config + let taskCost = 0 - worker.postMessage({ type: 'transcode', id: taskID, buffers: buffers, taskConfig: taskConfig }, buffers) - }) - }) - .then((message) => { - const { mipmaps, width, height, format } = message + for (let i = 0; i < buffers.length; i++) { + taskCost += buffers[i].byteLength + } - const texture = new CompressedTexture(mipmaps, width, height, format, UnsignedByteType) - texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter - texture.magFilter = LinearFilter - texture.generateMipmaps = false - texture.needsUpdate = true + const texturePending = this._allocateWorker(taskCost) + .then((_worker) => { + worker = _worker + taskID = this.workerNextTaskID++ - return texture - }) + return new Promise((resolve, reject) => { + worker._callbacks[taskID] = { resolve, reject } - // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) - texturePending - .catch(() => true) - .then(() => { - if (worker && taskID) { - worker._taskLoad -= taskCost - delete worker._callbacks[taskID] - } - }) + worker.postMessage({ type: 'transcode', id: taskID, buffers: buffers, taskConfig: taskConfig }, buffers) + }) + }) + .then((message) => { + const { mipmaps, width, height, format } = message - // Cache the task result. - _taskCache.set(buffers[0], { promise: texturePending }) + const texture = new CompressedTexture(mipmaps, width, height, format, UnsignedByteType) + texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter + texture.magFilter = LinearFilter + texture.generateMipmaps = false + texture.needsUpdate = true - return texturePending - } + return texture + }) - _initTranscoder() { - if (!this.transcoderPending) { - // Load transcoder wrapper. - const jsLoader = new FileLoader(this.manager) - jsLoader.setPath(this.transcoderPath) - jsLoader.setWithCredentials(this.withCredentials) - const jsContent = new Promise((resolve, reject) => { - jsLoader.load('basis_transcoder.js', resolve, undefined, reject) - }) + // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416) + texturePending + .catch(() => true) + .then(() => { + if (worker && taskID) { + worker._taskLoad -= taskCost + delete worker._callbacks[taskID] + } + }) - // Load transcoder WASM binary. - const binaryLoader = new FileLoader(this.manager) - binaryLoader.setPath(this.transcoderPath) - binaryLoader.setResponseType('arraybuffer') - binaryLoader.setWithCredentials(this.withCredentials) - const binaryContent = new Promise((resolve, reject) => { - binaryLoader.load('basis_transcoder.wasm', resolve, undefined, reject) - }) + // Cache the task result. + _taskCache.set(buffers[0], { promise: texturePending }) - this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent, binaryContent]) => { - const fn = BasisTextureLoader.BasisWorker.toString() - - const body = [ - '/* constants */', - 'let _EngineFormat = ' + JSON.stringify(BasisTextureLoader.EngineFormat), - 'let _TranscoderFormat = ' + JSON.stringify(BasisTextureLoader.TranscoderFormat), - 'let _BasisFormat = ' + JSON.stringify(BasisTextureLoader.BasisFormat), - '/* basis_transcoder.js */', - jsContent, - '/* worker */', - fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}')), - ].join('\n') - - this.workerSourceURL = URL.createObjectURL(new Blob([body])) - this.transcoderBinary = binaryContent - }) + return texturePending } - return this.transcoderPending - } - - _allocateWorker(taskCost) { - return this._initTranscoder().then(() => { - if (this.workerPool.length < this.workerLimit) { - const worker = new Worker(this.workerSourceURL) + _initTranscoder() { + if (!this.transcoderPending) { + // Load transcoder wrapper. + const jsLoader = new FileLoader(this.manager) + jsLoader.setPath(this.transcoderPath) + jsLoader.setWithCredentials(this.withCredentials) + const jsContent = new Promise((resolve, reject) => { + jsLoader.load('basis_transcoder.js', resolve, undefined, reject) + }) - worker._callbacks = {} - worker._taskLoad = 0 + // Load transcoder WASM binary. + const binaryLoader = new FileLoader(this.manager) + binaryLoader.setPath(this.transcoderPath) + binaryLoader.setResponseType('arraybuffer') + binaryLoader.setWithCredentials(this.withCredentials) + const binaryContent = new Promise((resolve, reject) => { + binaryLoader.load('basis_transcoder.wasm', resolve, undefined, reject) + }) - worker.postMessage({ - type: 'init', - config: this.workerConfig, - transcoderBinary: this.transcoderBinary, + this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent, binaryContent]) => { + const fn = BasisTextureLoader.BasisWorker.toString() + + const body = [ + '/* constants */', + 'let _EngineFormat = ' + JSON.stringify(BasisTextureLoader.EngineFormat), + 'let _TranscoderFormat = ' + JSON.stringify(BasisTextureLoader.TranscoderFormat), + 'let _BasisFormat = ' + JSON.stringify(BasisTextureLoader.BasisFormat), + '/* basis_transcoder.js */', + jsContent, + '/* worker */', + fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}')), + ].join('\n') + + this.workerSourceURL = URL.createObjectURL(new Blob([body])) + this.transcoderBinary = binaryContent }) + } - worker.onmessage = function (e) { - const message = e.data + return this.transcoderPending + } - switch (message.type) { - case 'transcode': - worker._callbacks[message.id].resolve(message) - break + _allocateWorker(taskCost) { + return this._initTranscoder().then(() => { + if (this.workerPool.length < this.workerLimit) { + const worker = new Worker(this.workerSourceURL) - case 'error': - worker._callbacks[message.id].reject(message) - break + worker._callbacks = {} + worker._taskLoad = 0 - default: - console.error('THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"') - } - } + worker.postMessage({ + type: 'init', + config: this.workerConfig, + transcoderBinary: this.transcoderBinary, + }) - this.workerPool.push(worker) - } else { - this.workerPool.sort(function (a, b) { - return a._taskLoad > b._taskLoad ? -1 : 1 - }) - } + worker.onmessage = function (e) { + const message = e.data - const worker = this.workerPool[this.workerPool.length - 1] + switch (message.type) { + case 'transcode': + worker._callbacks[message.id].resolve(message) + break - worker._taskLoad += taskCost + case 'error': + worker._callbacks[message.id].reject(message) + break - return worker - }) - } + default: + console.error('THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"') + } + } + + this.workerPool.push(worker) + } else { + this.workerPool.sort(function (a, b) { + return a._taskLoad > b._taskLoad ? -1 : 1 + }) + } - dispose() { - for (let i = 0; i < this.workerPool.length; i++) { - this.workerPool[i].terminate() + const worker = this.workerPool[this.workerPool.length - 1] + + worker._taskLoad += taskCost + + return worker + }) } - this.workerPool.length = 0 + dispose() { + for (let i = 0; i < this.workerPool.length; i++) { + this.workerPool[i].terminate() + } - return this + this.workerPool.length = 0 + + return this + } } -} + + return BasisTextureLoader +})() export { BasisTextureLoader } diff --git a/src/loaders/EXRLoader.js b/src/loaders/EXRLoader.js index fd311038..002bfefd 100644 --- a/src/loaders/EXRLoader.js +++ b/src/loaders/EXRLoader.js @@ -9,6 +9,7 @@ import { RGBAFormat, } from 'three' import { unzlibSync } from 'fflate' +import { version } from '../_polyfill/constants' /** * OpenEXR loader currently supports uncompressed, ZIP(S), RLE, PIZ and DWA/B compression. @@ -83,7 +84,8 @@ import { unzlibSync } from 'fflate' // // End of OpenEXR license ------------------------------------------------- -const hasColorSpace = 'colorSpace' in new Texture() +// https://github.com/mrdoob/three.js/pull/25771 +const hasColorSpace = version >= 152 class EXRLoader extends DataTextureLoader { constructor(manager) { diff --git a/src/loaders/FBXLoader.js b/src/loaders/FBXLoader.js index e171ec87..034e4bb3 100644 --- a/src/loaders/FBXLoader.js +++ b/src/loaders/FBXLoader.js @@ -1275,8 +1275,8 @@ class GeometryParser { } buffers.uvs.forEach(function (uvBuffer, i) { - if (UV1 === 'uv2') i++; - const name = i === 0 ? 'uv' : `uv${i}`; + if (UV1 === 'uv2') i++ + const name = i === 0 ? 'uv' : `uv${i}` geo.setAttribute(name, new Float32BufferAttribute(buffers.uvs[i], 2)) }) @@ -3113,8 +3113,8 @@ function getData(polygonVertexIndex, polygonIndex, vertexIndex, infoObject) { return slice(dataArray, infoObject.buffer, from, to) } -const tempEuler = new Euler() -const tempVec = new Vector3() +const tempEuler = /* @__PURE__ */ new Euler() +const tempVec = /* @__PURE__ */ new Vector3() // generate transformation from FBX transform data // ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm diff --git a/src/loaders/FontLoader.ts b/src/loaders/FontLoader.ts index cb0e4ee4..00aa0643 100644 --- a/src/loaders/FontLoader.ts +++ b/src/loaders/FontLoader.ts @@ -66,8 +66,8 @@ type FontData = { export class Font { public data: FontData - public static isFont: true - public static type: 'Font' + public isFont = true + public type = 'Font' constructor(data: FontData) { this.data = data diff --git a/src/loaders/GLTFLoader.js b/src/loaders/GLTFLoader.js index c4b7d01c..dd037655 100644 --- a/src/loaders/GLTFLoader.js +++ b/src/loaders/GLTFLoader.js @@ -1676,7 +1676,7 @@ class GLTFCubicSplineInterpolant extends Interpolant { } } -const _q = new Quaternion() +const _q = /* @__PURE__ */ new Quaternion() class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant { interpolate_(i1, t0, t, t1) { @@ -2006,7 +2006,7 @@ function getImageURIMimeType(uri) { return 'image/png' } -const _identityMatrix = new Matrix4() +const _identityMatrix = /* @__PURE__ */ new Matrix4() /* GLTF PARSER */ diff --git a/src/loaders/KTX2Loader.js b/src/loaders/KTX2Loader.js index f2b25571..ae2758c7 100644 --- a/src/loaders/KTX2Loader.js +++ b/src/loaders/KTX2Loader.js @@ -81,534 +81,538 @@ let _activeLoaders = 0 let _zstd -class KTX2Loader extends Loader { - /* CONSTANTS */ +const KTX2Loader = /* @__PURE__ */ (() => { + class KTX2Loader extends Loader { + /* CONSTANTS */ - static BasisFormat = { - ETC1S: 0, - UASTC_4x4: 1, - } - - static TranscoderFormat = { - ETC1: 0, - ETC2: 1, - BC1: 2, - BC3: 3, - BC4: 4, - BC5: 5, - BC7_M6_OPAQUE_ONLY: 6, - BC7_M5: 7, - PVRTC1_4_RGB: 8, - PVRTC1_4_RGBA: 9, - ASTC_4x4: 10, - ATC_RGB: 11, - ATC_RGBA_INTERPOLATED_ALPHA: 12, - RGBA32: 13, - RGB565: 14, - BGR565: 15, - RGBA4444: 16, - } - - static EngineFormat = { - RGBAFormat: RGBAFormat, - RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format, - RGBA_BPTC_Format: RGBA_BPTC_Format, - RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format, - RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format, - RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format, - RGB_ETC1_Format: RGB_ETC1_Format, - RGB_ETC2_Format: RGB_ETC2_Format, - RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format, - RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format, - } + static BasisFormat = { + ETC1S: 0, + UASTC_4x4: 1, + } - /* WEB WORKER */ - - static BasisWorker = function () { - let config - let transcoderPending - let BasisModule - - /** @type KTX2Loader.EngineFormat */ - const EngineFormat = _EngineFormat - /** @type KTX2Loader.TranscoderFormat */ - const TranscoderFormat = _TranscoderFormat - /** @type KTX2Loader.BasisFormat */ - const BasisFormat = _BasisFormat - - self.addEventListener('message', function (e) { - const message = e.data - - switch (message.type) { - case 'init': - config = message.config - init(message.transcoderBinary) - break - - case 'transcode': - transcoderPending.then(() => { - try { - const { faces, buffers, width, height, hasAlpha, format, dfdFlags } = transcode(message.buffer) - - self.postMessage( - { type: 'transcode', id: message.id, faces, width, height, hasAlpha, format, dfdFlags }, - buffers, - ) - } catch (error) { - console.error(error) - - self.postMessage({ type: 'error', id: message.id, error: error.message }) - } - }) - break - } - }) + static TranscoderFormat = { + ETC1: 0, + ETC2: 1, + BC1: 2, + BC3: 3, + BC4: 4, + BC5: 5, + BC7_M6_OPAQUE_ONLY: 6, + BC7_M5: 7, + PVRTC1_4_RGB: 8, + PVRTC1_4_RGBA: 9, + ASTC_4x4: 10, + ATC_RGB: 11, + ATC_RGBA_INTERPOLATED_ALPHA: 12, + RGBA32: 13, + RGB565: 14, + BGR565: 15, + RGBA4444: 16, + } - function init(wasmBinary) { - transcoderPending = new Promise((resolve) => { - BasisModule = { wasmBinary, onRuntimeInitialized: resolve } - BASIS(BasisModule) - }).then(() => { - BasisModule.initializeBasis() + static EngineFormat = { + RGBAFormat: RGBAFormat, + RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format, + RGBA_BPTC_Format: RGBA_BPTC_Format, + RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format, + RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format, + RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format, + RGB_ETC1_Format: RGB_ETC1_Format, + RGB_ETC2_Format: RGB_ETC2_Format, + RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format, + RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format, + } - if (BasisModule.KTX2File === undefined) { - console.warn('THREE.KTX2Loader: Please update Basis Universal transcoder.') + /* WEB WORKER */ + + static BasisWorker = function () { + let config + let transcoderPending + let BasisModule + + /** @type KTX2Loader.EngineFormat */ + const EngineFormat = _EngineFormat + /** @type KTX2Loader.TranscoderFormat */ + const TranscoderFormat = _TranscoderFormat + /** @type KTX2Loader.BasisFormat */ + const BasisFormat = _BasisFormat + + self.addEventListener('message', function (e) { + const message = e.data + + switch (message.type) { + case 'init': + config = message.config + init(message.transcoderBinary) + break + + case 'transcode': + transcoderPending.then(() => { + try { + const { faces, buffers, width, height, hasAlpha, format, dfdFlags } = transcode(message.buffer) + + self.postMessage( + { type: 'transcode', id: message.id, faces, width, height, hasAlpha, format, dfdFlags }, + buffers, + ) + } catch (error) { + console.error(error) + + self.postMessage({ type: 'error', id: message.id, error: error.message }) + } + }) + break } }) - } - - function transcode(buffer) { - const ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer)) - - function cleanup() { - ktx2File.close() - ktx2File.delete() - } - - if (!ktx2File.isValid()) { - cleanup() - throw new Error('THREE.KTX2Loader: Invalid or unsupported .ktx2 file') - } - - const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S - const width = ktx2File.getWidth() - const height = ktx2File.getHeight() - const layerCount = ktx2File.getLayers() || 1 - const levelCount = ktx2File.getLevels() - const faceCount = ktx2File.getFaces() - const hasAlpha = ktx2File.getHasAlpha() - const dfdFlags = ktx2File.getDFDFlags() - const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) + function init(wasmBinary) { + transcoderPending = new Promise((resolve) => { + BasisModule = { wasmBinary, onRuntimeInitialized: resolve } + BASIS(BasisModule) + }).then(() => { + BasisModule.initializeBasis() - if (!width || !height || !levelCount) { - cleanup() - throw new Error('THREE.KTX2Loader: Invalid texture') + if (BasisModule.KTX2File === undefined) { + console.warn('THREE.KTX2Loader: Please update Basis Universal transcoder.') + } + }) } - if (!ktx2File.startTranscoding()) { - cleanup() - throw new Error('THREE.KTX2Loader: .startTranscoding failed') - } + function transcode(buffer) { + const ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer)) - const faces = [] - const buffers = [] + function cleanup() { + ktx2File.close() + ktx2File.delete() + } - for (let face = 0; face < faceCount; face++) { - const mipmaps = [] + if (!ktx2File.isValid()) { + cleanup() + throw new Error('THREE.KTX2Loader: Invalid or unsupported .ktx2 file') + } - for (let mip = 0; mip < levelCount; mip++) { - const layerMips = [] + const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S + const width = ktx2File.getWidth() + const height = ktx2File.getHeight() + const layerCount = ktx2File.getLayers() || 1 + const levelCount = ktx2File.getLevels() + const faceCount = ktx2File.getFaces() + const hasAlpha = ktx2File.getHasAlpha() + const dfdFlags = ktx2File.getDFDFlags() - let mipWidth, mipHeight + const { transcoderFormat, engineFormat } = getTranscoderFormat(basisFormat, width, height, hasAlpha) - for (let layer = 0; layer < layerCount; layer++) { - const levelInfo = ktx2File.getImageLevelInfo(mip, layer, face) + if (!width || !height || !levelCount) { + cleanup() + throw new Error('THREE.KTX2Loader: Invalid texture') + } - if ( - face === 0 && - mip === 0 && - layer === 0 && - (levelInfo.origWidth % 4 !== 0 || levelInfo.origHeight % 4 !== 0) - ) { - console.warn('THREE.KTX2Loader: ETC1S and UASTC textures should use multiple-of-four dimensions.') - } + if (!ktx2File.startTranscoding()) { + cleanup() + throw new Error('THREE.KTX2Loader: .startTranscoding failed') + } - if (levelCount > 1) { - mipWidth = levelInfo.origWidth - mipHeight = levelInfo.origHeight - } else { - // Handles non-multiple-of-four dimensions in textures without mipmaps. Textures with - // mipmaps must use multiple-of-four dimensions, for some texture formats and APIs. - // See mrdoob/three.js#25908. - mipWidth = levelInfo.width - mipHeight = levelInfo.height + const faces = [] + const buffers = [] + + for (let face = 0; face < faceCount; face++) { + const mipmaps = [] + + for (let mip = 0; mip < levelCount; mip++) { + const layerMips = [] + + let mipWidth, mipHeight + + for (let layer = 0; layer < layerCount; layer++) { + const levelInfo = ktx2File.getImageLevelInfo(mip, layer, face) + + if ( + face === 0 && + mip === 0 && + layer === 0 && + (levelInfo.origWidth % 4 !== 0 || levelInfo.origHeight % 4 !== 0) + ) { + console.warn('THREE.KTX2Loader: ETC1S and UASTC textures should use multiple-of-four dimensions.') + } + + if (levelCount > 1) { + mipWidth = levelInfo.origWidth + mipHeight = levelInfo.origHeight + } else { + // Handles non-multiple-of-four dimensions in textures without mipmaps. Textures with + // mipmaps must use multiple-of-four dimensions, for some texture formats and APIs. + // See mrdoob/three.js#25908. + mipWidth = levelInfo.width + mipHeight = levelInfo.height + } + + const dst = new Uint8Array(ktx2File.getImageTranscodedSizeInBytes(mip, layer, 0, transcoderFormat)) + const status = ktx2File.transcodeImage(dst, mip, layer, face, transcoderFormat, 0, -1, -1) + + if (!status) { + cleanup() + throw new Error('THREE.KTX2Loader: .transcodeImage failed.') + } + + layerMips.push(dst) } - const dst = new Uint8Array(ktx2File.getImageTranscodedSizeInBytes(mip, layer, 0, transcoderFormat)) - const status = ktx2File.transcodeImage(dst, mip, layer, face, transcoderFormat, 0, -1, -1) + const mipData = concat(layerMips) - if (!status) { - cleanup() - throw new Error('THREE.KTX2Loader: .transcodeImage failed.') - } - - layerMips.push(dst) + mipmaps.push({ data: mipData, width: mipWidth, height: mipHeight }) + buffers.push(mipData.buffer) } - const mipData = concat(layerMips) - - mipmaps.push({ data: mipData, width: mipWidth, height: mipHeight }) - buffers.push(mipData.buffer) + faces.push({ mipmaps, width, height, format: engineFormat }) } - faces.push({ mipmaps, width, height, format: engineFormat }) + cleanup() + + return { faces, buffers, width, height, hasAlpha, format: engineFormat, dfdFlags } } - cleanup() + // + + // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC), + // device capabilities, and texture dimensions. The list below ranks the formats separately + // for ETC1S and UASTC. + // + // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at + // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently + // chooses RGBA32 only as a last resort and does not expose that option to the caller. + const FORMAT_OPTIONS = [ + { + if: 'astcSupported', + basisFormat: [BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4], + engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format], + priorityETC1S: Infinity, + priorityUASTC: 1, + needsPowerOfTwo: false, + }, + { + if: 'bptcSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5], + engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format], + priorityETC1S: 3, + priorityUASTC: 2, + needsPowerOfTwo: false, + }, + { + if: 'dxtSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3], + engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format], + priorityETC1S: 4, + priorityUASTC: 5, + needsPowerOfTwo: false, + }, + { + if: 'etc2Supported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2], + engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format], + priorityETC1S: 1, + priorityUASTC: 3, + needsPowerOfTwo: false, + }, + { + if: 'etc1Supported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.ETC1], + engineFormat: [EngineFormat.RGB_ETC1_Format], + priorityETC1S: 2, + priorityUASTC: 4, + needsPowerOfTwo: false, + }, + { + if: 'pvrtcSupported', + basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], + transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA], + engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format], + priorityETC1S: 5, + priorityUASTC: 6, + needsPowerOfTwo: true, + }, + ] + + const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { + return a.priorityETC1S - b.priorityETC1S + }) + const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { + return a.priorityUASTC - b.priorityUASTC + }) + + function getTranscoderFormat(basisFormat, width, height, hasAlpha) { + let transcoderFormat + let engineFormat - return { faces, buffers, width, height, hasAlpha, format: engineFormat, dfdFlags } - } + const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS - // - - // Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC), - // device capabilities, and texture dimensions. The list below ranks the formats separately - // for ETC1S and UASTC. - // - // In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at - // significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently - // chooses RGBA32 only as a last resort and does not expose that option to the caller. - const FORMAT_OPTIONS = [ - { - if: 'astcSupported', - basisFormat: [BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4], - engineFormat: [EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format], - priorityETC1S: Infinity, - priorityUASTC: 1, - needsPowerOfTwo: false, - }, - { - if: 'bptcSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5], - engineFormat: [EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format], - priorityETC1S: 3, - priorityUASTC: 2, - needsPowerOfTwo: false, - }, - { - if: 'dxtSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.BC1, TranscoderFormat.BC3], - engineFormat: [EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format], - priorityETC1S: 4, - priorityUASTC: 5, - needsPowerOfTwo: false, - }, - { - if: 'etc2Supported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ETC1, TranscoderFormat.ETC2], - engineFormat: [EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format], - priorityETC1S: 1, - priorityUASTC: 3, - needsPowerOfTwo: false, - }, - { - if: 'etc1Supported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.ETC1], - engineFormat: [EngineFormat.RGB_ETC1_Format], - priorityETC1S: 2, - priorityUASTC: 4, - needsPowerOfTwo: false, - }, - { - if: 'pvrtcSupported', - basisFormat: [BasisFormat.ETC1S, BasisFormat.UASTC_4x4], - transcoderFormat: [TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA], - engineFormat: [EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format], - priorityETC1S: 5, - priorityUASTC: 6, - needsPowerOfTwo: true, - }, - ] - - const ETC1S_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { - return a.priorityETC1S - b.priorityETC1S - }) - const UASTC_OPTIONS = FORMAT_OPTIONS.sort(function (a, b) { - return a.priorityUASTC - b.priorityUASTC - }) + for (let i = 0; i < options.length; i++) { + const opt = options[i] - function getTranscoderFormat(basisFormat, width, height, hasAlpha) { - let transcoderFormat - let engineFormat + if (!config[opt.if]) continue + if (!opt.basisFormat.includes(basisFormat)) continue + if (hasAlpha && opt.transcoderFormat.length < 2) continue + if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue - const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS + transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0] + engineFormat = opt.engineFormat[hasAlpha ? 1 : 0] - for (let i = 0; i < options.length; i++) { - const opt = options[i] + return { transcoderFormat, engineFormat } + } - if (!config[opt.if]) continue - if (!opt.basisFormat.includes(basisFormat)) continue - if (hasAlpha && opt.transcoderFormat.length < 2) continue - if (opt.needsPowerOfTwo && !(isPowerOfTwo(width) && isPowerOfTwo(height))) continue + console.warn('THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.') - transcoderFormat = opt.transcoderFormat[hasAlpha ? 1 : 0] - engineFormat = opt.engineFormat[hasAlpha ? 1 : 0] + transcoderFormat = TranscoderFormat.RGBA32 + engineFormat = EngineFormat.RGBAFormat return { transcoderFormat, engineFormat } } - console.warn('THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.') + function isPowerOfTwo(value) { + if (value <= 2) return true - transcoderFormat = TranscoderFormat.RGBA32 - engineFormat = EngineFormat.RGBAFormat - - return { transcoderFormat, engineFormat } - } - - function isPowerOfTwo(value) { - if (value <= 2) return true + return (value & (value - 1)) === 0 && value !== 0 + } - return (value & (value - 1)) === 0 && value !== 0 - } + /** Concatenates N byte arrays. */ + function concat(arrays) { + if (arrays.length === 1) return arrays[0] - /** Concatenates N byte arrays. */ - function concat(arrays) { - if (arrays.length === 1) return arrays[0] + let totalByteLength = 0 - let totalByteLength = 0 + for (let i = 0; i < arrays.length; i++) { + const array = arrays[i] + totalByteLength += array.byteLength + } - for (let i = 0; i < arrays.length; i++) { - const array = arrays[i] - totalByteLength += array.byteLength - } + const result = new Uint8Array(totalByteLength) - const result = new Uint8Array(totalByteLength) + let byteOffset = 0 - let byteOffset = 0 + for (let i = 0; i < arrays.length; i++) { + const array = arrays[i] + result.set(array, byteOffset) - for (let i = 0; i < arrays.length; i++) { - const array = arrays[i] - result.set(array, byteOffset) + byteOffset += array.byteLength + } - byteOffset += array.byteLength + return result } - - return result } - } - constructor(manager) { - super(manager) + constructor(manager) { + super(manager) - this.transcoderPath = '' - this.transcoderBinary = null - this.transcoderPending = null + this.transcoderPath = '' + this.transcoderBinary = null + this.transcoderPending = null - this.workerPool = new WorkerPool() - this.workerSourceURL = '' - this.workerConfig = null + this.workerPool = new WorkerPool() + this.workerSourceURL = '' + this.workerConfig = null - if (typeof MSC_TRANSCODER !== 'undefined') { - console.warn( - 'THREE.KTX2Loader: Please update to latest "basis_transcoder".' + - ' "msc_basis_transcoder" is no longer supported in three.js r125+.', - ) + if (typeof MSC_TRANSCODER !== 'undefined') { + console.warn( + 'THREE.KTX2Loader: Please update to latest "basis_transcoder".' + + ' "msc_basis_transcoder" is no longer supported in three.js r125+.', + ) + } } - } - setTranscoderPath(path) { - this.transcoderPath = path + setTranscoderPath(path) { + this.transcoderPath = path - return this - } - - setWorkerLimit(num) { - this.workerPool.setWorkerLimit(num) + return this + } - return this - } + setWorkerLimit(num) { + this.workerPool.setWorkerLimit(num) - detectSupport(renderer) { - this.workerConfig = { - astcSupported: renderer.extensions.has('WEBGL_compressed_texture_astc'), - etc1Supported: renderer.extensions.has('WEBGL_compressed_texture_etc1'), - etc2Supported: renderer.extensions.has('WEBGL_compressed_texture_etc'), - dxtSupported: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), - bptcSupported: renderer.extensions.has('EXT_texture_compression_bptc'), - pvrtcSupported: - renderer.extensions.has('WEBGL_compressed_texture_pvrtc') || - renderer.extensions.has('WEBKIT_WEBGL_compressed_texture_pvrtc'), + return this } - if (renderer.capabilities.isWebGL2) { - // https://github.com/mrdoob/three.js/pull/22928 - this.workerConfig.etc1Supported = false - } + detectSupport(renderer) { + this.workerConfig = { + astcSupported: renderer.extensions.has('WEBGL_compressed_texture_astc'), + etc1Supported: renderer.extensions.has('WEBGL_compressed_texture_etc1'), + etc2Supported: renderer.extensions.has('WEBGL_compressed_texture_etc'), + dxtSupported: renderer.extensions.has('WEBGL_compressed_texture_s3tc'), + bptcSupported: renderer.extensions.has('EXT_texture_compression_bptc'), + pvrtcSupported: + renderer.extensions.has('WEBGL_compressed_texture_pvrtc') || + renderer.extensions.has('WEBKIT_WEBGL_compressed_texture_pvrtc'), + } - return this - } + if (renderer.capabilities.isWebGL2) { + // https://github.com/mrdoob/three.js/pull/22928 + this.workerConfig.etc1Supported = false + } + + return this + } - init() { - if (!this.transcoderPending) { - // Load transcoder wrapper. - const jsLoader = new FileLoader(this.manager) - jsLoader.setPath(this.transcoderPath) - jsLoader.setWithCredentials(this.withCredentials) - const jsContent = jsLoader.loadAsync('basis_transcoder.js') - - // Load transcoder WASM binary. - const binaryLoader = new FileLoader(this.manager) - binaryLoader.setPath(this.transcoderPath) - binaryLoader.setResponseType('arraybuffer') - binaryLoader.setWithCredentials(this.withCredentials) - const binaryContent = binaryLoader.loadAsync('basis_transcoder.wasm') - - this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent, binaryContent]) => { - const fn = KTX2Loader.BasisWorker.toString() - - const body = [ - '/* constants */', - 'let _EngineFormat = ' + JSON.stringify(KTX2Loader.EngineFormat), - 'let _TranscoderFormat = ' + JSON.stringify(KTX2Loader.TranscoderFormat), - 'let _BasisFormat = ' + JSON.stringify(KTX2Loader.BasisFormat), - '/* basis_transcoder.js */', - jsContent, - '/* worker */', - fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}')), - ].join('\n') - - this.workerSourceURL = URL.createObjectURL(new Blob([body])) - this.transcoderBinary = binaryContent - - this.workerPool.setWorkerCreator(() => { - const worker = new Worker(this.workerSourceURL) - const transcoderBinary = this.transcoderBinary.slice(0) - - worker.postMessage({ type: 'init', config: this.workerConfig, transcoderBinary }, [transcoderBinary]) - - return worker + init() { + if (!this.transcoderPending) { + // Load transcoder wrapper. + const jsLoader = new FileLoader(this.manager) + jsLoader.setPath(this.transcoderPath) + jsLoader.setWithCredentials(this.withCredentials) + const jsContent = jsLoader.loadAsync('basis_transcoder.js') + + // Load transcoder WASM binary. + const binaryLoader = new FileLoader(this.manager) + binaryLoader.setPath(this.transcoderPath) + binaryLoader.setResponseType('arraybuffer') + binaryLoader.setWithCredentials(this.withCredentials) + const binaryContent = binaryLoader.loadAsync('basis_transcoder.wasm') + + this.transcoderPending = Promise.all([jsContent, binaryContent]).then(([jsContent, binaryContent]) => { + const fn = KTX2Loader.BasisWorker.toString() + + const body = [ + '/* constants */', + 'let _EngineFormat = ' + JSON.stringify(KTX2Loader.EngineFormat), + 'let _TranscoderFormat = ' + JSON.stringify(KTX2Loader.TranscoderFormat), + 'let _BasisFormat = ' + JSON.stringify(KTX2Loader.BasisFormat), + '/* basis_transcoder.js */', + jsContent, + '/* worker */', + fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}')), + ].join('\n') + + this.workerSourceURL = URL.createObjectURL(new Blob([body])) + this.transcoderBinary = binaryContent + + this.workerPool.setWorkerCreator(() => { + const worker = new Worker(this.workerSourceURL) + const transcoderBinary = this.transcoderBinary.slice(0) + + worker.postMessage({ type: 'init', config: this.workerConfig, transcoderBinary }, [transcoderBinary]) + + return worker + }) }) - }) - if (_activeLoaders > 0) { - // Each instance loads a transcoder and allocates workers, increasing network and memory cost. + if (_activeLoaders > 0) { + // Each instance loads a transcoder and allocates workers, increasing network and memory cost. - console.warn( - 'THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.' + - ' Use a single KTX2Loader instance, or call .dispose() on old instances.', - ) + console.warn( + 'THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.' + + ' Use a single KTX2Loader instance, or call .dispose() on old instances.', + ) + } + + _activeLoaders++ } - _activeLoaders++ + return this.transcoderPending } - return this.transcoderPending - } - - load(url, onLoad, onProgress, onError) { - if (this.workerConfig === null) { - throw new Error('THREE.KTX2Loader: Missing initialization with `.detectSupport( renderer )`.') - } + load(url, onLoad, onProgress, onError) { + if (this.workerConfig === null) { + throw new Error('THREE.KTX2Loader: Missing initialization with `.detectSupport( renderer )`.') + } - const loader = new FileLoader(this.manager) + const loader = new FileLoader(this.manager) - loader.setResponseType('arraybuffer') - loader.setWithCredentials(this.withCredentials) + loader.setResponseType('arraybuffer') + loader.setWithCredentials(this.withCredentials) - loader.load( - url, - (buffer) => { - // Check for an existing task using this buffer. A transferred buffer cannot be transferred - // again from this thread. - if (_taskCache.has(buffer)) { - const cachedTask = _taskCache.get(buffer) + loader.load( + url, + (buffer) => { + // Check for an existing task using this buffer. A transferred buffer cannot be transferred + // again from this thread. + if (_taskCache.has(buffer)) { + const cachedTask = _taskCache.get(buffer) - return cachedTask.promise.then(onLoad).catch(onError) - } + return cachedTask.promise.then(onLoad).catch(onError) + } - this._createTexture(buffer) - .then((texture) => (onLoad ? onLoad(texture) : null)) - .catch(onError) - }, - onProgress, - onError, - ) - } + this._createTexture(buffer) + .then((texture) => (onLoad ? onLoad(texture) : null)) + .catch(onError) + }, + onProgress, + onError, + ) + } - _createTextureFrom(transcodeResult, container) { - const { faces, width, height, format, type, error, dfdFlags } = transcodeResult + _createTextureFrom(transcodeResult, container) { + const { faces, width, height, format, type, error, dfdFlags } = transcodeResult - if (type === 'error') return Promise.reject(error) + if (type === 'error') return Promise.reject(error) - let texture + let texture - if (container.faceCount === 6) { - texture = new CompressedCubeTexture(faces, format, UnsignedByteType) - } else { - const mipmaps = faces[0].mipmaps + if (container.faceCount === 6) { + texture = new CompressedCubeTexture(faces, format, UnsignedByteType) + } else { + const mipmaps = faces[0].mipmaps - texture = - container.layerCount > 1 - ? new CompressedArrayTexture(mipmaps, width, height, container.layerCount, format, UnsignedByteType) - : new CompressedTexture(mipmaps, width, height, format, UnsignedByteType) - } + texture = + container.layerCount > 1 + ? new CompressedArrayTexture(mipmaps, width, height, container.layerCount, format, UnsignedByteType) + : new CompressedTexture(mipmaps, width, height, format, UnsignedByteType) + } - texture.minFilter = faces[0].mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter - texture.magFilter = LinearFilter - texture.generateMipmaps = false - texture.needsUpdate = true + texture.minFilter = faces[0].mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter + texture.magFilter = LinearFilter + texture.generateMipmaps = false + texture.needsUpdate = true - const colorSpace = parseColorSpace(container) - if ('colorSpace' in texture) texture.colorSpace = colorSpace - else texture.encoding = colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding + const colorSpace = parseColorSpace(container) + if ('colorSpace' in texture) texture.colorSpace = colorSpace + else texture.encoding = colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding - texture.premultiplyAlpha = !!(dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED) + texture.premultiplyAlpha = !!(dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED) - return texture - } + return texture + } - /** - * @param {ArrayBuffer} buffer - * @param {object?} config - * @return {Promise} - */ - async _createTexture(buffer, config = {}) { - const container = read(new Uint8Array(buffer)) + /** + * @param {ArrayBuffer} buffer + * @param {object?} config + * @return {Promise} + */ + async _createTexture(buffer, config = {}) { + const container = read(new Uint8Array(buffer)) - if (container.vkFormat !== VK_FORMAT_UNDEFINED) { - return createRawTexture(container) - } + if (container.vkFormat !== VK_FORMAT_UNDEFINED) { + return createRawTexture(container) + } - // + // - const taskConfig = config - const texturePending = this.init() - .then(() => { - return this.workerPool.postMessage({ type: 'transcode', buffer, taskConfig: taskConfig }, [buffer]) - }) - .then((e) => this._createTextureFrom(e.data, container)) + const taskConfig = config + const texturePending = this.init() + .then(() => { + return this.workerPool.postMessage({ type: 'transcode', buffer, taskConfig: taskConfig }, [buffer]) + }) + .then((e) => this._createTextureFrom(e.data, container)) - // Cache the task result. - _taskCache.set(buffer, { promise: texturePending }) + // Cache the task result. + _taskCache.set(buffer, { promise: texturePending }) - return texturePending - } + return texturePending + } - dispose() { - this.workerPool.dispose() - if (this.workerSourceURL) URL.revokeObjectURL(this.workerSourceURL) + dispose() { + this.workerPool.dispose() + if (this.workerSourceURL) URL.revokeObjectURL(this.workerSourceURL) - _activeLoaders-- + _activeLoaders-- - return this + return this + } } -} + + return KTX2Loader +})() // // Parsing for non-Basis textures. These textures are may have supercompression diff --git a/src/loaders/LDrawLoader.js b/src/loaders/LDrawLoader.js index 41c39c67..ea6323a6 100644 --- a/src/loaders/LDrawLoader.js +++ b/src/loaders/LDrawLoader.js @@ -15,8 +15,8 @@ import { UniformsUtils, Vector3, Ray, - REVISION, } from 'three' +import { version } from '../_polyfill/constants' // Special surface finish tag types. // Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented @@ -40,8 +40,8 @@ const FILE_LOCATION_NOT_FOUND = 6 const MAIN_COLOUR_CODE = '16' const MAIN_EDGE_COLOUR_CODE = '24' -const _tempVec0 = new Vector3() -const _tempVec1 = new Vector3() +const _tempVec0 = /* @__PURE__ */ new Vector3() +const _tempVec1 = /* @__PURE__ */ new Vector3() class LDrawConditionalLineMaterial extends ShaderMaterial { constructor(parameters) { @@ -129,7 +129,7 @@ class LDrawConditionalLineMaterial extends ShaderMaterial { outgoingLight = diffuseColor.rgb; // simple shader gl_FragColor = vec4(outgoingLight, diffuseColor.a); #include - #include <${parseInt(REVISION.replace(/\D+/g, '')) >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> + #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> #include #include } @@ -180,7 +180,7 @@ function generateFaceNormals(faces) { } } -const _ray = new Ray() +const _ray = /* @__PURE__ */ new Ray() function smoothNormals(faces, lineSegments, checkSubSegments = false) { // NOTE: 1e2 is pretty coarse but was chosen to quantize the resulting value because // it allows edges to be smoothed as expected (see minifig arms). diff --git a/src/loaders/OBJLoader.js b/src/loaders/OBJLoader.js index f5a5ffce..82ffde04 100644 --- a/src/loaders/OBJLoader.js +++ b/src/loaders/OBJLoader.js @@ -23,12 +23,12 @@ const _material_use_pattern = /^usemtl / // usemap map_name const _map_use_pattern = /^usemap / -const _vA = new Vector3() -const _vB = new Vector3() -const _vC = new Vector3() +const _vA = /* @__PURE__ */ new Vector3() +const _vB = /* @__PURE__ */ new Vector3() +const _vC = /* @__PURE__ */ new Vector3() -const _ab = new Vector3() -const _cb = new Vector3() +const _ab = /* @__PURE__ */ new Vector3() +const _cb = /* @__PURE__ */ new Vector3() function ParserState() { const state = { diff --git a/src/loaders/PRWMLoader.js b/src/loaders/PRWMLoader.js index b839b1bf..a2604cc8 100644 --- a/src/loaders/PRWMLoader.js +++ b/src/loaders/PRWMLoader.js @@ -168,65 +168,69 @@ function decodePrwm(buffer) { // Define the public interface -class PRWMLoader extends Loader { - constructor(manager) { - super(manager) - } +const PRWMLoader = /* @__PURE__ */ (() => { + class PRWMLoader extends Loader { + constructor(manager) { + super(manager) + } - load(url, onLoad, onProgress, onError) { - const scope = this - - const loader = new FileLoader(scope.manager) - loader.setPath(scope.path) - loader.setResponseType('arraybuffer') - loader.setRequestHeader(scope.requestHeader) - loader.setWithCredentials(scope.withCredentials) - - url = url.replace(/\*/g, isBigEndianPlatform() ? 'be' : 'le') - - loader.load( - url, - function (arrayBuffer) { - try { - onLoad(scope.parse(arrayBuffer)) - } catch (e) { - if (onError) { - onError(e) - } else { - console.error(e) + load(url, onLoad, onProgress, onError) { + const scope = this + + const loader = new FileLoader(scope.manager) + loader.setPath(scope.path) + loader.setResponseType('arraybuffer') + loader.setRequestHeader(scope.requestHeader) + loader.setWithCredentials(scope.withCredentials) + + url = url.replace(/\*/g, isBigEndianPlatform() ? 'be' : 'le') + + loader.load( + url, + function (arrayBuffer) { + try { + onLoad(scope.parse(arrayBuffer)) + } catch (e) { + if (onError) { + onError(e) + } else { + console.error(e) + } + + scope.manager.itemError(url) } + }, + onProgress, + onError, + ) + } - scope.manager.itemError(url) - } - }, - onProgress, - onError, - ) - } + parse(arrayBuffer) { + const data = decodePrwm(arrayBuffer), + attributesKey = Object.keys(data.attributes), + bufferGeometry = new BufferGeometry() + + for (let i = 0; i < attributesKey.length; i++) { + const attribute = data.attributes[attributesKey[i]] + bufferGeometry.setAttribute( + attributesKey[i], + new BufferAttribute(attribute.values, attribute.cardinality, attribute.normalized), + ) + } - parse(arrayBuffer) { - const data = decodePrwm(arrayBuffer), - attributesKey = Object.keys(data.attributes), - bufferGeometry = new BufferGeometry() + if (data.indices !== null) { + bufferGeometry.setIndex(new BufferAttribute(data.indices, 1)) + } - for (let i = 0; i < attributesKey.length; i++) { - const attribute = data.attributes[attributesKey[i]] - bufferGeometry.setAttribute( - attributesKey[i], - new BufferAttribute(attribute.values, attribute.cardinality, attribute.normalized), - ) + return bufferGeometry } - if (data.indices !== null) { - bufferGeometry.setIndex(new BufferAttribute(data.indices, 1)) + static isBigEndianPlatform() { + return isBigEndianPlatform() } - - return bufferGeometry } - static isBigEndianPlatform() { - return isBigEndianPlatform() - } -} + return PRWMLoader +})() export { PRWMLoader } diff --git a/src/loaders/SVGLoader.js b/src/loaders/SVGLoader.js index b8879e62..8bbb51c2 100644 --- a/src/loaders/SVGLoader.js +++ b/src/loaders/SVGLoader.js @@ -15,2513 +15,2520 @@ import { const COLOR_SPACE_SVG = 'srgb' -class SVGLoader extends Loader { - constructor(manager) { - super(manager) +const SVGLoader = /* @__PURE__ */ (() => { + class SVGLoader extends Loader { + constructor(manager) { + super(manager) - // Default dots per inch - this.defaultDPI = 90 + // Default dots per inch + this.defaultDPI = 90 - // Accepted units: 'mm', 'cm', 'in', 'pt', 'pc', 'px' - this.defaultUnit = 'px' - } + // Accepted units: 'mm', 'cm', 'in', 'pt', 'pc', 'px' + this.defaultUnit = 'px' + } - load(url, onLoad, onProgress, onError) { - const scope = this - - const loader = new FileLoader(scope.manager) - loader.setPath(scope.path) - loader.setRequestHeader(scope.requestHeader) - loader.setWithCredentials(scope.withCredentials) - loader.load( - url, - function (text) { - try { - onLoad(scope.parse(text)) - } catch (e) { - if (onError) { - onError(e) - } else { - console.error(e) + load(url, onLoad, onProgress, onError) { + const scope = this + + const loader = new FileLoader(scope.manager) + loader.setPath(scope.path) + loader.setRequestHeader(scope.requestHeader) + loader.setWithCredentials(scope.withCredentials) + loader.load( + url, + function (text) { + try { + onLoad(scope.parse(text)) + } catch (e) { + if (onError) { + onError(e) + } else { + console.error(e) + } + + scope.manager.itemError(url) } + }, + onProgress, + onError, + ) + } - scope.manager.itemError(url) - } - }, - onProgress, - onError, - ) - } + parse(text) { + const scope = this - parse(text) { - const scope = this + function parseNode(node, style) { + if (node.nodeType !== 1) return - function parseNode(node, style) { - if (node.nodeType !== 1) return + const transform = getNodeTransform(node) - const transform = getNodeTransform(node) + let isDefsNode = false - let isDefsNode = false + let path = null - let path = null + switch (node.nodeName) { + case 'svg': + style = parseStyle(node, style) + break - switch (node.nodeName) { - case 'svg': - style = parseStyle(node, style) - break + case 'style': + parseCSSStylesheet(node) + break - case 'style': - parseCSSStylesheet(node) - break + case 'g': + style = parseStyle(node, style) + break - case 'g': - style = parseStyle(node, style) - break + case 'path': + style = parseStyle(node, style) + if (node.hasAttribute('d')) path = parsePathNode(node) + break - case 'path': - style = parseStyle(node, style) - if (node.hasAttribute('d')) path = parsePathNode(node) - break + case 'rect': + style = parseStyle(node, style) + path = parseRectNode(node) + break - case 'rect': - style = parseStyle(node, style) - path = parseRectNode(node) - break + case 'polygon': + style = parseStyle(node, style) + path = parsePolygonNode(node) + break - case 'polygon': - style = parseStyle(node, style) - path = parsePolygonNode(node) - break + case 'polyline': + style = parseStyle(node, style) + path = parsePolylineNode(node) + break - case 'polyline': - style = parseStyle(node, style) - path = parsePolylineNode(node) - break + case 'circle': + style = parseStyle(node, style) + path = parseCircleNode(node) + break - case 'circle': - style = parseStyle(node, style) - path = parseCircleNode(node) - break + case 'ellipse': + style = parseStyle(node, style) + path = parseEllipseNode(node) + break - case 'ellipse': - style = parseStyle(node, style) - path = parseEllipseNode(node) - break + case 'line': + style = parseStyle(node, style) + path = parseLineNode(node) + break - case 'line': - style = parseStyle(node, style) - path = parseLineNode(node) - break + case 'defs': + isDefsNode = true + break - case 'defs': - isDefsNode = true - break + case 'use': + style = parseStyle(node, style) - case 'use': - style = parseStyle(node, style) + const href = node.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || '' + const usedNodeId = href.substring(1) + const usedNode = node.viewportElement.getElementById(usedNodeId) + if (usedNode) { + parseNode(usedNode, style) + } else { + console.warn("SVGLoader: 'use node' references non-existent node id: " + usedNodeId) + } - const href = node.getAttributeNS('http://www.w3.org/1999/xlink', 'href') || '' - const usedNodeId = href.substring(1) - const usedNode = node.viewportElement.getElementById(usedNodeId) - if (usedNode) { - parseNode(usedNode, style) - } else { - console.warn("SVGLoader: 'use node' references non-existent node id: " + usedNodeId) - } + break - break + default: + // console.log( node ); + } - default: - // console.log( node ); - } + if (path) { + if (style.fill !== undefined && style.fill !== 'none') { + path.color.setStyle(style.fill, COLOR_SPACE_SVG) + } - if (path) { - if (style.fill !== undefined && style.fill !== 'none') { - path.color.setStyle(style.fill, COLOR_SPACE_SVG) - } + transformPath(path, currentTransform) - transformPath(path, currentTransform) + paths.push(path) - paths.push(path) + path.userData = { node: node, style: style } + } - path.userData = { node: node, style: style } - } + const childNodes = node.childNodes - const childNodes = node.childNodes + for (let i = 0; i < childNodes.length; i++) { + const node = childNodes[i] - for (let i = 0; i < childNodes.length; i++) { - const node = childNodes[i] + if (isDefsNode && node.nodeName !== 'style' && node.nodeName !== 'defs') { + // Ignore everything in defs except CSS style definitions + // and nested defs, because it is OK by the standard to have + //