TLoadMDL


Croquet-Teapot

Comment:

TLoadMDL is used to load Alice models.

Hierarchy:

ProtoObject
Object
TObject
TFrame
TLoadMDL

Summary:

methods:

instance class
parsing no messages

Detail:

instance methods:

parsing
buildMesh: vertices faces: faces normals: vtxNormals textureUV: vtxTexCoords textureMap: txtr

	| mat mesh |
	vertices ifNil:[^nil].
	faces ifNil:[^nil].
	vertices size < 3 ifTrue:[^nil].
	faces size < 1 ifTrue:[^nil].
	mat := TMaterial new.
	mat ambientColor: #(0.9 0.9 0.9 1) asFloatArray.
	mat diffuseColor: #(0.9 0.9 0.9 1) asFloatArray.
	txtr ifNotNil:[
		mat texture: txtr.
		mat textureMode: GLModulate.
	].

	vertices *= self scaleFactor.
	mesh := TMesh new initializeWithVertices: vertices
		alias: nil 
		norms: vtxNormals 
		textureUV: vtxTexCoords
		faceGroups: {1. faces} 
		material: mat.
	^mesh
fixTransformsIn: anActor

	"Traverse anActor and all its children checking for non-orthonormal transforms.
	Move these down until we get to the leaves and there transform the meshes (if any)."
	| tfm offset rowX rowY rowZ scaleX scaleY scaleZ scale |
	tfm := anActor localTransform copy.
	"Check if the transform is *really* weird"
	(tfm a41 = 0.0 and:[tfm a42 = 0.0 and:[tfm a43 = 0.0 and:[tfm a44 = 1.0]]]) ifFalse:[
		self error: 'Cannot fix transform'.
	].

	"Remember offset of the transform"
	offset := tfm translation.
	tfm translation: 0@0@0.

	"Compute unit vectors to see if their length changes"
	rowX := tfm localPointToGlobal: 1@0@0.
	rowY := tfm localPointToGlobal: 0@1@0.
	rowZ := tfm localPointToGlobal: 0@0@1.

	"Normalize"
	scaleX := 1.0 / rowX length.	rowX *= scaleX.
	scaleY := 1.0 / rowY length.	rowY *= scaleY.
	scaleZ := 1.0 / rowZ length.	rowZ *= scaleZ.

	"Orthogonalize (later...)"
	(rowX dot: rowY) < 0.01 ifFalse:[self error: 'Not orthogonal'].
	(rowY dot: rowZ) < 0.01 ifFalse:[self error: 'Not orthogonal'].
	(rowZ dot: rowX) < 0.01 ifFalse:[self error: 'Not orthogonal'].

	"Create new orthonormal transform"
	tfm := B3DMatrix4x4 identity.
	tfm a11: rowX x; a12: rowX y; a13: rowX z.
	tfm a21: rowY x; a22: rowY y; a23: rowY z.
	tfm a31: rowZ x; a32: rowZ y; a33: rowZ z.
	tfm translation: offset.
	anActor localTransform: tfm.

	"See if this a mesh"
	(anActor isKindOf: TMesh) ifTrue:[
		"Then rescale its vertices"
		scale := scaleX@scaleY@scaleZ negated.
		anActor vertices: (anActor vertices collect:[:vtx| vtx * scale]).
		anActor normals: (anActor normals collect:[:vtx| vtx x @ vtx y @ vtx z negated]).
		"And compute the bound spheres"
		anActor initBounds.
	].

	"Scale children (using a matrix since we'll orthonormalize them later) and recurse"
	scale := B3DMatrix4x4 withScale: scaleX@scaleY@scaleZ.
	anActor frameChildren ifNotNil:[
		anActor frameChildren do:[:child|
			child translation: child translation x negated@ child translation y @ child translation z negated.
			child localTransform: (scale composeWith: child localTransform).
			self fixTransformsIn: child.
		].
	].
getChildNamed: childName from: parent


	| theChild nameParts |
	(parent objectName sameAs: childName) ifTrue: [ ^parent].

	nameParts _ childName findTokens: ' '.
	(nameParts first sameAs: parent objectName) ifTrue: [nameParts removeFirst].
	theChild _ parent.
	nameParts do: [:aName|
		theChild := theChild frameChildren detect:[:any| any objectName sameAs: aName].
	].
	^ theChild.
loadMeshFromFile: meshFile texture: textureName

	"Load this object's mesh from the specified file"
	| words texture |
	textureName ifNotNil:[
		texture := TTexture new initializeWithFileName: textureName.
	].
	meshFile ifNil:[^nil].
	words _ (meshFile findTokens: #.).
	((words last) = 'vfb') 
		ifTrue: [^self parseVFBFile: meshFile texture: texture].

	((words last) = 'obj') 
		ifTrue: [^self parseOBJFile: meshFile texture: texture].
	^nil
parseFileNamed: filename

	"Creates a new actor using the specification from the given file"
	| aFile words line startSubstr index parent name texture meshFile matrix baseActor newActor fileVersion pos |

	words _ (filename findTokens: #.).
	aFile _ (CrLfFileStream readOnlyFileNamed: filename) ascii.

	"Check what version this mdl file is"
	line _ aFile upTo: (Character cr).
	line _ aFile upTo: (Character cr).
	line _ aFile upTo: (Character cr).

	((line truncateTo: 7) = 'version')
		ifTrue: [ fileVersion _ 1 ]
		ifFalse: [ fileVersion _ 0 ].

	[ line _ aFile upTo: (Character cr).
	aFile atEnd] whileFalse:[
		words _ line findTokens: '='.
		"See if we're creating a new object"
		(((words size) > 1) and: [ ((words at: 2) beginsWith: ' _MakeObject')
			or: [ (words at: 2) beginsWith: ' Alice.MakeObject' ] ]) ifTrue: [
			(fileVersion = 0) ifTrue: [
				words _ line findTokens: #,.
				parent _ (words at: 2) withBlanksTrimmed.
				name _ (((words at: 3) withBlanksTrimmed) findBetweenSubStrs: '"') at: 1.
			] ifFalse: [
				name _ (words at: 1) truncateTo: (((words at: 1) size) - 1).
				parent _ ((words at: 3) findTokens: #,) at: 1.
			].

			"Now pull in the texture to use"
			startSubstr _ name , '.SetTexture'.
			pos _ aFile position.
			texture := nil.
			[aFile atEnd or:[texture notNil]] whileFalse:[
				line _ aFile upTo: (Character cr).
				(line beginsWith: startSubstr) ifTrue:[
					texture _ (line findBetweenSubStrs: '"') at: 2.
					texture _ (aFile directory pathName), FileDirectory slash, texture]].
			texture ifNil:[aFile position: pos].

			"Read the composite matrix to use"
			startSubstr _ name , '._SetLocalTransformation'.
			pos _ aFile position.
			matrix := nil.
			[aFile atEnd or:[matrix notNil]] whileFalse:[
				line _ aFile upTo: (Character cr).
				(line beginsWith: startSubstr) ifTrue:[
					matrix _ B3DMatrix4x4 new.
					words _ line findBetweenSubStrs: ',()'.
					words removeAllSuchThat: [:str | str = ' '].
					index _ words size - 15.
					1 to: 4 do:[:j|
						1 to: 4 do:[:i|
							matrix at: i at: j put: 
								((words at: index) withBlanksTrimmed) asNumber.
							index _ index + 1.]].
				].
			].
			matrix ifNil:[aFile position: pos].

			"Read the mesh file to use"
			startSubstr _ 'LoadGeometry'.
			pos _ aFile position.
			meshFile := nil.
			[aFile atEnd or:[meshFile notNil]] whileFalse:[
				line _ aFile upTo: (Character cr).
				(line beginsWith: startSubstr) ifTrue:[
					meshFile _ (line findBetweenSubStrs: '"') at: 2.
					meshFile _ (aFile directory pathName), FileDirectory slash, meshFile.
				].
			].
			meshFile ifNil:[aFile position: pos].

			"Now build the actor name"
			words _ name findTokens: '.'.
			name _ words last.
			name at: 1 put: ((name at: 1) asLowercase).

			"Now build the parent name"
			parent _ parent copyReplaceAll: '.' with: ' '.

			"Now create the object"
			(parent = 'None') ifTrue: [
				baseActor := self loadMeshFromFile: meshFile texture: texture.
				baseActor ifNil:[
					baseActor := TFrame new.
					baseActor visible: false].
				baseActor objectName: name.
				matrix ifNotNil:[baseActor localTransform: matrix].
				baseActor translation: baseActor translation * self scaleFactor.
				baseActor solid: false.
				"end base actor creation"
			] ifFalse: [
				newActor := self loadMeshFromFile: meshFile texture: texture.
				newActor ifNil:[
					newActor := TFrame new.
					newActor visible: false].
				newActor objectOwner: baseActor.
				newActor objectName: name.
				parent _ self getChildNamed: parent from: baseActor.
				parent addChild: newActor.
				matrix ifNotNil:[newActor localTransform: matrix].
				newActor translation: newActor translation * self scaleFactor.
				newActor solid: false.
				"end new actor with parent"
			].
		"end MakeObject parsing"
		].

	]. "end file parsing"

	aFile close.
	self fixTransformsIn: baseActor.
	^baseActor
parseOBJFile: filename texture: txtr

	"Read in a mesh from the obj file"
	| aFile pos line words oldWords triple vCount fCount index vertices vtxTexCoords vtxNormals faces u v |
	aFile _ (CrLfFileStream readOnlyFileNamed: filename) ascii.
	line _ aFile upTo: (Character cr).
	words _ line findTokens: ' '.
	vCount _ 0.
	"Count the vertices"
	[ (words at: 1) = 'v' ] whileTrue: [
		vCount _ vCount + 1.
		line _ aFile upTo: (Character cr).
		words _ line findTokens: ' '.
	].

	"Now reset the file and grab the actual data"
	aFile position: 0.

	"Create the vertex array"
	vertices _ B3DVector3Array new: vCount.

	"Read in the vertices"
	line _ aFile upTo: (Character cr).
	words _ line findTokens: ' '.

	index _ 1.
	[ (words at: 1) = 'v' ] whileTrue: [ 
		vertices at: index put:
				(B3DVector3 x: ((words at: 2) asNumber)
							y: ((words at: 3) asNumber)
							z: ((words at: 4) asNumber)).
		index _ index + 1.
		line _ aFile upTo: (Character cr).
		words _ line findTokens: ' '.
	].

	"Read in the texture coordinates"
	index _ 1.
	vtxTexCoords _ B3DTexture2Array new: vCount.
	[ (words at: 1) = 'vt' ] whileTrue: [
		u _ (words at: 2) asNumber.
		v _ (words at: 3) asNumber.
		v := 1.0 + (0.0 - v).
		vtxTexCoords at: index put: (B3DVector2 u: u v: v).
		index _ index + 1.
		line _ aFile upTo: (Character cr).
	 	words _ line findTokens: ' '.
	].

	"Read in the normals"
	index _ 1.
	vtxNormals _ B3DVector3Array new: vCount.
	[ (words at: 1) = 'vn' ] whileTrue: [
		vtxNormals at: index
					put: (B3DVector3 x: ((words at: 2) asNumber)
									 y: ((words at: 3) asNumber)
									 z: ((words at: 4) asNumber)).
		index _ index + 1.
		line _ aFile upTo: (Character cr).
	 	words _ line findTokens: ' '.
	].

	pos _ aFile position.
	oldWords _ words.

	"Count the faces"
	fCount _ 0.
	[ (words size) > 0 ] whileTrue: [
		((words at: 1) = 'f') ifTrue: [fCount _ fCount + 1 ].
		line _ aFile upTo: (Character cr).
		words _ line findTokens: ' '.
	].

	"Create the faces array"
	faces _ WriteStream on: (IntegerArray new: fCount*3).

	aFile position: pos.
	words _ oldWords.

	"Read in the faces"
	[ (words size) > 0 ] whileTrue: [
		((words at: 1) = 'f') ifTrue: [ 
			triple _ (words at: 2) findTokens: '/'.
			faces nextPut: (triple at: 1) asNumber.

			triple _ (words at: 3) findTokens: '/'.
			faces nextPut: (triple at: 1) asNumber.

			triple _ (words at: 4) findTokens: '/'.
			faces nextPut: (triple at: 1) asNumber.
		].
		line _ aFile upTo: (Character cr).
	 	words _ line findTokens: ' '.
	].
	faces := faces contents.

	aFile close.
	^self buildMesh: vertices faces: faces normals: vtxNormals textureUV: vtxTexCoords textureMap: txtr.
parseVFBFile: filename texture: txtr

	"Read in a mesh from the vfb file"
	| aFile w x y z bytes version vCount fCount verticesPerFace vertices vtxNormals vtxTexCoords faces |
		aFile _ (StandardFileStream readOnlyFileNamed: filename) binary.
	"Read in the version number"
	bytes _ aFile next: 4.
	version _ bytes unsignedLongAt: 1 bigEndian: false.
	(version = 1) ifTrue: [
		"Read in the number of vertices"
		bytes _ aFile next: 4.
		vCount _ bytes unsignedLongAt: 1 bigEndian: false.
	] ifFalse: [
		vCount _ version.
		version _ 0.
	].
	(vCount <= 0) ifTrue: [
		aFile close.
		^nil
	].
	vertices _ B3DVector3Array new: vCount.
	vtxNormals _ B3DVector3Array new: vCount.
	vtxTexCoords _ B3DTexture2Array new: vCount.
	"Read in the vertices"
	1 to: vCount do: [:i | bytes _ aFile next: 32.
		x _ Float fromIEEE32Bit: (bytes unsignedLongAt: 1 bigEndian: false).
		y _ Float fromIEEE32Bit: (bytes unsignedLongAt: 5 bigEndian: false).
		z _ Float fromIEEE32Bit: (bytes unsignedLongAt: 9 bigEndian: false).
		vertices at: i put: (B3DVector3 x: (x negated) y: y z: z).
		x _ Float fromIEEE32Bit: (bytes unsignedLongAt: 13 bigEndian: false).
		y _ Float fromIEEE32Bit: (bytes unsignedLongAt: 17 bigEndian: false).
		z _ Float fromIEEE32Bit: (bytes unsignedLongAt: 21 bigEndian: false).
		vtxNormals at: i put: (B3DVector3 x: (x negated) y: y z: z).
		x _ Float fromIEEE32Bit: (bytes unsignedLongAt: 25 bigEndian: false).
		y _ Float fromIEEE32Bit: (bytes unsignedLongAt: 29 bigEndian: false).
		y := 1.0 + (0.0 - y).
		vtxTexCoords at: i put: (B3DVector2 u: x v: y).
	].

	"Read in the number of faces"
	bytes _ aFile next: 4.
	fCount _ bytes unsignedLongAt: 1 bigEndian: false.

	(fCount <= 0) ifTrue: [
		aFile close.
		^nil].

	"Read past the faceDataCount value"
	aFile next: 4.
	(version = 0) 
		ifTrue: [ verticesPerFace _ 0. ] 
		ifFalse: [bytes _ aFile next: 4.
				verticesPerFace _ bytes unsignedLongAt: 1 bigEndian: false].

	(verticesPerFace = 0) ifTrue: [
		faces _ WriteStream on: (IntegerArray new: fCount*3).
		1 to: fCount do: [: i |
			bytes _ aFile next: 4.
			w _ bytes unsignedLongAt: 1 bigEndian: false.
			(w = 3) ifTrue: [
				bytes _ aFile next: 4.
				x _ (bytes unsignedLongAt: 1 bigEndian: false).
				bytes _ aFile next: 4.
				y _ (bytes unsignedLongAt: 1 bigEndian: false).
				bytes _ aFile next: 4.
				z _ (bytes unsignedLongAt: 1 bigEndian: false).
				faces nextPut: z; nextPut: y; nextPut: x.
			] ifFalse: [
				1 to: w do: [: j | aFile next: 4]
			].
		].
		faces := faces contents.
	].

	aFile close.
	^self buildMesh: vertices faces: faces normals: vtxNormals textureUV: vtxTexCoords textureMap: txtr.
scaleFactor

	^3

class methods:

^top


- made by Dandelion -