mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
1595 lines
29 KiB
JavaScript
1595 lines
29 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
|
|
};
|
|
|
|
}
|
|
|
|
};
|