UNPKG

three-stdlib

Version:

stand-alone library of threejs examples

1,483 lines 83.4 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const THREE = require("three"); const TGALoader = require("./TGALoader.cjs"); const uv1 = require("../_polyfill/uv1.cjs"); class ColladaLoader extends THREE.Loader { constructor(manager) { super(manager); } load(url, onLoad, onProgress, onError) { const scope = this; const path = scope.path === "" ? THREE.LoaderUtils.extractUrlBase(url) : scope.path; const loader = new THREE.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, path)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError ); } parse(text, path) { function getElementsByTagName(xml2, name) { const array = []; const childNodes = xml2.childNodes; for (let i = 0, l = childNodes.length; i < l; i++) { const child = childNodes[i]; if (child.nodeName === name) { array.push(child); } } return array; } function parseStrings(text2) { if (text2.length === 0) return []; const parts = text2.trim().split(/\s+/); const array = new Array(parts.length); for (let i = 0, l = parts.length; i < l; i++) { array[i] = parts[i]; } return array; } function parseFloats(text2) { if (text2.length === 0) return []; const parts = text2.trim().split(/\s+/); const array = new Array(parts.length); for (let i = 0, l = parts.length; i < l; i++) { array[i] = parseFloat(parts[i]); } return array; } function parseInts(text2) { if (text2.length === 0) return []; const parts = text2.trim().split(/\s+/); const array = new Array(parts.length); for (let i = 0, l = parts.length; i < l; i++) { array[i] = parseInt(parts[i]); } return array; } function parseId(text2) { return text2.substring(1); } function generateId() { return "three_default_" + count++; } function isEmpty(object) { return Object.keys(object).length === 0; } function parseAsset(xml2) { return { unit: parseAssetUnit(getElementsByTagName(xml2, "unit")[0]), upAxis: parseAssetUpAxis(getElementsByTagName(xml2, "up_axis")[0]) }; } function parseAssetUnit(xml2) { if (xml2 !== void 0 && xml2.hasAttribute("meter") === true) { return parseFloat(xml2.getAttribute("meter")); } else { return 1; } } function parseAssetUpAxis(xml2) { return xml2 !== void 0 ? xml2.textContent : "Y_UP"; } function parseLibrary(xml2, libraryName, nodeName, parser) { const library2 = getElementsByTagName(xml2, libraryName)[0]; if (library2 !== void 0) { const elements = getElementsByTagName(library2, nodeName); for (let i = 0; i < elements.length; i++) { parser(elements[i]); } } } function buildLibrary(data, builder) { for (const name in data) { const object = data[name]; object.build = builder(data[name]); } } function getBuild(data, builder) { if (data.build !== void 0) return data.build; data.build = builder(data); return data.build; } function parseAnimation(xml2) { const data = { sources: {}, samplers: {}, channels: {} }; let hasChildren = false; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; let id; switch (child.nodeName) { case "source": id = child.getAttribute("id"); data.sources[id] = parseSource(child); break; case "sampler": id = child.getAttribute("id"); data.samplers[id] = parseAnimationSampler(child); break; case "channel": id = child.getAttribute("target"); data.channels[id] = parseAnimationChannel(child); break; case "animation": parseAnimation(child); hasChildren = true; break; default: console.log(child); } } if (hasChildren === false) { library.animations[xml2.getAttribute("id") || THREE.MathUtils.generateUUID()] = data; } } function parseAnimationSampler(xml2) { const data = { inputs: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "input": const id = parseId(child.getAttribute("source")); const semantic = child.getAttribute("semantic"); data.inputs[semantic] = id; break; } } return data; } function parseAnimationChannel(xml2) { const data = {}; const target = xml2.getAttribute("target"); let parts = target.split("/"); const id = parts.shift(); let sid = parts.shift(); const arraySyntax = sid.indexOf("(") !== -1; const memberSyntax = sid.indexOf(".") !== -1; if (memberSyntax) { parts = sid.split("."); sid = parts.shift(); data.member = parts.shift(); } else if (arraySyntax) { const indices = sid.split("("); sid = indices.shift(); for (let i = 0; i < indices.length; i++) { indices[i] = parseInt(indices[i].replace(/\)/, "")); } data.indices = indices; } data.id = id; data.sid = sid; data.arraySyntax = arraySyntax; data.memberSyntax = memberSyntax; data.sampler = parseId(xml2.getAttribute("source")); return data; } function buildAnimation(data) { const tracks = []; const channels = data.channels; const samplers = data.samplers; const sources = data.sources; for (const target in channels) { if (channels.hasOwnProperty(target)) { const channel = channels[target]; const sampler = samplers[channel.sampler]; const inputId = sampler.inputs.INPUT; const outputId = sampler.inputs.OUTPUT; const inputSource = sources[inputId]; const outputSource = sources[outputId]; const animation = buildAnimationChannel(channel, inputSource, outputSource); createKeyframeTracks(animation, tracks); } } return tracks; } function getAnimation(id) { return getBuild(library.animations[id], buildAnimation); } function buildAnimationChannel(channel, inputSource, outputSource) { const node = library.nodes[channel.id]; const object3D = getNode(node.id); const transform = node.transforms[channel.sid]; const defaultMatrix = node.matrix.clone().transpose(); let time, stride; let i, il, j, jl; const data = {}; switch (transform) { case "matrix": for (i = 0, il = inputSource.array.length; i < il; i++) { time = inputSource.array[i]; stride = i * outputSource.stride; if (data[time] === void 0) data[time] = {}; if (channel.arraySyntax === true) { const value = outputSource.array[stride]; const index = channel.indices[0] + 4 * channel.indices[1]; data[time][index] = value; } else { for (j = 0, jl = outputSource.stride; j < jl; j++) { data[time][j] = outputSource.array[stride + j]; } } } break; case "translate": console.warn('THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform); break; case "rotate": console.warn('THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform); break; case "scale": console.warn('THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform); break; } const keyframes = prepareAnimationData(data, defaultMatrix); const animation = { name: object3D.uuid, keyframes }; return animation; } function prepareAnimationData(data, defaultMatrix) { const keyframes = []; for (const time in data) { keyframes.push({ time: parseFloat(time), value: data[time] }); } keyframes.sort(ascending); for (let i = 0; i < 16; i++) { transformAnimationData(keyframes, i, defaultMatrix.elements[i]); } return keyframes; function ascending(a, b) { return a.time - b.time; } } const position = new THREE.Vector3(); const scale = new THREE.Vector3(); const quaternion = new THREE.Quaternion(); function createKeyframeTracks(animation, tracks) { const keyframes = animation.keyframes; const name = animation.name; const times = []; const positionData = []; const quaternionData = []; const scaleData = []; for (let i = 0, l = keyframes.length; i < l; i++) { const keyframe = keyframes[i]; const time = keyframe.time; const value = keyframe.value; matrix.fromArray(value).transpose(); matrix.decompose(position, quaternion, scale); times.push(time); positionData.push(position.x, position.y, position.z); quaternionData.push(quaternion.x, quaternion.y, quaternion.z, quaternion.w); scaleData.push(scale.x, scale.y, scale.z); } if (positionData.length > 0) tracks.push(new THREE.VectorKeyframeTrack(name + ".position", times, positionData)); if (quaternionData.length > 0) { tracks.push(new THREE.QuaternionKeyframeTrack(name + ".quaternion", times, quaternionData)); } if (scaleData.length > 0) tracks.push(new THREE.VectorKeyframeTrack(name + ".scale", times, scaleData)); return tracks; } function transformAnimationData(keyframes, property, defaultValue) { let keyframe; let empty = true; let i, l; for (i = 0, l = keyframes.length; i < l; i++) { keyframe = keyframes[i]; if (keyframe.value[property] === void 0) { keyframe.value[property] = null; } else { empty = false; } } if (empty === true) { for (i = 0, l = keyframes.length; i < l; i++) { keyframe = keyframes[i]; keyframe.value[property] = defaultValue; } } else { createMissingKeyframes(keyframes, property); } } function createMissingKeyframes(keyframes, property) { let prev, next; for (let i = 0, l = keyframes.length; i < l; i++) { const keyframe = keyframes[i]; if (keyframe.value[property] === null) { prev = getPrev(keyframes, i, property); next = getNext(keyframes, i, property); if (prev === null) { keyframe.value[property] = next.value[property]; continue; } if (next === null) { keyframe.value[property] = prev.value[property]; continue; } interpolate(keyframe, prev, next, property); } } } function getPrev(keyframes, i, property) { while (i >= 0) { const keyframe = keyframes[i]; if (keyframe.value[property] !== null) return keyframe; i--; } return null; } function getNext(keyframes, i, property) { while (i < keyframes.length) { const keyframe = keyframes[i]; if (keyframe.value[property] !== null) return keyframe; i++; } return null; } function interpolate(key, prev, next, property) { if (next.time - prev.time === 0) { key.value[property] = prev.value[property]; return; } key.value[property] = (key.time - prev.time) * (next.value[property] - prev.value[property]) / (next.time - prev.time) + prev.value[property]; } function parseAnimationClip(xml2) { const data = { name: xml2.getAttribute("id") || "default", start: parseFloat(xml2.getAttribute("start") || 0), end: parseFloat(xml2.getAttribute("end") || 0), animations: [] }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "instance_animation": data.animations.push(parseId(child.getAttribute("url"))); break; } } library.clips[xml2.getAttribute("id")] = data; } function buildAnimationClip(data) { const tracks = []; const name = data.name; const duration = data.end - data.start || -1; const animations2 = data.animations; for (let i = 0, il = animations2.length; i < il; i++) { const animationTracks = getAnimation(animations2[i]); for (let j = 0, jl = animationTracks.length; j < jl; j++) { tracks.push(animationTracks[j]); } } return new THREE.AnimationClip(name, duration, tracks); } function getAnimationClip(id) { return getBuild(library.clips[id], buildAnimationClip); } function parseController(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "skin": data.id = parseId(child.getAttribute("source")); data.skin = parseSkin(child); break; case "morph": data.id = parseId(child.getAttribute("source")); console.warn("THREE.ColladaLoader: Morph target animation not supported yet."); break; } } library.controllers[xml2.getAttribute("id")] = data; } function parseSkin(xml2) { const data = { sources: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "bind_shape_matrix": data.bindShapeMatrix = parseFloats(child.textContent); break; case "source": const id = child.getAttribute("id"); data.sources[id] = parseSource(child); break; case "joints": data.joints = parseJoints(child); break; case "vertex_weights": data.vertexWeights = parseVertexWeights(child); break; } } return data; } function parseJoints(xml2) { const data = { inputs: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "input": const semantic = child.getAttribute("semantic"); const id = parseId(child.getAttribute("source")); data.inputs[semantic] = id; break; } } return data; } function parseVertexWeights(xml2) { const data = { inputs: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "input": const semantic = child.getAttribute("semantic"); const id = parseId(child.getAttribute("source")); const offset = parseInt(child.getAttribute("offset")); data.inputs[semantic] = { id, offset }; break; case "vcount": data.vcount = parseInts(child.textContent); break; case "v": data.v = parseInts(child.textContent); break; } } return data; } function buildController(data) { const build = { id: data.id }; const geometry = library.geometries[build.id]; if (data.skin !== void 0) { build.skin = buildSkin(data.skin); geometry.sources.skinIndices = build.skin.indices; geometry.sources.skinWeights = build.skin.weights; } return build; } function buildSkin(data) { const BONE_LIMIT = 4; const build = { joints: [], // this must be an array to preserve the joint order indices: { array: [], stride: BONE_LIMIT }, weights: { array: [], stride: BONE_LIMIT } }; const sources = data.sources; const vertexWeights = data.vertexWeights; const vcount = vertexWeights.vcount; const v = vertexWeights.v; const jointOffset = vertexWeights.inputs.JOINT.offset; const weightOffset = vertexWeights.inputs.WEIGHT.offset; const jointSource = data.sources[data.joints.inputs.JOINT]; const inverseSource = data.sources[data.joints.inputs.INV_BIND_MATRIX]; const weights = sources[vertexWeights.inputs.WEIGHT.id].array; let stride = 0; let i, j, l; for (i = 0, l = vcount.length; i < l; i++) { const jointCount = vcount[i]; const vertexSkinData = []; for (j = 0; j < jointCount; j++) { const skinIndex = v[stride + jointOffset]; const weightId = v[stride + weightOffset]; const skinWeight = weights[weightId]; vertexSkinData.push({ index: skinIndex, weight: skinWeight }); stride += 2; } vertexSkinData.sort(descending); for (j = 0; j < BONE_LIMIT; j++) { const d = vertexSkinData[j]; if (d !== void 0) { build.indices.array.push(d.index); build.weights.array.push(d.weight); } else { build.indices.array.push(0); build.weights.array.push(0); } } } if (data.bindShapeMatrix) { build.bindMatrix = new THREE.Matrix4().fromArray(data.bindShapeMatrix).transpose(); } else { build.bindMatrix = new THREE.Matrix4().identity(); } for (i = 0, l = jointSource.array.length; i < l; i++) { const name = jointSource.array[i]; const boneInverse = new THREE.Matrix4().fromArray(inverseSource.array, i * inverseSource.stride).transpose(); build.joints.push({ name, boneInverse }); } return build; function descending(a, b) { return b.weight - a.weight; } } function getController(id) { return getBuild(library.controllers[id], buildController); } function parseImage(xml2) { const data = { init_from: getElementsByTagName(xml2, "init_from")[0].textContent }; library.images[xml2.getAttribute("id")] = data; } function buildImage(data) { if (data.build !== void 0) return data.build; return data.init_from; } function getImage(id) { const data = library.images[id]; if (data !== void 0) { return getBuild(data, buildImage); } console.warn("THREE.ColladaLoader: Couldn't find image with ID:", id); return null; } function parseEffect(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "profile_COMMON": data.profile = parseEffectProfileCOMMON(child); break; } } library.effects[xml2.getAttribute("id")] = data; } function parseEffectProfileCOMMON(xml2) { const data = { surfaces: {}, samplers: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "newparam": parseEffectNewparam(child, data); break; case "technique": data.technique = parseEffectTechnique(child); break; case "extra": data.extra = parseEffectExtra(child); break; } } return data; } function parseEffectNewparam(xml2, data) { const sid = xml2.getAttribute("sid"); for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "surface": data.surfaces[sid] = parseEffectSurface(child); break; case "sampler2D": data.samplers[sid] = parseEffectSampler(child); break; } } } function parseEffectSurface(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "init_from": data.init_from = child.textContent; break; } } return data; } function parseEffectSampler(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "source": data.source = child.textContent; break; } } return data; } function parseEffectTechnique(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "constant": case "lambert": case "blinn": case "phong": data.type = child.nodeName; data.parameters = parseEffectParameters(child); break; case "extra": data.extra = parseEffectExtra(child); break; } } return data; } function parseEffectParameters(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "emission": case "diffuse": case "specular": case "bump": case "ambient": case "shininess": case "transparency": data[child.nodeName] = parseEffectParameter(child); break; case "transparent": data[child.nodeName] = { opaque: child.hasAttribute("opaque") ? child.getAttribute("opaque") : "A_ONE", data: parseEffectParameter(child) }; break; } } return data; } function parseEffectParameter(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "color": data[child.nodeName] = parseFloats(child.textContent); break; case "float": data[child.nodeName] = parseFloat(child.textContent); break; case "texture": data[child.nodeName] = { id: child.getAttribute("texture"), extra: parseEffectParameterTexture(child) }; break; } } return data; } function parseEffectParameterTexture(xml2) { const data = { technique: {} }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "extra": parseEffectParameterTextureExtra(child, data); break; } } return data; } function parseEffectParameterTextureExtra(xml2, data) { for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "technique": parseEffectParameterTextureExtraTechnique(child, data); break; } } } function parseEffectParameterTextureExtraTechnique(xml2, data) { for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "repeatU": case "repeatV": case "offsetU": case "offsetV": data.technique[child.nodeName] = parseFloat(child.textContent); break; case "wrapU": case "wrapV": if (child.textContent.toUpperCase() === "TRUE") { data.technique[child.nodeName] = 1; } else if (child.textContent.toUpperCase() === "FALSE") { data.technique[child.nodeName] = 0; } else { data.technique[child.nodeName] = parseInt(child.textContent); } break; case "bump": data[child.nodeName] = parseEffectExtraTechniqueBump(child); break; } } } function parseEffectExtra(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "technique": data.technique = parseEffectExtraTechnique(child); break; } } return data; } function parseEffectExtraTechnique(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "double_sided": data[child.nodeName] = parseInt(child.textContent); break; case "bump": data[child.nodeName] = parseEffectExtraTechniqueBump(child); break; } } return data; } function parseEffectExtraTechniqueBump(xml2) { var data = {}; for (var i = 0, l = xml2.childNodes.length; i < l; i++) { var child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "texture": data[child.nodeName] = { id: child.getAttribute("texture"), texcoord: child.getAttribute("texcoord"), extra: parseEffectParameterTexture(child) }; break; } } return data; } function buildEffect(data) { return data; } function getEffect(id) { return getBuild(library.effects[id], buildEffect); } function parseMaterial(xml2) { const data = { name: xml2.getAttribute("name") }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "instance_effect": data.url = parseId(child.getAttribute("url")); break; } } library.materials[xml2.getAttribute("id")] = data; } function getTextureLoader(image) { let loader; let extension = image.slice((image.lastIndexOf(".") - 1 >>> 0) + 2); extension = extension.toLowerCase(); switch (extension) { case "tga": loader = tgaLoader; break; default: loader = textureLoader; } return loader; } function buildMaterial(data) { const effect = getEffect(data.url); const technique = effect.profile.technique; let material; switch (technique.type) { case "phong": case "blinn": material = new THREE.MeshPhongMaterial(); break; case "lambert": material = new THREE.MeshLambertMaterial(); break; default: material = new THREE.MeshBasicMaterial(); break; } material.name = data.name || ""; function getTexture(textureObject) { const sampler = effect.profile.samplers[textureObject.id]; let image = null; if (sampler !== void 0) { const surface = effect.profile.surfaces[sampler.source]; image = getImage(surface.init_from); } else { console.warn("THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530)."); image = getImage(textureObject.id); } if (image !== null) { const loader = getTextureLoader(image); if (loader !== void 0) { const texture = loader.load(image); const extra = textureObject.extra; if (extra !== void 0 && extra.technique !== void 0 && isEmpty(extra.technique) === false) { const technique2 = extra.technique; texture.wrapS = technique2.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; texture.wrapT = technique2.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; texture.offset.set(technique2.offsetU || 0, technique2.offsetV || 0); texture.repeat.set(technique2.repeatU || 1, technique2.repeatV || 1); } else { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; } return texture; } else { console.warn("THREE.ColladaLoader: Loader for texture %s not found.", image); return null; } } else { console.warn("THREE.ColladaLoader: Couldn't create texture with ID:", textureObject.id); return null; } } const parameters = technique.parameters; for (const key in parameters) { const parameter = parameters[key]; switch (key) { case "diffuse": if (parameter.color) material.color.fromArray(parameter.color); if (parameter.texture) material.map = getTexture(parameter.texture); break; case "specular": if (parameter.color && material.specular) material.specular.fromArray(parameter.color); if (parameter.texture) material.specularMap = getTexture(parameter.texture); break; case "bump": if (parameter.texture) material.normalMap = getTexture(parameter.texture); break; case "ambient": if (parameter.texture) material.lightMap = getTexture(parameter.texture); break; case "shininess": if (parameter.float && material.shininess) material.shininess = parameter.float; break; case "emission": if (parameter.color && material.emissive) material.emissive.fromArray(parameter.color); if (parameter.texture) material.emissiveMap = getTexture(parameter.texture); break; } } let transparent = parameters["transparent"]; let transparency = parameters["transparency"]; if (transparency === void 0 && transparent) { transparency = { float: 1 }; } if (transparent === void 0 && transparency) { transparent = { opaque: "A_ONE", data: { color: [1, 1, 1, 1] } }; } if (transparent && transparency) { if (transparent.data.texture) { material.transparent = true; } else { const color = transparent.data.color; switch (transparent.opaque) { case "A_ONE": material.opacity = color[3] * transparency.float; break; case "RGB_ZERO": material.opacity = 1 - color[0] * transparency.float; break; case "A_ZERO": material.opacity = 1 - color[3] * transparency.float; break; case "RGB_ONE": material.opacity = color[0] * transparency.float; break; default: console.warn('THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque); } if (material.opacity < 1) material.transparent = true; } } if (technique.extra !== void 0 && technique.extra.technique !== void 0) { const techniques = technique.extra.technique; for (const k in techniques) { const v = techniques[k]; switch (k) { case "double_sided": material.side = v === 1 ? THREE.DoubleSide : THREE.FrontSide; break; case "bump": material.normalMap = getTexture(v.texture); material.normalScale = new THREE.Vector2(1, 1); break; } } } return material; } function getMaterial(id) { return getBuild(library.materials[id], buildMaterial); } function parseCamera(xml2) { const data = { name: xml2.getAttribute("name") }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "optics": data.optics = parseCameraOptics(child); break; } } library.cameras[xml2.getAttribute("id")] = data; } function parseCameraOptics(xml2) { for (let i = 0; i < xml2.childNodes.length; i++) { const child = xml2.childNodes[i]; switch (child.nodeName) { case "technique_common": return parseCameraTechnique(child); } } return {}; } function parseCameraTechnique(xml2) { const data = {}; for (let i = 0; i < xml2.childNodes.length; i++) { const child = xml2.childNodes[i]; switch (child.nodeName) { case "perspective": case "orthographic": data.technique = child.nodeName; data.parameters = parseCameraParameters(child); break; } } return data; } function parseCameraParameters(xml2) { const data = {}; for (let i = 0; i < xml2.childNodes.length; i++) { const child = xml2.childNodes[i]; switch (child.nodeName) { case "xfov": case "yfov": case "xmag": case "ymag": case "znear": case "zfar": case "aspect_ratio": data[child.nodeName] = parseFloat(child.textContent); break; } } return data; } function buildCamera(data) { let camera; switch (data.optics.technique) { case "perspective": camera = new THREE.PerspectiveCamera( data.optics.parameters.yfov, data.optics.parameters.aspect_ratio, data.optics.parameters.znear, data.optics.parameters.zfar ); break; case "orthographic": let ymag = data.optics.parameters.ymag; let xmag = data.optics.parameters.xmag; const aspectRatio = data.optics.parameters.aspect_ratio; xmag = xmag === void 0 ? ymag * aspectRatio : xmag; ymag = ymag === void 0 ? xmag / aspectRatio : ymag; xmag *= 0.5; ymag *= 0.5; camera = new THREE.OrthographicCamera( -xmag, xmag, ymag, -ymag, // left, right, top, bottom data.optics.parameters.znear, data.optics.parameters.zfar ); break; default: camera = new THREE.PerspectiveCamera(); break; } camera.name = data.name || ""; return camera; } function getCamera(id) { const data = library.cameras[id]; if (data !== void 0) { return getBuild(data, buildCamera); } console.warn("THREE.ColladaLoader: Couldn't find camera with ID:", id); return null; } function parseLight(xml2) { let data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "technique_common": data = parseLightTechnique(child); break; } } library.lights[xml2.getAttribute("id")] = data; } function parseLightTechnique(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "directional": case "point": case "spot": case "ambient": data.technique = child.nodeName; data.parameters = parseLightParameters(child); } } return data; } function parseLightParameters(xml2) { const data = {}; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "color": const array = parseFloats(child.textContent); data.color = new THREE.Color().fromArray(array); break; case "falloff_angle": data.falloffAngle = parseFloat(child.textContent); break; case "quadratic_attenuation": const f = parseFloat(child.textContent); data.distance = f ? Math.sqrt(1 / f) : 0; break; } } return data; } function buildLight(data) { let light; switch (data.technique) { case "directional": light = new THREE.DirectionalLight(); break; case "point": light = new THREE.PointLight(); break; case "spot": light = new THREE.SpotLight(); break; case "ambient": light = new THREE.AmbientLight(); break; } if (data.parameters.color) light.color.copy(data.parameters.color); if (data.parameters.distance) light.distance = data.parameters.distance; return light; } function getLight(id) { const data = library.lights[id]; if (data !== void 0) { return getBuild(data, buildLight); } console.warn("THREE.ColladaLoader: Couldn't find light with ID:", id); return null; } function parseGeometry(xml2) { const data = { name: xml2.getAttribute("name"), sources: {}, vertices: {}, primitives: [] }; const mesh = getElementsByTagName(xml2, "mesh")[0]; if (mesh === void 0) return; for (let i = 0; i < mesh.childNodes.length; i++) { const child = mesh.childNodes[i]; if (child.nodeType !== 1) continue; const id = child.getAttribute("id"); switch (child.nodeName) { case "source": data.sources[id] = parseSource(child); break; case "vertices": data.vertices = parseGeometryVertices(child); break; case "polygons": console.warn("THREE.ColladaLoader: Unsupported primitive type: ", child.nodeName); break; case "lines": case "linestrips": case "polylist": case "triangles": data.primitives.push(parseGeometryPrimitive(child)); break; default: console.log(child); } } library.geometries[xml2.getAttribute("id")] = data; } function parseSource(xml2) { const data = { array: [], stride: 3 }; for (let i = 0; i < xml2.childNodes.length; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "float_array": data.array = parseFloats(child.textContent); break; case "Name_array": data.array = parseStrings(child.textContent); break; case "technique_common": const accessor = getElementsByTagName(child, "accessor")[0]; if (accessor !== void 0) { data.stride = parseInt(accessor.getAttribute("stride")); } break; } } return data; } function parseGeometryVertices(xml2) { const data = {}; for (let i = 0; i < xml2.childNodes.length; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; data[child.getAttribute("semantic")] = parseId(child.getAttribute("source")); } return data; } function parseGeometryPrimitive(xml2) { const primitive = { type: xml2.nodeName, material: xml2.getAttribute("material"), count: parseInt(xml2.getAttribute("count")), inputs: {}, stride: 0, hasUV: false }; for (let i = 0, l = xml2.childNodes.length; i < l; i++) { const child = xml2.childNodes[i]; if (child.nodeType !== 1) continue; switch (child.nodeName) { case "input": const id = parseId(child.getAttribute("source")); const semantic = child.getAttribute("semantic"); const offset = parseInt(child.getAttribute("offset")); const set = parseInt(child.getAttribute("set")); const inputname = set > 0 ? semantic + set : semantic; primitive.inputs[inputname] = { id, offset }; primitive.stride = Math.max(primitive.stride, offset + 1); if (semantic === "TEXCOORD") primitive.hasUV = true; break; case "vcount": primitive.vcount = parseInts(child.textContent); break; case "p": primitive.p = parseInts(child.textContent); break; } } return primitive; } function groupPrimitives(primitives) { const build = {}; for (let i = 0; i < primitives.length; i++) { const primitive = primitives[i]; if (build[primitive.type] === void 0) build[primitive.type] = []; build[primitive.type].push(primitive); } return build; } function checkUVCoordinates(primitives) { let count2 = 0; for (let i = 0, l = primitives.length; i < l; i++) { const primitive = primitives[i]; if (primitive.hasUV === true) { count2++; } } if (count2 > 0 && count2 < primitives.length) { primitives.uvsNeedsFix = true; } } function buildGeometry(data) { const build = {}; const sources = data.sources; const vertices = data.vertices; const primitives = data.primitives; if (primitives.length === 0) return {}; const groupedPrimitives = groupPrimitives(primitives); for (const type in groupedPrimitives) { const primitiveType = groupedPrimitives[type]; checkUVCoordinates(primitiveType); build[type] = buildGeometryType(primitiveType, sources, vertices); } return build; } function buildGeometryType(primitives, sources, vertices) { const build = {}; const position2 = { array: [], stride: 0 }; const normal = { array: [], stride: 0 }; const uv = { array: [], stride: 0 }; const uv1$1 = { array: [], stride: 0 }; const color = { array: [], stride: 0 }; const skinIndex = { array: [], stride: 4 }; const skinWeight = { array: [], stride: 4 }; const geometry = new THREE.BufferGeometry(); const materialKeys = []; let start = 0; for (let p = 0; p < primitives.length; p++) { const primitive = primitives[p]; const inputs = primitive.inputs; let count2 = 0; switch (primitive.type) { case "lines": case "linestrips": count2 = primitive.count * 2; break; case "triangles": count2 = primitive.count * 3; break; case "polylist": for (let g = 0; g < primitive.count; g++) { const vc = primitive.vcount[g]; switch (vc) { case 3: count2 += 3; break; case 4: count2 += 6; break; default: count2 += (vc - 2) * 3; break; } } break; default: console.warn("THREE.ColladaLoader: Unknow primitive type:", primitive.type); } geometry.addGroup(start, count2, p); start += count2; if (primitive.material) { materialKeys.push(primitive.material); } for (const name in inputs) { const input = inputs[name]; switch (name) { case "VERTEX": for (const key in vertices) { const id = vertices[key]; switch (key) { case "POSITION": const prevLength = position2.array.length; buildGeometryData(primitive, sources[id], input.offset, position2.array); position2.stride = sources[id].stride; if (sources.skinWeights && sources.skinIndices) { buildGeometryData(primitive, sources.skinIndices, input.offset, skinIndex.array); buildGeometryData(primitive, sources.skinWeights, input.offset, skinWeight.array); } if (primitive.hasUV === false && primitives.uvsNeedsFix === true) { const count3 = (position2.array.length - prevLength) / position2.stride; for (let i = 0; i < count3; i++) { uv.array.push(0, 0); } } break; case "NORMAL": buildGeometryData(primitive, sources[id], input.offset, normal.array); normal.stride = sources[id].stride; break; case "COLOR": buildGeometryData(primitive, sources[id], input.offset, color.array); color.stride = sources[id].stride; break; case "TEXCOORD": buildGeometryData(primitive, sources[id], input.offset, uv.array); uv.stride = sources[id].stride; break; case "TEXCOORD1": buildGeometryData(primitive, sources[id], input.offset, uv1$1.array); uv.stride = sources[id].stride; break; default: console.warn('THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key); } } break; case "NORMAL": buildGeometryData(primitive, sources[input.id], input.offset, normal.array); normal.stride = sources[input.id].stride; break; case "COLOR": buildGeometryData(primitive, sources[input.id], input.offset, color.array); color.stride = sources[input.id].stride; break; case "TEXCOORD": buildGeometryData(primitive, sources[input.id], input.offset, uv.array); uv.stride = sources[input.id].stride; break; case "TEXCOORD1": buildGeometryData(primitive, sources[input.id], input.offset, uv1$1.array); uv1$1.stride = sources[input.id].stride; break; } } } if (position2.array.length > 0) { geometry.setAttribute("position", new THREE.Float32BufferAttribute(position2.array, position2.stride)); } if (normal.array.length > 0) { geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normal.array, normal.stride)); } if (color.array.length > 0) geometry.setAttribute("color", new THREE.Float32BufferAttribute(color.array, color.stride)); if (uv.array.length > 0) geometry.setAttribute("uv