| 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
|