Home My Page Projects Code Snippets Project Openings 3D graphics for Standard ML
Summary Activity SCM

SCM Repository

[sml3d] Annotation of /trunk/sml3d/examples/md3-viewer/load-md3.sml
ViewVC logotype

Annotation of /trunk/sml3d/examples/md3-viewer/load-md3.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 389 - (view) (download)

1 : jhr 388 (* load-md3.sml
2 :     *
3 :     * COPYRIGHT (c) 2008 John Reppy (http://cs.uchicago.edu/~jhr)
4 :     * All rights reserved.
5 :     *
6 :     * A loader for "Quake 3: Arena" model files (MD3 files). This code is based on the
7 :     * description at
8 :     *
9 :     * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
10 :     *)
11 :    
12 :     structure LoadMD3 (*: sig
13 :    
14 :     datatype bone
15 :    
16 :     datatype header
17 :    
18 :     datatype tag -
19 :    
20 :     datatype mesh_header
21 :    
22 :     datatype vertex
23 :    
24 :     datatype model
25 :    
26 :     datatype mesh_object
27 :    
28 :     end*) = struct
29 :    
30 :     structure W8V = Word8Vector
31 :     structure DB = DataBuffer
32 :    
33 :     (* seek to the given position in a binary input stream *)
34 :     fun seek (inS, pos) = let
35 :     val (rd, _) = BinIO.StreamIO.getReader (BinIO.getInstream inS)
36 :     val BinPrimIO.RD{setPos = SOME setPos, ...} = rd
37 :     in
38 :     setPos pos;
39 :     BinIO.setInstream (inS, BinIO.StreamIO.mkInstream(rd, W8V.fromList[]))
40 :     end
41 :    
42 :     (* return the current file position in a binary input stream *)
43 :     fun filePos inS = let
44 :     val arg as (rd, _) = BinIO.StreamIO.getReader (BinIO.getInstream inS)
45 :     val BinPrimIO.RD{getPos = SOME getPos, ...} = rd
46 :     val pos = getPos ()
47 :     in
48 :     BinIO.setInstream (inS, BinIO.StreamIO.mkInstream arg);
49 :     pos
50 :     end
51 :    
52 :     (* read data from a binary input stream *)
53 :     fun readData (inS, nbytes) = let
54 :     val v = BinIO.inputN (inS, nbytes)
55 :     in
56 :     if (W8V.length v <> nbytes) then raise Fail "missing data" else ();
57 :     v
58 :     end
59 :    
60 :     fun readN readSomething (inS, n) = let
61 :     fun read (0, l) = List.rev l
62 :     | read (n, l) = read (n-1, readSomething inS :: l)
63 :     in
64 :     read (n, [])
65 :     end
66 :    
67 :     fun repeati f n = let
68 :     fun iter i = if (i < n) then (f i; iter(i+1)) else ()
69 :     in
70 :     iter 0
71 :     end
72 :    
73 :     (* extract values from a byte vector *)
74 :     fun getString (data, pos, sz) = let
75 :     fun strlen n = if (n < sz) andalso (W8V.sub(data, pos+n) <> 0w0)
76 :     then strlen(n+1)
77 :     else n
78 :     val len = strlen 0
79 :     in
80 :     (Byte.unpackStringVec (Word8VectorSlice.slice(data, pos, SOME len)), pos+sz)
81 :     end
82 :     fun getS16 (data, pos) = let
83 :     val n = LargeWord.toIntX (PackWord16Little.subVecX(data, pos div 2))
84 :     in
85 :     (n, pos+2)
86 :     end
87 :     fun getU16 (data, pos) = let
88 :     val n = Word.fromLarge (PackWord16Little.subVec(data, pos div 2))
89 :     in
90 :     (n, pos+2)
91 :     end
92 :     fun getS32 (data, pos) = let
93 :     val n = LargeWord.toIntX (PackWord32Little.subVecX(data, pos div 4))
94 :     in
95 :     (n, pos+4)
96 :     end
97 :     fun getU32 (data, pos) = let
98 :     val n = Word.fromLarge (PackWord32Little.subVecX(data, pos div 4))
99 :     in
100 :     (n, pos+4)
101 :     end
102 :     fun getF32 (data, pos) = (PackReal32Little.subVec(data, pos div 4), pos+4)
103 :     fun getVec3 (data, pos) = let
104 :     val (x, pos) = getF32(data, pos)
105 :     val (y, pos) = getF32(data, pos)
106 :     val (z, pos) = getF32(data, pos)
107 :     in
108 :     ({x=x, y=y, z=z}, pos)
109 :     end
110 :    
111 :     (*+DEBUG*)
112 : jhr 389 val v2s = SML3dTypeUtil.fmt3 Float.toString
113 : jhr 388 (*-DEBUG*)
114 :    
115 :     (* read the MD3 header *)
116 :     fun readHdr inS = let
117 :     val data = readData(inS, 108)
118 :     val (ident, pos) = getString (data, 0, 4) (* 0 *)
119 :     val (version, pos) = getS32 (data, pos) (* 4 *)
120 :     val (name, pos) = getString (data, pos, 64) (* 8 *)
121 :     val (flags, pos) = getS32 (data, pos) (* 72 *)
122 :     val (numFrames, pos) = getS32 (data, pos) (* 76 *)
123 :     val (numTags, pos) = getS32 (data, pos) (* 80 *)
124 :     val (numSurfaces, pos) = getS32 (data, pos) (* 84 *)
125 :     val (numSkins, pos) = getS32 (data, pos) (* 88 *)
126 :     val (framesOffset, pos) = getS32 (data, pos) (* 92 *)
127 :     val (tagsOffset, pos) = getS32 (data, pos) (* 96 *)
128 :     val (surfsOffset, pos) = getS32 (data, pos) (* 100 *)
129 :     val (eofOffset, pos) = getS32 (data, pos) (* 104 *)
130 :     in
131 :     if (ident <> "IDP3")
132 :     then raise Fail "bogus magic number"
133 :     else ();
134 :     { name = name,
135 :     numFrames = numFrames,
136 :     numTags = numTags,
137 :     numSurfaces = numSurfaces,
138 :     framesOffset = Position.fromInt framesOffset,
139 :     tagsOffset = Position.fromInt tagsOffset,
140 :     surfsOffset = Position.fromInt surfsOffset
141 :     }
142 :     end
143 :    
144 :     fun readFrame inS = let
145 :     val data = readData(inS, 56)
146 :     val (minBounds, pos) = getVec3 (data, 0)
147 :     val (maxBounds, pos) = getVec3 (data, pos)
148 :     val (localOrigin, pos) = getVec3 (data, pos)
149 :     val (radius, pos) = getF32 (data, pos)
150 :     val (name, pos) = getString (data, pos, 16)
151 :     val _ = print(concat[
152 : jhr 389 "frame: name = \"", name, "\"; bbox = (", v2s minBounds, ", ", v2s maxBounds, ")\n",
153 :     " origin = ", v2s localOrigin, "\n"
154 : jhr 388 ])
155 : jhr 389 in FRAME{
156 : jhr 388 name = name,
157 :     minBounds = minBounds,
158 :     maxBounds = maxBounds,
159 :     localOrigin = localOrigin,
160 :     radius = radius
161 :     } end
162 :    
163 :     fun readTag inS = let
164 :     val data = readData(inS, 112)
165 :     val (name, pos) = getString (data, 0, 64)
166 :     val (origin, pos) = getVec3 (data, pos)
167 :     val (xAxis, pos) = getVec3 (data, pos)
168 :     val (yAxis, pos) = getVec3 (data, pos)
169 :     val (zAxis, pos) = getVec3 (data, pos)
170 :     val _ = print(concat["tag: name = \"", name, "\"\n"])
171 :     in MD3.TAG{
172 :     name = name,
173 :     frame = {
174 :     origin = origin,
175 :     onb = {u = xAxis, v = yAxis, w = zAxis}
176 :     }
177 :     } end
178 :    
179 :     (* read the header for a surface *)
180 :     fun readSurfaceHdr inS = let
181 :     val data = readData (inS, 108)
182 :     val (ident, pos) = getString (data, 0, 4) (* 0 *)
183 :     val (name, pos) = getString (data, pos, 64) (* 4 *)
184 :     val (flags, pos) = getS32 (data, pos) (* 68 *)
185 :     val (numFrames, pos) = getS32 (data, pos) (* 72 *)
186 :     val (numShaders, pos) = getS32 (data, pos) (* 76 *)
187 :     val (numVerts, pos) = getS32 (data, pos) (* 80 *)
188 :     val (numTris, pos) = getS32 (data, pos) (* 84 *)
189 :     val (trisOffset, pos) = getS32 (data, pos) (* 88 *)
190 :     val (shadersOffset, pos) = getS32 (data, pos) (* 92 *)
191 :     val (stsOffset, pos) = getS32 (data, pos) (* 96 *)
192 :     val (vertsOffset, pos) = getS32 (data, pos) (* 100 *)
193 :     val (endOffset, pos) = getS32 (data, pos) (* 104 *)
194 :     in
195 :     if (ident <> "IDP3")
196 :     then raise Fail "bogus magic number in surface"
197 :     else ();
198 :     { name = name,
199 :     numFrames = numFrames,
200 :     numShaders = numShaders,
201 :     numVerts = numVerts,
202 :     numTris = numTris,
203 :     trisOffset = Position.fromInt trisOffset,
204 :     shadersOffset = Position.fromInt shadersOffset,
205 :     stsOffset = Position.fromInt stsOffset,
206 :     vertsOffset = Position.fromInt vertsOffset,
207 :     endOffset = Position.fromInt endOffset
208 :     }
209 :     end
210 :    
211 :     fun readShader inS = let
212 :     val data = readData (inS, 68)
213 :     val (name, pos) = getString (data, 0, 64)
214 :     val (shader, pos) = getS32 (data, pos)
215 :     val _ = print(concat["shader: name = \"", name, "\"\n"])
216 :     in
217 :     MD3.SHADER{name = name, index = shader}
218 :     end
219 :    
220 :     (* read the vertex indices of a triangle *)
221 :     fun readTriangle inS = let
222 :     val data = readData (inS, 12)
223 :     val (v1, pos) = getU32 (data, 0)
224 :     val (v2, pos) = getU32 (data, pos)
225 :     val (v3, pos) = getU32 (data, pos)
226 :     in
227 :     (v1, v2, v3)
228 :     end
229 :    
230 :     fun readST inS = let
231 :     val data = readData (inS, 8)
232 :     val (s, pos) = getF32 (data, 0)
233 :     val (t, pos) = getF32 (data, pos)
234 :     in
235 :     (s, t)
236 :     end
237 :    
238 :     (* read the scaled coordinates and compressed normal of a vertex. The scaling
239 :     * factor is 1/64. The normal vector is represented as latitude:longitude (8-bits
240 :     * each). To convert a compressed normal "n" to a normal vector "(nx, ny, nz)",
241 :     * we compute the following:
242 :     *
243 :     * lat = (PI * ((n >> 8) & 0xFF)) / 128.0
244 :     * lng = (PI * (n & 0xFF) / 128.0
245 :     * nx = cos(lat) * sin(lng)
246 :     * ny = sin(lat) * sin(lng)
247 :     * nz = cos(lng)
248 :     *)
249 :     local
250 :     val pi_128 = Float.M_PI / 128.0
251 :     val scale : Float.float = 1.0 / 64.0
252 :     fun wordToFloat w = Float.fromInt(Word.toIntX w)
253 :     in
254 :     fun readVertex inS = let
255 :     val data = readData (inS, 8)
256 :     fun cvtCoord i = scale * Float.fromInt i
257 :     val (x, pos) = getS16 (data, 0)
258 :     val (y, pos) = getS16 (data, pos)
259 :     val (z, pos) = getS16 (data, pos)
260 :     val v = (cvtCoord x, cvtCoord y, cvtCoord z)
261 :     val (norm, pos) = getU16 (data, pos)
262 :     val lat = pi_128 * wordToFloat(Word.andb(Word.>>(norm, 0w8), 0wxff))
263 :     val lng = pi_128 * wordToFloat(Word.andb(norm, 0wxff))
264 :     val sinLng = Float.sin lng
265 :     val n = (Float.cos lat * sinLng, Float.sin lat * sinLng, Float.cos lng)
266 :     in
267 :     (v, n)
268 :     end
269 :     end
270 :    
271 : jhr 389 fun readSurface inS = let
272 : jhr 388 val start = filePos inS
273 :     val {name = name, numFrames, numShaders, numVerts, numTris,
274 :     trisOffset, shadersOffset, stsOffset, vertsOffset, endOffset
275 :     } = readSurfaceHdr inS
276 :     val _ = print(concat["surface: name = \"", name, "\", #frames = ",
277 :     Int.toString numFrames, ", #shaders = ", Int.toString numShaders,
278 :     ", numVerts = ", Int.toString numVerts, ", #numTris = ", Int.toString numTris, "\n"
279 :     ])
280 :     val () = seek (inS, start + shadersOffset)
281 :     val shaders = readN readShader (inS, numShaders)
282 :     val () = seek (inS, start + trisOffset)
283 :     val tris = let
284 :     val buf = DB.new(DB.sizeui, 3*numTris)
285 :     fun readTri i = let
286 :     val (v1, v2, v3) = readTriangle inS
287 :     val i = 3*i
288 :     in
289 :     print(concat["T[", StringCvt.padLeft #" " 3 (Int.toString i), "] = (",
290 :     Word.fmt StringCvt.DEC v1, ", ",
291 :     Word.fmt StringCvt.DEC v2, ", ",
292 :     Word.fmt StringCvt.DEC v3, ")\n"]);
293 :     DB.setui(buf, i, v1);
294 :     DB.setui(buf, i+1, v2);
295 :     DB.setui(buf, i+2, v3)
296 :     end
297 :     in
298 :     repeati readTri numTris;
299 :     buf
300 :     end
301 :     val () = seek (inS, start + stsOffset)
302 :     val texCoords = let
303 :     val buf = DB.new(DB.size2f, numVerts)
304 :     fun read i = DB.set2f (buf, i, readST inS)
305 :     in
306 :     repeati read numVerts;
307 :     buf
308 :     end
309 :     val () = seek (inS, start + vertsOffset)
310 :     val frames = let
311 : jhr 389 fun readFrame _ = let
312 : jhr 388 (* there are numVerts vertices per frame of animation *)
313 :     val vBuf = DB.new(DB.size3f, numVerts)
314 :     val nBuf = DB.new(DB.size3f, numVerts)
315 :     fun read i = let
316 :     val (v, n) = readVertex inS
317 :     in
318 :     (**)
319 :     print(concat["V[", StringCvt.padLeft #" " 3 (Int.toString i), "] v = ",
320 : jhr 389 v2s v, ", n = ", v2s n, "\n"]);
321 : jhr 388 (**)
322 :     DB.set3f (vBuf, i, v);
323 :     DB.set3f (vBuf, i, n)
324 :     end
325 :     in
326 :     repeati read numVerts;
327 : jhr 389 MD3.MESH{verts = vBuf, norms = nBuf}
328 : jhr 388 end
329 :     in
330 : jhr 389 repeati readFrame numFrames
331 : jhr 388 end
332 :     val () = seek (inS, start + endOffset)
333 :     in
334 :     MD3.SURF{
335 :     name = name,
336 :     numVerts = numVerts,
337 :     shaders = shaders,
338 :     tris = tris,
339 :     texCoords = texCoords,
340 :     frames = frames
341 :     }
342 :     end
343 :    
344 :     fun load inS = let
345 :     val {name, numFrames, numTags, numSurfaces,
346 :     framesOffset, tagsOffset, surfsOffset
347 :     } = readHdr inS
348 :     val () = print(concat["HDR: name = \"", name, "\", #frames = ",
349 :     Int.toString numFrames, ", #tags = ", Int.toString numTags,
350 :     ", #surfaces = ", Int.toString numSurfaces, "\n"
351 :     ])
352 :     val () = seek (inS, framesOffset)
353 :     val frames = readN readFrame (inS, numFrames)
354 :     val () = seek (inS, tagsOffset)
355 :     val tags = readN readTag (inS, numTags)
356 :     val () = seek (inS, surfsOffset)
357 :     val surfs = readN (readSurface frames) (inS, numSurfaces)
358 :     in
359 :     MD3.MODEL{
360 :     tags = tags,
361 : jhr 389 surfs = surfs,
362 :     frames = frames
363 : jhr 388 }
364 :     end
365 :    
366 :     fun loadFile file = let
367 :     val inS = BinIO.openIn file
368 :     val model = (load inS) handle ex => (BinIO.closeIn inS; raise ex)
369 :     in
370 :     BinIO.closeIn inS;
371 :     model
372 :     end
373 :    
374 :     end
375 :    

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0