|
@@ -1,12 +1,12 @@
|
|
/**
|
|
/**
|
|
* @author Lewy Blue https://github.com/looeee
|
|
* @author Lewy Blue https://github.com/looeee
|
|
*
|
|
*
|
|
- * Load files in LWO3 format
|
|
|
|
|
|
+ * Load files in LWO3 and LWO2 format
|
|
*
|
|
*
|
|
* LWO3 format specification:
|
|
* LWO3 format specification:
|
|
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
|
|
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
|
|
*
|
|
*
|
|
- * LWO2 format specification (not tested, however the loader should be largely backwards compatible)
|
|
|
|
|
|
+ * LWO2 format specification:
|
|
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
|
|
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
|
|
*
|
|
*
|
|
*/
|
|
*/
|
|
@@ -291,7 +291,15 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
for ( var name in lwoTree.materials ) {
|
|
for ( var name in lwoTree.materials ) {
|
|
|
|
|
|
- materials.push( this.parseMaterial( lwoTree.materials[ name ], name, lwoTree.textures ) );
|
|
|
|
|
|
+ if ( lwoTree.format === 'LWO3' ) {
|
|
|
|
+
|
|
|
|
+ materials.push( this.parseMaterial( lwoTree.materials[ name ], name, lwoTree.textures ) );
|
|
|
|
+
|
|
|
|
+ } else if ( lwoTree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ materials.push( this.parseMaterialLwo2( lwoTree.materials[ name ], name, lwoTree.textures ) );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -326,6 +334,20 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ parseMaterialLwo2( materialData, name, textures ) {
|
|
|
|
+
|
|
|
|
+ var params = {
|
|
|
|
+ name: name,
|
|
|
|
+ side: this.getSide( materialData.attributes ),
|
|
|
|
+ flatShading: this.getSmooth( materialData.attributes ),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var attributes = this.parseAttributes( materialData.attributes, {} );
|
|
|
|
+ params = Object.assign( params, attributes );
|
|
|
|
+ return new THREE[ 'MeshPhongMaterial' ]( params );
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
// Note: converting from left to right handed coords by switching x -> -x in vertices, and
|
|
// Note: converting from left to right handed coords by switching x -> -x in vertices, and
|
|
// then switching mat FrontSide -> BackSide
|
|
// then switching mat FrontSide -> BackSide
|
|
// NB: this means that THREE.FrontSide and THREE.BackSide have been switched!
|
|
// NB: this means that THREE.FrontSide and THREE.BackSide have been switched!
|
|
@@ -744,7 +766,17 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
);
|
|
);
|
|
|
|
|
|
- } else if ( dim > 4 ) console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
|
|
|
|
|
|
+ } else if ( dim > 4 ) {
|
|
|
|
+
|
|
|
|
+ for ( var k = 1; k < dim - 1; k ++ ) {
|
|
|
|
+
|
|
|
|
+ remappedIndices.push( indices[ i ], indices[ i + k ], indices[ i + k + 1 ] );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console.warn( 'LWOLoader: polygons with greater than 4 sides are not supported' );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
i += dim;
|
|
i += dim;
|
|
|
|
|
|
@@ -850,7 +882,16 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
|
|
remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ], indices[ i * 2 ], indices[ i * 2 + 1 ] );
|
|
|
|
|
|
- } // ignore > 4 for now
|
|
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ // ignore > 4 for now
|
|
|
|
+ for ( var k = 0; k < dim - 2; k ++ ) {
|
|
|
|
+
|
|
|
|
+ remappedIndices.push( indices[ i * 2 ], indices[ i * 2 + 1 ] );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
|
|
} );
|
|
} );
|
|
|
|
|
|
@@ -1001,6 +1042,13 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
var blockID = this.reader.getIDTag();
|
|
var blockID = this.reader.getIDTag();
|
|
var length = this.reader.getUint32(); // size of data in bytes
|
|
var length = this.reader.getUint32(); // size of data in bytes
|
|
|
|
+ if ( this.tree.format === 'LWO2' && length > this.reader.dv.byteLength - this.reader.offset ) {
|
|
|
|
+
|
|
|
|
+ this.reader.offset -= 4;
|
|
|
|
+ length = this.reader.getUint16();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
|
|
// Data types may be found in either LWO2 OR LWO3 spec
|
|
// Data types may be found in either LWO2 OR LWO3 spec
|
|
switch ( blockID ) {
|
|
switch ( blockID ) {
|
|
@@ -1080,7 +1128,6 @@ THREE.LWOLoader = ( function () {
|
|
case 'NPLA':
|
|
case 'NPLA':
|
|
case 'VERS':
|
|
case 'VERS':
|
|
case 'ENUM':
|
|
case 'ENUM':
|
|
- case 'FLAG':
|
|
|
|
case 'TAG ':
|
|
case 'TAG ':
|
|
|
|
|
|
// Car Material CHUNKS
|
|
// Car Material CHUNKS
|
|
@@ -1096,8 +1143,26 @@ THREE.LWOLoader = ( function () {
|
|
this.reader.skip( length );
|
|
this.reader.skip( length );
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case 'FLAG':
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ this.reader.skip( 4 ); // not suported
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ this.reader.skip( length );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
// Skipped LWO2 chunks
|
|
// Skipped LWO2 chunks
|
|
case 'DIFF': // diffuse level, may be necessary to modulate COLR with this
|
|
case 'DIFF': // diffuse level, may be necessary to modulate COLR with this
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ this.currentSurface.diffusePower = this.reader.getFloat32();
|
|
|
|
+ this.reader.skip( 2 );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
case 'TRNL':
|
|
case 'TRNL':
|
|
case 'REFL':
|
|
case 'REFL':
|
|
case 'GLOS':
|
|
case 'GLOS':
|
|
@@ -1118,7 +1183,20 @@ THREE.LWOLoader = ( function () {
|
|
case 'ENAB':
|
|
case 'ENAB':
|
|
this.reader.skip( length );
|
|
this.reader.skip( length );
|
|
break;
|
|
break;
|
|
|
|
+ case 'SURF':
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ this.parseSurfaceLwo2( length );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case 'CLIP':
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
|
|
|
|
+ this.parseClipLwo2( length );
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
// Texture node chunks (not in spec)
|
|
// Texture node chunks (not in spec)
|
|
case 'IPIX': // usePixelBlending
|
|
case 'IPIX': // usePixelBlending
|
|
case 'IMIP': // useMipMaps
|
|
case 'IMIP': // useMipMaps
|
|
@@ -1276,7 +1354,8 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
// LWO2: Basic Surface Parameters
|
|
// LWO2: Basic Surface Parameters
|
|
case 'COLR':
|
|
case 'COLR':
|
|
- this.currentSurface.attributes.color = this.reader.getFloat32Array( 3 );
|
|
|
|
|
|
+ this.currentSurface.attributes.Color = {};
|
|
|
|
+ this.currentSurface.attributes.Color.value = this.reader.getFloat32Array( 3 );
|
|
this.reader.skip( 2 ); // VX: envelope
|
|
this.reader.skip( 2 ); // VX: envelope
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -1323,7 +1402,15 @@ THREE.LWOLoader = ( function () {
|
|
break;
|
|
break;
|
|
|
|
|
|
case 'IMAP':
|
|
case 'IMAP':
|
|
- this.currentSurface.attributes.imageMapIndex = this.reader.getUint32();
|
|
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ this.reader.skip( 2 );
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+
|
|
|
|
+ this.currentSurface.attributes.imageMapIndex = this.reader.getUint32();
|
|
|
|
+
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
|
|
|
|
case 'IUVI': // uv channel name
|
|
case 'IUVI': // uv channel name
|
|
@@ -1337,6 +1424,11 @@ THREE.LWOLoader = ( function () {
|
|
this.currentNode.heightWrappingMode = this.reader.getUint32();
|
|
this.currentNode.heightWrappingMode = this.reader.getUint32();
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ // LWO2 USE
|
|
|
|
+ case 'BLOK':
|
|
|
|
+ // skip
|
|
|
|
+ break;
|
|
|
|
+
|
|
default:
|
|
default:
|
|
this.parseUnknownCHUNK( blockID, length );
|
|
this.parseUnknownCHUNK( blockID, length );
|
|
|
|
|
|
@@ -1420,6 +1512,10 @@ THREE.LWOLoader = ( function () {
|
|
this.parseTextureNodeAttribute( type );
|
|
this.parseTextureNodeAttribute( type );
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case 'LWO2':
|
|
|
|
+ this.tree.format = type;
|
|
|
|
+ break;
|
|
|
|
+
|
|
case 'LWO3':
|
|
case 'LWO3':
|
|
this.tree.format = type;
|
|
this.tree.format = type;
|
|
break;
|
|
break;
|
|
@@ -1431,7 +1527,11 @@ THREE.LWOLoader = ( function () {
|
|
// CLIP FORM AND SUB FORMS
|
|
// CLIP FORM AND SUB FORMS
|
|
|
|
|
|
case 'CLIP':
|
|
case 'CLIP':
|
|
- this.parseClip( length );
|
|
|
|
|
|
+ if ( this.tree.format === 'LWO2' ) {
|
|
|
|
+
|
|
|
|
+ this.parseForm( length );
|
|
|
|
+
|
|
|
|
+ }
|
|
break;
|
|
break;
|
|
|
|
|
|
case 'STIL':
|
|
case 'STIL':
|
|
@@ -1574,6 +1674,29 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
var name = this.reader.getString();
|
|
var name = this.reader.getString();
|
|
|
|
|
|
|
|
+ var surface = {
|
|
|
|
+ attributes: {}, // LWO2 style non-node attributes will go here
|
|
|
|
+ connections: {},
|
|
|
|
+ name: name,
|
|
|
|
+ inputName: name,
|
|
|
|
+ nodes: {},
|
|
|
|
+ source: this.reader.getString(),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ this.tree.materials[ name ] = surface;
|
|
|
|
+ this.currentSurface = surface;
|
|
|
|
+
|
|
|
|
+ this.parentForm = this.tree.materials;
|
|
|
|
+ this.currentForm = surface;
|
|
|
|
+ this.currentFormEnd = this.reader.offset + length;
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ parseSurfaceLwo2( length ) {
|
|
|
|
+
|
|
|
|
+ var firstOffset = this.reader.offset;
|
|
|
|
+ var name = this.reader.getString();
|
|
|
|
+
|
|
var surface = {
|
|
var surface = {
|
|
attributes: {}, // LWO2 style non-node attributes will go here
|
|
attributes: {}, // LWO2 style non-node attributes will go here
|
|
connections: {},
|
|
connections: {},
|
|
@@ -1771,6 +1894,39 @@ THREE.LWOLoader = ( function () {
|
|
|
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
+ parseClipLwo2( length ) {
|
|
|
|
+
|
|
|
|
+ var texture = {
|
|
|
|
+ index: this.reader.getUint32(),
|
|
|
|
+ fileName: ""
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var readed = 4;
|
|
|
|
+ // seach STIL block
|
|
|
|
+ while ( true ) {
|
|
|
|
+
|
|
|
|
+ var tag = this.reader.getIDTag();
|
|
|
|
+ var n_length = this.reader.getUint16();
|
|
|
|
+ if ( tag === 'STIL' ) {
|
|
|
|
+
|
|
|
|
+ texture.fileName = this.reader.getString();
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ readed += 4 + n_length;
|
|
|
|
+ if ( n_length >= length ) {
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ this.tree.textures.push( texture );
|
|
|
|
+ this.currentForm = texture;
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
parseImage() {
|
|
parseImage() {
|
|
|
|
|
|
this.reader.skip( 8 ); // unknown
|
|
this.reader.skip( 8 ); // unknown
|