1963 lines
36 KiB
JavaScript
1963 lines
36 KiB
JavaScript
import {
|
|
Color,
|
|
ColorManagement,
|
|
MathUtils,
|
|
Matrix4,
|
|
Vector3,
|
|
SRGBColorSpace
|
|
} from 'three';
|
|
|
|
/**
|
|
* Utility functions for parsing
|
|
*/
|
|
|
|
function getElementsByTagName( xml, name ) {
|
|
|
|
// Non recursive xml.getElementsByTagName() ...
|
|
|
|
const array = [];
|
|
const childNodes = xml.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( text ) {
|
|
|
|
if ( text.length === 0 ) return [];
|
|
|
|
return text.trim().split( /\s+/ );
|
|
|
|
}
|
|
|
|
function parseFloats( text ) {
|
|
|
|
if ( text.length === 0 ) return [];
|
|
|
|
return text.trim().split( /\s+/ ).map( parseFloat );
|
|
|
|
}
|
|
|
|
function parseInts( text ) {
|
|
|
|
if ( text.length === 0 ) return [];
|
|
|
|
return text.trim().split( /\s+/ ).map( s => parseInt( s ) );
|
|
|
|
}
|
|
|
|
function parseId( text ) {
|
|
|
|
return text.substring( 1 );
|
|
|
|
}
|
|
|
|
/**
|
|
* ColladaParser handles XML parsing and converts Collada XML to intermediate data structures.
|
|
*/
|
|
class ColladaParser {
|
|
|
|
constructor() {
|
|
|
|
this.count = 0;
|
|
|
|
}
|
|
|
|
generateId() {
|
|
|
|
return 'three_default_' + ( this.count ++ );
|
|
|
|
}
|
|
|
|
parse( text ) {
|
|
|
|
if ( text.length === 0 ) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const xml = new DOMParser().parseFromString( text, 'application/xml' );
|
|
|
|
const collada = getElementsByTagName( xml, 'COLLADA' )[ 0 ];
|
|
|
|
const parserError = xml.getElementsByTagName( 'parsererror' )[ 0 ];
|
|
if ( parserError !== undefined ) {
|
|
|
|
// Chrome will return parser error with a div in it
|
|
|
|
const errorElement = getElementsByTagName( parserError, 'div' )[ 0 ];
|
|
let errorText;
|
|
|
|
if ( errorElement ) {
|
|
|
|
errorText = errorElement.textContent;
|
|
|
|
} else {
|
|
|
|
errorText = this.parserErrorToText( parserError );
|
|
|
|
}
|
|
|
|
console.error( 'THREE.ColladaLoader: Failed to parse collada file.\n', errorText );
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
// metadata
|
|
|
|
const version = collada.getAttribute( 'version' );
|
|
console.debug( 'THREE.ColladaLoader: File version', version );
|
|
|
|
const asset = this.parseAsset( getElementsByTagName( collada, 'asset' )[ 0 ] );
|
|
|
|
//
|
|
|
|
const library = {
|
|
animations: {},
|
|
clips: {},
|
|
controllers: {},
|
|
images: {},
|
|
effects: {},
|
|
materials: {},
|
|
cameras: {},
|
|
lights: {},
|
|
geometries: {},
|
|
nodes: {},
|
|
visualScenes: {},
|
|
kinematicsModels: {},
|
|
physicsModels: {},
|
|
kinematicsScenes: {}
|
|
};
|
|
|
|
this.library = library;
|
|
this.collada = collada;
|
|
|
|
this.parseLibrary( collada, 'library_animations', 'animation', this.parseAnimation.bind( this ) );
|
|
this.parseLibrary( collada, 'library_animation_clips', 'animation_clip', this.parseAnimationClip.bind( this ) );
|
|
this.parseLibrary( collada, 'library_controllers', 'controller', this.parseController.bind( this ) );
|
|
this.parseLibrary( collada, 'library_images', 'image', this.parseImage.bind( this ) );
|
|
this.parseLibrary( collada, 'library_effects', 'effect', this.parseEffect.bind( this ) );
|
|
this.parseLibrary( collada, 'library_materials', 'material', this.parseMaterial.bind( this ) );
|
|
this.parseLibrary( collada, 'library_cameras', 'camera', this.parseCamera.bind( this ) );
|
|
this.parseLibrary( collada, 'library_lights', 'light', this.parseLight.bind( this ) );
|
|
this.parseLibrary( collada, 'library_geometries', 'geometry', this.parseGeometry.bind( this ) );
|
|
this.parseLibrary( collada, 'library_nodes', 'node', this.parseNode.bind( this ) );
|
|
this.parseLibrary( collada, 'library_visual_scenes', 'visual_scene', this.parseVisualScene.bind( this ) );
|
|
this.parseLibrary( collada, 'library_kinematics_models', 'kinematics_model', this.parseKinematicsModel.bind( this ) );
|
|
this.parseLibrary( collada, 'library_physics_models', 'physics_model', this.parsePhysicsModel.bind( this ) );
|
|
this.parseLibrary( collada, 'scene', 'instance_kinematics_scene', this.parseKinematicsScene.bind( this ) );
|
|
|
|
return {
|
|
library: library,
|
|
asset: asset,
|
|
collada: collada
|
|
};
|
|
|
|
}
|
|
|
|
// convert the parser error element into text with each child elements text
|
|
// separated by new lines.
|
|
|
|
parserErrorToText( parserError ) {
|
|
|
|
const parts = [];
|
|
const stack = [ parserError ];
|
|
|
|
while ( stack.length ) {
|
|
|
|
const node = stack.shift();
|
|
|
|
if ( node.nodeType === Node.TEXT_NODE ) {
|
|
|
|
parts.push( node.textContent );
|
|
|
|
} else {
|
|
|
|
parts.push( '\n' );
|
|
stack.push( ...node.childNodes );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parts.join( '' ).trim();
|
|
|
|
}
|
|
|
|
// asset
|
|
|
|
parseAsset( xml ) {
|
|
|
|
return {
|
|
unit: this.parseAssetUnit( getElementsByTagName( xml, 'unit' )[ 0 ] ),
|
|
upAxis: this.parseAssetUpAxis( getElementsByTagName( xml, 'up_axis' )[ 0 ] )
|
|
};
|
|
|
|
}
|
|
|
|
parseAssetUnit( xml ) {
|
|
|
|
if ( ( xml !== undefined ) && ( xml.hasAttribute( 'meter' ) === true ) ) {
|
|
|
|
return parseFloat( xml.getAttribute( 'meter' ) );
|
|
|
|
} else {
|
|
|
|
return 1; // default 1 meter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseAssetUpAxis( xml ) {
|
|
|
|
return xml !== undefined ? xml.textContent : 'Y_UP';
|
|
|
|
}
|
|
|
|
// library
|
|
|
|
parseLibrary( xml, libraryName, nodeName, parser ) {
|
|
|
|
const library = getElementsByTagName( xml, libraryName )[ 0 ];
|
|
|
|
if ( library !== undefined ) {
|
|
|
|
const elements = getElementsByTagName( library, nodeName );
|
|
|
|
for ( let i = 0; i < elements.length; i ++ ) {
|
|
|
|
parser( elements[ i ] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// animation
|
|
|
|
parseAnimation( xml ) {
|
|
|
|
const data = {
|
|
sources: {},
|
|
samplers: {},
|
|
channels: {}
|
|
};
|
|
|
|
let hasChildren = false;
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
let id;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'source':
|
|
id = child.getAttribute( 'id' );
|
|
data.sources[ id ] = this.parseSource( child );
|
|
break;
|
|
|
|
case 'sampler':
|
|
id = child.getAttribute( 'id' );
|
|
data.samplers[ id ] = this.parseAnimationSampler( child );
|
|
break;
|
|
|
|
case 'channel':
|
|
id = child.getAttribute( 'target' );
|
|
data.channels[ id ] = this.parseAnimationChannel( child );
|
|
break;
|
|
|
|
case 'animation':
|
|
// hierarchy of related animations
|
|
this.parseAnimation( child );
|
|
hasChildren = true;
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( hasChildren === false ) {
|
|
|
|
// since 'id' attributes can be optional, it's necessary to generate a UUID for unique assignment
|
|
|
|
this.library.animations[ xml.getAttribute( 'id' ) || MathUtils.generateUUID() ] = data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseAnimationSampler( xml ) {
|
|
|
|
const data = {
|
|
inputs: {},
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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;
|
|
|
|
}
|
|
|
|
parseAnimationChannel( xml ) {
|
|
|
|
const data = {};
|
|
|
|
const target = xml.getAttribute( 'target' );
|
|
|
|
// parsing SID Addressing Syntax
|
|
|
|
let parts = target.split( '/' );
|
|
|
|
const id = parts.shift();
|
|
let sid = parts.shift();
|
|
|
|
// check selection syntax
|
|
|
|
const arraySyntax = ( sid.indexOf( '(' ) !== - 1 );
|
|
const memberSyntax = ( sid.indexOf( '.' ) !== - 1 );
|
|
|
|
if ( memberSyntax ) {
|
|
|
|
// member selection access
|
|
|
|
parts = sid.split( '.' );
|
|
sid = parts.shift();
|
|
data.member = parts.shift();
|
|
|
|
} else if ( arraySyntax ) {
|
|
|
|
// array-access syntax. can be used to express fields in one-dimensional vectors or two-dimensional matrices.
|
|
|
|
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( xml.getAttribute( 'source' ) );
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// animation clips
|
|
|
|
parseAnimationClip( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'id' ) || 'default',
|
|
start: parseFloat( xml.getAttribute( 'start' ) || 0 ),
|
|
end: parseFloat( xml.getAttribute( 'end' ) || 0 ),
|
|
animations: []
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'instance_animation':
|
|
data.animations.push( parseId( child.getAttribute( 'url' ) ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.clips[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
// controller
|
|
|
|
parseController( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'skin':
|
|
// there is exactly one skin per controller
|
|
data.id = parseId( child.getAttribute( 'source' ) );
|
|
data.skin = this.parseSkin( child );
|
|
break;
|
|
|
|
case 'morph':
|
|
data.id = parseId( child.getAttribute( 'source' ) );
|
|
console.warn( 'THREE.ColladaLoader: Morph target animation not supported yet.' );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.controllers[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseSkin( xml ) {
|
|
|
|
const data = {
|
|
sources: {}
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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 ] = this.parseSource( child );
|
|
break;
|
|
|
|
case 'joints':
|
|
data.joints = this.parseJoints( child );
|
|
break;
|
|
|
|
case 'vertex_weights':
|
|
data.vertexWeights = this.parseVertexWeights( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseJoints( xml ) {
|
|
|
|
const data = {
|
|
inputs: {}
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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;
|
|
|
|
}
|
|
|
|
parseVertexWeights( xml ) {
|
|
|
|
const data = {
|
|
inputs: {}
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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: id, offset: offset };
|
|
break;
|
|
|
|
case 'vcount':
|
|
data.vcount = parseInts( child.textContent );
|
|
break;
|
|
|
|
case 'v':
|
|
data.v = parseInts( child.textContent );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// image
|
|
|
|
parseImage( xml ) {
|
|
|
|
const data = {
|
|
init_from: getElementsByTagName( xml, 'init_from' )[ 0 ].textContent
|
|
};
|
|
|
|
this.library.images[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
// effect
|
|
|
|
parseEffect( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'profile_COMMON':
|
|
data.profile = this.parseEffectProfileCOMMON( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.effects[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseEffectProfileCOMMON( xml ) {
|
|
|
|
const data = {
|
|
surfaces: {},
|
|
samplers: {}
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'newparam':
|
|
this.parseEffectNewparam( child, data );
|
|
break;
|
|
|
|
case 'technique':
|
|
data.technique = this.parseEffectTechnique( child );
|
|
break;
|
|
|
|
case 'extra':
|
|
data.extra = this.parseEffectExtra( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectNewparam( xml, data ) {
|
|
|
|
const sid = xml.getAttribute( 'sid' );
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'surface':
|
|
data.surfaces[ sid ] = this.parseEffectSurface( child );
|
|
break;
|
|
|
|
case 'sampler2D':
|
|
data.samplers[ sid ] = this.parseEffectSampler( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseEffectSurface( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'init_from':
|
|
data.init_from = child.textContent;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectSampler( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'source':
|
|
data.source = child.textContent;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectTechnique( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const 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 = this.parseEffectParameters( child );
|
|
break;
|
|
|
|
case 'extra':
|
|
data.extra = this.parseEffectExtra( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectParameters( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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 ] = this.parseEffectParameter( child );
|
|
break;
|
|
case 'transparent':
|
|
data[ child.nodeName ] = {
|
|
opaque: child.hasAttribute( 'opaque' ) ? child.getAttribute( 'opaque' ) : 'A_ONE',
|
|
data: this.parseEffectParameter( child )
|
|
};
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectParameter( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const 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: this.parseEffectParameterTexture( child ) };
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectParameterTexture( xml ) {
|
|
|
|
const data = {
|
|
technique: {}
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'extra':
|
|
this.parseEffectParameterTextureExtra( child, data );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectParameterTextureExtra( xml, data ) {
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique':
|
|
this.parseEffectParameterTextureExtraTechnique( child, data );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseEffectParameterTextureExtraTechnique( xml, data ) {
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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':
|
|
|
|
// some files have values for wrapU/wrapV which become NaN via parseInt
|
|
|
|
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 ] = this.parseEffectExtraTechniqueBump( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseEffectExtra( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique':
|
|
data.technique = this.parseEffectExtraTechnique( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectExtraTechnique( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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 ] = this.parseEffectExtraTechniqueBump( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseEffectExtraTechniqueBump( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'texture':
|
|
data[ child.nodeName ] = { id: child.getAttribute( 'texture' ), texcoord: child.getAttribute( 'texcoord' ), extra: this.parseEffectParameterTexture( child ) };
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// material
|
|
|
|
parseMaterial( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' )
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'instance_effect':
|
|
data.url = parseId( child.getAttribute( 'url' ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.materials[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
// camera
|
|
|
|
parseCamera( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' )
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'optics':
|
|
data.optics = this.parseCameraOptics( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.cameras[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseCameraOptics( xml ) {
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique_common':
|
|
return this.parseCameraTechnique( child );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
parseCameraTechnique( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'perspective':
|
|
case 'orthographic':
|
|
|
|
data.technique = child.nodeName;
|
|
data.parameters = this.parseCameraParameters( child );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseCameraParameters( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const 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;
|
|
|
|
}
|
|
|
|
// light
|
|
|
|
parseLight( xml ) {
|
|
|
|
let data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique_common':
|
|
data = this.parseLightTechnique( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.lights[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseLightTechnique( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const 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 = this.parseLightParameters( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseLightParameters( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'color':
|
|
const array = parseFloats( child.textContent );
|
|
data.color = new Color().fromArray( array );
|
|
ColorManagement.colorSpaceToWorking( data.color, SRGBColorSpace );
|
|
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;
|
|
|
|
}
|
|
|
|
// geometry
|
|
|
|
parseGeometry( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' ),
|
|
sources: {},
|
|
vertices: {},
|
|
primitives: []
|
|
};
|
|
|
|
const mesh = getElementsByTagName( xml, 'mesh' )[ 0 ];
|
|
|
|
// the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep
|
|
if ( mesh === undefined ) 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 ] = this.parseSource( child );
|
|
break;
|
|
|
|
case 'vertices':
|
|
// data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ];
|
|
data.vertices = this.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( this.parseGeometryPrimitive( child ) );
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.geometries[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseSource( xml ) {
|
|
|
|
const data = {
|
|
array: [],
|
|
stride: 3
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.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 !== undefined ) {
|
|
|
|
data.stride = parseInt( accessor.getAttribute( 'stride' ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseGeometryVertices( xml ) {
|
|
|
|
const data = {};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
data[ child.getAttribute( 'semantic' ) ] = parseId( child.getAttribute( 'source' ) );
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseGeometryPrimitive( xml ) {
|
|
|
|
const primitive = {
|
|
type: xml.nodeName,
|
|
material: xml.getAttribute( 'material' ),
|
|
count: parseInt( xml.getAttribute( 'count' ) ),
|
|
inputs: {},
|
|
stride: 0,
|
|
hasUV: false
|
|
};
|
|
|
|
for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
|
|
|
|
const child = xml.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: id, offset: 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;
|
|
|
|
}
|
|
|
|
// kinematics
|
|
|
|
parseKinematicsModel( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' ) || '',
|
|
joints: {},
|
|
links: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique_common':
|
|
this.parseKinematicsTechniqueCommon( child, data );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.kinematicsModels[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parseKinematicsTechniqueCommon( xml, data ) {
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'joint':
|
|
data.joints[ child.getAttribute( 'sid' ) ] = this.parseKinematicsJoint( child );
|
|
break;
|
|
|
|
case 'link':
|
|
data.links.push( this.parseKinematicsLink( child ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseKinematicsJoint( xml ) {
|
|
|
|
let data;
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'prismatic':
|
|
case 'revolute':
|
|
data = this.parseKinematicsJointParameter( child );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseKinematicsJointParameter( xml ) {
|
|
|
|
const data = {
|
|
sid: xml.getAttribute( 'sid' ),
|
|
name: xml.getAttribute( 'name' ) || '',
|
|
axis: new Vector3(),
|
|
limits: {
|
|
min: 0,
|
|
max: 0
|
|
},
|
|
type: xml.nodeName,
|
|
static: false,
|
|
zeroPosition: 0,
|
|
middlePosition: 0
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'axis':
|
|
const array = parseFloats( child.textContent );
|
|
data.axis.fromArray( array );
|
|
break;
|
|
case 'limits':
|
|
const max = child.getElementsByTagName( 'max' )[ 0 ];
|
|
const min = child.getElementsByTagName( 'min' )[ 0 ];
|
|
|
|
data.limits.max = parseFloat( max.textContent );
|
|
data.limits.min = parseFloat( min.textContent );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if min is equal to or greater than max, consider the joint static
|
|
|
|
if ( data.limits.min >= data.limits.max ) {
|
|
|
|
data.static = true;
|
|
|
|
}
|
|
|
|
// calculate middle position
|
|
|
|
data.middlePosition = ( data.limits.min + data.limits.max ) / 2.0;
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseKinematicsLink( xml ) {
|
|
|
|
const data = {
|
|
sid: xml.getAttribute( 'sid' ),
|
|
name: xml.getAttribute( 'name' ) || '',
|
|
attachments: [],
|
|
transforms: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'attachment_full':
|
|
data.attachments.push( this.parseKinematicsAttachment( child ) );
|
|
break;
|
|
|
|
case 'matrix':
|
|
case 'translate':
|
|
case 'rotate':
|
|
data.transforms.push( this.parseKinematicsTransform( child ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseKinematicsAttachment( xml ) {
|
|
|
|
const data = {
|
|
joint: xml.getAttribute( 'joint' ).split( '/' ).pop(),
|
|
transforms: [],
|
|
links: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'link':
|
|
data.links.push( this.parseKinematicsLink( child ) );
|
|
break;
|
|
|
|
case 'matrix':
|
|
case 'translate':
|
|
case 'rotate':
|
|
data.transforms.push( this.parseKinematicsTransform( child ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseKinematicsTransform( xml ) {
|
|
|
|
const data = {
|
|
type: xml.nodeName
|
|
};
|
|
|
|
const array = parseFloats( xml.textContent );
|
|
|
|
switch ( data.type ) {
|
|
|
|
case 'matrix':
|
|
data.obj = new Matrix4();
|
|
data.obj.fromArray( array ).transpose();
|
|
break;
|
|
|
|
case 'translate':
|
|
data.obj = new Vector3();
|
|
data.obj.fromArray( array );
|
|
break;
|
|
|
|
case 'rotate':
|
|
data.obj = new Vector3();
|
|
data.obj.fromArray( array );
|
|
data.angle = MathUtils.degToRad( array[ 3 ] );
|
|
break;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// physics
|
|
|
|
parsePhysicsModel( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' ) || '',
|
|
rigidBodies: {}
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'rigid_body':
|
|
data.rigidBodies[ child.getAttribute( 'name' ) ] = {};
|
|
this.parsePhysicsRigidBody( child, data.rigidBodies[ child.getAttribute( 'name' ) ] );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.physicsModels[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
parsePhysicsRigidBody( xml, data ) {
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'technique_common':
|
|
this.parsePhysicsTechniqueCommon( child, data );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parsePhysicsTechniqueCommon( xml, data ) {
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'inertia':
|
|
data.inertia = parseFloats( child.textContent );
|
|
break;
|
|
|
|
case 'mass':
|
|
data.mass = parseFloats( child.textContent )[ 0 ];
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// scene
|
|
|
|
parseKinematicsScene( xml ) {
|
|
|
|
const data = {
|
|
bindJointAxis: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'bind_joint_axis':
|
|
data.bindJointAxis.push( this.parseKinematicsBindJointAxis( child ) );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.library.kinematicsScenes[ parseId( xml.getAttribute( 'url' ) ) ] = data;
|
|
|
|
}
|
|
|
|
parseKinematicsBindJointAxis( xml ) {
|
|
|
|
const data = {
|
|
target: xml.getAttribute( 'target' ).split( '/' ).pop()
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'axis':
|
|
const param = child.getElementsByTagName( 'param' )[ 0 ];
|
|
data.axis = param.textContent;
|
|
const tmpJointIndex = data.axis.split( 'inst_' ).pop().split( 'axis' )[ 0 ];
|
|
data.jointIndex = tmpJointIndex.substring( 0, tmpJointIndex.length - 1 );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// nodes
|
|
|
|
prepareNodes( xml ) {
|
|
|
|
const elements = xml.getElementsByTagName( 'node' );
|
|
|
|
// ensure all node elements have id attributes
|
|
|
|
for ( let i = 0; i < elements.length; i ++ ) {
|
|
|
|
const element = elements[ i ];
|
|
|
|
if ( element.hasAttribute( 'id' ) === false ) {
|
|
|
|
element.setAttribute( 'id', this.generateId() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parseNode( xml ) {
|
|
|
|
const matrix = new Matrix4();
|
|
const vector = new Vector3();
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' ) || '',
|
|
type: xml.getAttribute( 'type' ),
|
|
id: xml.getAttribute( 'id' ),
|
|
sid: xml.getAttribute( 'sid' ),
|
|
matrix: new Matrix4(),
|
|
nodes: [],
|
|
instanceCameras: [],
|
|
instanceControllers: [],
|
|
instanceLights: [],
|
|
instanceGeometries: [],
|
|
instanceNodes: [],
|
|
transforms: {},
|
|
transformData: {},
|
|
transformOrder: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
if ( child.nodeType !== 1 ) continue;
|
|
|
|
let array;
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'node':
|
|
data.nodes.push( child.getAttribute( 'id' ) );
|
|
this.parseNode( child );
|
|
break;
|
|
|
|
case 'instance_camera':
|
|
data.instanceCameras.push( parseId( child.getAttribute( 'url' ) ) );
|
|
break;
|
|
|
|
case 'instance_controller':
|
|
data.instanceControllers.push( this.parseNodeInstance( child ) );
|
|
break;
|
|
|
|
case 'instance_light':
|
|
data.instanceLights.push( parseId( child.getAttribute( 'url' ) ) );
|
|
break;
|
|
|
|
case 'instance_geometry':
|
|
data.instanceGeometries.push( this.parseNodeInstance( child ) );
|
|
break;
|
|
|
|
case 'instance_node':
|
|
data.instanceNodes.push( parseId( child.getAttribute( 'url' ) ) );
|
|
break;
|
|
|
|
case 'matrix':
|
|
array = parseFloats( child.textContent );
|
|
data.matrix.multiply( matrix.fromArray( array ).transpose() );
|
|
{
|
|
|
|
const sid = child.getAttribute( 'sid' );
|
|
data.transforms[ sid ] = child.nodeName;
|
|
data.transformData[ sid ] = { type: 'matrix', array: array };
|
|
data.transformOrder.push( sid );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'translate':
|
|
array = parseFloats( child.textContent );
|
|
vector.fromArray( array );
|
|
data.matrix.multiply( matrix.makeTranslation( vector.x, vector.y, vector.z ) );
|
|
{
|
|
|
|
const sid = child.getAttribute( 'sid' );
|
|
data.transforms[ sid ] = child.nodeName;
|
|
data.transformData[ sid ] = { type: 'translate', x: array[ 0 ], y: array[ 1 ], z: array[ 2 ] };
|
|
data.transformOrder.push( sid );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'rotate':
|
|
array = parseFloats( child.textContent );
|
|
{
|
|
|
|
const angle = MathUtils.degToRad( array[ 3 ] );
|
|
data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) );
|
|
const sid = child.getAttribute( 'sid' );
|
|
data.transforms[ sid ] = child.nodeName;
|
|
data.transformData[ sid ] = { type: 'rotate', axis: [ array[ 0 ], array[ 1 ], array[ 2 ] ], angle: array[ 3 ] };
|
|
data.transformOrder.push( sid );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'scale':
|
|
array = parseFloats( child.textContent );
|
|
data.matrix.scale( vector.fromArray( array ) );
|
|
{
|
|
|
|
const sid = child.getAttribute( 'sid' );
|
|
data.transforms[ sid ] = child.nodeName;
|
|
data.transformData[ sid ] = { type: 'scale', x: array[ 0 ], y: array[ 1 ], z: array[ 2 ] };
|
|
data.transformOrder.push( sid );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'extra':
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( this.hasNode( data.id ) ) {
|
|
|
|
console.warn( 'THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id );
|
|
|
|
} else {
|
|
|
|
this.library.nodes[ data.id ] = data;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
parseNodeInstance( xml ) {
|
|
|
|
const data = {
|
|
id: parseId( xml.getAttribute( 'url' ) ),
|
|
materials: {},
|
|
skeletons: []
|
|
};
|
|
|
|
for ( let i = 0; i < xml.childNodes.length; i ++ ) {
|
|
|
|
const child = xml.childNodes[ i ];
|
|
|
|
switch ( child.nodeName ) {
|
|
|
|
case 'bind_material':
|
|
const instances = child.getElementsByTagName( 'instance_material' );
|
|
|
|
for ( let j = 0; j < instances.length; j ++ ) {
|
|
|
|
const instance = instances[ j ];
|
|
const symbol = instance.getAttribute( 'symbol' );
|
|
const target = instance.getAttribute( 'target' );
|
|
|
|
data.materials[ symbol ] = parseId( target );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'skeleton':
|
|
data.skeletons.push( parseId( child.textContent ) );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
// visual scenes
|
|
|
|
parseVisualScene( xml ) {
|
|
|
|
const data = {
|
|
name: xml.getAttribute( 'name' ),
|
|
children: []
|
|
};
|
|
|
|
this.prepareNodes( xml );
|
|
|
|
const elements = getElementsByTagName( xml, 'node' );
|
|
|
|
for ( let i = 0; i < elements.length; i ++ ) {
|
|
|
|
data.children.push( this.parseNode( elements[ i ] ) );
|
|
|
|
}
|
|
|
|
this.library.visualScenes[ xml.getAttribute( 'id' ) ] = data;
|
|
|
|
}
|
|
|
|
hasNode( id ) {
|
|
|
|
return this.library.nodes[ id ] !== undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export { ColladaParser, getElementsByTagName, parseStrings, parseFloats, parseInts, parseId };
|