You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
9.9 KiB
415 lines
9.9 KiB
6 months ago
|
class LWO2Parser {
|
||
|
|
||
|
constructor( IFFParser ) {
|
||
|
|
||
|
this.IFF = IFFParser;
|
||
|
|
||
|
}
|
||
|
|
||
|
parseBlock() {
|
||
|
|
||
|
this.IFF.debugger.offset = this.IFF.reader.offset;
|
||
|
this.IFF.debugger.closeForms();
|
||
|
|
||
|
const blockID = this.IFF.reader.getIDTag();
|
||
|
let length = this.IFF.reader.getUint32(); // size of data in bytes
|
||
|
if ( length > this.IFF.reader.dv.byteLength - this.IFF.reader.offset ) {
|
||
|
|
||
|
this.IFF.reader.offset -= 4;
|
||
|
length = this.IFF.reader.getUint16();
|
||
|
|
||
|
}
|
||
|
|
||
|
this.IFF.debugger.dataOffset = this.IFF.reader.offset;
|
||
|
this.IFF.debugger.length = length;
|
||
|
|
||
|
// Data types may be found in either LWO2 OR LWO3 spec
|
||
|
switch ( blockID ) {
|
||
|
|
||
|
case 'FORM': // form blocks may consist of sub -chunks or sub-forms
|
||
|
this.IFF.parseForm( length );
|
||
|
break;
|
||
|
|
||
|
// SKIPPED CHUNKS
|
||
|
// if break; is called directly, the position in the lwoTree is not created
|
||
|
// any sub chunks and forms are added to the parent form instead
|
||
|
// MISC skipped
|
||
|
case 'ICON': // Thumbnail Icon Image
|
||
|
case 'VMPA': // Vertex Map Parameter
|
||
|
case 'BBOX': // bounding box
|
||
|
// case 'VMMD':
|
||
|
// case 'VTYP':
|
||
|
|
||
|
// normal maps can be specified, normally on models imported from other applications. Currently ignored
|
||
|
case 'NORM':
|
||
|
|
||
|
// ENVL FORM skipped
|
||
|
case 'PRE ':
|
||
|
case 'POST':
|
||
|
case 'KEY ':
|
||
|
case 'SPAN':
|
||
|
|
||
|
// CLIP FORM skipped
|
||
|
case 'TIME':
|
||
|
case 'CLRS':
|
||
|
case 'CLRA':
|
||
|
case 'FILT':
|
||
|
case 'DITH':
|
||
|
case 'CONT':
|
||
|
case 'BRIT':
|
||
|
case 'SATR':
|
||
|
case 'HUE ':
|
||
|
case 'GAMM':
|
||
|
case 'NEGA':
|
||
|
case 'IFLT':
|
||
|
case 'PFLT':
|
||
|
|
||
|
// Image Map Layer skipped
|
||
|
case 'PROJ':
|
||
|
case 'AXIS':
|
||
|
case 'AAST':
|
||
|
case 'PIXB':
|
||
|
case 'AUVO':
|
||
|
case 'STCK':
|
||
|
|
||
|
// Procedural Textures skipped
|
||
|
case 'PROC':
|
||
|
case 'VALU':
|
||
|
case 'FUNC':
|
||
|
|
||
|
// Gradient Textures skipped
|
||
|
case 'PNAM':
|
||
|
case 'INAM':
|
||
|
case 'GRST':
|
||
|
case 'GREN':
|
||
|
case 'GRPT':
|
||
|
case 'FKEY':
|
||
|
case 'IKEY':
|
||
|
|
||
|
// Texture Mapping Form skipped
|
||
|
case 'CSYS':
|
||
|
|
||
|
// Surface CHUNKs skipped
|
||
|
case 'OPAQ': // top level 'opacity' checkbox
|
||
|
case 'CMAP': // clip map
|
||
|
|
||
|
// Surface node CHUNKS skipped
|
||
|
// These mainly specify the node editor setup in LW
|
||
|
case 'NLOC':
|
||
|
case 'NZOM':
|
||
|
case 'NVER':
|
||
|
case 'NSRV':
|
||
|
case 'NVSK': // unknown
|
||
|
case 'NCRD':
|
||
|
case 'WRPW': // image wrap w ( for cylindrical and spherical projections)
|
||
|
case 'WRPH': // image wrap h
|
||
|
case 'NMOD':
|
||
|
case 'NSEL':
|
||
|
case 'NPRW':
|
||
|
case 'NPLA':
|
||
|
case 'NODS':
|
||
|
case 'VERS':
|
||
|
case 'ENUM':
|
||
|
case 'TAG ':
|
||
|
case 'OPAC':
|
||
|
|
||
|
// Car Material CHUNKS
|
||
|
case 'CGMD':
|
||
|
case 'CGTY':
|
||
|
case 'CGST':
|
||
|
case 'CGEN':
|
||
|
case 'CGTS':
|
||
|
case 'CGTE':
|
||
|
case 'OSMP':
|
||
|
case 'OMDE':
|
||
|
case 'OUTR':
|
||
|
case 'FLAG':
|
||
|
|
||
|
case 'TRNL':
|
||
|
case 'GLOW':
|
||
|
case 'GVAL': // glow intensity
|
||
|
case 'SHRP':
|
||
|
case 'RFOP':
|
||
|
case 'RSAN':
|
||
|
case 'TROP':
|
||
|
case 'RBLR':
|
||
|
case 'TBLR':
|
||
|
case 'CLRH':
|
||
|
case 'CLRF':
|
||
|
case 'ADTR':
|
||
|
case 'LINE':
|
||
|
case 'ALPH':
|
||
|
case 'VCOL':
|
||
|
case 'ENAB':
|
||
|
this.IFF.debugger.skipped = true;
|
||
|
this.IFF.reader.skip( length );
|
||
|
break;
|
||
|
|
||
|
case 'SURF':
|
||
|
this.IFF.parseSurfaceLwo2( length );
|
||
|
break;
|
||
|
|
||
|
case 'CLIP':
|
||
|
this.IFF.parseClipLwo2( length );
|
||
|
break;
|
||
|
|
||
|
// Texture node chunks (not in spec)
|
||
|
case 'IPIX': // usePixelBlending
|
||
|
case 'IMIP': // useMipMaps
|
||
|
case 'IMOD': // imageBlendingMode
|
||
|
case 'AMOD': // unknown
|
||
|
case 'IINV': // imageInvertAlpha
|
||
|
case 'INCR': // imageInvertColor
|
||
|
case 'IAXS': // imageAxis ( for non-UV maps)
|
||
|
case 'IFOT': // imageFallofType
|
||
|
case 'ITIM': // timing for animated textures
|
||
|
case 'IWRL':
|
||
|
case 'IUTI':
|
||
|
case 'IINX':
|
||
|
case 'IINY':
|
||
|
case 'IINZ':
|
||
|
case 'IREF': // possibly a VX for reused texture nodes
|
||
|
if ( length === 4 ) this.IFF.currentNode[ blockID ] = this.IFF.reader.getInt32();
|
||
|
else this.IFF.reader.skip( length );
|
||
|
break;
|
||
|
|
||
|
case 'OTAG':
|
||
|
this.IFF.parseObjectTag();
|
||
|
break;
|
||
|
|
||
|
case 'LAYR':
|
||
|
this.IFF.parseLayer( length );
|
||
|
break;
|
||
|
|
||
|
case 'PNTS':
|
||
|
this.IFF.parsePoints( length );
|
||
|
break;
|
||
|
|
||
|
case 'VMAP':
|
||
|
this.IFF.parseVertexMapping( length );
|
||
|
break;
|
||
|
|
||
|
case 'AUVU':
|
||
|
case 'AUVN':
|
||
|
this.IFF.reader.skip( length - 1 );
|
||
|
this.IFF.reader.getVariableLengthIndex(); // VX
|
||
|
break;
|
||
|
|
||
|
case 'POLS':
|
||
|
this.IFF.parsePolygonList( length );
|
||
|
break;
|
||
|
|
||
|
case 'TAGS':
|
||
|
this.IFF.parseTagStrings( length );
|
||
|
break;
|
||
|
|
||
|
case 'PTAG':
|
||
|
this.IFF.parsePolygonTagMapping( length );
|
||
|
break;
|
||
|
|
||
|
case 'VMAD':
|
||
|
this.IFF.parseVertexMapping( length, true );
|
||
|
break;
|
||
|
|
||
|
// Misc CHUNKS
|
||
|
case 'DESC': // Description Line
|
||
|
this.IFF.currentForm.description = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
case 'TEXT':
|
||
|
case 'CMNT':
|
||
|
case 'NCOM':
|
||
|
this.IFF.currentForm.comment = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
// Envelope Form
|
||
|
case 'NAME':
|
||
|
this.IFF.currentForm.channelName = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
// Image Map Layer
|
||
|
case 'WRAP':
|
||
|
this.IFF.currentForm.wrap = { w: this.IFF.reader.getUint16(), h: this.IFF.reader.getUint16() };
|
||
|
break;
|
||
|
|
||
|
case 'IMAG':
|
||
|
const index = this.IFF.reader.getVariableLengthIndex();
|
||
|
this.IFF.currentForm.imageIndex = index;
|
||
|
break;
|
||
|
|
||
|
// Texture Mapping Form
|
||
|
case 'OREF':
|
||
|
this.IFF.currentForm.referenceObject = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
case 'ROID':
|
||
|
this.IFF.currentForm.referenceObjectID = this.IFF.reader.getUint32();
|
||
|
break;
|
||
|
|
||
|
// Surface Blocks
|
||
|
case 'SSHN':
|
||
|
this.IFF.currentSurface.surfaceShaderName = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
case 'AOVN':
|
||
|
this.IFF.currentSurface.surfaceCustomAOVName = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
// Nodal Blocks
|
||
|
case 'NSTA':
|
||
|
this.IFF.currentForm.disabled = this.IFF.reader.getUint16();
|
||
|
break;
|
||
|
|
||
|
case 'NRNM':
|
||
|
this.IFF.currentForm.realName = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
case 'NNME':
|
||
|
this.IFF.currentForm.refName = this.IFF.reader.getString();
|
||
|
this.IFF.currentSurface.nodes[ this.IFF.currentForm.refName ] = this.IFF.currentForm;
|
||
|
break;
|
||
|
|
||
|
// Nodal Blocks : connections
|
||
|
case 'INME':
|
||
|
if ( ! this.IFF.currentForm.nodeName ) this.IFF.currentForm.nodeName = [];
|
||
|
this.IFF.currentForm.nodeName.push( this.IFF.reader.getString() );
|
||
|
break;
|
||
|
|
||
|
case 'IINN':
|
||
|
if ( ! this.IFF.currentForm.inputNodeName ) this.IFF.currentForm.inputNodeName = [];
|
||
|
this.IFF.currentForm.inputNodeName.push( this.IFF.reader.getString() );
|
||
|
break;
|
||
|
|
||
|
case 'IINM':
|
||
|
if ( ! this.IFF.currentForm.inputName ) this.IFF.currentForm.inputName = [];
|
||
|
this.IFF.currentForm.inputName.push( this.IFF.reader.getString() );
|
||
|
break;
|
||
|
|
||
|
case 'IONM':
|
||
|
if ( ! this.IFF.currentForm.inputOutputName ) this.IFF.currentForm.inputOutputName = [];
|
||
|
this.IFF.currentForm.inputOutputName.push( this.IFF.reader.getString() );
|
||
|
break;
|
||
|
|
||
|
case 'FNAM':
|
||
|
this.IFF.currentForm.fileName = this.IFF.reader.getString();
|
||
|
break;
|
||
|
|
||
|
case 'CHAN': // NOTE: ENVL Forms may also have CHAN chunk, however ENVL is currently ignored
|
||
|
if ( length === 4 ) this.IFF.currentForm.textureChannel = this.IFF.reader.getIDTag();
|
||
|
else this.IFF.reader.skip( length );
|
||
|
break;
|
||
|
|
||
|
// LWO2 Spec chunks: these are needed since the SURF FORMs are often in LWO2 format
|
||
|
case 'SMAN':
|
||
|
const maxSmoothingAngle = this.IFF.reader.getFloat32();
|
||
|
this.IFF.currentSurface.attributes.smooth = ( maxSmoothingAngle < 0 ) ? false : true;
|
||
|
break;
|
||
|
|
||
|
// LWO2: Basic Surface Parameters
|
||
|
case 'COLR':
|
||
|
this.IFF.currentSurface.attributes.Color = { value: this.IFF.reader.getFloat32Array( 3 ) };
|
||
|
this.IFF.reader.skip( 2 ); // VX: envelope
|
||
|
break;
|
||
|
|
||
|
case 'LUMI':
|
||
|
this.IFF.currentSurface.attributes.Luminosity = { value: this.IFF.reader.getFloat32() };
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'SPEC':
|
||
|
this.IFF.currentSurface.attributes.Specular = { value: this.IFF.reader.getFloat32() };
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'DIFF':
|
||
|
this.IFF.currentSurface.attributes.Diffuse = { value: this.IFF.reader.getFloat32() };
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'REFL':
|
||
|
this.IFF.currentSurface.attributes.Reflection = { value: this.IFF.reader.getFloat32() };
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'GLOS':
|
||
|
this.IFF.currentSurface.attributes.Glossiness = { value: this.IFF.reader.getFloat32() };
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'TRAN':
|
||
|
this.IFF.currentSurface.attributes.opacity = this.IFF.reader.getFloat32();
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'BUMP':
|
||
|
this.IFF.currentSurface.attributes.bumpStrength = this.IFF.reader.getFloat32();
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'SIDE':
|
||
|
this.IFF.currentSurface.attributes.side = this.IFF.reader.getUint16();
|
||
|
break;
|
||
|
|
||
|
case 'RIMG':
|
||
|
this.IFF.currentSurface.attributes.reflectionMap = this.IFF.reader.getVariableLengthIndex();
|
||
|
break;
|
||
|
|
||
|
case 'RIND':
|
||
|
this.IFF.currentSurface.attributes.refractiveIndex = this.IFF.reader.getFloat32();
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'TIMG':
|
||
|
this.IFF.currentSurface.attributes.refractionMap = this.IFF.reader.getVariableLengthIndex();
|
||
|
break;
|
||
|
|
||
|
case 'IMAP':
|
||
|
this.IFF.reader.skip( 2 );
|
||
|
break;
|
||
|
|
||
|
case 'TMAP':
|
||
|
this.IFF.debugger.skipped = true;
|
||
|
this.IFF.reader.skip( length ); // needs implementing
|
||
|
break;
|
||
|
|
||
|
case 'IUVI': // uv channel name
|
||
|
this.IFF.currentNode.UVChannel = this.IFF.reader.getString( length );
|
||
|
break;
|
||
|
|
||
|
case 'IUTL': // widthWrappingMode: 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
|
||
|
this.IFF.currentNode.widthWrappingMode = this.IFF.reader.getUint32();
|
||
|
break;
|
||
|
case 'IVTL': // heightWrappingMode
|
||
|
this.IFF.currentNode.heightWrappingMode = this.IFF.reader.getUint32();
|
||
|
break;
|
||
|
|
||
|
// LWO2 USE
|
||
|
case 'BLOK':
|
||
|
// skip
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
this.IFF.parseUnknownCHUNK( blockID, length );
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( blockID != 'FORM' ) {
|
||
|
|
||
|
this.IFF.debugger.node = 1;
|
||
|
this.IFF.debugger.nodeID = blockID;
|
||
|
this.IFF.debugger.log();
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( this.IFF.reader.offset >= this.IFF.currentFormEnd ) {
|
||
|
|
||
|
this.IFF.currentForm = this.IFF.parentForm;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export { LWO2Parser };
|