mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
1594 lines
28 KiB
JavaScript
1594 lines
28 KiB
JavaScript
/**
|
|
* @author mrdoob / http://mrdoob.com/
|
|
*/
|
|
|
|
THREE.ColladaLoader = function (manager) {
|
|
|
|
this.manager = (manager !== undefined) ? manager : THREE.DefaultLoadingManager;
|
|
|
|
};
|
|
|
|
THREE.ColladaLoader.prototype = {
|
|
|
|
constructor: THREE.ColladaLoader,
|
|
|
|
load: function (url, onLoad, onProgress, onError) {
|
|
|
|
var scope = this;
|
|
|
|
var loader = new THREE.FileLoader(scope.manager);
|
|
loader.load(url, function (text) {
|
|
|
|
onLoad(scope.parse(text));
|
|
|
|
}, onProgress, onError);
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
set convertUpAxis(value) {
|
|
|
|
console.log('ColladaLoder.options.convertUpAxis: TODO');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
setCrossOrigin: function (value) {
|
|
|
|
this.crossOrigin = value;
|
|
|
|
},
|
|
|
|
parse: function (text) {
|
|
|
|
function getElementsByTagName(xml, name) {
|
|
|
|
// Non recursive xml.getElementsByTagName() ...
|
|
|
|
var array = [];
|
|
var childNodes = xml.childNodes;
|
|
|
|
for (var i = 0, l = childNodes.length; i < l; i++) {
|
|
|
|
var child = childNodes[i];
|
|
|
|
if (child.nodeName === name) {
|
|
|
|
array.push(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
function parseFloats(text) {
|
|
|
|
if (text.length === 0) return [];
|
|
|
|
var parts = text.trim().split(/\s+/);
|
|
var array = new Array(parts.length);
|
|
|
|
for (var i = 0, l = parts.length; i < l; i++) {
|
|
|
|
array[i] = parseFloat(parts[i]);
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
function parseInts(text) {
|
|
|
|
if (text.length === 0) return [];
|
|
|
|
var parts = text.trim().split(/\s+/);
|
|
var array = new Array(parts.length);
|
|
|
|
for (var i = 0, l = parts.length; i < l; i++) {
|
|
|
|
array[i] = parseInt(parts[i]);
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
function parseId(text) {
|
|
|
|
return text.substring(1);
|
|
|
|
}
|
|
|
|
// asset
|
|
|
|
function parseAsset(xml) {
|
|
|
|
return {
|
|
unit: parseAssetUnit(getElementsByTagName(xml, 'unit')[0]),
|
|
upAxis: parseAssetUpAxis(getElementsByTagName(xml, 'up_axis')[0])
|
|
};
|
|
|
|
}
|
|
|
|
function parseAssetUnit(xml) {
|
|
|
|
return xml !== undefined ? parseFloat(xml.getAttribute('meter')) : 1;
|
|
|
|
}
|
|
|
|
function parseAssetUpAxis(xml) {
|
|
|
|
return xml !== undefined ? xml.textContent : 'Y_UP';
|
|
|
|
}
|
|
|
|
// library
|
|
|
|
function parseLibrary(xml, libraryName, nodeName, parser) {
|
|
|
|
var library = getElementsByTagName(xml, libraryName)[0];
|
|
|
|
if (library !== undefined) {
|
|
|
|
var elements = getElementsByTagName(library, nodeName);
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
|
|
parser(elements[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function buildLibrary(data, builder) {
|
|
|
|
for (var name in data) {
|
|
|
|
var object = data[name];
|
|
object.build = builder(data[name]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// get
|
|
|
|
function getBuild(data, builder) {
|
|
|
|
if (data.build !== undefined) return data.build;
|
|
|
|
data.build = builder(data);
|
|
|
|
return data.build;
|
|
|
|
}
|
|
|
|
// image
|
|
|
|
function parseImage(xml) {
|
|
|
|
var data = {
|
|
init_from: getElementsByTagName(xml, 'init_from')[0].textContent
|
|
};
|
|
|
|
library.images[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function buildImage(data) {
|
|
|
|
if (data.build !== undefined) return data.build;
|
|
|
|
return new Image();
|
|
|
|
}
|
|
|
|
function getImage(id) {
|
|
|
|
return getBuild(library.images[id], buildImage);
|
|
|
|
}
|
|
|
|
// effect
|
|
|
|
function parseEffect(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'profile_COMMON':
|
|
data.profile = parseEffectProfileCOMMON(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
library.effects[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function parseEffectProfileCOMMON(xml) {
|
|
|
|
var data = {
|
|
surfaces: {},
|
|
samplers: {}
|
|
};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'newparam':
|
|
parseEffectNewparam(child, data);
|
|
break;
|
|
|
|
case 'technique':
|
|
data.technique = parseEffectTechnique(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectNewparam(xml, data) {
|
|
|
|
var sid = xml.getAttribute('sid');
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.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(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'init_from':
|
|
data.init_from = child.textContent;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectSampler(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'source':
|
|
data.source = child.textContent;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectTechnique(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectParameters(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'emission':
|
|
case 'diffuse':
|
|
case 'specular':
|
|
case 'shininess':
|
|
case 'transparent':
|
|
case 'transparency':
|
|
data[child.nodeName] = parseEffectParameter(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectParameter(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.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(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'extra':
|
|
data = parseEffectParameterTextureExtra(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectParameterTextureExtra(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'technique':
|
|
data[child.nodeName] = parseEffectParameterTextureExtraTechnique(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseEffectParameterTextureExtraTechnique(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'repeatU':
|
|
case 'repeatV':
|
|
case 'offsetU':
|
|
case 'offsetV':
|
|
data[child.nodeName] = parseFloat(child.textContent);
|
|
break;
|
|
|
|
case 'wrapU':
|
|
case 'wrapV':
|
|
data[child.nodeName] = parseInt(child.textContent);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function buildEffect(data) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function getEffect(id) {
|
|
|
|
return getBuild(library.effects[id], buildEffect);
|
|
|
|
}
|
|
|
|
// material
|
|
|
|
function parseMaterial(xml) {
|
|
|
|
var data = {
|
|
name: xml.getAttribute('name')
|
|
};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'instance_effect':
|
|
data.url = parseId(child.getAttribute('url'));
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
library.materials[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function buildMaterial(data) {
|
|
|
|
var effect = getEffect(data.url);
|
|
var technique = effect.profile.technique;
|
|
|
|
var 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) {
|
|
|
|
var sampler = effect.profile.samplers[textureObject.id];
|
|
|
|
if (sampler !== undefined) {
|
|
|
|
var surface = effect.profile.surfaces[sampler.source];
|
|
|
|
var texture = new THREE.Texture(getImage(surface.init_from));
|
|
|
|
var extra = textureObject.extra;
|
|
|
|
if (extra !== undefined && extra.technique !== undefined) {
|
|
|
|
var technique = extra.technique;
|
|
|
|
texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
|
|
texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
|
|
|
|
texture.offset.set(technique.offsetU, technique.offsetV);
|
|
texture.repeat.set(technique.repeatU, technique.repeatV);
|
|
|
|
} else {
|
|
|
|
texture.wrapS = THREE.RepeatWrapping;
|
|
texture.wrapT = THREE.RepeatWrapping;
|
|
|
|
}
|
|
|
|
texture.needsUpdate = true;
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
console.error('ColladaLoder: Undefined sampler', textureObject.id);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var parameters = technique.parameters;
|
|
|
|
for (var key in parameters) {
|
|
|
|
var 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);
|
|
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);
|
|
break;
|
|
case 'transparent':
|
|
// if ( parameter.texture ) material.alphaMap = getTexture( parameter.texture );
|
|
material.transparent = true;
|
|
break;
|
|
case 'transparency':
|
|
if (parameter.float !== undefined) material.opacity = parameter.float;
|
|
material.transparent = true;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return material;
|
|
|
|
}
|
|
|
|
function getMaterial(id) {
|
|
|
|
return getBuild(library.materials[id], buildMaterial);
|
|
|
|
}
|
|
|
|
// camera
|
|
|
|
function parseCamera(xml) {
|
|
|
|
var data = {
|
|
name: xml.getAttribute('name')
|
|
};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'optics':
|
|
data.optics = parseCameraOptics(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
library.cameras[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function parseCameraOptics(xml) {
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'technique_common':
|
|
return parseCameraTechnique(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
function parseCameraTechnique(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'perspective':
|
|
case 'orthographic':
|
|
|
|
data.technique = child.nodeName;
|
|
data.parameters = parseCameraParameters(child);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseCameraParameters(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.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) {
|
|
|
|
var 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':
|
|
camera = new THREE.OrthographicCamera( /* TODO */);
|
|
break;
|
|
|
|
default:
|
|
camera = new THREE.PerspectiveCamera();
|
|
break;
|
|
|
|
}
|
|
|
|
camera.name = data.name;
|
|
|
|
return camera;
|
|
|
|
}
|
|
|
|
function getCamera(id) {
|
|
|
|
return getBuild(library.cameras[id], buildCamera);
|
|
|
|
}
|
|
|
|
// light
|
|
|
|
function parseLight(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'technique_common':
|
|
data = parseLightTechnique(child);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
library.lights[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function parseLightTechnique(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.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(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'color':
|
|
var array = parseFloats(child.textContent);
|
|
data.color = new THREE.Color().fromArray(array);
|
|
break;
|
|
|
|
case 'falloff_angle':
|
|
data.falloffAngle = parseFloat(child.textContent);
|
|
break;
|
|
|
|
case 'quadratic_attenuation':
|
|
var f = parseFloat(child.textContent);
|
|
data.distance = f ? Math.sqrt(1 / f) : 0;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function buildLight(data) {
|
|
|
|
var 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) {
|
|
|
|
return getBuild(library.lights[id], buildLight);
|
|
|
|
}
|
|
|
|
// geometry
|
|
|
|
function parseGeometry(xml) {
|
|
|
|
var data = {
|
|
name: xml.getAttribute('name'),
|
|
sources: {},
|
|
vertices: {},
|
|
primitives: []
|
|
};
|
|
|
|
var mesh = getElementsByTagName(xml, 'mesh')[0];
|
|
|
|
for (var i = 0; i < mesh.childNodes.length; i++) {
|
|
|
|
var child = mesh.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
var id = child.getAttribute('id');
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'source':
|
|
data.sources[id] = parseGeometrySource(child);
|
|
break;
|
|
|
|
case 'vertices':
|
|
// data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ];
|
|
data.vertices = parseGeometryVertices(child);
|
|
break;
|
|
|
|
case 'polygons':
|
|
console.warn('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[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function parseGeometrySource(xml) {
|
|
|
|
var data = {
|
|
array: [],
|
|
stride: 3
|
|
};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'float_array':
|
|
data.array = parseFloats(child.textContent);
|
|
break;
|
|
|
|
case 'technique_common':
|
|
var accessor = getElementsByTagName(child, 'accessor')[0];
|
|
|
|
if (accessor !== undefined) {
|
|
|
|
data.stride = parseInt(accessor.getAttribute('stride'));
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.log(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseGeometryVertices(xml) {
|
|
|
|
var data = {};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
data[child.getAttribute('semantic')] = parseId(child.getAttribute('source'));
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseGeometryPrimitive(xml) {
|
|
|
|
var primitive = {
|
|
type: xml.nodeName,
|
|
material: xml.getAttribute('material'),
|
|
inputs: {},
|
|
stride: 0
|
|
};
|
|
|
|
for (var i = 0, l = xml.childNodes.length; i < l; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'input':
|
|
var id = parseId(child.getAttribute('source'));
|
|
var semantic = child.getAttribute('semantic');
|
|
var offset = parseInt(child.getAttribute('offset'));
|
|
primitive.inputs[semantic] = { id: id, offset: offset };
|
|
primitive.stride = Math.max(primitive.stride, offset + 1);
|
|
break;
|
|
|
|
case 'vcount':
|
|
primitive.vcount = parseInts(child.textContent);
|
|
break;
|
|
|
|
case 'p':
|
|
primitive.p = parseInts(child.textContent);
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return primitive;
|
|
|
|
}
|
|
|
|
var DEFAULT_LINEMATERIAL = new THREE.LineBasicMaterial();
|
|
var DEFAULT_MESHMATERIAL = new THREE.MeshPhongMaterial();
|
|
|
|
function buildGeometry(data) {
|
|
|
|
var group = {};
|
|
|
|
var sources = data.sources;
|
|
var vertices = data.vertices;
|
|
var primitives = data.primitives;
|
|
|
|
if (primitives.length === 0) return group;
|
|
|
|
for (var p = 0; p < primitives.length; p++) {
|
|
|
|
var primitive = primitives[p];
|
|
var inputs = primitive.inputs;
|
|
|
|
var geometry = new THREE.BufferGeometry();
|
|
|
|
if (data.name) geometry.name = data.name;
|
|
|
|
for (var name in inputs) {
|
|
|
|
var input = inputs[name];
|
|
|
|
switch (name) {
|
|
|
|
case 'VERTEX':
|
|
for (var key in vertices) {
|
|
|
|
geometry.addAttribute(key.toLowerCase(), buildGeometryAttribute(primitive, sources[vertices[key]], input.offset));
|
|
|
|
}
|
|
break;
|
|
|
|
case 'NORMAL':
|
|
geometry.addAttribute('normal', buildGeometryAttribute(primitive, sources[input.id], input.offset));
|
|
break;
|
|
|
|
case 'COLOR':
|
|
geometry.addAttribute('color', buildGeometryAttribute(primitive, sources[input.id], input.offset));
|
|
break;
|
|
|
|
case 'TEXCOORD':
|
|
geometry.addAttribute('uv', buildGeometryAttribute(primitive, sources[input.id], input.offset));
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var object;
|
|
|
|
switch (primitive.type) {
|
|
|
|
case 'lines':
|
|
object = new THREE.LineSegments(geometry, DEFAULT_LINEMATERIAL);
|
|
break;
|
|
|
|
case 'linestrips':
|
|
object = new THREE.Line(geometry, DEFAULT_LINEMATERIAL);
|
|
break;
|
|
|
|
case 'triangles':
|
|
case 'polylist':
|
|
object = new THREE.Mesh(geometry, DEFAULT_MESHMATERIAL);
|
|
break;
|
|
|
|
}
|
|
|
|
group[primitive.material] = object;
|
|
|
|
}
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
function buildGeometryAttribute(primitive, source, offset) {
|
|
|
|
var indices = primitive.p;
|
|
var stride = primitive.stride;
|
|
var vcount = primitive.vcount;
|
|
|
|
function pushVector(i) {
|
|
|
|
var index = indices[i + offset] * sourceStride;
|
|
var length = index + sourceStride;
|
|
|
|
for (; index < length; index++) {
|
|
|
|
array.push(sourceArray[index]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var maxcount = 0;
|
|
|
|
var sourceArray = source.array;
|
|
var sourceStride = source.stride;
|
|
|
|
var array = [];
|
|
|
|
if (primitive.vcount !== undefined) {
|
|
|
|
var index = 0;
|
|
|
|
for (var i = 0, l = vcount.length; i < l; i++) {
|
|
|
|
var count = vcount[i];
|
|
|
|
if (count === 4) {
|
|
|
|
var a = index + stride * 0;
|
|
var b = index + stride * 1;
|
|
var c = index + stride * 2;
|
|
var d = index + stride * 3;
|
|
|
|
pushVector(a); pushVector(b); pushVector(d);
|
|
pushVector(b); pushVector(c); pushVector(d);
|
|
|
|
} else if (count === 3) {
|
|
|
|
var a = index + stride * 0;
|
|
var b = index + stride * 1;
|
|
var c = index + stride * 2;
|
|
|
|
pushVector(a); pushVector(b); pushVector(c);
|
|
|
|
} else {
|
|
|
|
maxcount = Math.max(maxcount, count);
|
|
|
|
}
|
|
|
|
index += stride * count;
|
|
|
|
}
|
|
|
|
if (maxcount > 0) {
|
|
|
|
console.log('ColladaLoader: Geometry has faces with more than 4 vertices.');
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (var i = 0, l = indices.length; i < l; i += stride) {
|
|
|
|
pushVector(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new THREE.Float32BufferAttribute(array, sourceStride);
|
|
|
|
}
|
|
|
|
function getGeometry(id) {
|
|
|
|
return getBuild(library.geometries[id], buildGeometry);
|
|
|
|
}
|
|
|
|
// nodes
|
|
|
|
var matrix = new THREE.Matrix4();
|
|
var vector = new THREE.Vector3();
|
|
|
|
function parseNode(xml) {
|
|
|
|
var data = {
|
|
name: xml.getAttribute('name'),
|
|
matrix: new THREE.Matrix4(),
|
|
nodes: [],
|
|
instanceCameras: [],
|
|
instanceLights: [],
|
|
instanceGeometries: [],
|
|
instanceNodes: []
|
|
};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeType !== 1) continue;
|
|
|
|
switch (child.nodeName) {
|
|
|
|
case 'node':
|
|
|
|
if (child.hasAttribute('id')) {
|
|
|
|
data.nodes.push(child.getAttribute('id'));
|
|
parseNode(child);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'instance_camera':
|
|
data.instanceCameras.push(parseId(child.getAttribute('url')));
|
|
break;
|
|
|
|
case 'instance_light':
|
|
data.instanceLights.push(parseId(child.getAttribute('url')));
|
|
break;
|
|
|
|
case 'instance_geometry':
|
|
data.instanceGeometries.push(parseNodeInstanceGeometry(child));
|
|
break;
|
|
|
|
case 'instance_node':
|
|
data.instanceNodes.push(parseId(child.getAttribute('url')));
|
|
break;
|
|
|
|
case 'matrix':
|
|
var array = parseFloats(child.textContent);
|
|
data.matrix.multiply(matrix.fromArray(array).transpose()); // .transpose() when Z_UP?
|
|
break;
|
|
|
|
case 'translate':
|
|
var array = parseFloats(child.textContent);
|
|
vector.fromArray(array);
|
|
data.matrix.multiply(matrix.makeTranslation(vector.x, vector.y, vector.z));
|
|
break;
|
|
|
|
case 'rotate':
|
|
var array = parseFloats(child.textContent);
|
|
var angle = THREE.Math.degToRad(array[3]);
|
|
data.matrix.multiply(matrix.makeRotationAxis(vector.fromArray(array), angle));
|
|
break;
|
|
|
|
case 'scale':
|
|
var array = parseFloats(child.textContent);
|
|
data.matrix.scale(vector.fromArray(array));
|
|
break;
|
|
|
|
case 'extra':
|
|
break;
|
|
|
|
default:
|
|
console.log(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (xml.hasAttribute('id')) {
|
|
|
|
library.nodes[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function parseNodeInstanceGeometry(xml) {
|
|
|
|
var data = {
|
|
id: parseId(xml.getAttribute('url')),
|
|
materials: {}
|
|
};
|
|
|
|
for (var i = 0; i < xml.childNodes.length; i++) {
|
|
|
|
var child = xml.childNodes[i];
|
|
|
|
if (child.nodeName === 'bind_material') {
|
|
|
|
var instances = child.getElementsByTagName('instance_material');
|
|
|
|
for (var j = 0; j < instances.length; j++) {
|
|
|
|
var instance = instances[j];
|
|
var symbol = instance.getAttribute('symbol');
|
|
var target = instance.getAttribute('target');
|
|
|
|
data.materials[symbol] = parseId(target);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function buildNode(data) {
|
|
|
|
var objects = [];
|
|
|
|
var matrix = data.matrix;
|
|
var nodes = data.nodes;
|
|
var instanceCameras = data.instanceCameras;
|
|
var instanceLights = data.instanceLights;
|
|
var instanceGeometries = data.instanceGeometries;
|
|
var instanceNodes = data.instanceNodes;
|
|
|
|
for (var i = 0, l = nodes.length; i < l; i++) {
|
|
|
|
objects.push(getNode(nodes[i]).clone());
|
|
|
|
}
|
|
|
|
for (var i = 0, l = instanceCameras.length; i < l; i++) {
|
|
|
|
objects.push(getCamera(instanceCameras[i]).clone());
|
|
|
|
}
|
|
|
|
for (var i = 0, l = instanceLights.length; i < l; i++) {
|
|
|
|
objects.push(getLight(instanceLights[i]).clone());
|
|
|
|
}
|
|
|
|
for (var i = 0, l = instanceGeometries.length; i < l; i++) {
|
|
|
|
var instance = instanceGeometries[i];
|
|
var geometries = getGeometry(instance.id);
|
|
|
|
for (var key in geometries) {
|
|
|
|
var object = geometries[key].clone();
|
|
|
|
if (instance.materials[key] !== undefined) {
|
|
|
|
object.material = getMaterial(instance.materials[key]);
|
|
|
|
}
|
|
|
|
objects.push(object);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var i = 0, l = instanceNodes.length; i < l; i++) {
|
|
|
|
objects.push(getNode(instanceNodes[i]).clone());
|
|
|
|
}
|
|
|
|
var object;
|
|
|
|
if (nodes.length === 0 && objects.length === 1) {
|
|
|
|
object = objects[0];
|
|
|
|
} else {
|
|
|
|
object = new THREE.Group();
|
|
|
|
for (var i = 0; i < objects.length; i++) {
|
|
|
|
object.add(objects[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
object.name = data.name;
|
|
matrix.decompose(object.position, object.quaternion, object.scale);
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
function getNode(id) {
|
|
|
|
return getBuild(library.nodes[id], buildNode);
|
|
|
|
}
|
|
|
|
// visual scenes
|
|
|
|
function parseVisualScene(xml) {
|
|
|
|
var data = {
|
|
name: xml.getAttribute('name'),
|
|
children: []
|
|
};
|
|
|
|
var elements = getElementsByTagName(xml, 'node');
|
|
|
|
for (var i = 0; i < elements.length; i++) {
|
|
|
|
data.children.push(parseNode(elements[i]));
|
|
|
|
}
|
|
|
|
library.visualScenes[xml.getAttribute('id')] = data;
|
|
|
|
}
|
|
|
|
function buildVisualScene(data) {
|
|
|
|
var group = new THREE.Group();
|
|
group.name = data.name;
|
|
|
|
var children = data.children;
|
|
|
|
for (var i = 0; i < children.length; i++) {
|
|
|
|
group.add(buildNode(children[i]));
|
|
|
|
}
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
function getVisualScene(id) {
|
|
|
|
return getBuild(library.visualScenes[id], buildVisualScene);
|
|
|
|
}
|
|
|
|
// scenes
|
|
|
|
function parseScene(xml) {
|
|
|
|
var instance = getElementsByTagName(xml, 'instance_visual_scene')[0];
|
|
return getVisualScene(parseId(instance.getAttribute('url')));
|
|
|
|
}
|
|
|
|
console.time('ColladaLoader');
|
|
|
|
if (text.length === 0) {
|
|
|
|
return { scene: new THREE.Scene() };
|
|
|
|
}
|
|
|
|
console.time('ColladaLoader: DOMParser');
|
|
|
|
var xml = new DOMParser().parseFromString(text, 'application/xml');
|
|
|
|
console.timeEnd('ColladaLoader: DOMParser');
|
|
|
|
var collada = getElementsByTagName(xml, 'COLLADA')[0];
|
|
|
|
// metadata
|
|
|
|
var version = collada.getAttribute('version');
|
|
console.log('ColladaLoader: File version', version);
|
|
|
|
var asset = parseAsset(getElementsByTagName(collada, 'asset')[0]);
|
|
|
|
//
|
|
|
|
var library = {
|
|
images: {},
|
|
effects: {},
|
|
materials: {},
|
|
cameras: {},
|
|
lights: {},
|
|
geometries: {},
|
|
nodes: {},
|
|
visualScenes: {}
|
|
};
|
|
|
|
console.time('ColladaLoader: Parse');
|
|
|
|
parseLibrary(collada, 'library_images', 'image', parseImage);
|
|
parseLibrary(collada, 'library_effects', 'effect', parseEffect);
|
|
parseLibrary(collada, 'library_materials', 'material', parseMaterial);
|
|
parseLibrary(collada, 'library_cameras', 'camera', parseCamera);
|
|
parseLibrary(collada, 'library_lights', 'light', parseLight);
|
|
parseLibrary(collada, 'library_geometries', 'geometry', parseGeometry);
|
|
parseLibrary(collada, 'library_nodes', 'node', parseNode);
|
|
parseLibrary(collada, 'library_visual_scenes', 'visual_scene', parseVisualScene);
|
|
|
|
console.timeEnd('ColladaLoader: Parse');
|
|
|
|
console.time('ColladaLoader: Build');
|
|
|
|
buildLibrary(library.images, buildImage);
|
|
buildLibrary(library.effects, buildEffect);
|
|
buildLibrary(library.materials, buildMaterial);
|
|
buildLibrary(library.cameras, buildCamera);
|
|
buildLibrary(library.lights, buildLight);
|
|
buildLibrary(library.geometries, buildGeometry);
|
|
// buildLibrary( library.nodes, buildNode );
|
|
buildLibrary(library.visualScenes, buildVisualScene);
|
|
|
|
console.timeEnd('ColladaLoader: Build');
|
|
|
|
// console.log( library );
|
|
|
|
var scene = parseScene(getElementsByTagName(collada, 'scene')[0]);
|
|
|
|
if (asset.upAxis === 'Z_UP') {
|
|
|
|
scene.rotation.x = - Math.PI / 2;
|
|
|
|
}
|
|
|
|
scene.scale.multiplyScalar(asset.unit);
|
|
|
|
console.timeEnd('ColladaLoader');
|
|
|
|
// console.log( scene );
|
|
|
|
return {
|
|
animations: [],
|
|
kinematics: { joints: [] },
|
|
library: library,
|
|
scene: scene
|
|
};
|
|
|
|
}
|
|
|
|
}; |