374 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			374 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | class LWO3Parser { | ||
|  | 
 | ||
|  | 	constructor( IFFParser ) { | ||
|  | 
 | ||
|  | 		this.IFF = IFFParser; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	parseBlock() { | ||
|  | 
 | ||
|  | 		this.IFF.debugger.offset = this.IFF.reader.offset; | ||
|  | 		this.IFF.debugger.closeForms(); | ||
|  | 
 | ||
|  | 		const blockID = this.IFF.reader.getIDTag(); | ||
|  | 		const length = this.IFF.reader.getUint32(); // size of data in bytes
 | ||
|  | 
 | ||
|  | 		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
 | ||
|  | 			// 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 ': // Pre-loop behavior for the keyframe
 | ||
|  | 			case 'POST': // Post-loop behavior for the keyframe
 | ||
|  | 			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 'STCK': | ||
|  | 
 | ||
|  | 			// Procedural Textures skipped
 | ||
|  | 			case 'VALU': | ||
|  | 
 | ||
|  | 			// 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 'NCRD': | ||
|  | 			case 'NMOD': | ||
|  | 			case 'NSEL': | ||
|  | 			case 'NPRW': | ||
|  | 			case 'NPLA': | ||
|  | 			case 'VERS': | ||
|  | 			case 'ENUM': | ||
|  | 			case 'TAG ': | ||
|  | 
 | ||
|  | 			// 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 'SHRP': | ||
|  | 			case 'RFOP': | ||
|  | 			case 'RSAN': | ||
|  | 			case 'TROP': | ||
|  | 			case 'RBLR': | ||
|  | 			case 'TBLR': | ||
|  | 			case 'CLRH': | ||
|  | 			case 'CLRF': | ||
|  | 			case 'ADTR': | ||
|  | 			case 'GLOW': | ||
|  | 			case 'LINE': | ||
|  | 			case 'ALPH': | ||
|  | 			case 'VCOL': | ||
|  | 			case 'ENAB': | ||
|  | 				this.IFF.debugger.skipped = true; | ||
|  | 				this.IFF.reader.skip( 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 '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.currentSurface.attributes.imageMapIndex = this.IFF.reader.getUint32(); | ||
|  | 				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; | ||
|  | 
 | ||
|  | 			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 { LWO3Parser }; |