• R/O
  • SSH
  • HTTPS

marathon: Commit


Commit MetaInfo

Revision527 (tree)
Zeit2012-06-02 14:10:57
Autorookawa_mi

Log Message

改行コード修正

Ändern Zusammenfassung

Diff

--- marathon/trunk/Source_Files/ModelView/StudioLoader.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/StudioLoader.cpp (revision 527)
@@ -1,512 +1,512 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- 3D Studio Max Object Loader
21-
22- By Loren Petrich, Sept 1, 2001
23-
24- Derived from the work of
25-
26- Jeff Lewis (werewolf@worldgate.com)
27- Martin van Velsen (vvelsen@ronix.ptf.hro.nl)
28- Robin Fercoq (robin@msrwww.fc-net.fr)
29- Jim Pitts (jim@micronetics.com)
30- Albert Szilvasy (szilvasy@almos.vein.hu)
31- Terry Caton (tcaton@umr.edu)
32- Matthew Fairfax
33-
34-*/
35-
36-#include "cseries.h"
37-
38-#ifdef HAVE_OPENGL
39-#ifdef __WIN32__
40-#include <windows.h>
41-#endif
42-
43-#include "Logging.h"
44-
45-#include "StudioLoader.h"
46-
47-// Use pack/unpack, but with little-endian data
48-#undef PACKED_DATA_IS_BIG_ENDIAN
49-#define PACKED_DATA_IS_LITTLE_ENDIAN
50-#include "Packing.h"
51-
52-/*
53- The various authors have different conventions for the chunk names;
54- the nomenclature here is a hybrid of these.
55- Only the chunks actually read will be listed here.
56-*/
57-
58-const uint16 MASTER = 0x4d4d;
59-const uint16 EDITOR = 0x3d3d;
60-const uint16 OBJECT = 0x4000;
61-const uint16 TRIMESH = 0x4100;
62-const uint16 VERTICES = 0x4110;
63-const uint16 TXTR_COORDS = 0x4140;
64-const uint16 FACE_DATA = 0x4120;
65-
66-static const char *Path = NULL; // Path to model file.
67-
68-struct ChunkHeaderData
69-{
70- uint16 ID;
71- uint32 Size;
72-};
73-const int SIZEOF_ChunkHeaderData = 6;
74-
75-// For read-in chunks
76-vector<uint8> ChunkBuffer;
77-inline uint8 *ChunkBufferBase() {return &ChunkBuffer[0];}
78-inline size_t ChunkBufferSize() {return ChunkBuffer.size();}
79-inline void SetChunkBufferSize(int Size) {ChunkBuffer.resize(Size);}
80-
81-// Local prototypes;
82-// these functions return "false" if there was a read or reposition error
83-static bool ReadChunkHeader(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
84-static bool LoadChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
85-static bool SkipChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
86-
87-// These functions read the contents of these container chunks
88-// to the file location "ChunkEnd".
89-// The generic reader takes specific readers as the last argument
90-static bool ReadContainer(OpenedFile& OFile, ChunkHeaderData& ChunkHeader,
91- bool (*ContainerCallback)(OpenedFile&,int32));
92-static bool ReadMaster(OpenedFile& OFile, int32 ParentChunkEnd);
93-static bool ReadEditor(OpenedFile& OFile, int32 ParentChunkEnd);
94-static bool ReadObject(OpenedFile& OFile, int32 ParentChunkEnd);
95-static bool ReadTrimesh(OpenedFile& OFile, int32 ParentChunkEnd);
96-static bool ReadFaceData(OpenedFile& OFile, int32 ParentChunkEnd);
97-
98-// For processing the raw chunk data into appropriate forms:
99-static void LoadVertices();
100-static void LoadTextureCoordinates();
101-
102-static void LoadFloats(int NVals, uint8 *Stream, GLfloat *Floats);
103-
104-
105-// For feeding into the read-in routines
106-static Model3D *ModelPtr = NULL;
107-
108-bool LoadModel_Studio(FileSpecifier& Spec, Model3D& Model)
109-{
110- ModelPtr = &Model;
111- Model.Clear();
112-
113- Path = Spec.GetPath();
114- logNote1("Loading 3D Studio Max model file %s",Path);
115-
116- OpenedFile OFile;
117- if (!Spec.Open(OFile))
118- {
119- logError1("ERROR opening %s",Path);
120- return false;
121- }
122-
123- ChunkHeaderData ChunkHeader;
124- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
125- if (ChunkHeader.ID != MASTER)
126- {
127- logError1("ERROR: not a 3DS Max model file: %s",Path);
128- return false;
129- }
130-
131- if (!ReadContainer(OFile,ChunkHeader,ReadMaster)) return false;
132-
133- if (Model.Positions.empty())
134- {
135- logError1("ERROR: no vertices found in %s",Path);
136- return false;
137- }
138- if (Model.VertIndices.empty())
139- {
140- logError1("ERROR: no faces found in %s",Path);
141- return false;
142- }
143- return true;
144-}
145-
146-bool ReadChunkHeader(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
147-{
148- uint8 Buffer[SIZEOF_ChunkHeaderData];
149- if (!OFile.Read(SIZEOF_ChunkHeaderData,Buffer))
150- {
151- logError1("ERROR reading chunk header in %s",Path);
152- return false;
153- }
154- uint8 *S = Buffer;
155- StreamToValue(S,ChunkHeader.ID);
156- StreamToValue(S,ChunkHeader.Size);
157- return true;
158-}
159-
160-bool LoadChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
161-{
162- logTrace2("Loading chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
163- int32 DataSize = ChunkHeader.Size - SIZEOF_ChunkHeaderData;
164- SetChunkBufferSize(DataSize);
165- if (!OFile.Read(DataSize,ChunkBufferBase()))
166- {
167- logError1("ERROR reading chunk contents in %s",Path);
168- return false;
169- }
170-
171- return true;
172-}
173-
174-bool SkipChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
175-{
176- logTrace2("Skipping chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
177- int32 DataSize = ChunkHeader.Size - SIZEOF_ChunkHeaderData;
178-
179- int32 Location = 0;
180- OFile.GetPosition(Location);
181- if (!OFile.SetPosition(Location + DataSize)) return false;
182- return true;
183-}
184-
185-// Generic container-chunk reader
186-bool ReadContainer(OpenedFile& OFile, ChunkHeaderData& ChunkHeader,
187- bool (*ContainerCallback)(OpenedFile&,int32))
188-{
189- logTrace2("Entering chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
190-
191- int32 ChunkEnd = 0;
192- OFile.GetPosition(ChunkEnd);
193- ChunkEnd += ChunkHeader.Size - SIZEOF_ChunkHeaderData;
194-
195- if (!ContainerCallback(OFile,ChunkEnd)) return false;
196-
197- logTrace2("Exiting chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
198- return true;
199-}
200-
201-
202-// For reading the master chunk (ideally, whole file)
203-static bool ReadMaster(OpenedFile& OFile, int32 ParentChunkEnd)
204-{
205- int32 Location = 0;
206- OFile.GetPosition(Location);
207-
208- while(Location < ParentChunkEnd)
209- {
210- ChunkHeaderData ChunkHeader;
211- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
212-
213- switch(ChunkHeader.ID)
214- {
215- case EDITOR:
216- if (!ReadContainer(OFile,ChunkHeader,ReadEditor)) return false;
217- break;
218-
219- default:
220- if (!SkipChunk(OFile,ChunkHeader)) return false;
221- }
222-
223- // Where are we now?
224- OFile.GetPosition(Location);
225- }
226-
227- if (Location > ParentChunkEnd)
228- {
229- logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
230- return false;
231- }
232- return true;
233-}
234-
235-// For reading the editor-data chunk
236-static bool ReadEditor(OpenedFile& OFile, int32 ParentChunkEnd)
237-{
238- int32 Location = 0;
239- OFile.GetPosition(Location);
240-
241- while(Location < ParentChunkEnd)
242- {
243- ChunkHeaderData ChunkHeader;
244- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
245-
246- switch(ChunkHeader.ID)
247- {
248- case OBJECT:
249- if (!ReadContainer(OFile,ChunkHeader,ReadObject)) return false;
250- break;
251-
252- default:
253- if (!SkipChunk(OFile,ChunkHeader)) return false;
254- }
255-
256- // Where are we now?
257- OFile.GetPosition(Location);
258- }
259-
260- if (Location > ParentChunkEnd)
261- {
262- logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
263- return false;
264- }
265- return true;
266-}
267-
268-// For reading the object-data chunk
269-static bool ReadObject(OpenedFile& OFile, int32 ParentChunkEnd)
270-{
271- // Read the name
272- char c;
273- do
274- {
275- if (!OFile.Read(1,&c))
276- {
277- logError1("ERROR when reading name in %s",Path);
278- return false;
279- }
280- }
281- while(c != 0);
282-
283- int32 Location = 0;
284- OFile.GetPosition(Location);
285-
286- while(Location < ParentChunkEnd)
287- {
288- ChunkHeaderData ChunkHeader;
289- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
290-
291- switch(ChunkHeader.ID)
292- {
293- case TRIMESH:
294- if (!ReadContainer(OFile,ChunkHeader,ReadTrimesh)) return false;
295- break;
296-
297- default:
298- if (!SkipChunk(OFile,ChunkHeader)) return false;
299- }
300-
301- // Where are we now?
302- OFile.GetPosition(Location);
303- }
304-
305- if (Location > ParentChunkEnd)
306- {
307- logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
308- return false;
309- }
310- return true;
311-}
312-
313-// For reading the triangle-mesh-data chunk
314-static bool ReadTrimesh(OpenedFile& OFile, int32 ParentChunkEnd)
315-{
316- int32 Location = 0;
317- OFile.GetPosition(Location);
318-
319- assert(ModelPtr);
320-
321- while(Location < ParentChunkEnd)
322- {
323- ChunkHeaderData ChunkHeader;
324- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
325-
326- switch(ChunkHeader.ID)
327- {
328- case VERTICES:
329- if (!LoadChunk(OFile,ChunkHeader)) return false;
330- LoadVertices();
331- break;
332-
333- case TXTR_COORDS:
334- if (!LoadChunk(OFile,ChunkHeader)) return false;
335- LoadTextureCoordinates();
336- break;
337-
338- case FACE_DATA:
339- if (!ReadContainer(OFile,ChunkHeader,ReadFaceData)) return false;
340- break;
341-
342- default:
343- if (!SkipChunk(OFile,ChunkHeader)) return false;
344- }
345-
346- // Where are we now?
347- OFile.GetPosition(Location);
348- }
349-
350- if (Location > ParentChunkEnd)
351- {
352- logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
353- return false;
354- }
355- return true;
356-}
357-
358-
359-// For reading the face-data chunk
360-static bool ReadFaceData(OpenedFile& OFile, int32 ParentChunkEnd)
361-{
362- uint8 NFBuffer[2];
363- uint16 NumFaces;
364- if (!OFile.Read(2,NFBuffer))
365- {
366- logError1("ERROR reading number of faces in %s",Path);
367- return false;
368- }
369- uint8 *S = NFBuffer;
370- StreamToValue(S,NumFaces);
371-
372- int32 DataSize = 4*sizeof(uint16)*int(NumFaces);
373- SetChunkBufferSize(DataSize);
374- if (!OFile.Read(DataSize,ChunkBufferBase()))
375- {
376- logError1("ERROR reading face-chunk contents in %s",Path);
377- return false;
378- }
379-
380- S = ChunkBufferBase();
381- ModelPtr->VertIndices.resize(3*NumFaces);
382- for (int k=0; k<NumFaces; k++)
383- {
384- uint16 *CurrPoly = ModelPtr->VIBase() + 3*k;
385- uint16 Flags;
386- StreamToList(S,CurrPoly,3);
387- StreamToValue(S,Flags);
388- }
389-
390- int32 Location = 0;
391- OFile.GetPosition(Location);
392-
393- while(Location < ParentChunkEnd)
394- {
395- ChunkHeaderData ChunkHeader;
396- if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
397-
398- switch(ChunkHeader.ID)
399- {
400- /*
401- case OBJECT:
402- if (!ReadContainer(OFile,ChunkHeader,ReadObject)) return false;
403- break;
404- */
405- default:
406- if (!SkipChunk(OFile,ChunkHeader)) return false;
407- }
408-
409- // Where are we now?
410- OFile.GetPosition(Location);
411- }
412-
413- if (Location > ParentChunkEnd)
414- {
415- logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
416- return false;
417- }
418- return true;
419-}
420-
421-
422-static void LoadVertices()
423-{
424- uint8 *S = ChunkBufferBase();
425- uint16 Size;
426- StreamToValue(S,Size);
427-
428- int NVals = 3*int(Size);
429-
430- ModelPtr->Positions.resize(NVals);
431-
432- LoadFloats(NVals,S,ModelPtr->PosBase());
433-}
434-
435-static void LoadTextureCoordinates()
436-{
437- uint8 *S = ChunkBufferBase();
438- uint16 Size;
439- StreamToValue(S,Size);
440-
441- int NVals = 2*int(Size);
442-
443- ModelPtr->TxtrCoords.resize(NVals);
444-
445- LoadFloats(NVals,S,ModelPtr->TCBase());
446-}
447-
448-
449-void LoadFloats(int NVals, uint8 *Stream, GLfloat *Floats)
450-{
451- // Test to see whether the destination floating-point values are the right size:
452- assert(sizeof(GLfloat) == 4);
453-
454- uint32 IntVal;
455- GLfloat *FloatPtr = Floats;
456- for (int k=0; k<NVals; k++, FloatPtr++)
457- {
458- // Intermediate step: 4-byte integer
459- // (won't have the right value if interpreted as an integer!)
460- StreamToValue(Stream,IntVal);
461-
462- // This will work on any platform where
463- // GLfloat is an IEEE 754 4-byte float.
464- // Otherwise, use whatever appropriate conversion tricks are appropriate
465- uint8 *SrcPtr = (uint8 *)(&IntVal);
466- uint8 *DestPtr = (uint8 *)FloatPtr;
467- for (int c=0; c<4; c++)
468- DestPtr[c] = SrcPtr[c];
469- }
470-}
471-
472-// Load a 3D Studio MAX model and convert its vertex and texture coordinates
473-// from 3DS' right-handed coordinate system to Aleph One's left-handed system.
474-bool LoadModel_Studio_RightHand(FileSpecifier& Spec, Model3D& Model)
475-{
476- bool Result = LoadModel_Studio(Spec, Model);
477- if (!Result) return Result;
478-
479- logTrace("Converting handedness.");
480-
481- // Wings 3d and Blender produce 3DS models with a z-up orientation,
482- // and for Blender models y increases towards the back. In Aleph One,
483- // z increases upward, and items that have been placed with 0 degrees
484- // of rotation face in the positive-x direction.
485- // Preserve the orientation and switch handedness by swapping and
486- // flipping x and y.
487- for (unsigned XPos = 0; XPos < Model.Positions.size(); XPos += 3)
488- {
489- GLfloat X = Model.Positions[XPos];
490- Model.Positions[XPos] = -Model.Positions[XPos + 1];
491- Model.Positions[XPos + 1] = -X;
492- }
493-
494- // Put the vertices of faces back into clockwise order.
495- for (unsigned IPos = 0; IPos < Model.VertIndices.size(); IPos += 3)
496- {
497- int Index = Model.VertIndices[IPos + 1];
498- Model.VertIndices[IPos + 1] = Model.VertIndices[IPos];
499- Model.VertIndices[IPos] = Index;
500- }
501-
502- // Switch texture coordinates from right-handed (x,y) to
503- // left-handed (row,column).
504- for (unsigned YPos = 1; YPos < Model.TxtrCoords.size(); YPos += 2)
505- {
506- Model.TxtrCoords[YPos] = 1.0 - Model.TxtrCoords[YPos];
507- }
508-
509- return true;
510-}
511-
512-#endif // def HAVE_OPENGL
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ 3D Studio Max Object Loader
21+
22+ By Loren Petrich, Sept 1, 2001
23+
24+ Derived from the work of
25+
26+ Jeff Lewis (werewolf@worldgate.com)
27+ Martin van Velsen (vvelsen@ronix.ptf.hro.nl)
28+ Robin Fercoq (robin@msrwww.fc-net.fr)
29+ Jim Pitts (jim@micronetics.com)
30+ Albert Szilvasy (szilvasy@almos.vein.hu)
31+ Terry Caton (tcaton@umr.edu)
32+ Matthew Fairfax
33+
34+*/
35+
36+#include "cseries.h"
37+
38+#ifdef HAVE_OPENGL
39+#ifdef __WIN32__
40+#include <windows.h>
41+#endif
42+
43+#include "Logging.h"
44+
45+#include "StudioLoader.h"
46+
47+// Use pack/unpack, but with little-endian data
48+#undef PACKED_DATA_IS_BIG_ENDIAN
49+#define PACKED_DATA_IS_LITTLE_ENDIAN
50+#include "Packing.h"
51+
52+/*
53+ The various authors have different conventions for the chunk names;
54+ the nomenclature here is a hybrid of these.
55+ Only the chunks actually read will be listed here.
56+*/
57+
58+const uint16 MASTER = 0x4d4d;
59+const uint16 EDITOR = 0x3d3d;
60+const uint16 OBJECT = 0x4000;
61+const uint16 TRIMESH = 0x4100;
62+const uint16 VERTICES = 0x4110;
63+const uint16 TXTR_COORDS = 0x4140;
64+const uint16 FACE_DATA = 0x4120;
65+
66+static const char *Path = NULL; // Path to model file.
67+
68+struct ChunkHeaderData
69+{
70+ uint16 ID;
71+ uint32 Size;
72+};
73+const int SIZEOF_ChunkHeaderData = 6;
74+
75+// For read-in chunks
76+vector<uint8> ChunkBuffer;
77+inline uint8 *ChunkBufferBase() {return &ChunkBuffer[0];}
78+inline size_t ChunkBufferSize() {return ChunkBuffer.size();}
79+inline void SetChunkBufferSize(int Size) {ChunkBuffer.resize(Size);}
80+
81+// Local prototypes;
82+// these functions return "false" if there was a read or reposition error
83+static bool ReadChunkHeader(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
84+static bool LoadChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
85+static bool SkipChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader);
86+
87+// These functions read the contents of these container chunks
88+// to the file location "ChunkEnd".
89+// The generic reader takes specific readers as the last argument
90+static bool ReadContainer(OpenedFile& OFile, ChunkHeaderData& ChunkHeader,
91+ bool (*ContainerCallback)(OpenedFile&,int32));
92+static bool ReadMaster(OpenedFile& OFile, int32 ParentChunkEnd);
93+static bool ReadEditor(OpenedFile& OFile, int32 ParentChunkEnd);
94+static bool ReadObject(OpenedFile& OFile, int32 ParentChunkEnd);
95+static bool ReadTrimesh(OpenedFile& OFile, int32 ParentChunkEnd);
96+static bool ReadFaceData(OpenedFile& OFile, int32 ParentChunkEnd);
97+
98+// For processing the raw chunk data into appropriate forms:
99+static void LoadVertices();
100+static void LoadTextureCoordinates();
101+
102+static void LoadFloats(int NVals, uint8 *Stream, GLfloat *Floats);
103+
104+
105+// For feeding into the read-in routines
106+static Model3D *ModelPtr = NULL;
107+
108+bool LoadModel_Studio(FileSpecifier& Spec, Model3D& Model)
109+{
110+ ModelPtr = &Model;
111+ Model.Clear();
112+
113+ Path = Spec.GetPath();
114+ logNote1("Loading 3D Studio Max model file %s",Path);
115+
116+ OpenedFile OFile;
117+ if (!Spec.Open(OFile))
118+ {
119+ logError1("ERROR opening %s",Path);
120+ return false;
121+ }
122+
123+ ChunkHeaderData ChunkHeader;
124+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
125+ if (ChunkHeader.ID != MASTER)
126+ {
127+ logError1("ERROR: not a 3DS Max model file: %s",Path);
128+ return false;
129+ }
130+
131+ if (!ReadContainer(OFile,ChunkHeader,ReadMaster)) return false;
132+
133+ if (Model.Positions.empty())
134+ {
135+ logError1("ERROR: no vertices found in %s",Path);
136+ return false;
137+ }
138+ if (Model.VertIndices.empty())
139+ {
140+ logError1("ERROR: no faces found in %s",Path);
141+ return false;
142+ }
143+ return true;
144+}
145+
146+bool ReadChunkHeader(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
147+{
148+ uint8 Buffer[SIZEOF_ChunkHeaderData];
149+ if (!OFile.Read(SIZEOF_ChunkHeaderData,Buffer))
150+ {
151+ logError1("ERROR reading chunk header in %s",Path);
152+ return false;
153+ }
154+ uint8 *S = Buffer;
155+ StreamToValue(S,ChunkHeader.ID);
156+ StreamToValue(S,ChunkHeader.Size);
157+ return true;
158+}
159+
160+bool LoadChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
161+{
162+ logTrace2("Loading chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
163+ int32 DataSize = ChunkHeader.Size - SIZEOF_ChunkHeaderData;
164+ SetChunkBufferSize(DataSize);
165+ if (!OFile.Read(DataSize,ChunkBufferBase()))
166+ {
167+ logError1("ERROR reading chunk contents in %s",Path);
168+ return false;
169+ }
170+
171+ return true;
172+}
173+
174+bool SkipChunk(OpenedFile& OFile, ChunkHeaderData& ChunkHeader)
175+{
176+ logTrace2("Skipping chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
177+ int32 DataSize = ChunkHeader.Size - SIZEOF_ChunkHeaderData;
178+
179+ int32 Location = 0;
180+ OFile.GetPosition(Location);
181+ if (!OFile.SetPosition(Location + DataSize)) return false;
182+ return true;
183+}
184+
185+// Generic container-chunk reader
186+bool ReadContainer(OpenedFile& OFile, ChunkHeaderData& ChunkHeader,
187+ bool (*ContainerCallback)(OpenedFile&,int32))
188+{
189+ logTrace2("Entering chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
190+
191+ int32 ChunkEnd = 0;
192+ OFile.GetPosition(ChunkEnd);
193+ ChunkEnd += ChunkHeader.Size - SIZEOF_ChunkHeaderData;
194+
195+ if (!ContainerCallback(OFile,ChunkEnd)) return false;
196+
197+ logTrace2("Exiting chunk 0x%04hx size %u",ChunkHeader.ID,ChunkHeader.Size);
198+ return true;
199+}
200+
201+
202+// For reading the master chunk (ideally, whole file)
203+static bool ReadMaster(OpenedFile& OFile, int32 ParentChunkEnd)
204+{
205+ int32 Location = 0;
206+ OFile.GetPosition(Location);
207+
208+ while(Location < ParentChunkEnd)
209+ {
210+ ChunkHeaderData ChunkHeader;
211+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
212+
213+ switch(ChunkHeader.ID)
214+ {
215+ case EDITOR:
216+ if (!ReadContainer(OFile,ChunkHeader,ReadEditor)) return false;
217+ break;
218+
219+ default:
220+ if (!SkipChunk(OFile,ChunkHeader)) return false;
221+ }
222+
223+ // Where are we now?
224+ OFile.GetPosition(Location);
225+ }
226+
227+ if (Location > ParentChunkEnd)
228+ {
229+ logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
230+ return false;
231+ }
232+ return true;
233+}
234+
235+// For reading the editor-data chunk
236+static bool ReadEditor(OpenedFile& OFile, int32 ParentChunkEnd)
237+{
238+ int32 Location = 0;
239+ OFile.GetPosition(Location);
240+
241+ while(Location < ParentChunkEnd)
242+ {
243+ ChunkHeaderData ChunkHeader;
244+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
245+
246+ switch(ChunkHeader.ID)
247+ {
248+ case OBJECT:
249+ if (!ReadContainer(OFile,ChunkHeader,ReadObject)) return false;
250+ break;
251+
252+ default:
253+ if (!SkipChunk(OFile,ChunkHeader)) return false;
254+ }
255+
256+ // Where are we now?
257+ OFile.GetPosition(Location);
258+ }
259+
260+ if (Location > ParentChunkEnd)
261+ {
262+ logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
263+ return false;
264+ }
265+ return true;
266+}
267+
268+// For reading the object-data chunk
269+static bool ReadObject(OpenedFile& OFile, int32 ParentChunkEnd)
270+{
271+ // Read the name
272+ char c;
273+ do
274+ {
275+ if (!OFile.Read(1,&c))
276+ {
277+ logError1("ERROR when reading name in %s",Path);
278+ return false;
279+ }
280+ }
281+ while(c != 0);
282+
283+ int32 Location = 0;
284+ OFile.GetPosition(Location);
285+
286+ while(Location < ParentChunkEnd)
287+ {
288+ ChunkHeaderData ChunkHeader;
289+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
290+
291+ switch(ChunkHeader.ID)
292+ {
293+ case TRIMESH:
294+ if (!ReadContainer(OFile,ChunkHeader,ReadTrimesh)) return false;
295+ break;
296+
297+ default:
298+ if (!SkipChunk(OFile,ChunkHeader)) return false;
299+ }
300+
301+ // Where are we now?
302+ OFile.GetPosition(Location);
303+ }
304+
305+ if (Location > ParentChunkEnd)
306+ {
307+ logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
308+ return false;
309+ }
310+ return true;
311+}
312+
313+// For reading the triangle-mesh-data chunk
314+static bool ReadTrimesh(OpenedFile& OFile, int32 ParentChunkEnd)
315+{
316+ int32 Location = 0;
317+ OFile.GetPosition(Location);
318+
319+ assert(ModelPtr);
320+
321+ while(Location < ParentChunkEnd)
322+ {
323+ ChunkHeaderData ChunkHeader;
324+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
325+
326+ switch(ChunkHeader.ID)
327+ {
328+ case VERTICES:
329+ if (!LoadChunk(OFile,ChunkHeader)) return false;
330+ LoadVertices();
331+ break;
332+
333+ case TXTR_COORDS:
334+ if (!LoadChunk(OFile,ChunkHeader)) return false;
335+ LoadTextureCoordinates();
336+ break;
337+
338+ case FACE_DATA:
339+ if (!ReadContainer(OFile,ChunkHeader,ReadFaceData)) return false;
340+ break;
341+
342+ default:
343+ if (!SkipChunk(OFile,ChunkHeader)) return false;
344+ }
345+
346+ // Where are we now?
347+ OFile.GetPosition(Location);
348+ }
349+
350+ if (Location > ParentChunkEnd)
351+ {
352+ logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
353+ return false;
354+ }
355+ return true;
356+}
357+
358+
359+// For reading the face-data chunk
360+static bool ReadFaceData(OpenedFile& OFile, int32 ParentChunkEnd)
361+{
362+ uint8 NFBuffer[2];
363+ uint16 NumFaces;
364+ if (!OFile.Read(2,NFBuffer))
365+ {
366+ logError1("ERROR reading number of faces in %s",Path);
367+ return false;
368+ }
369+ uint8 *S = NFBuffer;
370+ StreamToValue(S,NumFaces);
371+
372+ int32 DataSize = 4*sizeof(uint16)*int(NumFaces);
373+ SetChunkBufferSize(DataSize);
374+ if (!OFile.Read(DataSize,ChunkBufferBase()))
375+ {
376+ logError1("ERROR reading face-chunk contents in %s",Path);
377+ return false;
378+ }
379+
380+ S = ChunkBufferBase();
381+ ModelPtr->VertIndices.resize(3*NumFaces);
382+ for (int k=0; k<NumFaces; k++)
383+ {
384+ uint16 *CurrPoly = ModelPtr->VIBase() + 3*k;
385+ uint16 Flags;
386+ StreamToList(S,CurrPoly,3);
387+ StreamToValue(S,Flags);
388+ }
389+
390+ int32 Location = 0;
391+ OFile.GetPosition(Location);
392+
393+ while(Location < ParentChunkEnd)
394+ {
395+ ChunkHeaderData ChunkHeader;
396+ if (!ReadChunkHeader(OFile,ChunkHeader)) return false;
397+
398+ switch(ChunkHeader.ID)
399+ {
400+ /*
401+ case OBJECT:
402+ if (!ReadContainer(OFile,ChunkHeader,ReadObject)) return false;
403+ break;
404+ */
405+ default:
406+ if (!SkipChunk(OFile,ChunkHeader)) return false;
407+ }
408+
409+ // Where are we now?
410+ OFile.GetPosition(Location);
411+ }
412+
413+ if (Location > ParentChunkEnd)
414+ {
415+ logError3("ERROR: Overran parent chunk: %d > %d in %s",Location,ParentChunkEnd,Path);
416+ return false;
417+ }
418+ return true;
419+}
420+
421+
422+static void LoadVertices()
423+{
424+ uint8 *S = ChunkBufferBase();
425+ uint16 Size;
426+ StreamToValue(S,Size);
427+
428+ int NVals = 3*int(Size);
429+
430+ ModelPtr->Positions.resize(NVals);
431+
432+ LoadFloats(NVals,S,ModelPtr->PosBase());
433+}
434+
435+static void LoadTextureCoordinates()
436+{
437+ uint8 *S = ChunkBufferBase();
438+ uint16 Size;
439+ StreamToValue(S,Size);
440+
441+ int NVals = 2*int(Size);
442+
443+ ModelPtr->TxtrCoords.resize(NVals);
444+
445+ LoadFloats(NVals,S,ModelPtr->TCBase());
446+}
447+
448+
449+void LoadFloats(int NVals, uint8 *Stream, GLfloat *Floats)
450+{
451+ // Test to see whether the destination floating-point values are the right size:
452+ assert(sizeof(GLfloat) == 4);
453+
454+ uint32 IntVal;
455+ GLfloat *FloatPtr = Floats;
456+ for (int k=0; k<NVals; k++, FloatPtr++)
457+ {
458+ // Intermediate step: 4-byte integer
459+ // (won't have the right value if interpreted as an integer!)
460+ StreamToValue(Stream,IntVal);
461+
462+ // This will work on any platform where
463+ // GLfloat is an IEEE 754 4-byte float.
464+ // Otherwise, use whatever appropriate conversion tricks are appropriate
465+ uint8 *SrcPtr = (uint8 *)(&IntVal);
466+ uint8 *DestPtr = (uint8 *)FloatPtr;
467+ for (int c=0; c<4; c++)
468+ DestPtr[c] = SrcPtr[c];
469+ }
470+}
471+
472+// Load a 3D Studio MAX model and convert its vertex and texture coordinates
473+// from 3DS' right-handed coordinate system to Aleph One's left-handed system.
474+bool LoadModel_Studio_RightHand(FileSpecifier& Spec, Model3D& Model)
475+{
476+ bool Result = LoadModel_Studio(Spec, Model);
477+ if (!Result) return Result;
478+
479+ logTrace("Converting handedness.");
480+
481+ // Wings 3d and Blender produce 3DS models with a z-up orientation,
482+ // and for Blender models y increases towards the back. In Aleph One,
483+ // z increases upward, and items that have been placed with 0 degrees
484+ // of rotation face in the positive-x direction.
485+ // Preserve the orientation and switch handedness by swapping and
486+ // flipping x and y.
487+ for (unsigned XPos = 0; XPos < Model.Positions.size(); XPos += 3)
488+ {
489+ GLfloat X = Model.Positions[XPos];
490+ Model.Positions[XPos] = -Model.Positions[XPos + 1];
491+ Model.Positions[XPos + 1] = -X;
492+ }
493+
494+ // Put the vertices of faces back into clockwise order.
495+ for (unsigned IPos = 0; IPos < Model.VertIndices.size(); IPos += 3)
496+ {
497+ int Index = Model.VertIndices[IPos + 1];
498+ Model.VertIndices[IPos + 1] = Model.VertIndices[IPos];
499+ Model.VertIndices[IPos] = Index;
500+ }
501+
502+ // Switch texture coordinates from right-handed (x,y) to
503+ // left-handed (row,column).
504+ for (unsigned YPos = 1; YPos < Model.TxtrCoords.size(); YPos += 2)
505+ {
506+ Model.TxtrCoords[YPos] = 1.0 - Model.TxtrCoords[YPos];
507+ }
508+
509+ return true;
510+}
511+
512+#endif // def HAVE_OPENGL
--- marathon/trunk/Source_Files/ModelView/QD3D_Loader.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/QD3D_Loader.cpp (revision 527)
@@ -1,662 +1,662 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- Alias|Wavefront Object Loader
21-
22- By Loren Petrich, September 1, 2001
23-
24- Much of the QuickDraw 3D code uses code inspired by
25- Brian Greenstone's utility 3DMF Mapper; some of it is actually copied.
26-
27-May 4, 2003 (Loren Petrich):
28- QD3D changed to Quesa
29-*/
30-
31-#include <Quesa.h>
32-#include <QuesaGroup.h>
33-#include <QuesaIO.h>
34-#include <QuesaStorage.h>
35-#include <QuesaErrors.h>
36-#include <QuesaShader.h>
37-#include <QuesaCamera.h>
38-#include <QuesaMath.h>
39-#include <QuesaLight.h>
40-#include <QuesaExtension.h>
41-#include <QuesaRenderer.h>
42-#include <QuesaGeometry.h>
43-
44-#include <ctype.h>
45-#include <stdlib.h>
46-#include <string.h>
47-#include <algorithm>
48-#include "cseries.h"
49-#include "QD3D_Loader.h"
50-
51-
52-// Debug-message destination
53-static FILE *DBOut = NULL;
54-
55-// QD3D's presence and whether it was checked
56-static bool QD3D_Present = false;
57-static bool QD3D_Presence_Checked = false;
58-
59-void SetDebugOutput_QD3D(FILE *DebugOutput)
60-{
61- DBOut = DebugOutput;
62-}
63-
64-// The level of tesselation to be used on curved surfaces
65-// So as to automatically get a reasonable value
66-const TQ3SubdivisionMethod DefaultTesselationMethod = kQ3SubdivisionMethodConstant;
67-// Reduced from BG's value of 8; important to avoid polygon overload
68-const float DefaultTesselationLength = 4;
69-static TQ3SubdivisionStyleData TesselationData =
70-{
71- DefaultTesselationMethod,
72- DefaultTesselationLength,
73- DefaultTesselationLength
74-};
75-
76-void SetTesselation_QD3D(bool IsWorldLength, float TessLength)
77-{
78- TesselationData.method =
79- IsWorldLength ? kQ3SubdivisionMethodWorldSpace : kQ3SubdivisionMethodConstant;
80- TesselationData.c1 = TesselationData.c2 = TessLength;
81-}
82-
83-void SetDefaultTesselation_QD3D()
84-{
85- TesselationData.method = DefaultTesselationMethod;
86- TesselationData.c1 = TesselationData.c2 = DefaultTesselationLength;
87-}
88-
89-// Local globals needed for triangulating objects:
90-static TQ3XObjectClass TriangulatorClass = NULL;
91-static TQ3ObjectType TriangulatorClassType = NULL;
92-static TQ3ViewObject TriangulatorView = NULL;
93-
94-// For displaying QD3D errors:
95-static void QD3D_Error_Handler(TQ3Error FirstError, TQ3Error LastError, int32 UserData)
96-{
97- if (DBOut) fprintf(DBOut,"QD3D Error: %d\n",LastError);
98-}
99-
100-// Loads a QD3D model as a group object; returns NULL if it could not be loaded
101-static TQ3Object LoadModel(FileSpecifier& File);
102-
103-// Creates a fake renderer (TriangulatorView) that decomposes surfaces into triangles;
104-// if already created, this call will not do anything
105-static bool CreateTriangulator();
106-static TQ3XFunctionPointer TriangulatorMetaHandler(TQ3XMethodType MethodType);
107-static TQ3Status TriangulatorStartFrame(TQ3ViewObject View, void *PrivateData,
108- TQ3DrawContextObject DrawContext);
109-static TQ3Status TriangulatorStartPass(TQ3ViewObject View, void *PrivateData,
110- TQ3CameraObject Camera, TQ3GroupObject LightGroup);
111-static TQ3ViewStatus TriangulatorEndPass(TQ3ViewObject View, void *PrivateData);
112-static void TriangulatorCancel(TQ3ViewObject View, void *PrivateData);
113-static TQ3XFunctionPointer TriangulatorGeometry_MetaHandler(TQ3XMethodType MethodType);
114-static TQ3Status TriangulatorGeometry_Triangle(TQ3ViewObject View, void *PrivateData,
115- TQ3GeometryObject Triangle, const TQ3TriangleData *TriangleData);
116-
117-// Accumulated triangle vertices:
118-static bool TxtrCoordsPresent = false;
119-static bool NormalsPresent = false;
120-static bool ColorsPresent = false;
121-
122-struct FullVertexData
123-{
124- // For speeding up working with it
125- enum
126- {
127- POS_BASE = 0, // 3 position components
128- TC_BASE = 3, // 2 txtr-coord components
129- NORM_BASE = 5, // 3 normal components
130- COLOR_BASE = 8, // 3 color components
131- SIZE = 11
132- };
133- GLfloat Data[SIZE];
134-};
135-static vector<FullVertexData> FullVertexList;
136-
137-// For deciding whether vertices are coincident
138-static GLfloat Thresholds[FullVertexData::SIZE];
139-
140-// Function for testing two indexed vertices:
141-static int CompareVertices(const void *VI1, const void *VI2);
142-
143-static void StartAccumulatingVertices();
144-static void GetVerticesIntoModel(Model3D& Model);
145-
146-
147-bool LoadModel_QD3D(FileSpecifier& Spec, Model3D& Model)
148-{
149- // Clear out the final model object
150- Model.Clear();
151-
152- // Test for QD3D/Quesa's presence and initialize it if not present
153- if (QD3D_Presence_Checked)
154- {
155- if (!QD3D_Present) return false;
156- }
157- else
158- {
159- QD3D_Presence_Checked = true;
160-
161- // MacOS QD3D; modify this for Quesa as appropriate
162- if ((void*)Q3Initialize != (void*)kUnresolvedCFragSymbolAddress)
163- {
164- TQ3Status Success = Q3Initialize();
165- QD3D_Present = (Success == kQ3Success);
166- }
167-
168- // Do additional setup;
169- // if the triangulator could not be created, then act as if
170- // QD3D/Quesa had not been loaded
171- if (QD3D_Present)
172- {
173- Q3Error_Register(QD3D_Error_Handler,0);
174- QD3D_Present = CreateTriangulator();
175- }
176- if (!QD3D_Present)
177- Q3Exit();
178- }
179-
180- if (DBOut)
181- {
182- // Read buffer
183- const int BufferSize = 256;
184- char Buffer[BufferSize];
185- Spec.GetName(Buffer);
186- fprintf(DBOut,"Loading QuickDraw-3D model file %s\n",Buffer);
187- }
188-
189- TQ3Object ModelObject = LoadModel(Spec);
190- if (!ModelObject) return false;
191-
192- StartAccumulatingVertices();
193-
194- if (Q3View_StartRendering(TriangulatorView) == kQ3Failure)
195- {
196- if (DBOut) fprintf(DBOut,"ERROR: couldn't start triangulation 'rendering'\n");
197- Q3Object_Dispose(ModelObject);
198- return false;
199- }
200- do
201- {
202- Q3SubdivisionStyle_Submit(&TesselationData, TriangulatorView);
203- if (Q3Object_Submit(ModelObject, TriangulatorView) == kQ3Failure)
204- {
205- if (DBOut) fprintf(DBOut,"ERROR: model could not be 'rendered'\n");
206- }
207- }
208- while (Q3View_EndRendering(TriangulatorView) == kQ3ViewStatusRetraverse);
209-
210- // Done with the model
211- Q3Object_Dispose(ModelObject);
212-
213- GetVerticesIntoModel(Model);
214-
215- return !Model.Positions.empty();
216-}
217-
218-
219-// Load a QD3D model object:
220-TQ3Object LoadModel(FileSpecifier& Spec)
221-{
222- // First create a file object and a storage object,
223- // and associate the storage object with the file object.
224-
225- // MacOS / FSSpec version:
226- // modify as necessary for Quesa and SDL --
227- // Quesa has similar calls with the MacOS FSSpec being replaced by a Windows file handle
228- // and a Unix file path
229- TQ3StorageObject StorageObject = Q3FSSpecStorage_New(&Spec.GetSpec());
230- if (!StorageObject) return NULL;
231-
232- TQ3FileObject FileObject = Q3File_New();
233- if (!FileObject)
234- {
235- Q3Object_Dispose(StorageObject);
236- return NULL;
237- }
238-
239- Q3File_SetStorage(FileObject, StorageObject);
240- Q3Object_Dispose(StorageObject);
241-
242- // Read in the model object
243- if (Q3File_OpenRead(FileObject, NULL) != kQ3Success)
244- {
245- Q3Object_Dispose(FileObject);
246- return NULL;
247- }
248-
249- // Create a group for holding all the read-in objects
250- TQ3Object ModelObject = Q3DisplayGroup_New();
251- if (!ModelObject)
252- {
253- Q3Object_Dispose(FileObject);
254- return NULL;
255- }
256-
257- // All at once: slurp in now and process later
258- while (Q3File_IsEndOfFile(FileObject) == kQ3False)
259- {
260- // Grab an object from the file
261- TQ3Object MemberObject = Q3File_ReadObject(FileObject);
262- if (!MemberObject)
263- continue;
264-
265- // Only interested in renderable objects (no hints or comments or ...)
266- if (Q3Object_IsDrawable(MemberObject))
267- Q3Group_AddObject(ModelObject, MemberObject);
268-
269- // Clean up
270- if (MemberObject)
271- Q3Object_Dispose(MemberObject);
272- }
273-
274- // Done with the file object
275- Q3Object_Dispose(FileObject);
276-
277- if (Q3Error_Get(NULL) != kQ3ErrorNone)
278- {
279- if (ModelObject)
280- Q3Object_Dispose(ModelObject);
281- return NULL;
282- }
283-
284- // Finally!
285- return ModelObject;
286-}
287-
288-
289-// Returns whether the creation was a success
290-bool CreateTriangulator(void)
291-{
292- // No need to create it more than once
293- if (TriangulatorView) return true;
294-
295- TriangulatorClass =
296- Q3XObjectHierarchy_RegisterClass(
297- kQ3SharedTypeRenderer,
298- &TriangulatorClassType,
299- "BG-Inspired Triangulator",
300- TriangulatorMetaHandler,
301- NULL, 0, 0);
302-
303- if(!TriangulatorClass) return false;
304- if(!TriangulatorClassType) return false;
305-
306- // Dummy draw context: a pixmap (image buffer) that is empty
307- TQ3PixmapDrawContextData PixmapContextData =
308- {
309- // drawContextData
310- {
311- kQ3ClearMethodWithColor,
312- {0,0,0,0},
313- {{0,0},{0,0}},
314- kQ3False,
315- NULL,
316- kQ3False,
317- kQ3True
318- },
319- // pixmap
320- {
321- NULL,
322- 0,
323- 0,
324- 0,
325- 32,
326- kQ3PixelTypeARGB32,
327- kQ3EndianBig,
328- kQ3EndianBig
329- }
330- };
331-
332- TQ3DrawContextObject DrawContextObject = Q3PixmapDrawContext_New(&PixmapContextData);
333- if (!DrawContextObject) return false;
334-
335- TQ3RendererObject RendererObject = Q3Renderer_NewFromType(TriangulatorClassType);
336- if (!RendererObject)
337- {
338- Q3Object_Dispose(DrawContextObject);
339- return false;
340- }
341-
342- TriangulatorView = Q3View_New();
343- bool Success = TriangulatorView != NULL;
344- if(Success)
345- Success = (Q3View_SetDrawContext(TriangulatorView, DrawContextObject) == kQ3Success);
346- if(Success)
347- Success = (Q3View_SetRenderer(TriangulatorView, RendererObject) == kQ3Success);
348-
349- // No longer needed
350- Q3Object_Dispose(DrawContextObject);
351- Q3Object_Dispose(RendererObject);
352- return Success;
353-}
354-
355-static TQ3XFunctionPointer TriangulatorMetaHandler(TQ3XMethodType MethodType)
356-{
357- switch(MethodType)
358- {
359- case kQ3XMethodTypeRendererStartFrame:
360- return (TQ3XFunctionPointer)TriangulatorStartFrame;
361-
362- case kQ3XMethodTypeRendererStartPass:
363- return (TQ3XFunctionPointer)TriangulatorStartPass;
364-
365- case kQ3XMethodTypeRendererEndPass:
366- return (TQ3XFunctionPointer)TriangulatorEndPass;
367-
368- case kQ3XMethodTypeRendererCancel:
369- return (TQ3XFunctionPointer)TriangulatorCancel;
370-
371- case kQ3XMethodTypeRendererSubmitGeometryMetaHandler:
372- return (TQ3XFunctionPointer)TriangulatorGeometry_MetaHandler;
373-
374- case kQ3XMethodTypeRendererIsInteractive: // Why does BG's code do this?
375- return (TQ3XFunctionPointer)kQ3True;
376-
377- default:
378- return NULL;
379- }
380-}
381-
382-TQ3Status TriangulatorStartFrame(TQ3ViewObject View, void *PrivateData,
383- TQ3DrawContextObject DrawContext)
384-{
385- return kQ3Success;
386-}
387-
388-TQ3Status TriangulatorStartPass(TQ3ViewObject View, void *PrivateData,
389- TQ3CameraObject Camera, TQ3GroupObject LightGroup)
390-{
391- return kQ3Success;
392-}
393-
394-TQ3ViewStatus TriangulatorEndPass(TQ3ViewObject View, void *PrivateData)
395-{
396- return kQ3ViewStatusDone;
397-}
398-
399-void TriangulatorCancel(TQ3ViewObject View, void *PrivateData)
400-{
401-}
402-
403-TQ3XFunctionPointer TriangulatorGeometry_MetaHandler(TQ3XMethodType MethodType)
404-{
405- // Force the renderer to make triangles (Brian Greenstone's idea)
406- switch(MethodType)
407- {
408- case kQ3GeometryTypeTriangle:
409- return (TQ3XFunctionPointer)TriangulatorGeometry_Triangle;
410-
411- default:
412- return NULL;
413- }
414-}
415-
416-TQ3Status TriangulatorGeometry_Triangle(TQ3ViewObject View, void *PrivateData,
417- TQ3GeometryObject Triangle, const TQ3TriangleData *TriangleData)
418-{
419- // Just in case...
420- if (!TriangleData) return kQ3Success;
421-
422- // Get the overall normal and color (DiffuseColor)
423- TQ3Boolean OverallColorPresent =
424- Q3AttributeSet_Contains(TriangleData->triangleAttributeSet,kQ3AttributeTypeDiffuseColor);
425-
426- TQ3ColorRGB OverallColor;
427- if (OverallColorPresent);
428- Q3AttributeSet_Get(TriangleData->triangleAttributeSet,kQ3AttributeTypeDiffuseColor,&OverallColor);
429-
430- // Load the triangle's contents into a vertex object
431- for (int k=0; k<3; k++)
432- {
433- FullVertexData FullVertex;
434- obj_clear(FullVertex);
435-
436- const TQ3Vertex3D& Vertex = TriangleData->vertices[k];
437- GLfloat *FV_Pos = FullVertex.Data + FullVertexData::POS_BASE;
438- FV_Pos[0] = Vertex.point.x;
439- FV_Pos[1] = Vertex.point.y;
440- FV_Pos[2] = Vertex.point.z;
441-
442- TQ3Param2D TxtrCoord;
443- TQ3Boolean TxtrCoord_Present =
444- Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeShadingUV);
445- if (TxtrCoord_Present)
446- Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeShadingUV,&TxtrCoord);
447- else
448- {
449- TxtrCoord_Present =
450- Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeSurfaceUV);
451- if (TxtrCoord_Present)
452- Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeSurfaceUV,&TxtrCoord);
453- else
454- TxtrCoordsPresent = false;
455- }
456-
457- if (TxtrCoordsPresent)
458- {
459- GLfloat *FV_TC = FullVertex.Data + FullVertexData::TC_BASE;
460- FV_TC[0] = TxtrCoord.u;
461- FV_TC[1] = TxtrCoord.v;
462- }
463-
464- TQ3Vector3D Normal;
465- TQ3Boolean Normal_Present =
466- Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeNormal);
467- if (Normal_Present)
468- Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeNormal,&Normal);
469- else
470- NormalsPresent = false;
471-
472- if (NormalsPresent)
473- {
474- GLfloat *FV_Norm = FullVertex.Data + FullVertexData::NORM_BASE;
475- FV_Norm[0] = Normal.x;
476- FV_Norm[1] = Normal.y;
477- FV_Norm[2] = Normal.z;
478- }
479-
480- TQ3ColorRGB Color;
481- TQ3Boolean Color_Present =
482- Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeDiffuseColor);
483- if (Color_Present)
484- Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeDiffuseColor,&Color);
485- else if (OverallColorPresent)
486- Color = OverallColor;
487- else
488- ColorsPresent = false;
489-
490- if (ColorsPresent)
491- {
492- GLfloat *FV_Color = FullVertex.Data + FullVertexData::COLOR_BASE;
493- FV_Color[0] = Color.r;
494- FV_Color[1] = Color.g;
495- FV_Color[2] = Color.b;
496- }
497- FullVertexList.push_back(FullVertex);
498- }
499-
500- return kQ3Success;
501-}
502-
503-
504-void StartAccumulatingVertices()
505-{
506- // One of them absent means ignore all of them
507- TxtrCoordsPresent = true;
508- NormalsPresent = true;
509- ColorsPresent = true;
510- FullVertexList.clear();
511-}
512-
513-void GetVerticesIntoModel(Model3D& Model)
514-{
515- // Search for redundant vertices -- if there are any vertices to look for
516- if (FullVertexList.empty()) return;
517-
518- // First, index-sort them
519-
520- // Set up for index sorting by finding the position range
521- // (this code is a bit smarter than BG's 3DMF Mapper code for doing that);
522- // this is unnecessary for the other vertex data
523- GLfloat PosMin[3], PosMax[3];
524- for (int c=0; c<3; c++)
525- PosMin[c] = PosMax[c] = FullVertexList[0].Data[FullVertexData::POS_BASE+c];
526-
527- int OldNumVertices = FullVertexList.size();
528- for (int k=0; k<OldNumVertices; k++)
529- {
530- GLfloat *Pos = FullVertexList[0].Data + FullVertexData::POS_BASE;
531- for (int c=0; c<3; c++)
532- {
533- GLfloat PC = Pos[c];
534- PosMin[c] = MIN(PosMin[c],PC);
535- PosMax[c] = MAX(PosMax[c],PC);
536- }
537- }
538- const GLfloat ThresholdConstant = 0.001;
539- for (int c=0; c<3; c++)
540- Thresholds[FullVertexData::POS_BASE+c] = ThresholdConstant*(PosMax[c]-PosMin[c]);
541- for (int c=0; c<2; c++)
542- Thresholds[FullVertexData::TC_BASE+c] = ThresholdConstant;
543- for (int c=0; c<3; c++)
544- Thresholds[FullVertexData::NORM_BASE+c] = ThresholdConstant;
545- for (int c=0; c<3; c++)
546- Thresholds[FullVertexData::COLOR_BASE+c] = ThresholdConstant;
547-
548- // Now the actual sorting
549- vector<int> Indices(OldNumVertices);
550- for (int k=0; k<Indices.size(); k++)
551- Indices[k] = k;
552-
553- // STL sort may be slow
554- qsort(&Indices[0],OldNumVertices,sizeof(int),CompareVertices);
555-
556- // Set up vertex ranks in place; count the distinct vertices.
557- // Also, use the first one of a non-distinct set in sorted order
558- // as the new vertex
559- Model.VertIndices.resize(OldNumVertices);
560- vector<int> VertexSelect;
561- VertexSelect.resize(OldNumVertices);
562-
563- int TopRank = 0;
564- int PrevIndex = Indices[0];
565- VertexSelect[TopRank] = PrevIndex;
566- Model.VertIndices[PrevIndex] = TopRank;
567- for (int k=1; k<OldNumVertices; k++)
568- {
569- int CurrIndex = Indices[k];
570- if (CompareVertices(&PrevIndex,&CurrIndex) != 0)
571- {
572- TopRank++;
573- VertexSelect[TopRank] = CurrIndex;
574- }
575- Model.VertIndices[CurrIndex] = TopRank;
576- PrevIndex = CurrIndex;
577- }
578- int NewNumVertices = TopRank + 1;
579-
580- // Fill up the rest of model arrays
581- Model.Positions.resize(3*NewNumVertices);
582- GLfloat *PosPtr = &Model.Positions[0];
583-
584- GLfloat *TCPtr = NULL;
585- if (TxtrCoordsPresent)
586- {
587- Model.TxtrCoords.resize(2*NewNumVertices);
588- TCPtr = &Model.TxtrCoords[0];
589- }
590-
591- GLfloat *NormPtr = NULL;
592- if (NormalsPresent)
593- {
594- Model.Normals.resize(3*NewNumVertices);
595- NormPtr = &Model.Normals[0];
596- }
597-
598- GLfloat *ColorPtr = NULL;
599- if (ColorsPresent)
600- {
601- Model.Colors.resize(3*NewNumVertices);
602- ColorPtr = &Model.Colors[0];
603- }
604-
605- for (int k=0; k<NewNumVertices; k++)
606- {
607- FullVertexData& FullVertex = FullVertexList[VertexSelect[k]];
608-
609- GLfloat *Pos = FullVertex.Data + FullVertexData::POS_BASE;
610- for (int c=0; c<3; c++)
611- *(PosPtr++) = *(Pos++);
612-
613- if (TxtrCoordsPresent)
614- {
615- GLfloat *TC = FullVertex.Data + FullVertexData::TC_BASE;
616- for (int c=0; c<2; c++)
617- *(TCPtr++) = *(TC++);
618- }
619-
620- if (NormalsPresent)
621- {
622- GLfloat *Norm = FullVertex.Data + FullVertexData::NORM_BASE;
623- for (int c=0; c<3; c++)
624- *(NormPtr++) = *(Norm++);
625- }
626-
627- if (ColorsPresent)
628- {
629- GLfloat *Color = FullVertex.Data + FullVertexData::COLOR_BASE;
630- for (int c=0; c<3; c++)
631- *(ColorPtr++) = *(Color++);
632- }
633- }
634-
635- // All done!
636- FullVertexList.clear();
637-}
638-
639-int CompareVertices(const void *VI1, const void *VI2)
640-{
641- int *IP1 = (int *)VI1;
642- int *IP2 = (int *)VI2;
643- GLfloat *Data1 = FullVertexList[*IP1].Data;
644- GLfloat *Data2 = FullVertexList[*IP2].Data;
645- GLfloat *ThrPtr = Thresholds;
646-
647- // Compare the components one at a time; this will give a reasonable sort order
648- for (int c=0; c<FullVertexData::SIZE; c++)
649- {
650- // Make the operation exactly reversible
651- GLfloat DVal1 = *Data1;
652- GLfloat DVal2 = *Data2;
653- GLfloat Thr = *ThrPtr;
654- if (DVal1 > DVal2 + Thr) return -1;
655- else if (DVal2 > DVal1 + Thr) return 1;
656- Data1++;
657- Data2++;
658- ThrPtr++;
659- }
660- // All within range
661- return 0;
662-}
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ Alias|Wavefront Object Loader
21+
22+ By Loren Petrich, September 1, 2001
23+
24+ Much of the QuickDraw 3D code uses code inspired by
25+ Brian Greenstone's utility 3DMF Mapper; some of it is actually copied.
26+
27+May 4, 2003 (Loren Petrich):
28+ QD3D changed to Quesa
29+*/
30+
31+#include <Quesa.h>
32+#include <QuesaGroup.h>
33+#include <QuesaIO.h>
34+#include <QuesaStorage.h>
35+#include <QuesaErrors.h>
36+#include <QuesaShader.h>
37+#include <QuesaCamera.h>
38+#include <QuesaMath.h>
39+#include <QuesaLight.h>
40+#include <QuesaExtension.h>
41+#include <QuesaRenderer.h>
42+#include <QuesaGeometry.h>
43+
44+#include <ctype.h>
45+#include <stdlib.h>
46+#include <string.h>
47+#include <algorithm>
48+#include "cseries.h"
49+#include "QD3D_Loader.h"
50+
51+
52+// Debug-message destination
53+static FILE *DBOut = NULL;
54+
55+// QD3D's presence and whether it was checked
56+static bool QD3D_Present = false;
57+static bool QD3D_Presence_Checked = false;
58+
59+void SetDebugOutput_QD3D(FILE *DebugOutput)
60+{
61+ DBOut = DebugOutput;
62+}
63+
64+// The level of tesselation to be used on curved surfaces
65+// So as to automatically get a reasonable value
66+const TQ3SubdivisionMethod DefaultTesselationMethod = kQ3SubdivisionMethodConstant;
67+// Reduced from BG's value of 8; important to avoid polygon overload
68+const float DefaultTesselationLength = 4;
69+static TQ3SubdivisionStyleData TesselationData =
70+{
71+ DefaultTesselationMethod,
72+ DefaultTesselationLength,
73+ DefaultTesselationLength
74+};
75+
76+void SetTesselation_QD3D(bool IsWorldLength, float TessLength)
77+{
78+ TesselationData.method =
79+ IsWorldLength ? kQ3SubdivisionMethodWorldSpace : kQ3SubdivisionMethodConstant;
80+ TesselationData.c1 = TesselationData.c2 = TessLength;
81+}
82+
83+void SetDefaultTesselation_QD3D()
84+{
85+ TesselationData.method = DefaultTesselationMethod;
86+ TesselationData.c1 = TesselationData.c2 = DefaultTesselationLength;
87+}
88+
89+// Local globals needed for triangulating objects:
90+static TQ3XObjectClass TriangulatorClass = NULL;
91+static TQ3ObjectType TriangulatorClassType = NULL;
92+static TQ3ViewObject TriangulatorView = NULL;
93+
94+// For displaying QD3D errors:
95+static void QD3D_Error_Handler(TQ3Error FirstError, TQ3Error LastError, int32 UserData)
96+{
97+ if (DBOut) fprintf(DBOut,"QD3D Error: %d\n",LastError);
98+}
99+
100+// Loads a QD3D model as a group object; returns NULL if it could not be loaded
101+static TQ3Object LoadModel(FileSpecifier& File);
102+
103+// Creates a fake renderer (TriangulatorView) that decomposes surfaces into triangles;
104+// if already created, this call will not do anything
105+static bool CreateTriangulator();
106+static TQ3XFunctionPointer TriangulatorMetaHandler(TQ3XMethodType MethodType);
107+static TQ3Status TriangulatorStartFrame(TQ3ViewObject View, void *PrivateData,
108+ TQ3DrawContextObject DrawContext);
109+static TQ3Status TriangulatorStartPass(TQ3ViewObject View, void *PrivateData,
110+ TQ3CameraObject Camera, TQ3GroupObject LightGroup);
111+static TQ3ViewStatus TriangulatorEndPass(TQ3ViewObject View, void *PrivateData);
112+static void TriangulatorCancel(TQ3ViewObject View, void *PrivateData);
113+static TQ3XFunctionPointer TriangulatorGeometry_MetaHandler(TQ3XMethodType MethodType);
114+static TQ3Status TriangulatorGeometry_Triangle(TQ3ViewObject View, void *PrivateData,
115+ TQ3GeometryObject Triangle, const TQ3TriangleData *TriangleData);
116+
117+// Accumulated triangle vertices:
118+static bool TxtrCoordsPresent = false;
119+static bool NormalsPresent = false;
120+static bool ColorsPresent = false;
121+
122+struct FullVertexData
123+{
124+ // For speeding up working with it
125+ enum
126+ {
127+ POS_BASE = 0, // 3 position components
128+ TC_BASE = 3, // 2 txtr-coord components
129+ NORM_BASE = 5, // 3 normal components
130+ COLOR_BASE = 8, // 3 color components
131+ SIZE = 11
132+ };
133+ GLfloat Data[SIZE];
134+};
135+static vector<FullVertexData> FullVertexList;
136+
137+// For deciding whether vertices are coincident
138+static GLfloat Thresholds[FullVertexData::SIZE];
139+
140+// Function for testing two indexed vertices:
141+static int CompareVertices(const void *VI1, const void *VI2);
142+
143+static void StartAccumulatingVertices();
144+static void GetVerticesIntoModel(Model3D& Model);
145+
146+
147+bool LoadModel_QD3D(FileSpecifier& Spec, Model3D& Model)
148+{
149+ // Clear out the final model object
150+ Model.Clear();
151+
152+ // Test for QD3D/Quesa's presence and initialize it if not present
153+ if (QD3D_Presence_Checked)
154+ {
155+ if (!QD3D_Present) return false;
156+ }
157+ else
158+ {
159+ QD3D_Presence_Checked = true;
160+
161+ // MacOS QD3D; modify this for Quesa as appropriate
162+ if ((void*)Q3Initialize != (void*)kUnresolvedCFragSymbolAddress)
163+ {
164+ TQ3Status Success = Q3Initialize();
165+ QD3D_Present = (Success == kQ3Success);
166+ }
167+
168+ // Do additional setup;
169+ // if the triangulator could not be created, then act as if
170+ // QD3D/Quesa had not been loaded
171+ if (QD3D_Present)
172+ {
173+ Q3Error_Register(QD3D_Error_Handler,0);
174+ QD3D_Present = CreateTriangulator();
175+ }
176+ if (!QD3D_Present)
177+ Q3Exit();
178+ }
179+
180+ if (DBOut)
181+ {
182+ // Read buffer
183+ const int BufferSize = 256;
184+ char Buffer[BufferSize];
185+ Spec.GetName(Buffer);
186+ fprintf(DBOut,"Loading QuickDraw-3D model file %s\n",Buffer);
187+ }
188+
189+ TQ3Object ModelObject = LoadModel(Spec);
190+ if (!ModelObject) return false;
191+
192+ StartAccumulatingVertices();
193+
194+ if (Q3View_StartRendering(TriangulatorView) == kQ3Failure)
195+ {
196+ if (DBOut) fprintf(DBOut,"ERROR: couldn't start triangulation 'rendering'\n");
197+ Q3Object_Dispose(ModelObject);
198+ return false;
199+ }
200+ do
201+ {
202+ Q3SubdivisionStyle_Submit(&TesselationData, TriangulatorView);
203+ if (Q3Object_Submit(ModelObject, TriangulatorView) == kQ3Failure)
204+ {
205+ if (DBOut) fprintf(DBOut,"ERROR: model could not be 'rendered'\n");
206+ }
207+ }
208+ while (Q3View_EndRendering(TriangulatorView) == kQ3ViewStatusRetraverse);
209+
210+ // Done with the model
211+ Q3Object_Dispose(ModelObject);
212+
213+ GetVerticesIntoModel(Model);
214+
215+ return !Model.Positions.empty();
216+}
217+
218+
219+// Load a QD3D model object:
220+TQ3Object LoadModel(FileSpecifier& Spec)
221+{
222+ // First create a file object and a storage object,
223+ // and associate the storage object with the file object.
224+
225+ // MacOS / FSSpec version:
226+ // modify as necessary for Quesa and SDL --
227+ // Quesa has similar calls with the MacOS FSSpec being replaced by a Windows file handle
228+ // and a Unix file path
229+ TQ3StorageObject StorageObject = Q3FSSpecStorage_New(&Spec.GetSpec());
230+ if (!StorageObject) return NULL;
231+
232+ TQ3FileObject FileObject = Q3File_New();
233+ if (!FileObject)
234+ {
235+ Q3Object_Dispose(StorageObject);
236+ return NULL;
237+ }
238+
239+ Q3File_SetStorage(FileObject, StorageObject);
240+ Q3Object_Dispose(StorageObject);
241+
242+ // Read in the model object
243+ if (Q3File_OpenRead(FileObject, NULL) != kQ3Success)
244+ {
245+ Q3Object_Dispose(FileObject);
246+ return NULL;
247+ }
248+
249+ // Create a group for holding all the read-in objects
250+ TQ3Object ModelObject = Q3DisplayGroup_New();
251+ if (!ModelObject)
252+ {
253+ Q3Object_Dispose(FileObject);
254+ return NULL;
255+ }
256+
257+ // All at once: slurp in now and process later
258+ while (Q3File_IsEndOfFile(FileObject) == kQ3False)
259+ {
260+ // Grab an object from the file
261+ TQ3Object MemberObject = Q3File_ReadObject(FileObject);
262+ if (!MemberObject)
263+ continue;
264+
265+ // Only interested in renderable objects (no hints or comments or ...)
266+ if (Q3Object_IsDrawable(MemberObject))
267+ Q3Group_AddObject(ModelObject, MemberObject);
268+
269+ // Clean up
270+ if (MemberObject)
271+ Q3Object_Dispose(MemberObject);
272+ }
273+
274+ // Done with the file object
275+ Q3Object_Dispose(FileObject);
276+
277+ if (Q3Error_Get(NULL) != kQ3ErrorNone)
278+ {
279+ if (ModelObject)
280+ Q3Object_Dispose(ModelObject);
281+ return NULL;
282+ }
283+
284+ // Finally!
285+ return ModelObject;
286+}
287+
288+
289+// Returns whether the creation was a success
290+bool CreateTriangulator(void)
291+{
292+ // No need to create it more than once
293+ if (TriangulatorView) return true;
294+
295+ TriangulatorClass =
296+ Q3XObjectHierarchy_RegisterClass(
297+ kQ3SharedTypeRenderer,
298+ &TriangulatorClassType,
299+ "BG-Inspired Triangulator",
300+ TriangulatorMetaHandler,
301+ NULL, 0, 0);
302+
303+ if(!TriangulatorClass) return false;
304+ if(!TriangulatorClassType) return false;
305+
306+ // Dummy draw context: a pixmap (image buffer) that is empty
307+ TQ3PixmapDrawContextData PixmapContextData =
308+ {
309+ // drawContextData
310+ {
311+ kQ3ClearMethodWithColor,
312+ {0,0,0,0},
313+ {{0,0},{0,0}},
314+ kQ3False,
315+ NULL,
316+ kQ3False,
317+ kQ3True
318+ },
319+ // pixmap
320+ {
321+ NULL,
322+ 0,
323+ 0,
324+ 0,
325+ 32,
326+ kQ3PixelTypeARGB32,
327+ kQ3EndianBig,
328+ kQ3EndianBig
329+ }
330+ };
331+
332+ TQ3DrawContextObject DrawContextObject = Q3PixmapDrawContext_New(&PixmapContextData);
333+ if (!DrawContextObject) return false;
334+
335+ TQ3RendererObject RendererObject = Q3Renderer_NewFromType(TriangulatorClassType);
336+ if (!RendererObject)
337+ {
338+ Q3Object_Dispose(DrawContextObject);
339+ return false;
340+ }
341+
342+ TriangulatorView = Q3View_New();
343+ bool Success = TriangulatorView != NULL;
344+ if(Success)
345+ Success = (Q3View_SetDrawContext(TriangulatorView, DrawContextObject) == kQ3Success);
346+ if(Success)
347+ Success = (Q3View_SetRenderer(TriangulatorView, RendererObject) == kQ3Success);
348+
349+ // No longer needed
350+ Q3Object_Dispose(DrawContextObject);
351+ Q3Object_Dispose(RendererObject);
352+ return Success;
353+}
354+
355+static TQ3XFunctionPointer TriangulatorMetaHandler(TQ3XMethodType MethodType)
356+{
357+ switch(MethodType)
358+ {
359+ case kQ3XMethodTypeRendererStartFrame:
360+ return (TQ3XFunctionPointer)TriangulatorStartFrame;
361+
362+ case kQ3XMethodTypeRendererStartPass:
363+ return (TQ3XFunctionPointer)TriangulatorStartPass;
364+
365+ case kQ3XMethodTypeRendererEndPass:
366+ return (TQ3XFunctionPointer)TriangulatorEndPass;
367+
368+ case kQ3XMethodTypeRendererCancel:
369+ return (TQ3XFunctionPointer)TriangulatorCancel;
370+
371+ case kQ3XMethodTypeRendererSubmitGeometryMetaHandler:
372+ return (TQ3XFunctionPointer)TriangulatorGeometry_MetaHandler;
373+
374+ case kQ3XMethodTypeRendererIsInteractive: // Why does BG's code do this?
375+ return (TQ3XFunctionPointer)kQ3True;
376+
377+ default:
378+ return NULL;
379+ }
380+}
381+
382+TQ3Status TriangulatorStartFrame(TQ3ViewObject View, void *PrivateData,
383+ TQ3DrawContextObject DrawContext)
384+{
385+ return kQ3Success;
386+}
387+
388+TQ3Status TriangulatorStartPass(TQ3ViewObject View, void *PrivateData,
389+ TQ3CameraObject Camera, TQ3GroupObject LightGroup)
390+{
391+ return kQ3Success;
392+}
393+
394+TQ3ViewStatus TriangulatorEndPass(TQ3ViewObject View, void *PrivateData)
395+{
396+ return kQ3ViewStatusDone;
397+}
398+
399+void TriangulatorCancel(TQ3ViewObject View, void *PrivateData)
400+{
401+}
402+
403+TQ3XFunctionPointer TriangulatorGeometry_MetaHandler(TQ3XMethodType MethodType)
404+{
405+ // Force the renderer to make triangles (Brian Greenstone's idea)
406+ switch(MethodType)
407+ {
408+ case kQ3GeometryTypeTriangle:
409+ return (TQ3XFunctionPointer)TriangulatorGeometry_Triangle;
410+
411+ default:
412+ return NULL;
413+ }
414+}
415+
416+TQ3Status TriangulatorGeometry_Triangle(TQ3ViewObject View, void *PrivateData,
417+ TQ3GeometryObject Triangle, const TQ3TriangleData *TriangleData)
418+{
419+ // Just in case...
420+ if (!TriangleData) return kQ3Success;
421+
422+ // Get the overall normal and color (DiffuseColor)
423+ TQ3Boolean OverallColorPresent =
424+ Q3AttributeSet_Contains(TriangleData->triangleAttributeSet,kQ3AttributeTypeDiffuseColor);
425+
426+ TQ3ColorRGB OverallColor;
427+ if (OverallColorPresent);
428+ Q3AttributeSet_Get(TriangleData->triangleAttributeSet,kQ3AttributeTypeDiffuseColor,&OverallColor);
429+
430+ // Load the triangle's contents into a vertex object
431+ for (int k=0; k<3; k++)
432+ {
433+ FullVertexData FullVertex;
434+ obj_clear(FullVertex);
435+
436+ const TQ3Vertex3D& Vertex = TriangleData->vertices[k];
437+ GLfloat *FV_Pos = FullVertex.Data + FullVertexData::POS_BASE;
438+ FV_Pos[0] = Vertex.point.x;
439+ FV_Pos[1] = Vertex.point.y;
440+ FV_Pos[2] = Vertex.point.z;
441+
442+ TQ3Param2D TxtrCoord;
443+ TQ3Boolean TxtrCoord_Present =
444+ Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeShadingUV);
445+ if (TxtrCoord_Present)
446+ Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeShadingUV,&TxtrCoord);
447+ else
448+ {
449+ TxtrCoord_Present =
450+ Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeSurfaceUV);
451+ if (TxtrCoord_Present)
452+ Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeSurfaceUV,&TxtrCoord);
453+ else
454+ TxtrCoordsPresent = false;
455+ }
456+
457+ if (TxtrCoordsPresent)
458+ {
459+ GLfloat *FV_TC = FullVertex.Data + FullVertexData::TC_BASE;
460+ FV_TC[0] = TxtrCoord.u;
461+ FV_TC[1] = TxtrCoord.v;
462+ }
463+
464+ TQ3Vector3D Normal;
465+ TQ3Boolean Normal_Present =
466+ Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeNormal);
467+ if (Normal_Present)
468+ Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeNormal,&Normal);
469+ else
470+ NormalsPresent = false;
471+
472+ if (NormalsPresent)
473+ {
474+ GLfloat *FV_Norm = FullVertex.Data + FullVertexData::NORM_BASE;
475+ FV_Norm[0] = Normal.x;
476+ FV_Norm[1] = Normal.y;
477+ FV_Norm[2] = Normal.z;
478+ }
479+
480+ TQ3ColorRGB Color;
481+ TQ3Boolean Color_Present =
482+ Q3AttributeSet_Contains(Vertex.attributeSet,kQ3AttributeTypeDiffuseColor);
483+ if (Color_Present)
484+ Q3AttributeSet_Get(Vertex.attributeSet,kQ3AttributeTypeDiffuseColor,&Color);
485+ else if (OverallColorPresent)
486+ Color = OverallColor;
487+ else
488+ ColorsPresent = false;
489+
490+ if (ColorsPresent)
491+ {
492+ GLfloat *FV_Color = FullVertex.Data + FullVertexData::COLOR_BASE;
493+ FV_Color[0] = Color.r;
494+ FV_Color[1] = Color.g;
495+ FV_Color[2] = Color.b;
496+ }
497+ FullVertexList.push_back(FullVertex);
498+ }
499+
500+ return kQ3Success;
501+}
502+
503+
504+void StartAccumulatingVertices()
505+{
506+ // One of them absent means ignore all of them
507+ TxtrCoordsPresent = true;
508+ NormalsPresent = true;
509+ ColorsPresent = true;
510+ FullVertexList.clear();
511+}
512+
513+void GetVerticesIntoModel(Model3D& Model)
514+{
515+ // Search for redundant vertices -- if there are any vertices to look for
516+ if (FullVertexList.empty()) return;
517+
518+ // First, index-sort them
519+
520+ // Set up for index sorting by finding the position range
521+ // (this code is a bit smarter than BG's 3DMF Mapper code for doing that);
522+ // this is unnecessary for the other vertex data
523+ GLfloat PosMin[3], PosMax[3];
524+ for (int c=0; c<3; c++)
525+ PosMin[c] = PosMax[c] = FullVertexList[0].Data[FullVertexData::POS_BASE+c];
526+
527+ int OldNumVertices = FullVertexList.size();
528+ for (int k=0; k<OldNumVertices; k++)
529+ {
530+ GLfloat *Pos = FullVertexList[0].Data + FullVertexData::POS_BASE;
531+ for (int c=0; c<3; c++)
532+ {
533+ GLfloat PC = Pos[c];
534+ PosMin[c] = MIN(PosMin[c],PC);
535+ PosMax[c] = MAX(PosMax[c],PC);
536+ }
537+ }
538+ const GLfloat ThresholdConstant = 0.001;
539+ for (int c=0; c<3; c++)
540+ Thresholds[FullVertexData::POS_BASE+c] = ThresholdConstant*(PosMax[c]-PosMin[c]);
541+ for (int c=0; c<2; c++)
542+ Thresholds[FullVertexData::TC_BASE+c] = ThresholdConstant;
543+ for (int c=0; c<3; c++)
544+ Thresholds[FullVertexData::NORM_BASE+c] = ThresholdConstant;
545+ for (int c=0; c<3; c++)
546+ Thresholds[FullVertexData::COLOR_BASE+c] = ThresholdConstant;
547+
548+ // Now the actual sorting
549+ vector<int> Indices(OldNumVertices);
550+ for (int k=0; k<Indices.size(); k++)
551+ Indices[k] = k;
552+
553+ // STL sort may be slow
554+ qsort(&Indices[0],OldNumVertices,sizeof(int),CompareVertices);
555+
556+ // Set up vertex ranks in place; count the distinct vertices.
557+ // Also, use the first one of a non-distinct set in sorted order
558+ // as the new vertex
559+ Model.VertIndices.resize(OldNumVertices);
560+ vector<int> VertexSelect;
561+ VertexSelect.resize(OldNumVertices);
562+
563+ int TopRank = 0;
564+ int PrevIndex = Indices[0];
565+ VertexSelect[TopRank] = PrevIndex;
566+ Model.VertIndices[PrevIndex] = TopRank;
567+ for (int k=1; k<OldNumVertices; k++)
568+ {
569+ int CurrIndex = Indices[k];
570+ if (CompareVertices(&PrevIndex,&CurrIndex) != 0)
571+ {
572+ TopRank++;
573+ VertexSelect[TopRank] = CurrIndex;
574+ }
575+ Model.VertIndices[CurrIndex] = TopRank;
576+ PrevIndex = CurrIndex;
577+ }
578+ int NewNumVertices = TopRank + 1;
579+
580+ // Fill up the rest of model arrays
581+ Model.Positions.resize(3*NewNumVertices);
582+ GLfloat *PosPtr = &Model.Positions[0];
583+
584+ GLfloat *TCPtr = NULL;
585+ if (TxtrCoordsPresent)
586+ {
587+ Model.TxtrCoords.resize(2*NewNumVertices);
588+ TCPtr = &Model.TxtrCoords[0];
589+ }
590+
591+ GLfloat *NormPtr = NULL;
592+ if (NormalsPresent)
593+ {
594+ Model.Normals.resize(3*NewNumVertices);
595+ NormPtr = &Model.Normals[0];
596+ }
597+
598+ GLfloat *ColorPtr = NULL;
599+ if (ColorsPresent)
600+ {
601+ Model.Colors.resize(3*NewNumVertices);
602+ ColorPtr = &Model.Colors[0];
603+ }
604+
605+ for (int k=0; k<NewNumVertices; k++)
606+ {
607+ FullVertexData& FullVertex = FullVertexList[VertexSelect[k]];
608+
609+ GLfloat *Pos = FullVertex.Data + FullVertexData::POS_BASE;
610+ for (int c=0; c<3; c++)
611+ *(PosPtr++) = *(Pos++);
612+
613+ if (TxtrCoordsPresent)
614+ {
615+ GLfloat *TC = FullVertex.Data + FullVertexData::TC_BASE;
616+ for (int c=0; c<2; c++)
617+ *(TCPtr++) = *(TC++);
618+ }
619+
620+ if (NormalsPresent)
621+ {
622+ GLfloat *Norm = FullVertex.Data + FullVertexData::NORM_BASE;
623+ for (int c=0; c<3; c++)
624+ *(NormPtr++) = *(Norm++);
625+ }
626+
627+ if (ColorsPresent)
628+ {
629+ GLfloat *Color = FullVertex.Data + FullVertexData::COLOR_BASE;
630+ for (int c=0; c<3; c++)
631+ *(ColorPtr++) = *(Color++);
632+ }
633+ }
634+
635+ // All done!
636+ FullVertexList.clear();
637+}
638+
639+int CompareVertices(const void *VI1, const void *VI2)
640+{
641+ int *IP1 = (int *)VI1;
642+ int *IP2 = (int *)VI2;
643+ GLfloat *Data1 = FullVertexList[*IP1].Data;
644+ GLfloat *Data2 = FullVertexList[*IP2].Data;
645+ GLfloat *ThrPtr = Thresholds;
646+
647+ // Compare the components one at a time; this will give a reasonable sort order
648+ for (int c=0; c<FullVertexData::SIZE; c++)
649+ {
650+ // Make the operation exactly reversible
651+ GLfloat DVal1 = *Data1;
652+ GLfloat DVal2 = *Data2;
653+ GLfloat Thr = *ThrPtr;
654+ if (DVal1 > DVal2 + Thr) return -1;
655+ else if (DVal2 > DVal1 + Thr) return 1;
656+ Data1++;
657+ Data2++;
658+ ThrPtr++;
659+ }
660+ // All within range
661+ return 0;
662+}
--- marathon/trunk/Source_Files/ModelView/Dim3_Loader.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/Dim3_Loader.cpp (revision 527)
@@ -1,1147 +1,1147 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- Dim3 Object Loader
21-
22- By Loren Petrich, Dec 29, 2001
23-
24- Derived from the work of
25-
26- Brian Barnes (bbarnes@klinksoftware.com)
27-
28-*/
29-
30-#include "cseries.h"
31-
32-#ifdef HAVE_OPENGL
33-#ifdef __WIN32__
34-#include <windows.h>
35-#endif
36-
37-#include <math.h>
38-
39-#include "Dim3_Loader.h"
40-#include "world.h"
41-#include "XML_Configure.h"
42-#include "XML_ElementParser.h"
43-
44-
45-const float DegreesToInternal = float(FULL_CIRCLE)/float(360);
46-
47-// Convert angle from degrees to the Marathon engine's internal units
48-static int16 GetAngle(float InAngle)
49-{
50- float A = DegreesToInternal*InAngle;
51- int16 IA = (A >= 0) ? int16(A + 0.5) : - int16(-A + 0.5);
52- return NORMALIZE_ANGLE(IA);
53-}
54-
55-
56-// Debug-message destination
57-static FILE *DBOut = NULL;
58-
59-void SetDebugOutput_Dim3(FILE *DebugOutput)
60-{
61- DBOut = DebugOutput;
62-}
63-
64-
65-class XML_Dim3DataBlock: public XML_Configure
66-{
67- // Gets some XML data to parse
68- bool GetData();
69-
70- // Reports a read error
71- void ReportReadError();
72-
73- // Reports an XML parsing error
74- void ReportParseError(const char *ErrorString, int LineNumber);
75-
76- // Reports an interpretation error
77- void ReportInterpretError(const char *ErrorString);
78-
79- // Requests aborting of parsing (reasonable if there were lots of errors)
80- bool RequestAbort();
81-
82-public:
83-
84- // Parse a data block:
85- bool ParseData(char *_Buffer, int _BufLen)
86- {Buffer = _Buffer; BufLen = _BufLen; return DoParse();}
87-
88- // Pointer to name of XML-code source for error-message convenience (C string)
89- char *SourceName;
90-
91- XML_Dim3DataBlock(): SourceName(NULL) {Buffer = NULL;}
92-};
93-
94-
95-// XML root parser stuff; set up for a lazy init.
96-// Child parsers are toward the end of the file.
97-static XML_Dim3DataBlock XML_DataBlockLoader;
98-static XML_ElementParser Dim3_RootParser(""), Dim3_Parser("Model");
99-bool Dim3_ParserInited = false;
100-
101-static void Dim3_SetupParseTree();
102-
103-
104-// Local globals; these are to be persistent across calls when loading several files.
105-
106-// Bone-tag and name-tag intermediate arrays:
107-
108-const int BoneTagSize = 8;
109-
110-struct BoneTagWrapper
111-{
112- char Tag0[BoneTagSize], Tag1[BoneTagSize];
113-};
114-
115-// For VertexBoneTags, this means major bone tag, then minor bone tag.
116-// For BoneOwnTags, this means its own tag, then its parent tag.
117-static vector<BoneTagWrapper> VertexBoneTags, BoneOwnTags;
118-
119-// Translation from read-in bone order to "true" order
120-static vector<size_t> BoneIndices;
121-
122-// Names of frames and seqeunces:
123-
124-const int NameTagSize = 32;
125-
126-struct NameTagWrapper
127-{
128- char Tag[NameTagSize];
129-};
130-
131-static vector<NameTagWrapper> FrameTags;
132-
133-// Where the data for each frame goes before it's loaded into the model array;
134-// the bones may be only partially listed or not listed at all.
135-static vector<Model3D_Frame> ReadFrame;
136-
137-// Normals (per vertex source)
138-static vector<GLfloat> Normals;
139-
140-
141-// For feeding into the read-in routines
142-static Model3D *ModelPtr = NULL;
143-
144-bool LoadModel_Dim3(FileSpecifier& Spec, Model3D& Model, int WhichPass)
145-{
146- ModelPtr = &Model;
147-
148- if (WhichPass == LoadModelDim3_First)
149- {
150- // Clear everything
151- Model.Clear();
152- VertexBoneTags.clear();
153- BoneOwnTags.clear();
154- BoneIndices.clear();
155- FrameTags.clear();
156- Normals.clear();
157- }
158-
159- if (DBOut)
160- {
161- // Name buffer
162- const int BufferSize = 256;
163- char Buffer[BufferSize];
164- Spec.GetName(Buffer);
165- fprintf(DBOut,"Loading Dim3 model file %s\n",Buffer);
166- }
167-
168- OpenedFile OFile;
169- if (!Spec.Open(OFile))
170- {
171- if (DBOut) fprintf(DBOut,"ERROR opening the file\n");
172- return false;
173- }
174-
175- Dim3_SetupParseTree();
176- XML_DataBlockLoader.CurrentElement = &Dim3_RootParser;
177-
178- int32 Len = 0;
179- OFile.GetLength(Len);
180- if (Len <= 0) return false;
181-
182- vector<char> FileContents(Len);
183- if (!OFile.Read(Len,&FileContents[0])) return false;
184-
185- char FileName[256];
186- Spec.GetName(FileName);
187- FileName[31] = 0; // Use only first 31 characters of filename (MacOS Classic)
188- // fdprintf("Loading from text file %s",FileName);
189-
190- XML_DataBlockLoader.SourceName = FileName;
191- if (!XML_DataBlockLoader.ParseData(&FileContents[0],Len))
192- {
193- if (DBOut) fprintf(DBOut, "There were parsing errors in Dim3 model file %s\n",FileName);
194- }
195-
196- // Set these up now
197- if (Model.InverseVSIndices.empty()) Model.BuildInverseVSIndices();
198-
199- if (!Normals.empty())
200- {
201- Model.NormSources.resize(3*Model.VtxSrcIndices.size());
202- Model.Normals.resize(Model.NormSources.size());
203- for (unsigned k=0; k<Model.VtxSources.size(); k++)
204- {
205- GLfloat *Norm = &Normals[3*k];
206- GLushort P0 = Model.InvVSIPointers[k];
207- GLushort P1 = Model.InvVSIPointers[k+1];
208- for (int p=P0; p<P1; p++)
209- {
210- GLfloat *DestNorm = &Model.NormSources[3*Model.InverseVSIndices[p]];
211- for (int c=0; c<3; c++)
212- DestNorm[c] = Norm[c];
213- }
214- }
215- // All done with them
216-
217- Normals.clear();
218- }
219-
220- // First, find the neutral-position vertices
221- Model.FindPositions_Neutral(false);
222-
223- // Work out the sorted order for the bones; be sure not to repeat this if already done.
224- if (BoneIndices.empty() && !Model.Bones.empty())
225- {
226- size_t NumBones = Model.Bones.size();
227- BoneIndices.resize(NumBones);
228- fill(BoneIndices.begin(),BoneIndices.end(),(size_t)UNONE); // No bones listed -- yet
229- vector<Model3D_Bone> SortedBones(NumBones);
230- vector<size_t> BoneStack(NumBones);
231- vector<bool> BonesUsed(NumBones);
232- fill(BonesUsed.begin(),BonesUsed.end(),false);
233-
234- // Add the bones, one by one;
235- // the bone stack's height is originally zero
236- int StackTop = -1;
237- for (vector<size_t>::value_type ib=0; ib<NumBones; ib++)
238- {
239- // Scan down the bone stack to find a bone that's the parent of some unlisted bone;
240- vector<size_t>::value_type ibsrch =
241- static_cast<vector<size_t>::value_type>(NumBones); // "Bone not found" value
242- int ibstck = -1; // Empty stack
243- for (ibstck=StackTop; ibstck>=0; ibstck--)
244- {
245- // Note: the bone stack is indexed relative to the original,
246- // as is the bones-used list
247- char *StackBoneTag = BoneOwnTags[BoneStack[ibstck]].Tag0;
248- for (ibsrch=0; ibsrch<NumBones; ibsrch++)
249- {
250- if (BonesUsed[ibsrch]) continue;
251- char *ParentTag = BoneOwnTags[ibsrch].Tag1;
252- if (strncmp(ParentTag,StackBoneTag,BoneTagSize)==0)
253- break;
254- }
255- // If a bone was found, then readjust the stack size appropriately and quit.
256- if (ibsrch < NumBones)
257- {
258- if (ibstck < StackTop)
259- {
260- // Be sure to get the traversal push/pop straight.
261- Model.Bones[BoneStack[ibstck+1]].Flags |= Model3D_Bone::Push;
262- Model.Bones[ibsrch].Flags |= Model3D_Bone::Pop;
263- StackTop = ibstck;
264- }
265- break;
266- }
267- }
268- // If none was found, then the bone's parent is the assumed root bone.
269- if (ibstck < 0)
270- {
271- for (ibsrch=0; ibsrch<NumBones; ibsrch++)
272- {
273- if (BonesUsed[ibsrch]) continue;
274-
275- // Check if the parent is not one of the bones
276- char *ParentTag = BoneOwnTags[ibsrch].Tag1;
277- size_t ibsx;
278- for (ibsx=0; ibsx<NumBones; ibsx++)
279- {
280- if (strncmp(ParentTag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
281- break;
282- }
283- // If a match was not found, then quit searching
284- if (ibsx >= NumBones) break;
285- }
286-
287- // Not sure how to handle this sort of error;
288- // it could be produced by circular bone references:
289- // B1 -> B2 -> B3 -> ... -> B1
290- assert(ibsrch < NumBones);
291-
292- // Be sure to get the traversal push/pop straight.
293- if (StackTop >= 0)
294- {
295- Model.Bones[BoneStack[0]].Flags |= Model3D_Bone::Push;
296- Model.Bones[ibsrch].Flags |= Model3D_Bone::Pop;
297- StackTop = -1;
298- }
299- }
300-
301- // Add the bone to the stack
302- BoneStack[++StackTop] = ibsrch;
303-
304- // Don't look for it anymore
305- BonesUsed[ibsrch] = true;
306-
307- // Index for remapping
308- BoneIndices[ibsrch] = ib;
309- }
310-
311- // Reorder the bones
312- for (size_t ib=0; ib<NumBones; ib++)
313- SortedBones[BoneIndices[ib]] = Model.Bones[ib];
314-
315- // Put them back into the model in one step
316- Model.Bones.swap(SortedBones);
317-
318- // Find the vertex bone indices; this assumes that the vertices have already been read in.
319- for (unsigned iv=0; iv<Model.VtxSources.size(); iv++)
320- {
321- Model3D_VertexSource& VS = Model.VtxSources[iv];
322- size_t ibsx;
323- char *Tag;
324-
325- Tag = VertexBoneTags[iv].Tag0;
326- for (ibsx=0; ibsx<NumBones; ibsx++)
327- {
328- if (strncmp(Tag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
329- break;
330- }
331- VS.Bone0 = ibsx < NumBones ? BoneIndices[ibsx] : static_cast<GLshort>(NONE);
332-
333- Tag = VertexBoneTags[iv].Tag1;
334- for (ibsx=0; ibsx<NumBones; ibsx++)
335- {
336- if (strncmp(Tag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
337- break;
338- }
339- VS.Bone1 = ibsx < NumBones ? BoneIndices[ibsx] : static_cast<GLshort>(NONE);
340- }
341- }
342-
343- return (!Model.Positions.empty() && !Model.VertIndices.empty());
344-}
345-
346-
347-// Gets some XML data to parse
348-bool XML_Dim3DataBlock::GetData()
349-{
350- // Check...
351- assert(Buffer);
352- assert(BufLen > 0);
353-
354- // Only one buffer
355- LastOne = true;
356-
357- return true;
358-}
359-
360-
361-// Reports a read error
362-void XML_Dim3DataBlock::ReportReadError()
363-{
364- const char *Name = SourceName ? SourceName : "[]";
365-
366- if (DBOut)
367- fprintf(DBOut, "Error in reading data/resources from object %s\n",Name);
368-}
369-
370-
371-// Reports an XML parsing error
372-void XML_Dim3DataBlock::ReportParseError(const char *ErrorString, int LineNumber)
373-{
374- const char *Name = SourceName ? SourceName : "[]";
375-
376- if (DBOut)
377- fprintf(DBOut, "XML parsing error: %s at line %d in object %s\n", ErrorString, LineNumber, Name);
378-}
379-
380-
381-// Reports an interpretation error
382-void XML_Dim3DataBlock::ReportInterpretError(const char *ErrorString)
383-{
384- if (DBOut)
385- fprintf(DBOut, "%s\n",ErrorString);
386-}
387-
388-// Requests aborting of parsing (reasonable if there were lots of errors)
389-bool XML_Dim3DataBlock::RequestAbort()
390-{
391- return false;
392-}
393-
394-
395-// Dummy elements:
396-static XML_ElementParser
397- CreatorParser("Creator"),
398- CenterParser("Center"),
399- LightParser("Light"),
400- ShadingParser("Shading"),
401- ShadowBoxParser("Shadow_Box"),
402- VerticesParser("Vertexes"),
403- BonesParser("Bones"),
404- EffectsParser("Effects"),
405- EffectParser("Effect"),
406- FillsParser("Fills"),
407- FillParser("Fill"),
408- D3ColorsParser("Colors"),
409- D3ColorParser("Color"),
410- D3ImagesParser("Images"),
411- D3ImageParser("Image"),
412- TrianglesParser("Triangles"),
413- FramesParser("Poses"),
414- FrameBonesParser("Bones"),
415- SequencesParser("Animations"),
416- SeqLoopParser("Loop"),
417- SeqFramesParser("Poses");
418-
419-
420-// "Real" elements:
421-
422-class XML_BoundingBoxParser: public XML_ElementParser
423-{
424- GLfloat x_size, y_size, z_size, x_offset, y_offset, z_offset;
425-
426-public:
427- bool Start();
428- bool HandleAttribute(const char *Tag, const char *Value);
429- bool AttributesDone();
430-
431- XML_BoundingBoxParser(const char *Name): XML_ElementParser(Name) {}
432-};
433-
434-
435-
436-bool XML_BoundingBoxParser::Start()
437-{
438- x_size = y_size = z_size = x_offset = y_offset = z_offset = 0;
439-
440- return true;
441-}
442-
443-bool XML_BoundingBoxParser::HandleAttribute(const char *Tag, const char *Value)
444-{
445- if (StringsEqual(Tag,"size"))
446- {
447- return (sscanf(Value,"%f,%f,%f",&x_size,&y_size,&z_size) == 3);
448- }
449- else if (StringsEqual(Tag,"offset"))
450- {
451- return (sscanf(Value,"%f,%f,%f",&x_offset,&y_offset,&z_offset) == 3);
452- }
453- else if (StringsEqual(Tag,"x_size"))
454- {
455- return ReadFloatValue(Value,x_size);
456- }
457- else if (StringsEqual(Tag,"y_size"))
458- {
459- return ReadFloatValue(Value,y_size);
460- }
461- else if (StringsEqual(Tag,"z_size"))
462- {
463- return ReadFloatValue(Value,z_size);
464- }
465- else if (StringsEqual(Tag,"x_offset"))
466- {
467- return ReadFloatValue(Value,x_offset);
468- }
469- else if (StringsEqual(Tag,"y_offset"))
470- {
471- return ReadFloatValue(Value,y_offset);
472- }
473- else if (StringsEqual(Tag,"z_offset"))
474- {
475- return ReadFloatValue(Value,z_offset);
476- }
477-
478- UnrecognizedTag();
479- return false;
480-}
481-
482-bool XML_BoundingBoxParser::AttributesDone()
483-{
484- // Inconsistent resizing: weird bug in ggadwa's code
485-
486- ModelPtr->BoundingBox[0][0] = x_offset - x_size/2;
487- ModelPtr->BoundingBox[0][1] = y_offset - y_size;
488- ModelPtr->BoundingBox[0][2] = z_offset - z_size/2;
489-
490- ModelPtr->BoundingBox[1][0] = x_offset + x_size/2;
491- ModelPtr->BoundingBox[1][1] = y_offset;
492- ModelPtr->BoundingBox[1][2] = z_offset + z_size/2;
493-
494- return true;
495-}
496-
497-static XML_BoundingBoxParser BoundingBoxParser("Bound_Box");
498-static XML_BoundingBoxParser ViewBoxParser("View_Box");
499-
500-
501-class XML_VertexParser: public XML_ElementParser
502-{
503- Model3D_VertexSource Data;
504- GLfloat Norm[3];
505-
506- // For adding to the bone-tag array as each vertex is added
507- BoneTagWrapper BT;
508-
509-public:
510- bool Start();
511- bool HandleAttribute(const char *Tag, const char *Value);
512- bool AttributesDone();
513-
514- XML_VertexParser(): XML_ElementParser("v") {}
515-};
516-
517-
518-bool XML_VertexParser::Start()
519-{
520- for (int c=0; c<3; c++)
521- Norm[c] = Data.Position[c] = 0;
522-
523- // Initially: no bones
524- Data.Bone0 = Data.Bone1 = (GLushort)NONE;
525- Data.Blend = 0;
526-
527- // No bone: zero-length strings:
528- BT.Tag0[0] = BT.Tag1[0] = 0;
529-
530- return true;
531-}
532-
533-bool XML_VertexParser::HandleAttribute(const char *Tag, const char *Value)
534-{
535- if (StringsEqual(Tag,"c3"))
536- {
537- GLfloat *Pos = Data.Position;
538- return (sscanf(Value,"%f,%f,%f",&Pos[0],&Pos[1],&Pos[2]) == 3);
539- }
540- else if (StringsEqual(Tag,"n3"))
541- {
542- return (sscanf(Value,"%f,%f,%f",&Norm[0],&Norm[1],&Norm[2]) == 3);
543- }
544- else if (StringsEqual(Tag,"x"))
545- {
546- return ReadFloatValue(Value,Data.Position[0]);
547- }
548- else if (StringsEqual(Tag,"y"))
549- {
550- return ReadFloatValue(Value,Data.Position[1]);
551- }
552- else if (StringsEqual(Tag,"z"))
553- {
554- return ReadFloatValue(Value,Data.Position[2]);
555- }
556- else if (StringsEqual(Tag,"major"))
557- {
558- strncpy(BT.Tag0,Value,BoneTagSize);
559- return true;
560- }
561- else if (StringsEqual(Tag,"minor"))
562- {
563- strncpy(BT.Tag1,Value,BoneTagSize);
564- return true;
565- }
566- else if (StringsEqual(Tag,"factor"))
567- {
568- GLfloat Factor;
569- if (ReadFloatValue(Value,Factor))
570- {
571- // Convert from ggadwa's definition (100 to 0) to mine (0 to 1)
572- // for first to second bone.
573- Data.Blend = 1 - Factor/100;
574- return true;
575- }
576- else return false;
577- }
578-
579- UnrecognizedTag();
580- return false;
581-}
582-
583-bool XML_VertexParser::AttributesDone()
584-{
585- // Always handle the bone data, even for a blank bone, to maintain coherence.
586- // Also always handle normal data for that reason.
587- ModelPtr->VtxSources.push_back(Data);
588- for (int c=0; c<3; c++)
589- Normals.push_back(Norm[c]);
590- VertexBoneTags.push_back(BT);
591-
592- return true;
593-}
594-
595-static XML_VertexParser VertexParser;
596-
597-
598-class XML_BoneParser: public XML_ElementParser
599-{
600- Model3D_Bone Data;
601-
602- // For adding to the bone-tag array as each bone is added
603- BoneTagWrapper BT;
604-
605-public:
606- bool Start();
607- bool HandleAttribute(const char *Tag, const char *Value);
608- bool AttributesDone();
609-
610- XML_BoneParser(): XML_ElementParser("Bone") {}
611-};
612-
613-
614-bool XML_BoneParser::Start()
615-{
616- for (int c=0; c<3; c++)
617- Data.Position[c] = 0;
618-
619- // Initially: don't do anything special
620- // (might produce screwy models without further processing)
621- Data.Flags = 0;
622-
623- // No bone: zero-length strings:
624- BT.Tag0[0] = BT.Tag1[0] = 0;
625-
626- return true;
627-}
628-
629-bool XML_BoneParser::HandleAttribute(const char *Tag, const char *Value)
630-{
631- if (StringsEqual(Tag,"c3"))
632- {
633- GLfloat *Pos = Data.Position;
634- return (sscanf(Value,"%f,%f,%f",&Pos[0],&Pos[1],&Pos[2]) == 3);
635- }
636- else if (StringsEqual(Tag,"x"))
637- {
638- return ReadFloatValue(Value,Data.Position[0]);
639- }
640- else if (StringsEqual(Tag,"y"))
641- {
642- return ReadFloatValue(Value,Data.Position[1]);
643- }
644- else if (StringsEqual(Tag,"z"))
645- {
646- return ReadFloatValue(Value,Data.Position[2]);
647- }
648- else if (StringsEqual(Tag,"tag"))
649- {
650- strncpy(BT.Tag0,Value,BoneTagSize);
651- return true;
652- }
653- else if (StringsEqual(Tag,"parent"))
654- {
655- strncpy(BT.Tag1,Value,BoneTagSize);
656- return true;
657- }
658-
659- UnrecognizedTag();
660- return false;
661-}
662-
663-bool XML_BoneParser::AttributesDone()
664-{
665- // Always handle the bone data, even for a blank bone, to maintain coherence.
666- ModelPtr->Bones.push_back(Data);
667- BoneOwnTags.push_back(BT);
668-
669- return true;
670-}
671-
672-static XML_BoneParser BoneParser;
673-
674-
675-
676-class XML_TriVertexParser: public XML_ElementParser
677-{
678- uint16 ID;
679- float Txtr_X, Txtr_Y;
680- float Norm_X, Norm_Y, Norm_Z;
681-
682-public:
683- bool Start();
684- bool HandleAttribute(const char *Tag, const char *Value);
685- bool AttributesDone();
686-
687- XML_TriVertexParser(): XML_ElementParser("v") {}
688-};
689-
690-
691-bool XML_TriVertexParser::Start()
692-{
693- // Reasonable defaults:
694- ID = (uint16)NONE;
695- Txtr_X = 0.5, Txtr_Y = 0.5;
696- Norm_X = Norm_Y = Norm_Z = 0;
697-
698- return true;
699-}
700-
701-bool XML_TriVertexParser::HandleAttribute(const char *Tag, const char *Value)
702-{
703- if (StringsEqual(Tag,"ID"))
704- {
705- return ReadUInt16Value(Value,ID);
706- }
707- else if (StringsEqual(Tag,"uv"))
708- {
709- return (sscanf(Value,"%f,%f",&Txtr_X,&Txtr_Y) == 2);
710- }
711- else if (StringsEqual(Tag,"n"))
712- {
713- return (sscanf(Value,"%f,%f,%f",&Norm_X,&Norm_Z,&Norm_Y) == 3); // Dim3's odd order
714- }
715- else if (StringsEqual(Tag,"xtxt"))
716- {
717- return ReadFloatValue(Value,Txtr_X);
718- }
719- else if (StringsEqual(Tag,"ytxt"))
720- {
721- return ReadFloatValue(Value,Txtr_Y);
722- }
723-
724- UnrecognizedTag();
725- return false;
726-}
727-
728-bool XML_TriVertexParser::AttributesDone()
729-{
730- // Older dim3-Animator normal support suppressed
731- /*
732- // Normalize the normal, if nonzero
733- float NSQ = Norm_X*Norm_X + Norm_Y*Norm_Y + Norm_Z*Norm_Z;
734- if (NSQ != 0)
735- {
736- float NMult = (float)(1/sqrt(NSQ));
737- Norm_X *= NMult;
738- Norm_Y *= NMult;
739- Norm_Z *= NMult;
740- }
741- */
742-
743- GLushort Index = ((GLushort)ModelPtr->VertIndices.size());
744- ModelPtr->VertIndices.push_back(Index);
745- ModelPtr->VtxSrcIndices.push_back(ID);
746- ModelPtr->TxtrCoords.push_back(Txtr_X);
747- ModelPtr->TxtrCoords.push_back(Txtr_Y);
748- /*
749- ModelPtr->Normals.push_back(Norm_X);
750- ModelPtr->Normals.push_back(Norm_Y);
751- ModelPtr->Normals.push_back(Norm_Z);
752- */
753- return true;
754-}
755-
756-static XML_TriVertexParser TriVertexParser;
757-
758-
759-
760-class XML_FrameParser: public XML_ElementParser
761-{
762- // For adding to the frame-name array as frames are added
763- NameTagWrapper NT;
764-
765-public:
766- bool Start();
767- bool HandleAttribute(const char *Tag, const char *Value);
768- bool End();
769-
770- XML_FrameParser(): XML_ElementParser("Pose") {}
771-};
772-
773-
774-bool XML_FrameParser::Start()
775-{
776- // Be sure to have the right number of frame members --
777- // and blank them out
778- size_t NumBones = ModelPtr->Bones.size();
779- ReadFrame.resize(NumBones);
780- objlist_clear(&ReadFrame[0],NumBones);
781-
782- // No name: zero-length name
783- NT.Tag[0] = 0;
784-
785- return true;
786-}
787-
788-bool XML_FrameParser::HandleAttribute(const char *Tag, const char *Value)
789-{
790- if (StringsEqual(Tag,"name"))
791- {
792- strncpy(NT.Tag,Value,NameTagSize);
793- return true;
794- }
795-
796- UnrecognizedTag();
797- return false;
798-}
799-
800-bool XML_FrameParser::End()
801-{
802- // Some of the data was set up by child elements, so all the processing
803- // can be back here.
804- for (size_t b=0; b<ReadFrame.size(); b++)
805- ModelPtr->Frames.push_back(ReadFrame[b]);
806-
807- FrameTags.push_back(NT);
808-
809- return true;
810-}
811-
812-static XML_FrameParser FrameParser;
813-
814-
815-
816-class XML_FrameBoneParser: public XML_ElementParser
817-{
818- Model3D_Frame Data;
819-
820- // The bone tag to look for
821- char BoneTag[BoneTagSize];
822-
823-public:
824- bool Start();
825- bool HandleAttribute(const char *Tag, const char *Value);
826- bool AttributesDone();
827-
828- XML_FrameBoneParser(): XML_ElementParser("Bone") {}
829-};
830-
831-
832-bool XML_FrameBoneParser::Start()
833-{
834- // Clear everything out:
835- obj_clear(Data);
836-
837- // Empty string
838- BoneTag[0] = 0;
839-
840- return true;
841-}
842-
843-
844-// Some of angles have their signs reversed to translate BB's sign conventions
845-// into more my more geometrically-elegant ones.
846-
847-bool XML_FrameBoneParser::HandleAttribute(const char *Tag, const char *Value)
848-{
849- if (StringsEqual(Tag,"rot"))
850- {
851- float InAngle[3];
852- if (sscanf(Value,"%f,%f,%f",&InAngle[0],&InAngle[1],&InAngle[2]) == 3)
853- {
854- for (int c=0; c<3; c++)
855- Data.Angles[c] = GetAngle(- InAngle[c]);
856- return true;
857- }
858- else
859- return false;
860- }
861- else if (StringsEqual(Tag,"move"))
862- {
863- GLfloat *Ofst = Data.Offset;
864- return (sscanf(Value,"%f,%f,%f",&Ofst[0],&Ofst[1],&Ofst[2]) == 3);
865- }
866- else if (StringsEqual(Tag,"acceleration"))
867- {
868- // Ignore the acceleration for now
869- return true;
870- }
871- else if (StringsEqual(Tag,"xmove"))
872- {
873- return ReadFloatValue(Value,Data.Offset[0]);
874- }
875- else if (StringsEqual(Tag,"ymove"))
876- {
877- return ReadFloatValue(Value,Data.Offset[1]);
878- }
879- else if (StringsEqual(Tag,"zmove"))
880- {
881- return ReadFloatValue(Value,Data.Offset[2]);
882- }
883- else if (StringsEqual(Tag,"xrot"))
884- {
885- float InAngle;
886- if (ReadFloatValue(Value,InAngle))
887- {
888- Data.Angles[0] = GetAngle(InAngle);
889- return true;
890- }
891- else
892- return false;
893- }
894- else if (StringsEqual(Tag,"yrot"))
895- {
896- float InAngle;
897- if (ReadFloatValue(Value,InAngle))
898- {
899- Data.Angles[1] = GetAngle(-InAngle);
900- return true;
901- }
902- else
903- return false;
904- }
905- else if (StringsEqual(Tag,"zrot"))
906- {
907- float InAngle;
908- if (ReadFloatValue(Value,InAngle))
909- {
910- Data.Angles[2] = GetAngle(-InAngle);
911- return true;
912- }
913- else
914- return false;
915- }
916- else if (StringsEqual(Tag,"tag"))
917- {
918- strncpy(BoneTag,Value,BoneTagSize);
919- return true;
920- }
921-
922- UnrecognizedTag();
923- return false;
924-}
925-
926-bool XML_FrameBoneParser::AttributesDone()
927-{
928- // Place the bone info into the appropriate temporary-array location
929- size_t NumBones = BoneOwnTags.size();
930- size_t ib;
931- for (ib=0; ib<NumBones; ib++)
932- {
933- // Compare tag to bone's self tag
934- if (strncmp(BoneTag,BoneOwnTags[ib].Tag0,BoneTagSize) == 0)
935- break;
936- }
937- if (ib < NumBones)
938- obj_copy(ReadFrame[BoneIndices[ib]],Data);
939-
940- return true;
941-}
942-
943-static XML_FrameBoneParser FrameBoneParser;
944-
945-
946-class XML_SequenceParser: public XML_ElementParser
947-{
948-
949-public:
950- bool End();
951-
952- XML_SequenceParser(): XML_ElementParser("Animation") {}
953-};
954-
955-bool XML_SequenceParser::End()
956-{
957- // Add pointer index to end of sequences list;
958- // create that list if it had been absent.
959- if (ModelPtr->SeqFrmPointers.empty())
960- {
961- ModelPtr->SeqFrmPointers.push_back(0);
962- }
963- ModelPtr->SeqFrmPointers.push_back((GLushort)(ModelPtr->SeqFrames.size()));
964- return true;
965-}
966-
967-static XML_SequenceParser SequenceParser;
968-
969-
970-class XML_SeqFrameParser: public XML_ElementParser
971-{
972- Model3D_SeqFrame Data;
973-
974-public:
975- bool Start();
976- bool HandleAttribute(const char *Tag, const char *Value);
977- bool AttributesDone();
978-
979- XML_SeqFrameParser(): XML_ElementParser("Pose") {}
980-};
981-
982-
983-bool XML_SeqFrameParser::Start()
984-{
985- // Clear everything out:
986- obj_clear(Data);
987-
988- // No frame
989- Data.Frame = NONE;
990-
991- return true;
992-}
993-
994-// Some of angles have their signs reversed to translate BB's sign conventions
995-// into more my more geometrically-elegant ones.
996-
997-bool XML_SeqFrameParser::HandleAttribute(const char *Tag, const char *Value)
998-{
999- if (StringsEqual(Tag,"sway"))
1000- {
1001- float InAngle[3];
1002- if (sscanf(Value,"%f,%f,%f",&InAngle[0],&InAngle[1],&InAngle[2]) == 3)
1003- {
1004- for (int c=0; c<3; c++)
1005- Data.Angles[c] = GetAngle(- InAngle[c]);
1006- return true;
1007- }
1008- else
1009- return false;
1010- }
1011- else if (StringsEqual(Tag,"move"))
1012- {
1013- GLfloat *Ofst = Data.Offset;
1014- return (sscanf(Value,"%f,%f,%f",&Ofst[0],&Ofst[1],&Ofst[2]) == 3);
1015- }
1016- else if (StringsEqual(Tag,"xmove"))
1017- {
1018- return ReadFloatValue(Value,Data.Offset[0]);
1019- }
1020- else if (StringsEqual(Tag,"ymove"))
1021- {
1022- return ReadFloatValue(Value,Data.Offset[1]);
1023- }
1024- else if (StringsEqual(Tag,"zmove"))
1025- {
1026- return ReadFloatValue(Value,Data.Offset[2]);
1027- }
1028- else if (StringsEqual(Tag,"xsway"))
1029- {
1030- float InAngle;
1031- if (ReadFloatValue(Value,InAngle))
1032- {
1033- Data.Angles[0] = GetAngle(InAngle);
1034- return true;
1035- }
1036- else
1037- return false;
1038- }
1039- else if (StringsEqual(Tag,"ysway"))
1040- {
1041- float InAngle;
1042- if (ReadFloatValue(Value,InAngle))
1043- {
1044- Data.Angles[1] = GetAngle(-InAngle);
1045- return true;
1046- }
1047- else
1048- return false;
1049- }
1050- else if (StringsEqual(Tag,"zsway"))
1051- {
1052- float InAngle;
1053- if (ReadFloatValue(Value,InAngle))
1054- {
1055- Data.Angles[2] = GetAngle(-InAngle);
1056- return true;
1057- }
1058- else
1059- return false;
1060- }
1061- else if (StringsEqual(Tag,"name"))
1062- {
1063- // Find which frame
1064- size_t ifr;
1065- size_t NumFrames = FrameTags.size();
1066- for (ifr=0; ifr<NumFrames; ifr++)
1067- {
1068- if (strncmp(Value,FrameTags[ifr].Tag,BoneTagSize) == 0) break;
1069- }
1070- if (ifr >= NumFrames) ifr = static_cast<size_t>(NONE);
1071- Data.Frame = (GLshort)ifr;
1072- return true;
1073- }
1074- else if (StringsEqual(Tag,"time"))
1075- {
1076- // Ignore; all timing info will come from the shapes file
1077- return true;
1078- }
1079-
1080- UnrecognizedTag();
1081- return false;
1082-}
1083-
1084-bool XML_SeqFrameParser::AttributesDone()
1085-{
1086- // Add the frame
1087- ModelPtr->SeqFrames.push_back(Data);
1088-
1089- return true;
1090-}
1091-
1092-static XML_SeqFrameParser SeqFrameParser;
1093-
1094-
1095-void Dim3_SetupParseTree()
1096-{
1097- // Lazy init
1098- if (Dim3_ParserInited) return;
1099-
1100- // Set up the root object
1101- Dim3_RootParser.AddChild(&Dim3_Parser);
1102-
1103- Dim3_Parser.AddChild(&CreatorParser);
1104- Dim3_Parser.AddChild(&CenterParser);
1105- Dim3_Parser.AddChild(&LightParser);
1106- Dim3_Parser.AddChild(&BoundingBoxParser);
1107- Dim3_Parser.AddChild(&ViewBoxParser);
1108- Dim3_Parser.AddChild(&ShadingParser);
1109- Dim3_Parser.AddChild(&ShadowBoxParser);
1110-
1111- VerticesParser.AddChild(&VertexParser);
1112- Dim3_Parser.AddChild(&VerticesParser);
1113-
1114- BonesParser.AddChild(&BoneParser);
1115- Dim3_Parser.AddChild(&BonesParser);
1116-
1117- EffectsParser.AddChild(&EffectParser);
1118- Dim3_Parser.AddChild(&EffectsParser);
1119-
1120- D3ColorsParser.AddChild(&D3ColorParser);
1121- D3ImagesParser.AddChild(&D3ImageParser);
1122- TrianglesParser.AddChild(&TriVertexParser);
1123-
1124- FillParser.AddChild(&D3ColorsParser);
1125- FillParser.AddChild(&D3ImagesParser);
1126- FillParser.AddChild(&TrianglesParser);
1127-
1128- FillsParser.AddChild(&FillParser);
1129- Dim3_Parser.AddChild(&FillsParser);
1130-
1131- FrameBonesParser.AddChild(&FrameBoneParser);
1132- FrameParser.AddChild(&FrameBonesParser);
1133- FramesParser.AddChild(&FrameParser);
1134- Dim3_Parser.AddChild(&FramesParser);
1135-
1136- SeqFramesParser.AddChild(&SeqFrameParser);
1137- SequenceParser.AddChild(&SeqFramesParser);
1138- SequenceParser.AddChild(&SeqLoopParser);
1139- SequencesParser.AddChild(&SequenceParser);
1140- Dim3_Parser.AddChild(&SequencesParser);
1141-
1142- Dim3_ParserInited = true;
1143-}
1144-
1145-
1146-// HAVE_OPENGL
1147-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ Dim3 Object Loader
21+
22+ By Loren Petrich, Dec 29, 2001
23+
24+ Derived from the work of
25+
26+ Brian Barnes (bbarnes@klinksoftware.com)
27+
28+*/
29+
30+#include "cseries.h"
31+
32+#ifdef HAVE_OPENGL
33+#ifdef __WIN32__
34+#include <windows.h>
35+#endif
36+
37+#include <math.h>
38+
39+#include "Dim3_Loader.h"
40+#include "world.h"
41+#include "XML_Configure.h"
42+#include "XML_ElementParser.h"
43+
44+
45+const float DegreesToInternal = float(FULL_CIRCLE)/float(360);
46+
47+// Convert angle from degrees to the Marathon engine's internal units
48+static int16 GetAngle(float InAngle)
49+{
50+ float A = DegreesToInternal*InAngle;
51+ int16 IA = (A >= 0) ? int16(A + 0.5) : - int16(-A + 0.5);
52+ return NORMALIZE_ANGLE(IA);
53+}
54+
55+
56+// Debug-message destination
57+static FILE *DBOut = NULL;
58+
59+void SetDebugOutput_Dim3(FILE *DebugOutput)
60+{
61+ DBOut = DebugOutput;
62+}
63+
64+
65+class XML_Dim3DataBlock: public XML_Configure
66+{
67+ // Gets some XML data to parse
68+ bool GetData();
69+
70+ // Reports a read error
71+ void ReportReadError();
72+
73+ // Reports an XML parsing error
74+ void ReportParseError(const char *ErrorString, int LineNumber);
75+
76+ // Reports an interpretation error
77+ void ReportInterpretError(const char *ErrorString);
78+
79+ // Requests aborting of parsing (reasonable if there were lots of errors)
80+ bool RequestAbort();
81+
82+public:
83+
84+ // Parse a data block:
85+ bool ParseData(char *_Buffer, int _BufLen)
86+ {Buffer = _Buffer; BufLen = _BufLen; return DoParse();}
87+
88+ // Pointer to name of XML-code source for error-message convenience (C string)
89+ char *SourceName;
90+
91+ XML_Dim3DataBlock(): SourceName(NULL) {Buffer = NULL;}
92+};
93+
94+
95+// XML root parser stuff; set up for a lazy init.
96+// Child parsers are toward the end of the file.
97+static XML_Dim3DataBlock XML_DataBlockLoader;
98+static XML_ElementParser Dim3_RootParser(""), Dim3_Parser("Model");
99+bool Dim3_ParserInited = false;
100+
101+static void Dim3_SetupParseTree();
102+
103+
104+// Local globals; these are to be persistent across calls when loading several files.
105+
106+// Bone-tag and name-tag intermediate arrays:
107+
108+const int BoneTagSize = 8;
109+
110+struct BoneTagWrapper
111+{
112+ char Tag0[BoneTagSize], Tag1[BoneTagSize];
113+};
114+
115+// For VertexBoneTags, this means major bone tag, then minor bone tag.
116+// For BoneOwnTags, this means its own tag, then its parent tag.
117+static vector<BoneTagWrapper> VertexBoneTags, BoneOwnTags;
118+
119+// Translation from read-in bone order to "true" order
120+static vector<size_t> BoneIndices;
121+
122+// Names of frames and seqeunces:
123+
124+const int NameTagSize = 32;
125+
126+struct NameTagWrapper
127+{
128+ char Tag[NameTagSize];
129+};
130+
131+static vector<NameTagWrapper> FrameTags;
132+
133+// Where the data for each frame goes before it's loaded into the model array;
134+// the bones may be only partially listed or not listed at all.
135+static vector<Model3D_Frame> ReadFrame;
136+
137+// Normals (per vertex source)
138+static vector<GLfloat> Normals;
139+
140+
141+// For feeding into the read-in routines
142+static Model3D *ModelPtr = NULL;
143+
144+bool LoadModel_Dim3(FileSpecifier& Spec, Model3D& Model, int WhichPass)
145+{
146+ ModelPtr = &Model;
147+
148+ if (WhichPass == LoadModelDim3_First)
149+ {
150+ // Clear everything
151+ Model.Clear();
152+ VertexBoneTags.clear();
153+ BoneOwnTags.clear();
154+ BoneIndices.clear();
155+ FrameTags.clear();
156+ Normals.clear();
157+ }
158+
159+ if (DBOut)
160+ {
161+ // Name buffer
162+ const int BufferSize = 256;
163+ char Buffer[BufferSize];
164+ Spec.GetName(Buffer);
165+ fprintf(DBOut,"Loading Dim3 model file %s\n",Buffer);
166+ }
167+
168+ OpenedFile OFile;
169+ if (!Spec.Open(OFile))
170+ {
171+ if (DBOut) fprintf(DBOut,"ERROR opening the file\n");
172+ return false;
173+ }
174+
175+ Dim3_SetupParseTree();
176+ XML_DataBlockLoader.CurrentElement = &Dim3_RootParser;
177+
178+ int32 Len = 0;
179+ OFile.GetLength(Len);
180+ if (Len <= 0) return false;
181+
182+ vector<char> FileContents(Len);
183+ if (!OFile.Read(Len,&FileContents[0])) return false;
184+
185+ char FileName[256];
186+ Spec.GetName(FileName);
187+ FileName[31] = 0; // Use only first 31 characters of filename (MacOS Classic)
188+ // fdprintf("Loading from text file %s",FileName);
189+
190+ XML_DataBlockLoader.SourceName = FileName;
191+ if (!XML_DataBlockLoader.ParseData(&FileContents[0],Len))
192+ {
193+ if (DBOut) fprintf(DBOut, "There were parsing errors in Dim3 model file %s\n",FileName);
194+ }
195+
196+ // Set these up now
197+ if (Model.InverseVSIndices.empty()) Model.BuildInverseVSIndices();
198+
199+ if (!Normals.empty())
200+ {
201+ Model.NormSources.resize(3*Model.VtxSrcIndices.size());
202+ Model.Normals.resize(Model.NormSources.size());
203+ for (unsigned k=0; k<Model.VtxSources.size(); k++)
204+ {
205+ GLfloat *Norm = &Normals[3*k];
206+ GLushort P0 = Model.InvVSIPointers[k];
207+ GLushort P1 = Model.InvVSIPointers[k+1];
208+ for (int p=P0; p<P1; p++)
209+ {
210+ GLfloat *DestNorm = &Model.NormSources[3*Model.InverseVSIndices[p]];
211+ for (int c=0; c<3; c++)
212+ DestNorm[c] = Norm[c];
213+ }
214+ }
215+ // All done with them
216+
217+ Normals.clear();
218+ }
219+
220+ // First, find the neutral-position vertices
221+ Model.FindPositions_Neutral(false);
222+
223+ // Work out the sorted order for the bones; be sure not to repeat this if already done.
224+ if (BoneIndices.empty() && !Model.Bones.empty())
225+ {
226+ size_t NumBones = Model.Bones.size();
227+ BoneIndices.resize(NumBones);
228+ fill(BoneIndices.begin(),BoneIndices.end(),(size_t)UNONE); // No bones listed -- yet
229+ vector<Model3D_Bone> SortedBones(NumBones);
230+ vector<size_t> BoneStack(NumBones);
231+ vector<bool> BonesUsed(NumBones);
232+ fill(BonesUsed.begin(),BonesUsed.end(),false);
233+
234+ // Add the bones, one by one;
235+ // the bone stack's height is originally zero
236+ int StackTop = -1;
237+ for (vector<size_t>::value_type ib=0; ib<NumBones; ib++)
238+ {
239+ // Scan down the bone stack to find a bone that's the parent of some unlisted bone;
240+ vector<size_t>::value_type ibsrch =
241+ static_cast<vector<size_t>::value_type>(NumBones); // "Bone not found" value
242+ int ibstck = -1; // Empty stack
243+ for (ibstck=StackTop; ibstck>=0; ibstck--)
244+ {
245+ // Note: the bone stack is indexed relative to the original,
246+ // as is the bones-used list
247+ char *StackBoneTag = BoneOwnTags[BoneStack[ibstck]].Tag0;
248+ for (ibsrch=0; ibsrch<NumBones; ibsrch++)
249+ {
250+ if (BonesUsed[ibsrch]) continue;
251+ char *ParentTag = BoneOwnTags[ibsrch].Tag1;
252+ if (strncmp(ParentTag,StackBoneTag,BoneTagSize)==0)
253+ break;
254+ }
255+ // If a bone was found, then readjust the stack size appropriately and quit.
256+ if (ibsrch < NumBones)
257+ {
258+ if (ibstck < StackTop)
259+ {
260+ // Be sure to get the traversal push/pop straight.
261+ Model.Bones[BoneStack[ibstck+1]].Flags |= Model3D_Bone::Push;
262+ Model.Bones[ibsrch].Flags |= Model3D_Bone::Pop;
263+ StackTop = ibstck;
264+ }
265+ break;
266+ }
267+ }
268+ // If none was found, then the bone's parent is the assumed root bone.
269+ if (ibstck < 0)
270+ {
271+ for (ibsrch=0; ibsrch<NumBones; ibsrch++)
272+ {
273+ if (BonesUsed[ibsrch]) continue;
274+
275+ // Check if the parent is not one of the bones
276+ char *ParentTag = BoneOwnTags[ibsrch].Tag1;
277+ size_t ibsx;
278+ for (ibsx=0; ibsx<NumBones; ibsx++)
279+ {
280+ if (strncmp(ParentTag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
281+ break;
282+ }
283+ // If a match was not found, then quit searching
284+ if (ibsx >= NumBones) break;
285+ }
286+
287+ // Not sure how to handle this sort of error;
288+ // it could be produced by circular bone references:
289+ // B1 -> B2 -> B3 -> ... -> B1
290+ assert(ibsrch < NumBones);
291+
292+ // Be sure to get the traversal push/pop straight.
293+ if (StackTop >= 0)
294+ {
295+ Model.Bones[BoneStack[0]].Flags |= Model3D_Bone::Push;
296+ Model.Bones[ibsrch].Flags |= Model3D_Bone::Pop;
297+ StackTop = -1;
298+ }
299+ }
300+
301+ // Add the bone to the stack
302+ BoneStack[++StackTop] = ibsrch;
303+
304+ // Don't look for it anymore
305+ BonesUsed[ibsrch] = true;
306+
307+ // Index for remapping
308+ BoneIndices[ibsrch] = ib;
309+ }
310+
311+ // Reorder the bones
312+ for (size_t ib=0; ib<NumBones; ib++)
313+ SortedBones[BoneIndices[ib]] = Model.Bones[ib];
314+
315+ // Put them back into the model in one step
316+ Model.Bones.swap(SortedBones);
317+
318+ // Find the vertex bone indices; this assumes that the vertices have already been read in.
319+ for (unsigned iv=0; iv<Model.VtxSources.size(); iv++)
320+ {
321+ Model3D_VertexSource& VS = Model.VtxSources[iv];
322+ size_t ibsx;
323+ char *Tag;
324+
325+ Tag = VertexBoneTags[iv].Tag0;
326+ for (ibsx=0; ibsx<NumBones; ibsx++)
327+ {
328+ if (strncmp(Tag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
329+ break;
330+ }
331+ VS.Bone0 = ibsx < NumBones ? BoneIndices[ibsx] : static_cast<GLshort>(NONE);
332+
333+ Tag = VertexBoneTags[iv].Tag1;
334+ for (ibsx=0; ibsx<NumBones; ibsx++)
335+ {
336+ if (strncmp(Tag,BoneOwnTags[ibsx].Tag0,BoneTagSize)==0)
337+ break;
338+ }
339+ VS.Bone1 = ibsx < NumBones ? BoneIndices[ibsx] : static_cast<GLshort>(NONE);
340+ }
341+ }
342+
343+ return (!Model.Positions.empty() && !Model.VertIndices.empty());
344+}
345+
346+
347+// Gets some XML data to parse
348+bool XML_Dim3DataBlock::GetData()
349+{
350+ // Check...
351+ assert(Buffer);
352+ assert(BufLen > 0);
353+
354+ // Only one buffer
355+ LastOne = true;
356+
357+ return true;
358+}
359+
360+
361+// Reports a read error
362+void XML_Dim3DataBlock::ReportReadError()
363+{
364+ const char *Name = SourceName ? SourceName : "[]";
365+
366+ if (DBOut)
367+ fprintf(DBOut, "Error in reading data/resources from object %s\n",Name);
368+}
369+
370+
371+// Reports an XML parsing error
372+void XML_Dim3DataBlock::ReportParseError(const char *ErrorString, int LineNumber)
373+{
374+ const char *Name = SourceName ? SourceName : "[]";
375+
376+ if (DBOut)
377+ fprintf(DBOut, "XML parsing error: %s at line %d in object %s\n", ErrorString, LineNumber, Name);
378+}
379+
380+
381+// Reports an interpretation error
382+void XML_Dim3DataBlock::ReportInterpretError(const char *ErrorString)
383+{
384+ if (DBOut)
385+ fprintf(DBOut, "%s\n",ErrorString);
386+}
387+
388+// Requests aborting of parsing (reasonable if there were lots of errors)
389+bool XML_Dim3DataBlock::RequestAbort()
390+{
391+ return false;
392+}
393+
394+
395+// Dummy elements:
396+static XML_ElementParser
397+ CreatorParser("Creator"),
398+ CenterParser("Center"),
399+ LightParser("Light"),
400+ ShadingParser("Shading"),
401+ ShadowBoxParser("Shadow_Box"),
402+ VerticesParser("Vertexes"),
403+ BonesParser("Bones"),
404+ EffectsParser("Effects"),
405+ EffectParser("Effect"),
406+ FillsParser("Fills"),
407+ FillParser("Fill"),
408+ D3ColorsParser("Colors"),
409+ D3ColorParser("Color"),
410+ D3ImagesParser("Images"),
411+ D3ImageParser("Image"),
412+ TrianglesParser("Triangles"),
413+ FramesParser("Poses"),
414+ FrameBonesParser("Bones"),
415+ SequencesParser("Animations"),
416+ SeqLoopParser("Loop"),
417+ SeqFramesParser("Poses");
418+
419+
420+// "Real" elements:
421+
422+class XML_BoundingBoxParser: public XML_ElementParser
423+{
424+ GLfloat x_size, y_size, z_size, x_offset, y_offset, z_offset;
425+
426+public:
427+ bool Start();
428+ bool HandleAttribute(const char *Tag, const char *Value);
429+ bool AttributesDone();
430+
431+ XML_BoundingBoxParser(const char *Name): XML_ElementParser(Name) {}
432+};
433+
434+
435+
436+bool XML_BoundingBoxParser::Start()
437+{
438+ x_size = y_size = z_size = x_offset = y_offset = z_offset = 0;
439+
440+ return true;
441+}
442+
443+bool XML_BoundingBoxParser::HandleAttribute(const char *Tag, const char *Value)
444+{
445+ if (StringsEqual(Tag,"size"))
446+ {
447+ return (sscanf(Value,"%f,%f,%f",&x_size,&y_size,&z_size) == 3);
448+ }
449+ else if (StringsEqual(Tag,"offset"))
450+ {
451+ return (sscanf(Value,"%f,%f,%f",&x_offset,&y_offset,&z_offset) == 3);
452+ }
453+ else if (StringsEqual(Tag,"x_size"))
454+ {
455+ return ReadFloatValue(Value,x_size);
456+ }
457+ else if (StringsEqual(Tag,"y_size"))
458+ {
459+ return ReadFloatValue(Value,y_size);
460+ }
461+ else if (StringsEqual(Tag,"z_size"))
462+ {
463+ return ReadFloatValue(Value,z_size);
464+ }
465+ else if (StringsEqual(Tag,"x_offset"))
466+ {
467+ return ReadFloatValue(Value,x_offset);
468+ }
469+ else if (StringsEqual(Tag,"y_offset"))
470+ {
471+ return ReadFloatValue(Value,y_offset);
472+ }
473+ else if (StringsEqual(Tag,"z_offset"))
474+ {
475+ return ReadFloatValue(Value,z_offset);
476+ }
477+
478+ UnrecognizedTag();
479+ return false;
480+}
481+
482+bool XML_BoundingBoxParser::AttributesDone()
483+{
484+ // Inconsistent resizing: weird bug in ggadwa's code
485+
486+ ModelPtr->BoundingBox[0][0] = x_offset - x_size/2;
487+ ModelPtr->BoundingBox[0][1] = y_offset - y_size;
488+ ModelPtr->BoundingBox[0][2] = z_offset - z_size/2;
489+
490+ ModelPtr->BoundingBox[1][0] = x_offset + x_size/2;
491+ ModelPtr->BoundingBox[1][1] = y_offset;
492+ ModelPtr->BoundingBox[1][2] = z_offset + z_size/2;
493+
494+ return true;
495+}
496+
497+static XML_BoundingBoxParser BoundingBoxParser("Bound_Box");
498+static XML_BoundingBoxParser ViewBoxParser("View_Box");
499+
500+
501+class XML_VertexParser: public XML_ElementParser
502+{
503+ Model3D_VertexSource Data;
504+ GLfloat Norm[3];
505+
506+ // For adding to the bone-tag array as each vertex is added
507+ BoneTagWrapper BT;
508+
509+public:
510+ bool Start();
511+ bool HandleAttribute(const char *Tag, const char *Value);
512+ bool AttributesDone();
513+
514+ XML_VertexParser(): XML_ElementParser("v") {}
515+};
516+
517+
518+bool XML_VertexParser::Start()
519+{
520+ for (int c=0; c<3; c++)
521+ Norm[c] = Data.Position[c] = 0;
522+
523+ // Initially: no bones
524+ Data.Bone0 = Data.Bone1 = (GLushort)NONE;
525+ Data.Blend = 0;
526+
527+ // No bone: zero-length strings:
528+ BT.Tag0[0] = BT.Tag1[0] = 0;
529+
530+ return true;
531+}
532+
533+bool XML_VertexParser::HandleAttribute(const char *Tag, const char *Value)
534+{
535+ if (StringsEqual(Tag,"c3"))
536+ {
537+ GLfloat *Pos = Data.Position;
538+ return (sscanf(Value,"%f,%f,%f",&Pos[0],&Pos[1],&Pos[2]) == 3);
539+ }
540+ else if (StringsEqual(Tag,"n3"))
541+ {
542+ return (sscanf(Value,"%f,%f,%f",&Norm[0],&Norm[1],&Norm[2]) == 3);
543+ }
544+ else if (StringsEqual(Tag,"x"))
545+ {
546+ return ReadFloatValue(Value,Data.Position[0]);
547+ }
548+ else if (StringsEqual(Tag,"y"))
549+ {
550+ return ReadFloatValue(Value,Data.Position[1]);
551+ }
552+ else if (StringsEqual(Tag,"z"))
553+ {
554+ return ReadFloatValue(Value,Data.Position[2]);
555+ }
556+ else if (StringsEqual(Tag,"major"))
557+ {
558+ strncpy(BT.Tag0,Value,BoneTagSize);
559+ return true;
560+ }
561+ else if (StringsEqual(Tag,"minor"))
562+ {
563+ strncpy(BT.Tag1,Value,BoneTagSize);
564+ return true;
565+ }
566+ else if (StringsEqual(Tag,"factor"))
567+ {
568+ GLfloat Factor;
569+ if (ReadFloatValue(Value,Factor))
570+ {
571+ // Convert from ggadwa's definition (100 to 0) to mine (0 to 1)
572+ // for first to second bone.
573+ Data.Blend = 1 - Factor/100;
574+ return true;
575+ }
576+ else return false;
577+ }
578+
579+ UnrecognizedTag();
580+ return false;
581+}
582+
583+bool XML_VertexParser::AttributesDone()
584+{
585+ // Always handle the bone data, even for a blank bone, to maintain coherence.
586+ // Also always handle normal data for that reason.
587+ ModelPtr->VtxSources.push_back(Data);
588+ for (int c=0; c<3; c++)
589+ Normals.push_back(Norm[c]);
590+ VertexBoneTags.push_back(BT);
591+
592+ return true;
593+}
594+
595+static XML_VertexParser VertexParser;
596+
597+
598+class XML_BoneParser: public XML_ElementParser
599+{
600+ Model3D_Bone Data;
601+
602+ // For adding to the bone-tag array as each bone is added
603+ BoneTagWrapper BT;
604+
605+public:
606+ bool Start();
607+ bool HandleAttribute(const char *Tag, const char *Value);
608+ bool AttributesDone();
609+
610+ XML_BoneParser(): XML_ElementParser("Bone") {}
611+};
612+
613+
614+bool XML_BoneParser::Start()
615+{
616+ for (int c=0; c<3; c++)
617+ Data.Position[c] = 0;
618+
619+ // Initially: don't do anything special
620+ // (might produce screwy models without further processing)
621+ Data.Flags = 0;
622+
623+ // No bone: zero-length strings:
624+ BT.Tag0[0] = BT.Tag1[0] = 0;
625+
626+ return true;
627+}
628+
629+bool XML_BoneParser::HandleAttribute(const char *Tag, const char *Value)
630+{
631+ if (StringsEqual(Tag,"c3"))
632+ {
633+ GLfloat *Pos = Data.Position;
634+ return (sscanf(Value,"%f,%f,%f",&Pos[0],&Pos[1],&Pos[2]) == 3);
635+ }
636+ else if (StringsEqual(Tag,"x"))
637+ {
638+ return ReadFloatValue(Value,Data.Position[0]);
639+ }
640+ else if (StringsEqual(Tag,"y"))
641+ {
642+ return ReadFloatValue(Value,Data.Position[1]);
643+ }
644+ else if (StringsEqual(Tag,"z"))
645+ {
646+ return ReadFloatValue(Value,Data.Position[2]);
647+ }
648+ else if (StringsEqual(Tag,"tag"))
649+ {
650+ strncpy(BT.Tag0,Value,BoneTagSize);
651+ return true;
652+ }
653+ else if (StringsEqual(Tag,"parent"))
654+ {
655+ strncpy(BT.Tag1,Value,BoneTagSize);
656+ return true;
657+ }
658+
659+ UnrecognizedTag();
660+ return false;
661+}
662+
663+bool XML_BoneParser::AttributesDone()
664+{
665+ // Always handle the bone data, even for a blank bone, to maintain coherence.
666+ ModelPtr->Bones.push_back(Data);
667+ BoneOwnTags.push_back(BT);
668+
669+ return true;
670+}
671+
672+static XML_BoneParser BoneParser;
673+
674+
675+
676+class XML_TriVertexParser: public XML_ElementParser
677+{
678+ uint16 ID;
679+ float Txtr_X, Txtr_Y;
680+ float Norm_X, Norm_Y, Norm_Z;
681+
682+public:
683+ bool Start();
684+ bool HandleAttribute(const char *Tag, const char *Value);
685+ bool AttributesDone();
686+
687+ XML_TriVertexParser(): XML_ElementParser("v") {}
688+};
689+
690+
691+bool XML_TriVertexParser::Start()
692+{
693+ // Reasonable defaults:
694+ ID = (uint16)NONE;
695+ Txtr_X = 0.5, Txtr_Y = 0.5;
696+ Norm_X = Norm_Y = Norm_Z = 0;
697+
698+ return true;
699+}
700+
701+bool XML_TriVertexParser::HandleAttribute(const char *Tag, const char *Value)
702+{
703+ if (StringsEqual(Tag,"ID"))
704+ {
705+ return ReadUInt16Value(Value,ID);
706+ }
707+ else if (StringsEqual(Tag,"uv"))
708+ {
709+ return (sscanf(Value,"%f,%f",&Txtr_X,&Txtr_Y) == 2);
710+ }
711+ else if (StringsEqual(Tag,"n"))
712+ {
713+ return (sscanf(Value,"%f,%f,%f",&Norm_X,&Norm_Z,&Norm_Y) == 3); // Dim3's odd order
714+ }
715+ else if (StringsEqual(Tag,"xtxt"))
716+ {
717+ return ReadFloatValue(Value,Txtr_X);
718+ }
719+ else if (StringsEqual(Tag,"ytxt"))
720+ {
721+ return ReadFloatValue(Value,Txtr_Y);
722+ }
723+
724+ UnrecognizedTag();
725+ return false;
726+}
727+
728+bool XML_TriVertexParser::AttributesDone()
729+{
730+ // Older dim3-Animator normal support suppressed
731+ /*
732+ // Normalize the normal, if nonzero
733+ float NSQ = Norm_X*Norm_X + Norm_Y*Norm_Y + Norm_Z*Norm_Z;
734+ if (NSQ != 0)
735+ {
736+ float NMult = (float)(1/sqrt(NSQ));
737+ Norm_X *= NMult;
738+ Norm_Y *= NMult;
739+ Norm_Z *= NMult;
740+ }
741+ */
742+
743+ GLushort Index = ((GLushort)ModelPtr->VertIndices.size());
744+ ModelPtr->VertIndices.push_back(Index);
745+ ModelPtr->VtxSrcIndices.push_back(ID);
746+ ModelPtr->TxtrCoords.push_back(Txtr_X);
747+ ModelPtr->TxtrCoords.push_back(Txtr_Y);
748+ /*
749+ ModelPtr->Normals.push_back(Norm_X);
750+ ModelPtr->Normals.push_back(Norm_Y);
751+ ModelPtr->Normals.push_back(Norm_Z);
752+ */
753+ return true;
754+}
755+
756+static XML_TriVertexParser TriVertexParser;
757+
758+
759+
760+class XML_FrameParser: public XML_ElementParser
761+{
762+ // For adding to the frame-name array as frames are added
763+ NameTagWrapper NT;
764+
765+public:
766+ bool Start();
767+ bool HandleAttribute(const char *Tag, const char *Value);
768+ bool End();
769+
770+ XML_FrameParser(): XML_ElementParser("Pose") {}
771+};
772+
773+
774+bool XML_FrameParser::Start()
775+{
776+ // Be sure to have the right number of frame members --
777+ // and blank them out
778+ size_t NumBones = ModelPtr->Bones.size();
779+ ReadFrame.resize(NumBones);
780+ objlist_clear(&ReadFrame[0],NumBones);
781+
782+ // No name: zero-length name
783+ NT.Tag[0] = 0;
784+
785+ return true;
786+}
787+
788+bool XML_FrameParser::HandleAttribute(const char *Tag, const char *Value)
789+{
790+ if (StringsEqual(Tag,"name"))
791+ {
792+ strncpy(NT.Tag,Value,NameTagSize);
793+ return true;
794+ }
795+
796+ UnrecognizedTag();
797+ return false;
798+}
799+
800+bool XML_FrameParser::End()
801+{
802+ // Some of the data was set up by child elements, so all the processing
803+ // can be back here.
804+ for (size_t b=0; b<ReadFrame.size(); b++)
805+ ModelPtr->Frames.push_back(ReadFrame[b]);
806+
807+ FrameTags.push_back(NT);
808+
809+ return true;
810+}
811+
812+static XML_FrameParser FrameParser;
813+
814+
815+
816+class XML_FrameBoneParser: public XML_ElementParser
817+{
818+ Model3D_Frame Data;
819+
820+ // The bone tag to look for
821+ char BoneTag[BoneTagSize];
822+
823+public:
824+ bool Start();
825+ bool HandleAttribute(const char *Tag, const char *Value);
826+ bool AttributesDone();
827+
828+ XML_FrameBoneParser(): XML_ElementParser("Bone") {}
829+};
830+
831+
832+bool XML_FrameBoneParser::Start()
833+{
834+ // Clear everything out:
835+ obj_clear(Data);
836+
837+ // Empty string
838+ BoneTag[0] = 0;
839+
840+ return true;
841+}
842+
843+
844+// Some of angles have their signs reversed to translate BB's sign conventions
845+// into more my more geometrically-elegant ones.
846+
847+bool XML_FrameBoneParser::HandleAttribute(const char *Tag, const char *Value)
848+{
849+ if (StringsEqual(Tag,"rot"))
850+ {
851+ float InAngle[3];
852+ if (sscanf(Value,"%f,%f,%f",&InAngle[0],&InAngle[1],&InAngle[2]) == 3)
853+ {
854+ for (int c=0; c<3; c++)
855+ Data.Angles[c] = GetAngle(- InAngle[c]);
856+ return true;
857+ }
858+ else
859+ return false;
860+ }
861+ else if (StringsEqual(Tag,"move"))
862+ {
863+ GLfloat *Ofst = Data.Offset;
864+ return (sscanf(Value,"%f,%f,%f",&Ofst[0],&Ofst[1],&Ofst[2]) == 3);
865+ }
866+ else if (StringsEqual(Tag,"acceleration"))
867+ {
868+ // Ignore the acceleration for now
869+ return true;
870+ }
871+ else if (StringsEqual(Tag,"xmove"))
872+ {
873+ return ReadFloatValue(Value,Data.Offset[0]);
874+ }
875+ else if (StringsEqual(Tag,"ymove"))
876+ {
877+ return ReadFloatValue(Value,Data.Offset[1]);
878+ }
879+ else if (StringsEqual(Tag,"zmove"))
880+ {
881+ return ReadFloatValue(Value,Data.Offset[2]);
882+ }
883+ else if (StringsEqual(Tag,"xrot"))
884+ {
885+ float InAngle;
886+ if (ReadFloatValue(Value,InAngle))
887+ {
888+ Data.Angles[0] = GetAngle(InAngle);
889+ return true;
890+ }
891+ else
892+ return false;
893+ }
894+ else if (StringsEqual(Tag,"yrot"))
895+ {
896+ float InAngle;
897+ if (ReadFloatValue(Value,InAngle))
898+ {
899+ Data.Angles[1] = GetAngle(-InAngle);
900+ return true;
901+ }
902+ else
903+ return false;
904+ }
905+ else if (StringsEqual(Tag,"zrot"))
906+ {
907+ float InAngle;
908+ if (ReadFloatValue(Value,InAngle))
909+ {
910+ Data.Angles[2] = GetAngle(-InAngle);
911+ return true;
912+ }
913+ else
914+ return false;
915+ }
916+ else if (StringsEqual(Tag,"tag"))
917+ {
918+ strncpy(BoneTag,Value,BoneTagSize);
919+ return true;
920+ }
921+
922+ UnrecognizedTag();
923+ return false;
924+}
925+
926+bool XML_FrameBoneParser::AttributesDone()
927+{
928+ // Place the bone info into the appropriate temporary-array location
929+ size_t NumBones = BoneOwnTags.size();
930+ size_t ib;
931+ for (ib=0; ib<NumBones; ib++)
932+ {
933+ // Compare tag to bone's self tag
934+ if (strncmp(BoneTag,BoneOwnTags[ib].Tag0,BoneTagSize) == 0)
935+ break;
936+ }
937+ if (ib < NumBones)
938+ obj_copy(ReadFrame[BoneIndices[ib]],Data);
939+
940+ return true;
941+}
942+
943+static XML_FrameBoneParser FrameBoneParser;
944+
945+
946+class XML_SequenceParser: public XML_ElementParser
947+{
948+
949+public:
950+ bool End();
951+
952+ XML_SequenceParser(): XML_ElementParser("Animation") {}
953+};
954+
955+bool XML_SequenceParser::End()
956+{
957+ // Add pointer index to end of sequences list;
958+ // create that list if it had been absent.
959+ if (ModelPtr->SeqFrmPointers.empty())
960+ {
961+ ModelPtr->SeqFrmPointers.push_back(0);
962+ }
963+ ModelPtr->SeqFrmPointers.push_back((GLushort)(ModelPtr->SeqFrames.size()));
964+ return true;
965+}
966+
967+static XML_SequenceParser SequenceParser;
968+
969+
970+class XML_SeqFrameParser: public XML_ElementParser
971+{
972+ Model3D_SeqFrame Data;
973+
974+public:
975+ bool Start();
976+ bool HandleAttribute(const char *Tag, const char *Value);
977+ bool AttributesDone();
978+
979+ XML_SeqFrameParser(): XML_ElementParser("Pose") {}
980+};
981+
982+
983+bool XML_SeqFrameParser::Start()
984+{
985+ // Clear everything out:
986+ obj_clear(Data);
987+
988+ // No frame
989+ Data.Frame = NONE;
990+
991+ return true;
992+}
993+
994+// Some of angles have their signs reversed to translate BB's sign conventions
995+// into more my more geometrically-elegant ones.
996+
997+bool XML_SeqFrameParser::HandleAttribute(const char *Tag, const char *Value)
998+{
999+ if (StringsEqual(Tag,"sway"))
1000+ {
1001+ float InAngle[3];
1002+ if (sscanf(Value,"%f,%f,%f",&InAngle[0],&InAngle[1],&InAngle[2]) == 3)
1003+ {
1004+ for (int c=0; c<3; c++)
1005+ Data.Angles[c] = GetAngle(- InAngle[c]);
1006+ return true;
1007+ }
1008+ else
1009+ return false;
1010+ }
1011+ else if (StringsEqual(Tag,"move"))
1012+ {
1013+ GLfloat *Ofst = Data.Offset;
1014+ return (sscanf(Value,"%f,%f,%f",&Ofst[0],&Ofst[1],&Ofst[2]) == 3);
1015+ }
1016+ else if (StringsEqual(Tag,"xmove"))
1017+ {
1018+ return ReadFloatValue(Value,Data.Offset[0]);
1019+ }
1020+ else if (StringsEqual(Tag,"ymove"))
1021+ {
1022+ return ReadFloatValue(Value,Data.Offset[1]);
1023+ }
1024+ else if (StringsEqual(Tag,"zmove"))
1025+ {
1026+ return ReadFloatValue(Value,Data.Offset[2]);
1027+ }
1028+ else if (StringsEqual(Tag,"xsway"))
1029+ {
1030+ float InAngle;
1031+ if (ReadFloatValue(Value,InAngle))
1032+ {
1033+ Data.Angles[0] = GetAngle(InAngle);
1034+ return true;
1035+ }
1036+ else
1037+ return false;
1038+ }
1039+ else if (StringsEqual(Tag,"ysway"))
1040+ {
1041+ float InAngle;
1042+ if (ReadFloatValue(Value,InAngle))
1043+ {
1044+ Data.Angles[1] = GetAngle(-InAngle);
1045+ return true;
1046+ }
1047+ else
1048+ return false;
1049+ }
1050+ else if (StringsEqual(Tag,"zsway"))
1051+ {
1052+ float InAngle;
1053+ if (ReadFloatValue(Value,InAngle))
1054+ {
1055+ Data.Angles[2] = GetAngle(-InAngle);
1056+ return true;
1057+ }
1058+ else
1059+ return false;
1060+ }
1061+ else if (StringsEqual(Tag,"name"))
1062+ {
1063+ // Find which frame
1064+ size_t ifr;
1065+ size_t NumFrames = FrameTags.size();
1066+ for (ifr=0; ifr<NumFrames; ifr++)
1067+ {
1068+ if (strncmp(Value,FrameTags[ifr].Tag,BoneTagSize) == 0) break;
1069+ }
1070+ if (ifr >= NumFrames) ifr = static_cast<size_t>(NONE);
1071+ Data.Frame = (GLshort)ifr;
1072+ return true;
1073+ }
1074+ else if (StringsEqual(Tag,"time"))
1075+ {
1076+ // Ignore; all timing info will come from the shapes file
1077+ return true;
1078+ }
1079+
1080+ UnrecognizedTag();
1081+ return false;
1082+}
1083+
1084+bool XML_SeqFrameParser::AttributesDone()
1085+{
1086+ // Add the frame
1087+ ModelPtr->SeqFrames.push_back(Data);
1088+
1089+ return true;
1090+}
1091+
1092+static XML_SeqFrameParser SeqFrameParser;
1093+
1094+
1095+void Dim3_SetupParseTree()
1096+{
1097+ // Lazy init
1098+ if (Dim3_ParserInited) return;
1099+
1100+ // Set up the root object
1101+ Dim3_RootParser.AddChild(&Dim3_Parser);
1102+
1103+ Dim3_Parser.AddChild(&CreatorParser);
1104+ Dim3_Parser.AddChild(&CenterParser);
1105+ Dim3_Parser.AddChild(&LightParser);
1106+ Dim3_Parser.AddChild(&BoundingBoxParser);
1107+ Dim3_Parser.AddChild(&ViewBoxParser);
1108+ Dim3_Parser.AddChild(&ShadingParser);
1109+ Dim3_Parser.AddChild(&ShadowBoxParser);
1110+
1111+ VerticesParser.AddChild(&VertexParser);
1112+ Dim3_Parser.AddChild(&VerticesParser);
1113+
1114+ BonesParser.AddChild(&BoneParser);
1115+ Dim3_Parser.AddChild(&BonesParser);
1116+
1117+ EffectsParser.AddChild(&EffectParser);
1118+ Dim3_Parser.AddChild(&EffectsParser);
1119+
1120+ D3ColorsParser.AddChild(&D3ColorParser);
1121+ D3ImagesParser.AddChild(&D3ImageParser);
1122+ TrianglesParser.AddChild(&TriVertexParser);
1123+
1124+ FillParser.AddChild(&D3ColorsParser);
1125+ FillParser.AddChild(&D3ImagesParser);
1126+ FillParser.AddChild(&TrianglesParser);
1127+
1128+ FillsParser.AddChild(&FillParser);
1129+ Dim3_Parser.AddChild(&FillsParser);
1130+
1131+ FrameBonesParser.AddChild(&FrameBoneParser);
1132+ FrameParser.AddChild(&FrameBonesParser);
1133+ FramesParser.AddChild(&FrameParser);
1134+ Dim3_Parser.AddChild(&FramesParser);
1135+
1136+ SeqFramesParser.AddChild(&SeqFrameParser);
1137+ SequenceParser.AddChild(&SeqFramesParser);
1138+ SequenceParser.AddChild(&SeqLoopParser);
1139+ SequencesParser.AddChild(&SequenceParser);
1140+ Dim3_Parser.AddChild(&SequencesParser);
1141+
1142+ Dim3_ParserInited = true;
1143+}
1144+
1145+
1146+// HAVE_OPENGL
1147+#endif
--- marathon/trunk/Source_Files/ModelView/WavefrontLoader.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/WavefrontLoader.cpp (revision 527)
@@ -1,699 +1,699 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- Alias|Wavefront Object Loader
21-
22- By Loren Petrich, June 16, 2001
23-*/
24-
25-#include <ctype.h>
26-#include <stdlib.h>
27-#include <string.h>
28-#include <algorithm>
29-
30-#include "cseries.h"
31-
32-#include "Logging.h"
33-
34-#ifdef HAVE_OPENGL
35-#ifdef __WIN32__
36-#include <windows.h>
37-#endif
38-
39-#include "WavefrontLoader.h"
40-
41-
42-// Which of these is present in the vertex-info data:
43-enum {
44- Present_Position = 0x0001,
45- Present_TxtrCoord = 0x0002,
46- Present_Normal = 0x0004
47-};
48-
49-static const char *Path = NULL; // Path to model file.
50-
51-// Input line will be able to stretch as much as necessary
52-static vector<char> InputLine(64);
53-
54-// Compare input-line beginning to a keyword;
55-// returns pointer to rest of line if it was found,
56-// otherwise returns NULL
57-char *CompareToKeyword(const char *Keyword);
58-
59-// Gets a pointer to a string of vertex-index sets and picks off one of them,
60-// returning a pointer to the character just after it. Also returns the presence and values
61-// picked off.
62-// Returns NULL if there are none remaining to be found.
63-char *GetVertIndxSet(char *Buffer, short& Presence,
64- short& PosIndx, short& TCIndx, short& NormIndx);
65-
66-// Gets a vertex index and returns whether or not an index value was found
67-// what it was if found, and a pointer to the character just after the index value
68-// (either '/' or '\0'). And also whether the scanning hit the end of the set.
69-// Returns NULL if there are none remaining to be found.
70-char *GetVertIndx(char *Buffer, bool& WasFound, short& Val, bool& HitEnd);
71-
72-
73-// The purpose of the sorting is to find all the unique index sets;
74-// this is some data for the STL sorter
75-struct IndexedVertListCompare
76-{
77- short *VertIndxSets;
78-
79- // The comparison operation
80- bool operator() (int i1, int i2) const
81- {
82- short *VISet1 = VertIndxSets + 4*i1;
83- short *VISet2 = VertIndxSets + 4*i2;
84-
85- // Sort by position first, then texture coordinate, then normal
86-
87- if (VISet1[1] > VISet2[1])
88- return false;
89- else if (VISet1[1] < VISet2[1])
90- return true;
91-
92- if (VISet1[2] > VISet2[2])
93- return false;
94- else if (VISet1[2] < VISet2[2])
95- return true;
96-
97- if (VISet1[3] > VISet2[3])
98- return false;
99- else if (VISet1[3] < VISet2[3])
100- return true;
101-
102- // All equal!
103-// return true;
104- return false;
105- }
106-};
107-
108-bool LoadModel_Wavefront(FileSpecifier& Spec, Model3D& Model)
109-{
110- // Clear out the final model object
111- Model.Clear();
112-
113- // Intermediate lists of positions, texture coordinates, and normals
114- vector<GLfloat> Positions;
115- vector<GLfloat> TxtrCoords;
116- vector<GLfloat> Normals;
117-
118- // Intermediate list of polygon features:
119- // Polygon sizes (how many vertices):
120- vector<short> PolygonSizes;
121- // Vertex indices (how many read, position, txtr-coord, normal)
122- vector<short> VertIndxSets;
123-
124- Path = Spec.GetPath();
125- logNote1("Loading Alias|Wavefront model file %s",Path);
126-
127- OpenedFile OFile;
128- if (!Spec.Open(OFile))
129- {
130- logError1("ERROR opening %s",Path);
131- return false;
132- }
133-
134- // Reading loop; create temporary lists of positions, texture coordinates, and normals
135-
136- // Load the lines, one by one, and then parse them. Be sure to take care of the continuation
137- // character "\" [Wavefront files follow some Unix conventions]
138- bool MoreLines = true;
139- while(MoreLines)
140- {
141- InputLine.clear();
142-
143- // Fill up the line
144- bool LineContinued = false;
145- while(true)
146- {
147- // Try to read a character; if it is not possible to read anymore,
148- // the line has ended
149- char c;
150- MoreLines = OFile.Read(1,&c);
151- if (!MoreLines) break;
152-
153- // End-of-line characters; ignore if the line is to be continued
154- if (c == '\r' || c == '\n')
155- {
156- if (!LineContinued)
157- {
158- // If the line is not empty, then break; otherwise ignore.
159- // Blank lines will be ignored, and this will allow starting a line
160- // at the first non-end-of-line character
161- if (!InputLine.empty()) break;
162- }
163- }
164- // Backslash character indicates that the current line continues into the next one
165- else if (c == '\\')
166- {
167- LineContinued = true;
168- }
169- else
170- {
171- // Continuation will stop if a non-end-of-line character is encounted
172- LineContinued = false;
173-
174- // Add that character!
175- InputLine.push_back(c);
176- }
177- }
178- // Line-end at end of file will produce an empty line, so do this test
179- if (InputLine.empty()) continue;
180-
181- // If the line is a comment line, then ignore it
182- if (InputLine[0] == '#') continue;
183-
184- // Make the line look like a C string
185- InputLine.push_back('\0');
186-
187- // Now parse the line; notice the = instead of == (substitute and test in one line)
188- // Unhandled keywords are currently commented out for speed;
189- // many of those are for handling curved surfaces, which are currently ignored.
190- char *RestOfLine = NULL;
191- if ((RestOfLine = CompareToKeyword("v")) != NULL) // Vertex position
192- {
193- GLfloat Position[3];
194- objlist_clear(Position,3);
195-
196- sscanf(RestOfLine," %f %f %f",Position,Position+1,Position+2);
197-
198- for (int k=0; k<3; k++)
199- Positions.push_back(Position[k]);
200- }
201- else if ((RestOfLine = CompareToKeyword("vt")) != NULL) // Vertex texture coordinate
202- {
203- GLfloat TxtrCoord[2];
204- objlist_clear(TxtrCoord,2);
205-
206- sscanf(RestOfLine," %f %f",TxtrCoord,TxtrCoord+1);
207-
208- for (int k=0; k<2; k++)
209- TxtrCoords.push_back(TxtrCoord[k]);
210- }
211- else if ((RestOfLine = CompareToKeyword("vn")) != NULL) // Vertex normal
212- {
213- GLfloat Normal[3];
214- objlist_clear(Normal,3);
215-
216- sscanf(RestOfLine," %f %f %f",Normal,Normal+1,Normal+2);
217-
218- for (int k=0; k<3; k++)
219- Normals.push_back(Normal[k]);
220- }
221- /*
222- else if ((RestOfLine = CompareToKeyword("vp")) // Vertex parameter value
223- {
224- // For curved objects, which are not supported here
225- }
226- else if ((RestOfLine = CompareToKeyword("deg")) != NULL) // Degree
227- {
228- // Curved objects not supported here
229- }
230- else if ((RestOfLine = CompareToKeyword("bmat")) != NULL) // Basis matrix
231- {
232- // Curved objects not supported here
233- }
234- else if ((RestOfLine = CompareToKeyword("step")) != NULL) // Step size
235- {
236- // Curved objects not supported here
237- }
238- else if ((RestOfLine = CompareToKeyword("cstype")) != NULL) // Curve/surface type
239- {
240- // Curved objects not supported here
241- }
242- else if ((RestOfLine = CompareToKeyword("p")) != NULL) // Point
243- {
244- // Not supported here
245- }
246- else if ((RestOfLine = CompareToKeyword("l")) != NULL) // Line
247- {
248- // Not supported here
249- }
250- */
251- else if ((RestOfLine = CompareToKeyword("f")) != NULL) // Face (polygon)
252- {
253- // Pick off the face vertices one by one;
254- // stuff their contents into a token and then process that token
255- int NumVertices = 0;
256-
257- short Presence = 0, PosIndx = 0, TCIndx = 0, NormIndx = 0;
258- while((RestOfLine = GetVertIndxSet(RestOfLine, Presence, PosIndx, TCIndx, NormIndx)) != NULL)
259- {
260- NumVertices++;
261-
262- // Wavefront vertex-index conventions:
263- // Positive is 1-based indexing
264- // Negative is from end of current list
265-
266- if (PosIndx < 0)
267- PosIndx += static_cast<short>(Positions.size())/3;
268- else
269- PosIndx--;
270-
271- if (TCIndx < 0)
272- TCIndx += static_cast<short>(TxtrCoords.size())/2;
273- else
274- TCIndx--;
275-
276- if (NormIndx < 0)
277- NormIndx += static_cast<short>(Normals.size())/3;
278- else
279- NormIndx--;
280-
281- // Add!
282- VertIndxSets.push_back(Presence);
283- VertIndxSets.push_back(PosIndx);
284- VertIndxSets.push_back(TCIndx);
285- VertIndxSets.push_back(NormIndx);
286- }
287- // Polygon complete!
288- PolygonSizes.push_back(NumVertices);
289- }
290- /*
291- else if ((RestOfLine = CompareToKeyword("curv")) != NULL) // Curve
292- {
293- // Curved objects not supported here
294- }
295- else if ((RestOfLine = CompareToKeyword("curv2")) != NULL) // 2D Curve
296- {
297- // Curved objects not supported here
298- }
299- else if ((RestOfLine = CompareToKeyword("surf")) != NULL) // Surface
300- {
301- // Curved objects not supported here
302- }
303- else if ((RestOfLine = CompareToKeyword("parm")) != NULL) // Parameter values
304- {
305- // Curved objects not supported here
306- }
307- else if ((RestOfLine = CompareToKeyword("trim")) != NULL) // Outer trimming loop
308- {
309- // Curved objects not supported here
310- }
311- else if ((RestOfLine = CompareToKeyword("hole")) != NULL) // Inner trimming loop
312- {
313- // Curved objects not supported here
314- }
315- else if ((RestOfLine = CompareToKeyword("scrv")) != NULL) // Special curve
316- {
317- // Curved objects not supported here
318- }
319- else if ((RestOfLine = CompareToKeyword("sp")) != NULL) // Special point
320- {
321- // Curved objects not supported here
322- }
323- else if ((RestOfLine = CompareToKeyword("end")) != NULL) // End statement
324- {
325- // Curved objects not supported here
326- }
327- else if ((RestOfLine = CompareToKeyword("con")) != NULL) // Connect
328- {
329- // Curved objects not supported here
330- }
331- else if ((RestOfLine = CompareToKeyword("g")) != NULL) // Group name
332- {
333- // Not supported here
334- }
335- else if ((RestOfLine = CompareToKeyword("s")) != NULL) // Smoothing group
336- {
337- // Not supported here
338- }
339- else if ((RestOfLine = CompareToKeyword("mg")) != NULL) // Merging group
340- {
341- // Not supported here
342- }
343- else if ((RestOfLine = CompareToKeyword("o")) != NULL) // Object name
344- {
345- // Not supported here
346- }
347- else if ((RestOfLine = CompareToKeyword("bevel")) != NULL) // Bevel interpolation
348- {
349- // Not supported here
350- }
351- else if ((RestOfLine = CompareToKeyword("c_interp")) != NULL) // Color interpolation
352- {
353- // Not supported here
354- }
355- else if ((RestOfLine = CompareToKeyword("d_interp")) != NULL) // Dissolve interpolation
356- {
357- // Not supported here
358- }
359- else if ((RestOfLine = CompareToKeyword("lod")) != NULL) // Level of detail
360- {
361- // Not supported here
362- }
363- else if ((RestOfLine = CompareToKeyword("usemtl")) != NULL) // Material name
364- {
365- // Not supported here
366- }
367- else if ((RestOfLine = CompareToKeyword("mtllib")) != NULL) // Material library
368- {
369- // Not supported here
370- }
371- else if ((RestOfLine = CompareToKeyword("shadow_obj")) != NULL) // Shadow casting
372- {
373- // Not supported here
374- }
375- else if ((RestOfLine = CompareToKeyword("trace_obje")) != NULL) // Ray tracing
376- {
377- // Not supported here
378- }
379- else if ((RestOfLine = CompareToKeyword("ctech")) != NULL) // Curve approximation technique
380- {
381- // Curved objects not supported here
382- }
383- else if ((RestOfLine = CompareToKeyword("stech")) != NULL) // Surface approximation technique
384- {
385- // Curved objects not supported here
386- }
387- */
388- }
389-
390- if (PolygonSizes.size() <= 0)
391- {
392- logError1("ERROR: the model in %s has no polygons",Path);
393- return false;
394- }
395-
396- // How many vertices do the polygons have?
397- for (unsigned k=0; k<PolygonSizes.size(); k++)
398- {
399- short PSize = PolygonSizes[k];
400- if (PSize < 3)
401- {
402- logWarning3("WARNING: polygon ignored; it had bad size %u: %d in %s",k,PSize,Path);
403- }
404- }
405-
406- // What is the lowest common denominator of the polygon data
407- // (which is present of vertex positions, texture coordinates, and normals)
408- short WhatsPresent = Present_Position | Present_TxtrCoord | Present_Normal;
409-
410- for (unsigned k=0; k<VertIndxSets.size()/4; k++)
411- {
412- short Presence = VertIndxSets[4*k];
413- WhatsPresent &= Presence;
414- if (!(Presence & Present_Position))
415- {
416- logError2("ERROR: Vertex has no position index: %u in %s",k,Path);
417- }
418- }
419-
420- if (!(WhatsPresent & Present_Position)) return false;
421-
422- bool AllInRange = true;
423-
424- for (unsigned k=0; k<VertIndxSets.size()/4; k++)
425- {
426- short PosIndx = VertIndxSets[4*k+1];
427- if (PosIndx < 0 || PosIndx >= int(Positions.size()))
428- {
429- logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,PosIndx,(unsigned long)Positions.size()-1,Path);
430- AllInRange = false;
431- }
432-
433- if (WhatsPresent & Present_TxtrCoord)
434- {
435- short TCIndx = VertIndxSets[4*k+2];
436- if (TCIndx < 0 || TCIndx >= int(TxtrCoords.size()))
437- {
438- logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,TCIndx,(unsigned long)(TxtrCoords.size()-1),Path);
439- AllInRange = false;
440- }
441- }
442- else
443- VertIndxSets[4*k+2] = -1; // What "0" gets turned into by the Wavefront-conversion-translation code
444-
445- if (WhatsPresent & Present_Normal)
446- {
447- short NormIndx = VertIndxSets[4*k+3];
448- if (NormIndx < 0 || NormIndx >= int(Normals.size()))
449- {
450- logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,NormIndx,(unsigned long)(Normals.size()-1),Path);
451- AllInRange = false;
452- }
453- }
454- else
455- VertIndxSets[4*k+3] = -1; // What "0" gets turned into by the Wavefront-conversion-translation code
456- }
457-
458- if (!AllInRange) return false;
459-
460- // Find unique vertex sets:
461-
462- // First, do an index sort of them
463- vector<int> VertIndxRefs(VertIndxSets.size()/4);
464- for (unsigned k=0; k<VertIndxRefs.size(); k++)
465- VertIndxRefs[k] = k;
466-
467- IndexedVertListCompare Compare;
468- Compare.VertIndxSets = &VertIndxSets[0];
469- sort(VertIndxRefs.begin(),VertIndxRefs.end(),Compare);
470-
471- // Find the unique entries:
472- vector<int> WhichUniqueSet(VertIndxRefs.size());
473-
474- // Previous index values:
475- short PrevPosIndx = -1, PrevTCIndx = -1, PrevNormIndx = -1;
476- // For doing zero-based indexing
477- int NumUnique = -1;
478-
479- // Scan the vertices in index-sort order:
480- for (unsigned k=0; k<VertIndxRefs.size(); k++)
481- {
482- int n = VertIndxRefs[k];
483-
484- short *VISet = &VertIndxSets[4*n];
485- short PosIndx = VISet[1];
486- short TCIndx = VISet[2];
487- short NormIndx = VISet[3];
488-
489- if (PosIndx == PrevPosIndx && TCIndx == PrevTCIndx && NormIndx == PrevNormIndx)
490- {
491- WhichUniqueSet[n] = NumUnique;
492- continue;
493- }
494-
495- // Found a unique set
496- WhichUniqueSet[n] = ++NumUnique;
497-
498- // These are all for the model object
499-
500- // Load the positions
501- {
502- GLfloat *PosPtr = &Positions[3*PosIndx];
503- for (int m=0; m<3; m++)
504- Model.Positions.push_back(*(PosPtr++));
505- }
506-
507- // Load the texture coordinates
508- if (WhatsPresent & Present_TxtrCoord)
509- {
510- GLfloat *TCPtr = &TxtrCoords[2*TCIndx];
511- for (int m=0; m<2; m++)
512- Model.TxtrCoords.push_back(*(TCPtr++));
513- }
514-
515- // Load the normals
516- if (WhatsPresent & Present_Normal)
517- {
518- GLfloat *NormPtr = &Normals[3*NormIndx];
519- for (int m=0; m<3; m++)
520- Model.Normals.push_back(*(NormPtr++));
521- }
522-
523- // Save these new unique-set values for comparison to the next ones
524- PrevPosIndx = PosIndx;
525- PrevTCIndx = TCIndx;
526- PrevNormIndx = NormIndx;
527- }
528-
529- // Decompose the polygons into triangles by turning them into fans
530- int IndxBase = 0;
531- for (unsigned k=0; k<PolygonSizes.size(); k++)
532- {
533- short PolySize = PolygonSizes[k];
534- int *PolyIndices = &WhichUniqueSet[IndxBase];
535-
536- for (int m=0; m<PolySize-2; m++)
537- {
538- Model.VertIndices.push_back(PolyIndices[0]);
539- Model.VertIndices.push_back(PolyIndices[m+1]);
540- Model.VertIndices.push_back(PolyIndices[m+2]);
541- }
542-
543- IndxBase += PolySize;
544- }
545-
546- if (Model.VertIndices.size() <= 0)
547- {
548- logError1("ERROR: the model in %s has no good polygons",Path);
549- return false;
550- }
551-
552- logTrace("Successfully read the file:");
553- if (WhatsPresent & Present_Position) logTrace(" Positions");
554- if (WhatsPresent & Present_TxtrCoord) logTrace(" TxtrCoords");
555- if (WhatsPresent & Present_Normal) logTrace(" Normals");
556- return true;
557-}
558-
559-
560-char *CompareToKeyword(const char *Keyword)
561-{
562- size_t KWLen = strlen(Keyword);
563-
564- if (InputLine.size() < KWLen) return NULL;
565-
566- for (unsigned k=0; k<KWLen; k++)
567- if (InputLine[k] != Keyword[k]) return NULL;
568-
569- char *RestOfLine = &InputLine[KWLen];
570-
571- while(RestOfLine - &InputLine[0] < int(InputLine.size()))
572- {
573- // End of line?
574- if (*RestOfLine == '\0') return RestOfLine;
575-
576- // Other than whitespace -- assume it to be part of the keyword if just after it;
577- // otherwise, it is to be returned to the rest of the code to work on
578- if (!(*RestOfLine == ' ' || *RestOfLine == '\t'))
579- return ((RestOfLine == &InputLine[KWLen]) ? NULL : RestOfLine);
580-
581- // Whitespace: move on to the next character
582- RestOfLine++;
583- }
584-
585- // Shouldn't happen
586- return NULL;
587-}
588-
589-
590-char *GetVertIndxSet(char *Buffer, short& Presence,
591- short& PosIndx, short& TCIndx, short& NormIndx)
592-{
593- // Initialize...
594- Presence = 0; PosIndx = 0; TCIndx = 0; NormIndx = 0;
595-
596- // Eat initial whitespace; return NULL if end-of-string was hit
597- // OK to modify Buffer, since it's called by value
598- while(*Buffer == ' ' || *Buffer == '\t')
599- {
600- Buffer++;
601- }
602- if (*Buffer == '\0') return NULL;
603-
604- // Hit non-whitespace; now grab the individual vertex values
605- bool WasFound = false, HitEnd = false;
606- Buffer = GetVertIndx(Buffer,WasFound,PosIndx,HitEnd);
607- if (WasFound) Presence |= Present_Position;
608- if (HitEnd) return Buffer;
609-
610- Buffer = GetVertIndx(Buffer,WasFound,TCIndx,HitEnd);
611- if (WasFound) Presence |= Present_TxtrCoord;
612- if (HitEnd) return Buffer;
613-
614- Buffer = GetVertIndx(Buffer,WasFound,NormIndx,HitEnd);
615- if (WasFound) Presence |= Present_Normal;
616- return Buffer;
617-}
618-
619-char *GetVertIndx(char *Buffer, bool& WasFound, short& Val, bool& HitEnd)
620-{
621- const int VIBLen = 64;
622- char VIBuffer[VIBLen];
623- int VIBIndx = 0;
624-
625- // Load the vertex-index buffer and make it a C string
626- HitEnd = false;
627- WasFound = false;
628- bool HitInternalBdry = false; // Use this variable to avoid duplicating an evaluation
629- while (!(HitInternalBdry = (*Buffer == '/')))
630- {
631- HitEnd = (*Buffer == ' ' || *Buffer == '\t' || *Buffer == '\0');
632- if (HitEnd) break;
633-
634- if (VIBIndx < VIBLen-1)
635- VIBuffer[VIBIndx++] = *Buffer;
636-
637- Buffer++;
638- }
639- if (HitInternalBdry) Buffer++;
640- VIBuffer[VIBIndx] = '\0';
641-
642- // Interpret it!
643- WasFound = (sscanf(VIBuffer,"%hd",&Val) > 0);
644-
645- return Buffer;
646-}
647-
648-// Load a Wavefront model and convert its vertex and texture coordinates from
649-// OBJ's right-handed coordinate system to Aleph One's left-handed system.
650-bool LoadModel_Wavefront_RightHand(FileSpecifier& Spec, Model3D& Model)
651-{
652- bool Result = LoadModel_Wavefront(Spec, Model);
653- if (!Result) return Result;
654-
655- logTrace("Converting handedness.");
656-
657- // OBJ files produced by Blender and Wings 3D are oriented with
658- // y increasing upwards, and the front of a Blender model faces in the
659- // positive-Z direction. (Wings 3D does not distinguish a "front"
660- // view.) In Aleph One's coordinate system Z increases upwards, and
661- // items that have been placed with 0 degrees of rotation face in the
662- // positive-x direction.
663- for (unsigned XPos = 0; XPos < Model.Positions.size(); XPos += 3)
664- {
665- GLfloat X = Model.Positions[XPos];
666- Model.Positions[XPos] = Model.Positions[XPos + 2];
667- Model.Positions[XPos + 2] = Model.Positions[XPos + 1];
668- Model.Positions[XPos + 1] = -X;
669- }
670-
671- // Ditto for vertex normals, if present.
672- for (unsigned XPos = 0; XPos < Model.Normals.size(); XPos += 3)
673- {
674- GLfloat X = Model.Normals[XPos];
675- Model.Normals[XPos] = Model.Normals[XPos + 2];
676- Model.Normals[XPos + 2] = Model.Normals[XPos + 1];
677- Model.Normals[XPos + 1] = -X;
678- }
679-
680- // Vertices of each face are now listed in clockwise order.
681- // Reverse them.
682- for (unsigned IPos = 0; IPos < Model.VertIndices.size(); IPos += 3)
683- {
684- int Index = Model.VertIndices[IPos + 1];
685- Model.VertIndices[IPos + 1] = Model.VertIndices[IPos];
686- Model.VertIndices[IPos] = Index;
687- }
688-
689- // Switch texture coordinates from right-handed (x,y) to
690- // left-handed (row,column).
691- for (unsigned YPos = 1; YPos < Model.TxtrCoords.size(); YPos += 2)
692- {
693- Model.TxtrCoords[YPos] = 1.0 - Model.TxtrCoords[YPos];
694- }
695-
696- return true;
697-}
698-
699-#endif // def HAVE_OPENGL
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ Alias|Wavefront Object Loader
21+
22+ By Loren Petrich, June 16, 2001
23+*/
24+
25+#include <ctype.h>
26+#include <stdlib.h>
27+#include <string.h>
28+#include <algorithm>
29+
30+#include "cseries.h"
31+
32+#include "Logging.h"
33+
34+#ifdef HAVE_OPENGL
35+#ifdef __WIN32__
36+#include <windows.h>
37+#endif
38+
39+#include "WavefrontLoader.h"
40+
41+
42+// Which of these is present in the vertex-info data:
43+enum {
44+ Present_Position = 0x0001,
45+ Present_TxtrCoord = 0x0002,
46+ Present_Normal = 0x0004
47+};
48+
49+static const char *Path = NULL; // Path to model file.
50+
51+// Input line will be able to stretch as much as necessary
52+static vector<char> InputLine(64);
53+
54+// Compare input-line beginning to a keyword;
55+// returns pointer to rest of line if it was found,
56+// otherwise returns NULL
57+char *CompareToKeyword(const char *Keyword);
58+
59+// Gets a pointer to a string of vertex-index sets and picks off one of them,
60+// returning a pointer to the character just after it. Also returns the presence and values
61+// picked off.
62+// Returns NULL if there are none remaining to be found.
63+char *GetVertIndxSet(char *Buffer, short& Presence,
64+ short& PosIndx, short& TCIndx, short& NormIndx);
65+
66+// Gets a vertex index and returns whether or not an index value was found
67+// what it was if found, and a pointer to the character just after the index value
68+// (either '/' or '\0'). And also whether the scanning hit the end of the set.
69+// Returns NULL if there are none remaining to be found.
70+char *GetVertIndx(char *Buffer, bool& WasFound, short& Val, bool& HitEnd);
71+
72+
73+// The purpose of the sorting is to find all the unique index sets;
74+// this is some data for the STL sorter
75+struct IndexedVertListCompare
76+{
77+ short *VertIndxSets;
78+
79+ // The comparison operation
80+ bool operator() (int i1, int i2) const
81+ {
82+ short *VISet1 = VertIndxSets + 4*i1;
83+ short *VISet2 = VertIndxSets + 4*i2;
84+
85+ // Sort by position first, then texture coordinate, then normal
86+
87+ if (VISet1[1] > VISet2[1])
88+ return false;
89+ else if (VISet1[1] < VISet2[1])
90+ return true;
91+
92+ if (VISet1[2] > VISet2[2])
93+ return false;
94+ else if (VISet1[2] < VISet2[2])
95+ return true;
96+
97+ if (VISet1[3] > VISet2[3])
98+ return false;
99+ else if (VISet1[3] < VISet2[3])
100+ return true;
101+
102+ // All equal!
103+// return true;
104+ return false;
105+ }
106+};
107+
108+bool LoadModel_Wavefront(FileSpecifier& Spec, Model3D& Model)
109+{
110+ // Clear out the final model object
111+ Model.Clear();
112+
113+ // Intermediate lists of positions, texture coordinates, and normals
114+ vector<GLfloat> Positions;
115+ vector<GLfloat> TxtrCoords;
116+ vector<GLfloat> Normals;
117+
118+ // Intermediate list of polygon features:
119+ // Polygon sizes (how many vertices):
120+ vector<short> PolygonSizes;
121+ // Vertex indices (how many read, position, txtr-coord, normal)
122+ vector<short> VertIndxSets;
123+
124+ Path = Spec.GetPath();
125+ logNote1("Loading Alias|Wavefront model file %s",Path);
126+
127+ OpenedFile OFile;
128+ if (!Spec.Open(OFile))
129+ {
130+ logError1("ERROR opening %s",Path);
131+ return false;
132+ }
133+
134+ // Reading loop; create temporary lists of positions, texture coordinates, and normals
135+
136+ // Load the lines, one by one, and then parse them. Be sure to take care of the continuation
137+ // character "\" [Wavefront files follow some Unix conventions]
138+ bool MoreLines = true;
139+ while(MoreLines)
140+ {
141+ InputLine.clear();
142+
143+ // Fill up the line
144+ bool LineContinued = false;
145+ while(true)
146+ {
147+ // Try to read a character; if it is not possible to read anymore,
148+ // the line has ended
149+ char c;
150+ MoreLines = OFile.Read(1,&c);
151+ if (!MoreLines) break;
152+
153+ // End-of-line characters; ignore if the line is to be continued
154+ if (c == '\r' || c == '\n')
155+ {
156+ if (!LineContinued)
157+ {
158+ // If the line is not empty, then break; otherwise ignore.
159+ // Blank lines will be ignored, and this will allow starting a line
160+ // at the first non-end-of-line character
161+ if (!InputLine.empty()) break;
162+ }
163+ }
164+ // Backslash character indicates that the current line continues into the next one
165+ else if (c == '\\')
166+ {
167+ LineContinued = true;
168+ }
169+ else
170+ {
171+ // Continuation will stop if a non-end-of-line character is encounted
172+ LineContinued = false;
173+
174+ // Add that character!
175+ InputLine.push_back(c);
176+ }
177+ }
178+ // Line-end at end of file will produce an empty line, so do this test
179+ if (InputLine.empty()) continue;
180+
181+ // If the line is a comment line, then ignore it
182+ if (InputLine[0] == '#') continue;
183+
184+ // Make the line look like a C string
185+ InputLine.push_back('\0');
186+
187+ // Now parse the line; notice the = instead of == (substitute and test in one line)
188+ // Unhandled keywords are currently commented out for speed;
189+ // many of those are for handling curved surfaces, which are currently ignored.
190+ char *RestOfLine = NULL;
191+ if ((RestOfLine = CompareToKeyword("v")) != NULL) // Vertex position
192+ {
193+ GLfloat Position[3];
194+ objlist_clear(Position,3);
195+
196+ sscanf(RestOfLine," %f %f %f",Position,Position+1,Position+2);
197+
198+ for (int k=0; k<3; k++)
199+ Positions.push_back(Position[k]);
200+ }
201+ else if ((RestOfLine = CompareToKeyword("vt")) != NULL) // Vertex texture coordinate
202+ {
203+ GLfloat TxtrCoord[2];
204+ objlist_clear(TxtrCoord,2);
205+
206+ sscanf(RestOfLine," %f %f",TxtrCoord,TxtrCoord+1);
207+
208+ for (int k=0; k<2; k++)
209+ TxtrCoords.push_back(TxtrCoord[k]);
210+ }
211+ else if ((RestOfLine = CompareToKeyword("vn")) != NULL) // Vertex normal
212+ {
213+ GLfloat Normal[3];
214+ objlist_clear(Normal,3);
215+
216+ sscanf(RestOfLine," %f %f %f",Normal,Normal+1,Normal+2);
217+
218+ for (int k=0; k<3; k++)
219+ Normals.push_back(Normal[k]);
220+ }
221+ /*
222+ else if ((RestOfLine = CompareToKeyword("vp")) // Vertex parameter value
223+ {
224+ // For curved objects, which are not supported here
225+ }
226+ else if ((RestOfLine = CompareToKeyword("deg")) != NULL) // Degree
227+ {
228+ // Curved objects not supported here
229+ }
230+ else if ((RestOfLine = CompareToKeyword("bmat")) != NULL) // Basis matrix
231+ {
232+ // Curved objects not supported here
233+ }
234+ else if ((RestOfLine = CompareToKeyword("step")) != NULL) // Step size
235+ {
236+ // Curved objects not supported here
237+ }
238+ else if ((RestOfLine = CompareToKeyword("cstype")) != NULL) // Curve/surface type
239+ {
240+ // Curved objects not supported here
241+ }
242+ else if ((RestOfLine = CompareToKeyword("p")) != NULL) // Point
243+ {
244+ // Not supported here
245+ }
246+ else if ((RestOfLine = CompareToKeyword("l")) != NULL) // Line
247+ {
248+ // Not supported here
249+ }
250+ */
251+ else if ((RestOfLine = CompareToKeyword("f")) != NULL) // Face (polygon)
252+ {
253+ // Pick off the face vertices one by one;
254+ // stuff their contents into a token and then process that token
255+ int NumVertices = 0;
256+
257+ short Presence = 0, PosIndx = 0, TCIndx = 0, NormIndx = 0;
258+ while((RestOfLine = GetVertIndxSet(RestOfLine, Presence, PosIndx, TCIndx, NormIndx)) != NULL)
259+ {
260+ NumVertices++;
261+
262+ // Wavefront vertex-index conventions:
263+ // Positive is 1-based indexing
264+ // Negative is from end of current list
265+
266+ if (PosIndx < 0)
267+ PosIndx += static_cast<short>(Positions.size())/3;
268+ else
269+ PosIndx--;
270+
271+ if (TCIndx < 0)
272+ TCIndx += static_cast<short>(TxtrCoords.size())/2;
273+ else
274+ TCIndx--;
275+
276+ if (NormIndx < 0)
277+ NormIndx += static_cast<short>(Normals.size())/3;
278+ else
279+ NormIndx--;
280+
281+ // Add!
282+ VertIndxSets.push_back(Presence);
283+ VertIndxSets.push_back(PosIndx);
284+ VertIndxSets.push_back(TCIndx);
285+ VertIndxSets.push_back(NormIndx);
286+ }
287+ // Polygon complete!
288+ PolygonSizes.push_back(NumVertices);
289+ }
290+ /*
291+ else if ((RestOfLine = CompareToKeyword("curv")) != NULL) // Curve
292+ {
293+ // Curved objects not supported here
294+ }
295+ else if ((RestOfLine = CompareToKeyword("curv2")) != NULL) // 2D Curve
296+ {
297+ // Curved objects not supported here
298+ }
299+ else if ((RestOfLine = CompareToKeyword("surf")) != NULL) // Surface
300+ {
301+ // Curved objects not supported here
302+ }
303+ else if ((RestOfLine = CompareToKeyword("parm")) != NULL) // Parameter values
304+ {
305+ // Curved objects not supported here
306+ }
307+ else if ((RestOfLine = CompareToKeyword("trim")) != NULL) // Outer trimming loop
308+ {
309+ // Curved objects not supported here
310+ }
311+ else if ((RestOfLine = CompareToKeyword("hole")) != NULL) // Inner trimming loop
312+ {
313+ // Curved objects not supported here
314+ }
315+ else if ((RestOfLine = CompareToKeyword("scrv")) != NULL) // Special curve
316+ {
317+ // Curved objects not supported here
318+ }
319+ else if ((RestOfLine = CompareToKeyword("sp")) != NULL) // Special point
320+ {
321+ // Curved objects not supported here
322+ }
323+ else if ((RestOfLine = CompareToKeyword("end")) != NULL) // End statement
324+ {
325+ // Curved objects not supported here
326+ }
327+ else if ((RestOfLine = CompareToKeyword("con")) != NULL) // Connect
328+ {
329+ // Curved objects not supported here
330+ }
331+ else if ((RestOfLine = CompareToKeyword("g")) != NULL) // Group name
332+ {
333+ // Not supported here
334+ }
335+ else if ((RestOfLine = CompareToKeyword("s")) != NULL) // Smoothing group
336+ {
337+ // Not supported here
338+ }
339+ else if ((RestOfLine = CompareToKeyword("mg")) != NULL) // Merging group
340+ {
341+ // Not supported here
342+ }
343+ else if ((RestOfLine = CompareToKeyword("o")) != NULL) // Object name
344+ {
345+ // Not supported here
346+ }
347+ else if ((RestOfLine = CompareToKeyword("bevel")) != NULL) // Bevel interpolation
348+ {
349+ // Not supported here
350+ }
351+ else if ((RestOfLine = CompareToKeyword("c_interp")) != NULL) // Color interpolation
352+ {
353+ // Not supported here
354+ }
355+ else if ((RestOfLine = CompareToKeyword("d_interp")) != NULL) // Dissolve interpolation
356+ {
357+ // Not supported here
358+ }
359+ else if ((RestOfLine = CompareToKeyword("lod")) != NULL) // Level of detail
360+ {
361+ // Not supported here
362+ }
363+ else if ((RestOfLine = CompareToKeyword("usemtl")) != NULL) // Material name
364+ {
365+ // Not supported here
366+ }
367+ else if ((RestOfLine = CompareToKeyword("mtllib")) != NULL) // Material library
368+ {
369+ // Not supported here
370+ }
371+ else if ((RestOfLine = CompareToKeyword("shadow_obj")) != NULL) // Shadow casting
372+ {
373+ // Not supported here
374+ }
375+ else if ((RestOfLine = CompareToKeyword("trace_obje")) != NULL) // Ray tracing
376+ {
377+ // Not supported here
378+ }
379+ else if ((RestOfLine = CompareToKeyword("ctech")) != NULL) // Curve approximation technique
380+ {
381+ // Curved objects not supported here
382+ }
383+ else if ((RestOfLine = CompareToKeyword("stech")) != NULL) // Surface approximation technique
384+ {
385+ // Curved objects not supported here
386+ }
387+ */
388+ }
389+
390+ if (PolygonSizes.size() <= 0)
391+ {
392+ logError1("ERROR: the model in %s has no polygons",Path);
393+ return false;
394+ }
395+
396+ // How many vertices do the polygons have?
397+ for (unsigned k=0; k<PolygonSizes.size(); k++)
398+ {
399+ short PSize = PolygonSizes[k];
400+ if (PSize < 3)
401+ {
402+ logWarning3("WARNING: polygon ignored; it had bad size %u: %d in %s",k,PSize,Path);
403+ }
404+ }
405+
406+ // What is the lowest common denominator of the polygon data
407+ // (which is present of vertex positions, texture coordinates, and normals)
408+ short WhatsPresent = Present_Position | Present_TxtrCoord | Present_Normal;
409+
410+ for (unsigned k=0; k<VertIndxSets.size()/4; k++)
411+ {
412+ short Presence = VertIndxSets[4*k];
413+ WhatsPresent &= Presence;
414+ if (!(Presence & Present_Position))
415+ {
416+ logError2("ERROR: Vertex has no position index: %u in %s",k,Path);
417+ }
418+ }
419+
420+ if (!(WhatsPresent & Present_Position)) return false;
421+
422+ bool AllInRange = true;
423+
424+ for (unsigned k=0; k<VertIndxSets.size()/4; k++)
425+ {
426+ short PosIndx = VertIndxSets[4*k+1];
427+ if (PosIndx < 0 || PosIndx >= int(Positions.size()))
428+ {
429+ logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,PosIndx,(unsigned long)Positions.size()-1,Path);
430+ AllInRange = false;
431+ }
432+
433+ if (WhatsPresent & Present_TxtrCoord)
434+ {
435+ short TCIndx = VertIndxSets[4*k+2];
436+ if (TCIndx < 0 || TCIndx >= int(TxtrCoords.size()))
437+ {
438+ logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,TCIndx,(unsigned long)(TxtrCoords.size()-1),Path);
439+ AllInRange = false;
440+ }
441+ }
442+ else
443+ VertIndxSets[4*k+2] = -1; // What "0" gets turned into by the Wavefront-conversion-translation code
444+
445+ if (WhatsPresent & Present_Normal)
446+ {
447+ short NormIndx = VertIndxSets[4*k+3];
448+ if (NormIndx < 0 || NormIndx >= int(Normals.size()))
449+ {
450+ logError4("ERROR: Out of range vertex position: %u: %d (0,%lu) in %s",k,NormIndx,(unsigned long)(Normals.size()-1),Path);
451+ AllInRange = false;
452+ }
453+ }
454+ else
455+ VertIndxSets[4*k+3] = -1; // What "0" gets turned into by the Wavefront-conversion-translation code
456+ }
457+
458+ if (!AllInRange) return false;
459+
460+ // Find unique vertex sets:
461+
462+ // First, do an index sort of them
463+ vector<int> VertIndxRefs(VertIndxSets.size()/4);
464+ for (unsigned k=0; k<VertIndxRefs.size(); k++)
465+ VertIndxRefs[k] = k;
466+
467+ IndexedVertListCompare Compare;
468+ Compare.VertIndxSets = &VertIndxSets[0];
469+ sort(VertIndxRefs.begin(),VertIndxRefs.end(),Compare);
470+
471+ // Find the unique entries:
472+ vector<int> WhichUniqueSet(VertIndxRefs.size());
473+
474+ // Previous index values:
475+ short PrevPosIndx = -1, PrevTCIndx = -1, PrevNormIndx = -1;
476+ // For doing zero-based indexing
477+ int NumUnique = -1;
478+
479+ // Scan the vertices in index-sort order:
480+ for (unsigned k=0; k<VertIndxRefs.size(); k++)
481+ {
482+ int n = VertIndxRefs[k];
483+
484+ short *VISet = &VertIndxSets[4*n];
485+ short PosIndx = VISet[1];
486+ short TCIndx = VISet[2];
487+ short NormIndx = VISet[3];
488+
489+ if (PosIndx == PrevPosIndx && TCIndx == PrevTCIndx && NormIndx == PrevNormIndx)
490+ {
491+ WhichUniqueSet[n] = NumUnique;
492+ continue;
493+ }
494+
495+ // Found a unique set
496+ WhichUniqueSet[n] = ++NumUnique;
497+
498+ // These are all for the model object
499+
500+ // Load the positions
501+ {
502+ GLfloat *PosPtr = &Positions[3*PosIndx];
503+ for (int m=0; m<3; m++)
504+ Model.Positions.push_back(*(PosPtr++));
505+ }
506+
507+ // Load the texture coordinates
508+ if (WhatsPresent & Present_TxtrCoord)
509+ {
510+ GLfloat *TCPtr = &TxtrCoords[2*TCIndx];
511+ for (int m=0; m<2; m++)
512+ Model.TxtrCoords.push_back(*(TCPtr++));
513+ }
514+
515+ // Load the normals
516+ if (WhatsPresent & Present_Normal)
517+ {
518+ GLfloat *NormPtr = &Normals[3*NormIndx];
519+ for (int m=0; m<3; m++)
520+ Model.Normals.push_back(*(NormPtr++));
521+ }
522+
523+ // Save these new unique-set values for comparison to the next ones
524+ PrevPosIndx = PosIndx;
525+ PrevTCIndx = TCIndx;
526+ PrevNormIndx = NormIndx;
527+ }
528+
529+ // Decompose the polygons into triangles by turning them into fans
530+ int IndxBase = 0;
531+ for (unsigned k=0; k<PolygonSizes.size(); k++)
532+ {
533+ short PolySize = PolygonSizes[k];
534+ int *PolyIndices = &WhichUniqueSet[IndxBase];
535+
536+ for (int m=0; m<PolySize-2; m++)
537+ {
538+ Model.VertIndices.push_back(PolyIndices[0]);
539+ Model.VertIndices.push_back(PolyIndices[m+1]);
540+ Model.VertIndices.push_back(PolyIndices[m+2]);
541+ }
542+
543+ IndxBase += PolySize;
544+ }
545+
546+ if (Model.VertIndices.size() <= 0)
547+ {
548+ logError1("ERROR: the model in %s has no good polygons",Path);
549+ return false;
550+ }
551+
552+ logTrace("Successfully read the file:");
553+ if (WhatsPresent & Present_Position) logTrace(" Positions");
554+ if (WhatsPresent & Present_TxtrCoord) logTrace(" TxtrCoords");
555+ if (WhatsPresent & Present_Normal) logTrace(" Normals");
556+ return true;
557+}
558+
559+
560+char *CompareToKeyword(const char *Keyword)
561+{
562+ size_t KWLen = strlen(Keyword);
563+
564+ if (InputLine.size() < KWLen) return NULL;
565+
566+ for (unsigned k=0; k<KWLen; k++)
567+ if (InputLine[k] != Keyword[k]) return NULL;
568+
569+ char *RestOfLine = &InputLine[KWLen];
570+
571+ while(RestOfLine - &InputLine[0] < int(InputLine.size()))
572+ {
573+ // End of line?
574+ if (*RestOfLine == '\0') return RestOfLine;
575+
576+ // Other than whitespace -- assume it to be part of the keyword if just after it;
577+ // otherwise, it is to be returned to the rest of the code to work on
578+ if (!(*RestOfLine == ' ' || *RestOfLine == '\t'))
579+ return ((RestOfLine == &InputLine[KWLen]) ? NULL : RestOfLine);
580+
581+ // Whitespace: move on to the next character
582+ RestOfLine++;
583+ }
584+
585+ // Shouldn't happen
586+ return NULL;
587+}
588+
589+
590+char *GetVertIndxSet(char *Buffer, short& Presence,
591+ short& PosIndx, short& TCIndx, short& NormIndx)
592+{
593+ // Initialize...
594+ Presence = 0; PosIndx = 0; TCIndx = 0; NormIndx = 0;
595+
596+ // Eat initial whitespace; return NULL if end-of-string was hit
597+ // OK to modify Buffer, since it's called by value
598+ while(*Buffer == ' ' || *Buffer == '\t')
599+ {
600+ Buffer++;
601+ }
602+ if (*Buffer == '\0') return NULL;
603+
604+ // Hit non-whitespace; now grab the individual vertex values
605+ bool WasFound = false, HitEnd = false;
606+ Buffer = GetVertIndx(Buffer,WasFound,PosIndx,HitEnd);
607+ if (WasFound) Presence |= Present_Position;
608+ if (HitEnd) return Buffer;
609+
610+ Buffer = GetVertIndx(Buffer,WasFound,TCIndx,HitEnd);
611+ if (WasFound) Presence |= Present_TxtrCoord;
612+ if (HitEnd) return Buffer;
613+
614+ Buffer = GetVertIndx(Buffer,WasFound,NormIndx,HitEnd);
615+ if (WasFound) Presence |= Present_Normal;
616+ return Buffer;
617+}
618+
619+char *GetVertIndx(char *Buffer, bool& WasFound, short& Val, bool& HitEnd)
620+{
621+ const int VIBLen = 64;
622+ char VIBuffer[VIBLen];
623+ int VIBIndx = 0;
624+
625+ // Load the vertex-index buffer and make it a C string
626+ HitEnd = false;
627+ WasFound = false;
628+ bool HitInternalBdry = false; // Use this variable to avoid duplicating an evaluation
629+ while (!(HitInternalBdry = (*Buffer == '/')))
630+ {
631+ HitEnd = (*Buffer == ' ' || *Buffer == '\t' || *Buffer == '\0');
632+ if (HitEnd) break;
633+
634+ if (VIBIndx < VIBLen-1)
635+ VIBuffer[VIBIndx++] = *Buffer;
636+
637+ Buffer++;
638+ }
639+ if (HitInternalBdry) Buffer++;
640+ VIBuffer[VIBIndx] = '\0';
641+
642+ // Interpret it!
643+ WasFound = (sscanf(VIBuffer,"%hd",&Val) > 0);
644+
645+ return Buffer;
646+}
647+
648+// Load a Wavefront model and convert its vertex and texture coordinates from
649+// OBJ's right-handed coordinate system to Aleph One's left-handed system.
650+bool LoadModel_Wavefront_RightHand(FileSpecifier& Spec, Model3D& Model)
651+{
652+ bool Result = LoadModel_Wavefront(Spec, Model);
653+ if (!Result) return Result;
654+
655+ logTrace("Converting handedness.");
656+
657+ // OBJ files produced by Blender and Wings 3D are oriented with
658+ // y increasing upwards, and the front of a Blender model faces in the
659+ // positive-Z direction. (Wings 3D does not distinguish a "front"
660+ // view.) In Aleph One's coordinate system Z increases upwards, and
661+ // items that have been placed with 0 degrees of rotation face in the
662+ // positive-x direction.
663+ for (unsigned XPos = 0; XPos < Model.Positions.size(); XPos += 3)
664+ {
665+ GLfloat X = Model.Positions[XPos];
666+ Model.Positions[XPos] = Model.Positions[XPos + 2];
667+ Model.Positions[XPos + 2] = Model.Positions[XPos + 1];
668+ Model.Positions[XPos + 1] = -X;
669+ }
670+
671+ // Ditto for vertex normals, if present.
672+ for (unsigned XPos = 0; XPos < Model.Normals.size(); XPos += 3)
673+ {
674+ GLfloat X = Model.Normals[XPos];
675+ Model.Normals[XPos] = Model.Normals[XPos + 2];
676+ Model.Normals[XPos + 2] = Model.Normals[XPos + 1];
677+ Model.Normals[XPos + 1] = -X;
678+ }
679+
680+ // Vertices of each face are now listed in clockwise order.
681+ // Reverse them.
682+ for (unsigned IPos = 0; IPos < Model.VertIndices.size(); IPos += 3)
683+ {
684+ int Index = Model.VertIndices[IPos + 1];
685+ Model.VertIndices[IPos + 1] = Model.VertIndices[IPos];
686+ Model.VertIndices[IPos] = Index;
687+ }
688+
689+ // Switch texture coordinates from right-handed (x,y) to
690+ // left-handed (row,column).
691+ for (unsigned YPos = 1; YPos < Model.TxtrCoords.size(); YPos += 2)
692+ {
693+ Model.TxtrCoords[YPos] = 1.0 - Model.TxtrCoords[YPos];
694+ }
695+
696+ return true;
697+}
698+
699+#endif // def HAVE_OPENGL
--- marathon/trunk/Source_Files/ModelView/StudioLoader.h (revision 526)
+++ marathon/trunk/Source_Files/ModelView/StudioLoader.h (revision 527)
@@ -1,38 +1,38 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- 3D Studio Max Object Loader
21-
22- By Loren Petrich, Sept 1, 2001
23-*/
24-#ifndef STUDIO_LOADER
25-#define STUDIO_LOADER
26-
27-#include <stdio.h>
28-#include "Model3D.h"
29-#include "FileHandler.h"
30-
31-// Load a 3D Studio MAX model, without converting its coordinate system.
32-bool LoadModel_Studio(FileSpecifier& Spec, Model3D& Model);
33-
34-// Load a 3D Studio MAX model, and convert its vertex and texture coordinates
35-// from its right-handed coordinate system to Aleph One's left-handed system.
36-bool LoadModel_Studio_RightHand(FileSpecifier& Spec, Model3D& Model);
37-
38-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ 3D Studio Max Object Loader
21+
22+ By Loren Petrich, Sept 1, 2001
23+*/
24+#ifndef STUDIO_LOADER
25+#define STUDIO_LOADER
26+
27+#include <stdio.h>
28+#include "Model3D.h"
29+#include "FileHandler.h"
30+
31+// Load a 3D Studio MAX model, without converting its coordinate system.
32+bool LoadModel_Studio(FileSpecifier& Spec, Model3D& Model);
33+
34+// Load a 3D Studio MAX model, and convert its vertex and texture coordinates
35+// from its right-handed coordinate system to Aleph One's left-handed system.
36+bool LoadModel_Studio_RightHand(FileSpecifier& Spec, Model3D& Model);
37+
38+#endif
--- marathon/trunk/Source_Files/ModelView/QD3D_Loader.h (revision 526)
+++ marathon/trunk/Source_Files/ModelView/QD3D_Loader.h (revision 527)
@@ -1,46 +1,46 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- QuickDraw 3D / Quesa Object Loader
21-
22- By Loren Petrich, September 1, 2001
23-*/
24-#ifndef QD3D_LOADER
25-#define QD3D_LOADER
26-
27-#include <stdio.h>
28-#include "Model3D.h"
29-#include "FileHandler.h"
30-
31-bool LoadModel_QD3D(FileSpecifier& Spec, Model3D& Model);
32-
33-// Where to emit status messages
34-void SetDebugOutput_QD3D(FILE *DebugOutput);
35-
36-// Set the QD3D tesselation factor:
37-// The first argument means
38-// if true, tesselation length = length in world-geometry units
39-// if false, tesselation length = constant subdivision factor
40-// The second argument is that length
41-void SetTesselation_QD3D(bool IsWorldLength, float TessLength);
42-
43-// Set to defaults
44-void SetDefaultTesselation_QD3D();
45-
46-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ QuickDraw 3D / Quesa Object Loader
21+
22+ By Loren Petrich, September 1, 2001
23+*/
24+#ifndef QD3D_LOADER
25+#define QD3D_LOADER
26+
27+#include <stdio.h>
28+#include "Model3D.h"
29+#include "FileHandler.h"
30+
31+bool LoadModel_QD3D(FileSpecifier& Spec, Model3D& Model);
32+
33+// Where to emit status messages
34+void SetDebugOutput_QD3D(FILE *DebugOutput);
35+
36+// Set the QD3D tesselation factor:
37+// The first argument means
38+// if true, tesselation length = length in world-geometry units
39+// if false, tesselation length = constant subdivision factor
40+// The second argument is that length
41+void SetTesselation_QD3D(bool IsWorldLength, float TessLength);
42+
43+// Set to defaults
44+void SetDefaultTesselation_QD3D();
45+
46+#endif
--- marathon/trunk/Source_Files/ModelView/Dim3_Loader.h (revision 526)
+++ marathon/trunk/Source_Files/ModelView/Dim3_Loader.h (revision 527)
@@ -1,45 +1,45 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- "Dim3" Object Loader
21-
22- By Loren Petrich, Dec 29, 2001
23-*/
24-#ifndef DIM3_LOADER
25-#define DIM3_LOADER
26-
27-#include <stdio.h>
28-#include "Model3D.h"
29-#include "FileHandler.h"
30-
31-
32-// Do multifile models by doing multiple passes;
33-// the first one is special because it sets up for the run
34-enum
35-{
36- LoadModelDim3_First,
37- LoadModelDim3_Rest
38-};
39-
40-bool LoadModel_Dim3(FileSpecifier& Spec, Model3D& Model, int WhichPass);
41-
42-// Where to emit status messages
43-void SetDebugOutput_Dim3(FILE *DebugOutput);
44-
45-#endif
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ "Dim3" Object Loader
21+
22+ By Loren Petrich, Dec 29, 2001
23+*/
24+#ifndef DIM3_LOADER
25+#define DIM3_LOADER
26+
27+#include <stdio.h>
28+#include "Model3D.h"
29+#include "FileHandler.h"
30+
31+
32+// Do multifile models by doing multiple passes;
33+// the first one is special because it sets up for the run
34+enum
35+{
36+ LoadModelDim3_First,
37+ LoadModelDim3_Rest
38+};
39+
40+bool LoadModel_Dim3(FileSpecifier& Spec, Model3D& Model, int WhichPass);
41+
42+// Where to emit status messages
43+void SetDebugOutput_Dim3(FILE *DebugOutput);
44+
45+#endif
--- marathon/trunk/Source_Files/ModelView/Model3D.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/Model3D.cpp (revision 527)
@@ -1,1141 +1,1141 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- 3D-Model Object Storage Functions
21-
22- By Loren Petrich, July 8, 2001
23-*/
24-
25-#include <string.h>
26-#include <math.h>
27-
28-#include "VecOps.h"
29-#include "cseries.h"
30-#include "world.h"
31-
32-#ifdef HAVE_OPENGL
33-
34-#ifdef __WIN32__
35-#include <windows.h>
36-#endif
37-
38-#include "Model3D.h"
39-
40-#ifdef HAVE_OPENGL
41-#include "OGL_Headers.h"
42-#endif
43-
44-/* Need Sgl* macros */
45-#include "OGL_Setup.h"
46-
47-// Bone-stack and transformation-matrix locally-used arrays;
48-// the matrices have dimensions (output coords)(input-coord multipliers + offset for output)
49-static vector<Model3D_Transform> BoneMatrices;
50-static vector<size_t> BoneStack;
51-
52-
53-// Find transform of point (source and dest must be different arrays)
54-inline void TransformPoint(GLfloat *Dest, GLfloat *Src, Model3D_Transform& T)
55-{
56- for (int ic=0; ic<3; ic++)
57- {
58- GLfloat *Row = T.M[ic];
59- Dest[ic] = ScalarProd(Src,Row) + Row[3];
60- }
61-}
62-
63-// Like above, but a vector, such as a normal (source and dest must be different arrays)
64-inline void TransformVector(GLfloat *Dest, GLfloat *Src, Model3D_Transform& T)
65-{
66- for (int ic=0; ic<3; ic++)
67- {
68- GLfloat *Row = T.M[ic];
69- Dest[ic] = ScalarProd(Src,Row);
70- }
71-}
72-
73-// Bone and Frame (positions, angles) -> Transform Matrix
74-static void FindFrameTransform(Model3D_Transform& T,
75- Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame);
76-static void FindBoneTransform(Model3D_Transform& T, Model3D_Bone& Bone,
77- Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame);
78-
79-// Res = A * B, in that order
80-static void TMatMultiply(Model3D_Transform& Res, Model3D_Transform& A, Model3D_Transform& B);
81-
82-
83-// Trig-function conversion:
84-const GLfloat TrigNorm = GLfloat(1)/GLfloat(TRIG_MAGNITUDE);
85-
86-
87-// Erase everything
88-void Model3D::Clear()
89-{
90- Positions.clear();
91- TxtrCoords.clear();
92- Normals.clear();
93- Colors.clear();
94- VtxSrcIndices.clear();
95- VtxSources.clear();
96- NormSources.clear();
97- InverseVSIndices.clear();
98- InvVSIPointers.clear();
99- Bones.clear();
100- VertIndices.clear();
101- Frames.clear();
102- SeqFrames.clear();
103- SeqFrmPointers.clear();
104- FindBoundingBox();
105-}
106-
107-// Normalize an individual normal; return whether the normal had a nonzero length
108-static bool NormalizeNormal(GLfloat *Normal)
109-{
110- GLfloat NormalSqr =
111- Normal[0]*Normal[0] + Normal[1]*Normal[1] + Normal[2]*Normal[2];
112-
113- if (NormalSqr <= 0) return false;
114-
115- GLfloat NormalRecip = (GLfloat)(1/sqrt(NormalSqr));
116-
117- Normal[0] *= NormalRecip;
118- Normal[1] *= NormalRecip;
119- Normal[2] *= NormalRecip;
120-
121- return true;
122-}
123-
124-// Flagged vector for per-polygon and per-vertex normals;
125-// the flag is "true" for nonzero vectors and vectors meant to be used
126-struct FlaggedVector
127-{
128- GLfloat Vec[3];
129- bool Flag;
130-};
131-
132-void Model3D::CalculateTangents()
133-{
134- Tangents.resize(Positions.size() * 4 / 3);
135-
136- bool generate_normals = false;
137- if(Normals.size() != Positions.size()) {
138- Normals.resize(Positions.size());
139- memset(NormBase(), 0, sizeof(GLfloat)*Normals.size());
140- generate_normals = true;
141- }
142-
143- for(GLushort i = 0; i < VertIndices.size(); i+= 3) {
144- GLushort a = VertIndices[i];
145- GLushort b = VertIndices[i+1];
146- GLushort c = VertIndices[i+2];
147-
148- vertex3 v1(PosBase()+3*a);
149- vertex3 v2(PosBase()+3*b);
150- vertex3 v3(PosBase()+3*c);
151-
152- vertex2 w1(TCBase()+2*a);
153- vertex2 w2(TCBase()+2*b);
154- vertex2 w3(TCBase()+2*c);
155-
156- float x1 = v2[0] - v1[0];
157- float x2 = v3[0] - v1[0];
158- float y1 = v2[1] - v1[1];
159- float y2 = v3[1] - v1[1];
160- float z1 = v2[2] - v1[2];
161- float z2 = v3[2] - v1[2];
162-
163- float s1 = w2[0] - w1[0];
164- float s2 = w3[0] - w1[0];
165- float t1 = w2[1] - w1[1];
166- float t2 = w3[1] - w1[1];
167-
168- float r = 1.0f / (s1 * t2 - s2 * t1);
169- vec3 T((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
170- (t2 * z1 - t1 * z2) * r);
171- vec3 B((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
172- (s1 * z2 - s2 * z1) * r);
173- if ((s1 * t2 - s2 * t1) == 0.0) {
174- T = (v3 - v1).norm();
175- B = (v2 - v1).norm();
176- }
177-
178- vec3 N = (v3-v1).cross(v2-v1);
179- if (!generate_normals) {
180- N = vec3(NormBase()+3*a) + vec3(NormBase()+3*b) + vec3(NormBase()+3*c);
181- }
182-
183- if(N.dot(N) < 0.001) {
184- N = vec3(0.0,0.0,0.0);
185- if (generate_normals) {
186- VecCopy(N.p(), NormBase()+3*a);
187- VecCopy(N.p(), NormBase()+3*b);
188- VecCopy(N.p(), NormBase()+3*c);
189- }
190-
191- vec4 t(0.0,0.0,0.0,0.0);
192- Tangents[a] = vec4(t);
193- Tangents[b] = vec4(t);
194- Tangents[c] = vec4(t);
195- } else {
196- N = N.norm();
197- assert(N.dot(N) < 1.001);
198-
199- if (generate_normals) {
200- VecCopy(N.p(), NormBase()+3*a);
201- VecCopy(N.p(), NormBase()+3*b);
202- VecCopy(N.p(), NormBase()+3*c);
203- }
204-
205- float sign = (N.cross(T)).dot(B) < 0.0 ? -1.0 : 1.0;
206- vec4 t = (T - N * N.dot(T)).norm();
207- t[3] = sign;
208-
209- Tangents[a] = vec4(t);
210- Tangents[b] = vec4(t);
211- Tangents[c] = vec4(t);
212- }
213- }
214-}
215-
216-
217-// Normalize the normals
218-void Model3D::AdjustNormals(int NormalType, float SmoothThreshold)
219-{
220- // Copy in normal sources for processing
221- if (!NormSources.empty())
222- {
223- Normals.resize(NormSources.size());
224- objlist_copy(NormBase(),NormSrcBase(),NormSources.size());
225- }
226-
227- // Which kind of special processing?
228- switch(NormalType)
229- {
230- case None:
231- Normals.clear();
232- break;
233-
234- case Original:
235- case Reversed:
236- default:
237- // Normalize
238- for (unsigned k=0; k<Normals.size()/3; k++)
239- NormalizeNormal(&Normals[3*k]);
240- break;
241-
242- case ClockwiseSide:
243- case CounterclockwiseSide:
244- // The really interesting stuff
245- {
246- // First, create a list of per-polygon normals
247- size_t NumPolys = NumVI()/3;
248- vector<FlaggedVector> PerPolygonNormalList(NumPolys);
249-
250- GLushort *IndxPtr = VIBase();
251- for (unsigned k=0; k<NumPolys; k++)
252- {
253- // The three vertices:
254- GLfloat *P0 = &Positions[3*(*(IndxPtr++))];
255- GLfloat *P1 = &Positions[3*(*(IndxPtr++))];
256- GLfloat *P2 = &Positions[3*(*(IndxPtr++))];
257- // The two in-polygon vectors:
258- GLfloat P01[3];
259- P01[0] = P1[0] - P0[0];
260- P01[1] = P1[1] - P0[1];
261- P01[2] = P1[2] - P0[2];
262- GLfloat P02[3];
263- P02[0] = P2[0] - P0[0];
264- P02[1] = P2[1] - P0[1];
265- P02[2] = P2[2] - P0[2];
266- // Those vectors' normal:
267- FlaggedVector& PPN = PerPolygonNormalList[k];
268- PPN.Vec[0] = P01[1]*P02[2] - P01[2]*P02[1];
269- PPN.Vec[1] = P01[2]*P02[0] - P01[0]*P02[2];
270- PPN.Vec[2] = P01[0]*P02[1] - P01[1]*P02[0];
271- PPN.Flag = NormalizeNormal(PPN.Vec);
272- }
273-
274- // Create a list of per-vertex normals
275- size_t NumVerts = Positions.size()/3;
276- vector<FlaggedVector> PerVertexNormalList(NumVerts);
277- objlist_clear(&PerVertexNormalList[0],NumVerts);
278- IndxPtr = VIBase();
279- for (unsigned k=0; k<NumPolys; k++)
280- {
281- FlaggedVector& PPN = PerPolygonNormalList[k];
282- for (unsigned c=0; c<3; c++)
283- {
284- GLushort VertIndx = *(IndxPtr++);
285- GLfloat *V = PerVertexNormalList[VertIndx].Vec;
286- *(V++) += PPN.Vec[0];
287- *(V++) += PPN.Vec[1];
288- *(V++) += PPN.Vec[2];
289- }
290- }
291-
292- // Normalize the per-vertex normals
293- for (unsigned k=0; k<NumVerts; k++)
294- {
295- FlaggedVector& PVN = PerVertexNormalList[k];
296- PVN.Flag = NormalizeNormal(PVN.Vec);
297- }
298-
299- // Find the variance of each of the per-vertex normals;
300- // use that to decide whether to keep them unsplit;
301- // this also needs counting up the number of polygons per vertex.
302- vector<GLfloat> Variances(NumVerts);
303- objlist_clear(&Variances[0],NumVerts);
304- vector<short> NumPolysPerVert(NumVerts);
305- objlist_clear(&NumPolysPerVert[0],NumVerts);
306- IndxPtr = VIBase();
307- for (unsigned k=0; k<NumPolys; k++)
308- {
309- FlaggedVector& PPN = PerPolygonNormalList[k];
310- for (unsigned c=0; c<3; c++)
311- {
312- GLushort VertIndx = *(IndxPtr++);
313- FlaggedVector& PVN = PerVertexNormalList[VertIndx];
314- if (PVN.Flag)
315- {
316- GLfloat *V = PVN.Vec;
317- GLfloat D0 = *(V++) - PPN.Vec[0];
318- GLfloat D1 = *(V++) - PPN.Vec[1];
319- GLfloat D2 = *(V++) - PPN.Vec[2];
320- Variances[VertIndx] += (D0*D0 + D1*D1 + D2*D2);
321- NumPolysPerVert[VertIndx]++;
322- }
323- }
324- }
325-
326- // Decide whether to split each vertex;
327- // if the flag is "true", a vertex is not to be split
328- for (unsigned k=0; k<NumVerts; k++)
329- {
330- // Vertices without contributions will automatically have
331- // their flags be false, as a result of NormalizeNormal()
332- unsigned NumVertPolys = NumPolysPerVert[k];
333- if (NumVertPolys > 0 && PerVertexNormalList[k].Flag)
334- PerVertexNormalList[k].Flag =
335- sqrt(Variances[k]/NumVertPolys) <= SmoothThreshold;
336- }
337-
338- // The vertex flags are now set for whether to use that vertex's normal;
339- // re-count the number of polygons per vertex.
340- // Use NONE for unsplit ones
341- objlist_clear(&NumPolysPerVert[0],NumVerts);
342- IndxPtr = VIBase();
343- for (unsigned k=0; k<NumPolys; k++)
344- {
345- for (unsigned c=0; c<3; c++)
346- {
347- GLushort VertIndx = *(IndxPtr++);
348- FlaggedVector& PVN = PerVertexNormalList[VertIndx];
349- if (PVN.Flag)
350- NumPolysPerVert[VertIndx] = NONE;
351- else
352- NumPolysPerVert[VertIndx]++;
353- }
354- }
355-
356- // Construct a polygon-association list; this will indicate
357- // which polygons are associated with each of the resulting instances
358- // of a split vertex (unsplit: NONE).
359- // NumPolysPerVert will be recycled as a counter list,
360- // after being used to construct a cumulative index-in-list array.
361- // Finding that list will be used to find how many new vertices there are.
362- vector<short> IndicesInList(NumVerts);
363- short IndxInList = 0;
364- for (unsigned k=0; k<NumVerts; k++)
365- {
366- IndicesInList[k] = IndxInList;
367- FlaggedVector& PVN = PerVertexNormalList[k];
368- IndxInList += PVN.Flag ? 1 : NumPolysPerVert[k];
369- }
370- GLushort NewNumVerts = IndxInList;
371- vector<short> VertexPolygons(NewNumVerts);
372- objlist_clear(&NumPolysPerVert[0],NumVerts);
373-
374- // In creating that list, also remap the triangles' vertices
375- GLushort *VIPtr = VIBase();
376- for (unsigned k=0; k<NumPolys; k++)
377- {
378- for (unsigned c=0; c<3; c++)
379- {
380- GLushort VertIndx = *VIPtr;
381- GLushort NewVertIndx = IndicesInList[VertIndx];
382- FlaggedVector& PVN = PerVertexNormalList[VertIndx];
383- if (PVN.Flag)
384- {
385- NumPolysPerVert[VertIndx] = NONE;
386- VertexPolygons[NewVertIndx] = NONE;
387- }
388- else
389- {
390- NewVertIndx += (NumPolysPerVert[VertIndx]++);
391- VertexPolygons[NewVertIndx] = k;
392- }
393- *VIPtr = NewVertIndx;
394- VIPtr++;
395- }
396- }
397-
398- // Split the vertices
399- vector<GLfloat> NewPositions(3*NewNumVerts);
400- vector<GLfloat> NewTxtrCoords;
401- vector<GLfloat> NewNormals(3*NewNumVerts);
402- vector<GLfloat> NewColors;
403- vector<GLushort> NewVtxSrcIndices;
404-
405- bool TCPresent = !TxtrCoords.empty();
406- if (TCPresent) NewTxtrCoords.resize(2*NewNumVerts);
407-
408- bool ColPresent = !Colors.empty();
409- if (ColPresent) NewColors.resize(3*NewNumVerts);
410-
411- bool VSPresent = !VtxSrcIndices.empty();
412- if (VSPresent) NewVtxSrcIndices.resize(NewNumVerts);
413-
414- // Use marching pointers to speed up the copy-over
415- GLfloat *OldP = &Positions[0];
416- GLfloat *NewP = &NewPositions[0];
417- GLfloat *OldT = &TxtrCoords[0];
418- GLfloat *NewT = &NewTxtrCoords[0];
419- GLfloat *OldC = &Colors[0];
420- GLfloat *NewC = &NewColors[0];
421- GLushort *OldS = &VtxSrcIndices[0];
422- GLushort *NewS = &NewVtxSrcIndices[0];
423- GLfloat *NewN = &NewNormals[0];
424- for (unsigned k=0; k<NumVerts; k++)
425- {
426- FlaggedVector& PVN = PerVertexNormalList[k];
427- unsigned NumVertPolys = PVN.Flag ? 1 : NumPolysPerVert[k];
428- for (unsigned c=0; c<NumVertPolys; c++)
429- {
430- GLfloat *OldPP = OldP;
431- *(NewP++) = *(OldPP++);
432- *(NewP++) = *(OldPP++);
433- *(NewP++) = *(OldPP++);
434- }
435- if (TCPresent)
436- {
437- for (unsigned c=0; c<NumVertPolys; c++)
438- {
439- GLfloat *OldTP = OldT;
440- *(NewT++) = *(OldTP++);
441- *(NewT++) = *(OldTP++);
442- }
443- }
444- if (ColPresent)
445- {
446- for (unsigned c=0; c<NumVertPolys; c++)
447- {
448- GLfloat *OldCP = OldC;
449- *(NewC++) = *(OldCP++);
450- *(NewC++) = *(OldCP++);
451- *(NewC++) = *(OldCP++);
452- }
453- }
454- if (VSPresent)
455- {
456- for (unsigned c=0; c<NumVertPolys; c++)
457- *(NewS++) = *OldS;
458- }
459- if (PVN.Flag)
460- {
461- GLfloat *VP = PVN.Vec;
462- *(NewN++) = *(VP++);
463- *(NewN++) = *(VP++);
464- *(NewN++) = *(VP++);
465- }
466- else
467- {
468- // A reference so that the incrementing can work on it
469- short& IndxInList = IndicesInList[k];
470- for (unsigned c=0; c<NumVertPolys; c++)
471- {
472- GLfloat *VP = PerPolygonNormalList[VertexPolygons[IndxInList++]].Vec;
473- *(NewN++) = *(VP++);
474- *(NewN++) = *(VP++);
475- *(NewN++) = *(VP++);
476- }
477- }
478-
479- // Advance!
480- OldP += 3;
481- if (TCPresent)
482- OldT += 2;
483- if (ColPresent)
484- OldC += 3;
485- if (VSPresent)
486- OldS++;
487-
488- }
489- assert(OldP == &Positions[3*NumVerts]);
490- assert(NewP == &NewPositions[3*NewNumVerts]);
491- if (TCPresent)
492- {
493- assert(OldT == &TxtrCoords[2*NumVerts]);
494- assert(NewT == &NewTxtrCoords[2*NewNumVerts]);
495- }
496- if (ColPresent)
497- {
498- assert(OldC == &Colors[3*NumVerts]);
499- assert(NewC == &NewColors[3*NewNumVerts]);
500- }
501- if (VSPresent)
502- {
503- assert(OldS == &VtxSrcIndices[NumVerts]);
504- assert(NewS == &NewVtxSrcIndices[NewNumVerts]);
505- }
506- assert(NewN == &NewNormals[3*NewNumVerts]);
507-
508- // Accept the new vectors
509- Positions.swap(NewPositions);
510- TxtrCoords.swap(NewTxtrCoords);
511- Normals.swap(NewNormals);
512- Colors.swap(NewColors);
513- VtxSrcIndices.swap(NewVtxSrcIndices);
514- }
515- break;
516- }
517-
518- // Now flip
519- switch(NormalType)
520- {
521- case Reversed:
522- case CounterclockwiseSide:
523- {
524- GLfloat *NormalPtr = NormBase();
525- for (unsigned k=0; k<Normals.size(); k++)
526- *(NormalPtr++) *= -1;
527- }
528- }
529-
530- // Copy back out to the normal sources;
531- // do the copying out if the vertices have sources,
532- // which is the case for boned models.
533- if (!VtxSources.empty())
534- {
535- size_t NormSize = Normals.size();
536- if (NormSize > 0)
537- {
538- NormSources.resize(NormSize);
539- objlist_copy(NormSrcBase(),NormBase(),NormSize);
540- }
541- else
542- NormSources.clear();
543- }
544- else
545- NormSources.clear();
546-}
547-
548-
549-// From the position data
550-void Model3D::FindBoundingBox()
551-{
552- size_t NumVertices = Positions.size()/3;
553- if (NumVertices > 0)
554- {
555- // Find the min and max of the positions:
556- VecCopy(&Positions[0],BoundingBox[0]);
557- VecCopy(&Positions[0],BoundingBox[1]);
558- for (size_t i=1; i<NumVertices; i++)
559- {
560- GLfloat *Pos = &Positions[3*i];
561- for (int ib=0; ib<3; ib++)
562- {
563- BoundingBox[0][ib] = MIN(BoundingBox[0][ib],Pos[ib]);
564- BoundingBox[1][ib] = MAX(BoundingBox[1][ib],Pos[ib]);
565- }
566- }
567- }
568- else
569- {
570- // Clear the bounding box
571- objlist_clear(BoundingBox[0],3);
572- objlist_clear(BoundingBox[1],3);
573- }
574-}
575-
576-
577-// For debugging
578-void Model3D::RenderBoundingBox(const GLfloat *EdgeColor, const GLfloat *DiagonalColor)
579-{
580- GLfloat BBoxVertices[8][3];
581-
582- // Binary-number arrangement of expanded vertices:
583- for (int i1=0; i1<2; i1++)
584- for (int i2=0; i2<2; i2++)
585- for (int i3=0; i3<2; i3++)
586- {
587- int Indx = 4*i1 + 2*i2 + i3;
588- BBoxVertices[Indx][0] = BoundingBox[i1][0];
589- BBoxVertices[Indx][1] = BoundingBox[i2][1];
590- BBoxVertices[Indx][2] = BoundingBox[i3][2];
591- }
592-
593- glDisable(GL_TEXTURE_2D);
594- glDisableClientState(GL_COLOR_ARRAY);
595- glEnableClientState(GL_VERTEX_ARRAY);
596- glVertexPointer(3,GL_FLOAT,0,BBoxVertices[0]);
597-
598- if (EdgeColor)
599- {
600- SglColor3fv(EdgeColor);
601- const int NumEdgeVerts = 24;
602- const unsigned short EdgeVerts[NumEdgeVerts] = {
603- 0,1,
604- 1,3,
605- 3,2,
606- 2,0,
607-
608- 0,4,
609- 1,5,
610- 2,6,
611- 3,7,
612-
613- 4,5,
614- 5,7,
615- 7,6,
616- 6,4
617- };
618- glDrawElements(GL_LINES,NumEdgeVerts,GL_UNSIGNED_SHORT,EdgeVerts);
619- }
620-
621- if (DiagonalColor)
622- {
623- SglColor3fv(DiagonalColor);
624- const int NumDiagVerts = 24;
625- const unsigned short DiagVerts[NumDiagVerts] = {
626- 0,3,
627- 1,2,
628-
629- 0,5,
630- 1,4,
631-
632- 1,7,
633- 3,5,
634-
635- 3,6,
636- 2,7,
637-
638- 2,4,
639- 0,6,
640-
641- 4,7,
642- 5,6
643- };
644- glDrawElements(GL_LINES,NumDiagVerts,GL_UNSIGNED_SHORT,DiagVerts);
645- }
646-}
647-
648-void Model3D::BuildTrigTables()
649-{
650- build_trig_tables();
651-}
652-
653-
654-void Model3D::BuildInverseVSIndices()
655-{
656- if (VtxSrcIndices.empty()) return;
657-
658- InverseVSIndices.resize(VtxSrcIndices.size());
659- InvVSIPointers.resize(VtxSources.size()+1); // One extra member
660-
661- // Use the pointers as temporary storage for the count
662- objlist_clear(InvVSIPtrBase(),InvVSIPointers.size());
663- for (vector<GLushort>::iterator VSI_Iter = VtxSrcIndices.begin();
664- VSI_Iter < VtxSrcIndices.end();
665- VSI_Iter++)
666- InvVSIPointers[*VSI_Iter]++;
667-
668- // Find the positions from the counts
669- GLushort PtrSum = 0;
670- for (vector<GLushort>::iterator IVP_Iter = InvVSIPointers.begin();
671- IVP_Iter < InvVSIPointers.end();
672- IVP_Iter++)
673- {
674- GLushort NewPtrSum = PtrSum + *IVP_Iter;
675- *IVP_Iter = PtrSum;
676- PtrSum = NewPtrSum;
677- }
678-
679- // Place the inverse indices
680- for (unsigned k = 0; k<VtxSrcIndices.size(); k++)
681- InverseVSIndices[InvVSIPointers[VtxSrcIndices[k]]++] = k;
682-
683- // Push the pointer values forward in the list
684- // since they'd become their next values in it.
685- // The reverse iteration is necessary to avoid overwriting
686- for (vector<GLushort>::iterator IVP_Iter = InvVSIPointers.end()-1;
687- IVP_Iter > InvVSIPointers.begin();
688- IVP_Iter--)
689- {
690- *IVP_Iter = *(IVP_Iter - 1);
691- }
692- InvVSIPointers[0] = 0;
693-}
694-
695-
696-// Neutral case: returns whether vertex-source data was used (present in animated models)
697-bool Model3D::FindPositions_Neutral(bool UseModelTransform)
698-{
699- // Positions already there
700- if (VtxSrcIndices.empty()) return false;
701-
702- // Straight copy of the vertices:
703-
704- size_t NumVertices = VtxSrcIndices.size();
705- Positions.resize(3*NumVertices);
706-
707- GLfloat *PP = PosBase();
708- GLushort *IP = VtxSIBase();
709-
710- size_t NumVtxSources = VtxSources.size();
711-
712- if (UseModelTransform)
713- {
714- for (size_t k=0; k<NumVertices; k++, IP++, PP+=3)
715- {
716- size_t VSIndex = *IP;
717- if (VSIndex >= 0 && VSIndex < NumVtxSources)
718- {
719- Model3D_VertexSource& VS = VtxSources[VSIndex];
720- TransformPoint(PP,VS.Position,TransformPos);
721- }
722- else
723- {
724- GLfloat VP[3] = {0,0,0};
725- TransformPoint(PP,VP,TransformPos);
726- }
727- }
728- }
729- else
730- {
731- for (size_t k=0; k<NumVertices; k++, IP++)
732- {
733- size_t VSIndex = *IP;
734- if (VSIndex >= 0 && VSIndex < NumVtxSources)
735- {
736- Model3D_VertexSource& VS = VtxSources[VSIndex];
737- GLfloat *VP = VS.Position;
738- *(PP++) = *(VP++);
739- *(PP++) = *(VP++);
740- *(PP++) = *(VP++);
741- }
742- else
743- {
744- *(PP++) = 0;
745- *(PP++) = 0;
746- *(PP++) = 0;
747- }
748- }
749- }
750-
751- // Copy in the normals
752- Normals.resize(NormSources.size());
753-
754- if (UseModelTransform)
755- {
756- GLfloat *NormPtr = NormBase();
757- GLfloat *NormBasePtr = NormSrcBase();
758- size_t NumNorms = NormSources.size()/3;
759- for (size_t k=0; k<NumNorms; k++, NormPtr+=3, NormBasePtr+=3)
760- {
761- TransformVector(NormPtr, NormBasePtr, TransformNorm);
762- }
763- }
764- else
765- {
766- objlist_copy(NormBase(),NormSrcBase(),NormSources.size());
767- }
768-
769- return true;
770-}
771-
772-// Frame case
773-bool Model3D::FindPositions_Frame(bool UseModelTransform,
774- GLshort FrameIndex, GLfloat MixFrac, GLshort AddlFrameIndex)
775-{
776- // Bad inputs: do nothing and return false
777-
778- if (Frames.empty()) return false;
779-
780- size_t NumBones = Bones.size();
781- if (FrameIndex < 0 || NumBones*FrameIndex >= Frames.size()) return false;
782-
783- if (InverseVSIndices.empty()) BuildInverseVSIndices();
784-
785- size_t NumVertices = VtxSrcIndices.size();
786- Positions.resize(3*NumVertices);
787-
788- // Set sizes:
789- BoneMatrices.resize(NumBones);
790- BoneStack.resize(NumBones);
791-
792- // Find which frame; remember that frame data comes in [NumBones] sets
793- Model3D_Frame *FramePtr = &Frames[NumBones*FrameIndex];
794- Model3D_Frame *AddlFramePtr = &Frames[NumBones*AddlFrameIndex];
795-
796- // Find the individual-bone transformation matrices:
797- for (size_t ib=0; ib<NumBones; ib++)
798- FindBoneTransform(BoneMatrices[ib],Bones[ib],
799- FramePtr[ib],MixFrac,AddlFramePtr[ib]);
800-
801- // Find the cumulative-bone transformation matrices:
802- int StackIndx = -1;
803- size_t Parent = UNONE;
804- for (unsigned int ib=0; ib<NumBones; ib++)
805- {
806- Model3D_Bone& Bone = Bones[ib];
807-
808- // Do the pop-push with the stack
809- // to get the bone's parent bone
810- if (TEST_FLAG(Bone.Flags,Model3D_Bone::Pop))
811- {
812- if (StackIndx >= 0)
813- Parent = BoneStack[StackIndx--];
814- else
815- Parent = UNONE;
816- }
817- if (TEST_FLAG(Bone.Flags,Model3D_Bone::Push))
818- {
819- StackIndx = MAX(StackIndx,-1);
820- BoneStack[++StackIndx] = Parent;
821- }
822-
823- // Do the transform!
824- if (Parent != UNONE)
825- {
826- Model3D_Transform Res;
827- TMatMultiply(Res,BoneMatrices[Parent],BoneMatrices[ib]);
828- obj_copy(BoneMatrices[ib],Res);
829- }
830-
831- // Default: parent of next bone is current bone
832- Parent = ib;
833- }
834-
835- bool NormalsPresent = !NormSources.empty();
836- if (NormalsPresent) Normals.resize(NormSources.size());
837-
838- for (unsigned ivs=0; ivs<VtxSources.size(); ivs++)
839- {
840- Model3D_VertexSource& VS = VtxSources[ivs];
841- GLfloat Position[3];
842-
843- if (VS.Bone0 >= 0)
844- {
845- Model3D_Transform& T0 = BoneMatrices[VS.Bone0];
846- TransformPoint(Position,VS.Position,T0);
847-
848- if (NormalsPresent)
849- {
850- for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
851- {
852- int Indx = 3*InverseVSIndices[iv];
853- TransformVector(NormBase() + Indx, NormSrcBase() + Indx, T0);
854- }
855- }
856-
857- GLfloat Blend = VS.Blend;
858- if (VS.Bone1 >= 0 && Blend != 0)
859- {
860- Model3D_Transform& T1 = BoneMatrices[VS.Bone1];
861- GLfloat PosExtra[3];
862- GLfloat PosDiff[3];
863- TransformPoint(PosExtra,VS.Position,T1);
864- VecSub(PosExtra,Position,PosDiff);
865- VecScalarMultTo(PosDiff,Blend);
866- VecAddTo(Position,PosDiff);
867-
868- if (NormalsPresent)
869- {
870- for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
871- {
872- int Indx = 3*InverseVSIndices[iv];
873- GLfloat NormExtra[3];
874- GLfloat NormDiff[3];
875- GLfloat *OrigNorm = NormSrcBase() + Indx;
876- GLfloat *Norm = NormBase() + Indx;
877- TransformVector(NormExtra,OrigNorm,T1);
878- VecSub(NormExtra,Norm,NormDiff);
879- VecScalarMultTo(NormDiff,Blend);
880- VecAddTo(Norm,NormDiff);
881- }
882- }
883- }
884- }
885- else // The assumed root bone (identity transformation)
886- {
887- VecCopy(VS.Position,Position);
888- if (NormalsPresent)
889- {
890- for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
891- {
892- int Indx = 3*InverseVSIndices[iv];
893- VecCopy(NormSrcBase() + Indx, NormBase() + Indx);
894- }
895- }
896- }
897-
898- // Copy found position into vertex array!
899- for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
900- VecCopy(Position,PosBase() + 3*InverseVSIndices[iv]);
901- }
902-
903- if (UseModelTransform)
904- {
905- GLfloat *PP = PosBase();
906- for (size_t k=0; k<Positions.size()/3; k++, PP+=3)
907- {
908- GLfloat Position[3];
909- TransformPoint(Position,PP,TransformPos);
910- VecCopy(Position,PP);
911- }
912- GLfloat *NP = NormBase();
913- for (size_t k=0; k<Normals.size()/3; k++, NP+=3)
914- {
915- GLfloat Normal[3];
916- TransformVector(Normal,NP,TransformNorm);
917- VecCopy(Normal,NP);
918- }
919- }
920-
921- return true;
922-}
923-
924-// Returns 0 for out-of-range sequence
925-GLshort Model3D::NumSeqFrames(GLshort SeqIndex)
926-{
927- if (SeqFrmPointers.empty()) return 0;
928- if ((SeqIndex < 0) || (SeqIndex >= GLshort(SeqFrmPointers.size()))) return 0;
929-
930- return (SeqFrmPointers[SeqIndex+1] - SeqFrmPointers[SeqIndex]);
931-}
932-
933-bool Model3D::FindPositions_Sequence(bool UseModelTransform, GLshort SeqIndex,
934- GLshort FrameIndex, GLfloat MixFrac, GLshort AddlFrameIndex)
935-{
936- // Bad inputs: do nothing and return false
937-
938- GLshort NumSF = NumSeqFrames(SeqIndex);
939- if (NumSF <= 0) return false;
940-
941- if (FrameIndex < 0 || FrameIndex >= NumSF) return false;
942-
943- Model3D_Transform TSF;
944-
945- Model3D_SeqFrame& SF = SeqFrames[SeqFrmPointers[SeqIndex] + FrameIndex];
946-
947- if (MixFrac != 0 && AddlFrameIndex != FrameIndex)
948- {
949- if (AddlFrameIndex < 0 || AddlFrameIndex >= NumSF) return false;
950-
951- Model3D_SeqFrame& ASF = SeqFrames[SeqFrmPointers[SeqIndex] + AddlFrameIndex];
952- FindFrameTransform(TSF,SF,MixFrac,ASF);
953-
954- if (!FindPositions_Frame(false,SF.Frame,MixFrac,ASF.Frame)) return false;
955- }
956- else
957- {
958- if (!FindPositions_Frame(false,SF.Frame)) return false;
959- FindFrameTransform(TSF,SF,0,SF);
960- }
961-
962- Model3D_Transform TTot;
963- if (UseModelTransform)
964- TMatMultiply(TTot,TransformPos,TSF);
965- else
966- obj_copy(TTot,TSF);
967-
968- size_t NumVerts = Positions.size()/3;
969- GLfloat *Pos = PosBase();
970- for (size_t iv=0; iv<NumVerts; iv++)
971- {
972- GLfloat NewPos[3];
973- TransformPoint(NewPos,Pos,TTot);
974- VecCopy(NewPos,Pos);
975- Pos += 3;
976- }
977-
978- bool NormalsPresent = !NormSources.empty();
979- if (NormalsPresent)
980- {
981- // OK, since the bones don't change bulk
982- if (UseModelTransform)
983- TMatMultiply(TTot,TransformNorm,TSF);
984- else
985- obj_copy(TTot,TSF);
986-
987- GLfloat *Norm = NormBase();
988- for (size_t iv=0; iv<NumVerts; iv++)
989- {
990- GLfloat NewNorm[3];
991- TransformVector(NewNorm,Norm,TTot);
992- VecCopy(NewNorm,Norm);
993- Norm += 3;
994- }
995- }
996-
997- return true;
998-}
999-
1000-
1001-void Model3D_Transform::Identity()
1002-{
1003- obj_clear(*this);
1004- M[0][0] = M[1][1] = M[2][2] = 1;
1005-}
1006-
1007-
1008-static int16 InterpolateAngle(int16 Angle, GLfloat MixFrac, int16 AddlAngle)
1009-{
1010- if (MixFrac != 0 && AddlAngle != Angle)
1011- {
1012- int16 AngleDiff = NORMALIZE_ANGLE(AddlAngle - Angle);
1013- if (AngleDiff >= HALF_CIRCLE) AngleDiff -= FULL_CIRCLE;
1014- Angle += int16(MixFrac*AngleDiff);
1015- }
1016- return NORMALIZE_ANGLE(Angle);
1017-}
1018-
1019-
1020-// Bone and Frame (positions, angles) -> Transform Matrix
1021-static void FindFrameTransform(Model3D_Transform& T,
1022- Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame)
1023-{
1024- T.Identity();
1025-
1026- // First, do rotations:
1027- short Angle;
1028-
1029- // Right-to-left; in the right order for Dim3 (and Tomb Raider)
1030-
1031- // Z:
1032- Angle = InterpolateAngle(Frame.Angles[2],MixFrac,AddlFrame.Angles[2]);
1033-
1034- if (Angle != 0)
1035- {
1036- GLfloat C = TrigNorm*cosine_table[Angle];
1037- GLfloat S = TrigNorm*sine_table[Angle];
1038-
1039- for (int ic=0; ic<3; ic++)
1040- {
1041- GLfloat X = T.M[0][ic];
1042- GLfloat Y = T.M[1][ic];
1043- GLfloat XR = X*C - Y*S;
1044- GLfloat YR = X*S + Y*C;
1045- T.M[0][ic] = XR;
1046- T.M[1][ic] = YR;
1047- }
1048- }
1049-
1050- // X:
1051- Angle = InterpolateAngle(Frame.Angles[0],MixFrac,AddlFrame.Angles[0]);
1052-
1053- if (Angle != 0)
1054- {
1055- GLfloat C = TrigNorm*cosine_table[Angle];
1056- GLfloat S = TrigNorm*sine_table[Angle];
1057-
1058- for (int ic=0; ic<3; ic++)
1059- {
1060- GLfloat X = T.M[1][ic];
1061- GLfloat Y = T.M[2][ic];
1062- GLfloat XR = X*C - Y*S;
1063- GLfloat YR = X*S + Y*C;
1064- T.M[1][ic] = XR;
1065- T.M[2][ic] = YR;
1066- }
1067- }
1068-
1069- // Y:
1070- Angle = InterpolateAngle(Frame.Angles[1],MixFrac,AddlFrame.Angles[1]);
1071-
1072- if (Angle != 0)
1073- {
1074- GLfloat C = TrigNorm*cosine_table[Angle];
1075- GLfloat S = TrigNorm*sine_table[Angle];
1076-
1077- for (int ic=0; ic<3; ic++)
1078- {
1079- GLfloat X = T.M[2][ic];
1080- GLfloat Y = T.M[0][ic];
1081- GLfloat XR = X*C - Y*S;
1082- GLfloat YR = X*S + Y*C;
1083- T.M[2][ic] = XR;
1084- T.M[0][ic] = YR;
1085- }
1086- }
1087-
1088- // Set up overall translate:
1089- GLfloat *FrameOfst = Frame.Offset;
1090- if (MixFrac != 0)
1091- {
1092- GLfloat *AddlFrameOfst = AddlFrame.Offset;
1093- for (int ic=0; ic<3; ic++)
1094- T.M[ic][3] = FrameOfst[ic] + MixFrac*(AddlFrameOfst[ic]-FrameOfst[ic]);
1095- }
1096- else
1097- {
1098- for (int ic=0; ic<3; ic++)
1099- T.M[ic][3] = FrameOfst[ic];
1100- }
1101-}
1102-
1103-static void FindBoneTransform(Model3D_Transform& T, Model3D_Bone& Bone,
1104- Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame)
1105-{
1106- FindFrameTransform(T,Frame,MixFrac,AddlFrame);
1107-
1108- // Set up overall translate:
1109- GLfloat *BonePos = Bone.Position;
1110- for (int ic=0; ic<3; ic++)
1111- T.M[ic][3] += BonePos[ic] - ScalarProd(T.M[ic],BonePos);
1112-}
1113-
1114-
1115-// Res = A * B, in that order
1116-static void TMatMultiply(Model3D_Transform& Res, Model3D_Transform& A, Model3D_Transform& B)
1117-{
1118- // Multiply the rotation parts
1119- for (int i=0; i<3; i++)
1120- for (int j=0; j<3; j++)
1121- {
1122- GLfloat Sum = 0;
1123- for (int k=0; k<3; k++)
1124- Sum += A.M[i][k]*B.M[k][j];
1125-
1126- Res.M[i][j] = Sum;
1127- }
1128-
1129- // Now the translation part
1130- for (int i=0; i<3; i++)
1131- {
1132- GLfloat Sum = 0;
1133- for (int k=0; k<3; k++)
1134- Sum += A.M[i][k]*B.M[k][3];
1135-
1136- Res.M[i][3] = A.M[i][3] + Sum;
1137- }
1138-}
1139-
1140-
1141-#endif // def HAVE_OPENGL
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ 3D-Model Object Storage Functions
21+
22+ By Loren Petrich, July 8, 2001
23+*/
24+
25+#include <string.h>
26+#include <math.h>
27+
28+#include "VecOps.h"
29+#include "cseries.h"
30+#include "world.h"
31+
32+#ifdef HAVE_OPENGL
33+
34+#ifdef __WIN32__
35+#include <windows.h>
36+#endif
37+
38+#include "Model3D.h"
39+
40+#ifdef HAVE_OPENGL
41+#include "OGL_Headers.h"
42+#endif
43+
44+/* Need Sgl* macros */
45+#include "OGL_Setup.h"
46+
47+// Bone-stack and transformation-matrix locally-used arrays;
48+// the matrices have dimensions (output coords)(input-coord multipliers + offset for output)
49+static vector<Model3D_Transform> BoneMatrices;
50+static vector<size_t> BoneStack;
51+
52+
53+// Find transform of point (source and dest must be different arrays)
54+inline void TransformPoint(GLfloat *Dest, GLfloat *Src, Model3D_Transform& T)
55+{
56+ for (int ic=0; ic<3; ic++)
57+ {
58+ GLfloat *Row = T.M[ic];
59+ Dest[ic] = ScalarProd(Src,Row) + Row[3];
60+ }
61+}
62+
63+// Like above, but a vector, such as a normal (source and dest must be different arrays)
64+inline void TransformVector(GLfloat *Dest, GLfloat *Src, Model3D_Transform& T)
65+{
66+ for (int ic=0; ic<3; ic++)
67+ {
68+ GLfloat *Row = T.M[ic];
69+ Dest[ic] = ScalarProd(Src,Row);
70+ }
71+}
72+
73+// Bone and Frame (positions, angles) -> Transform Matrix
74+static void FindFrameTransform(Model3D_Transform& T,
75+ Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame);
76+static void FindBoneTransform(Model3D_Transform& T, Model3D_Bone& Bone,
77+ Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame);
78+
79+// Res = A * B, in that order
80+static void TMatMultiply(Model3D_Transform& Res, Model3D_Transform& A, Model3D_Transform& B);
81+
82+
83+// Trig-function conversion:
84+const GLfloat TrigNorm = GLfloat(1)/GLfloat(TRIG_MAGNITUDE);
85+
86+
87+// Erase everything
88+void Model3D::Clear()
89+{
90+ Positions.clear();
91+ TxtrCoords.clear();
92+ Normals.clear();
93+ Colors.clear();
94+ VtxSrcIndices.clear();
95+ VtxSources.clear();
96+ NormSources.clear();
97+ InverseVSIndices.clear();
98+ InvVSIPointers.clear();
99+ Bones.clear();
100+ VertIndices.clear();
101+ Frames.clear();
102+ SeqFrames.clear();
103+ SeqFrmPointers.clear();
104+ FindBoundingBox();
105+}
106+
107+// Normalize an individual normal; return whether the normal had a nonzero length
108+static bool NormalizeNormal(GLfloat *Normal)
109+{
110+ GLfloat NormalSqr =
111+ Normal[0]*Normal[0] + Normal[1]*Normal[1] + Normal[2]*Normal[2];
112+
113+ if (NormalSqr <= 0) return false;
114+
115+ GLfloat NormalRecip = (GLfloat)(1/sqrt(NormalSqr));
116+
117+ Normal[0] *= NormalRecip;
118+ Normal[1] *= NormalRecip;
119+ Normal[2] *= NormalRecip;
120+
121+ return true;
122+}
123+
124+// Flagged vector for per-polygon and per-vertex normals;
125+// the flag is "true" for nonzero vectors and vectors meant to be used
126+struct FlaggedVector
127+{
128+ GLfloat Vec[3];
129+ bool Flag;
130+};
131+
132+void Model3D::CalculateTangents()
133+{
134+ Tangents.resize(Positions.size() * 4 / 3);
135+
136+ bool generate_normals = false;
137+ if(Normals.size() != Positions.size()) {
138+ Normals.resize(Positions.size());
139+ memset(NormBase(), 0, sizeof(GLfloat)*Normals.size());
140+ generate_normals = true;
141+ }
142+
143+ for(GLushort i = 0; i < VertIndices.size(); i+= 3) {
144+ GLushort a = VertIndices[i];
145+ GLushort b = VertIndices[i+1];
146+ GLushort c = VertIndices[i+2];
147+
148+ vertex3 v1(PosBase()+3*a);
149+ vertex3 v2(PosBase()+3*b);
150+ vertex3 v3(PosBase()+3*c);
151+
152+ vertex2 w1(TCBase()+2*a);
153+ vertex2 w2(TCBase()+2*b);
154+ vertex2 w3(TCBase()+2*c);
155+
156+ float x1 = v2[0] - v1[0];
157+ float x2 = v3[0] - v1[0];
158+ float y1 = v2[1] - v1[1];
159+ float y2 = v3[1] - v1[1];
160+ float z1 = v2[2] - v1[2];
161+ float z2 = v3[2] - v1[2];
162+
163+ float s1 = w2[0] - w1[0];
164+ float s2 = w3[0] - w1[0];
165+ float t1 = w2[1] - w1[1];
166+ float t2 = w3[1] - w1[1];
167+
168+ float r = 1.0f / (s1 * t2 - s2 * t1);
169+ vec3 T((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
170+ (t2 * z1 - t1 * z2) * r);
171+ vec3 B((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
172+ (s1 * z2 - s2 * z1) * r);
173+ if ((s1 * t2 - s2 * t1) == 0.0) {
174+ T = (v3 - v1).norm();
175+ B = (v2 - v1).norm();
176+ }
177+
178+ vec3 N = (v3-v1).cross(v2-v1);
179+ if (!generate_normals) {
180+ N = vec3(NormBase()+3*a) + vec3(NormBase()+3*b) + vec3(NormBase()+3*c);
181+ }
182+
183+ if(N.dot(N) < 0.001) {
184+ N = vec3(0.0,0.0,0.0);
185+ if (generate_normals) {
186+ VecCopy(N.p(), NormBase()+3*a);
187+ VecCopy(N.p(), NormBase()+3*b);
188+ VecCopy(N.p(), NormBase()+3*c);
189+ }
190+
191+ vec4 t(0.0,0.0,0.0,0.0);
192+ Tangents[a] = vec4(t);
193+ Tangents[b] = vec4(t);
194+ Tangents[c] = vec4(t);
195+ } else {
196+ N = N.norm();
197+ assert(N.dot(N) < 1.001);
198+
199+ if (generate_normals) {
200+ VecCopy(N.p(), NormBase()+3*a);
201+ VecCopy(N.p(), NormBase()+3*b);
202+ VecCopy(N.p(), NormBase()+3*c);
203+ }
204+
205+ float sign = (N.cross(T)).dot(B) < 0.0 ? -1.0 : 1.0;
206+ vec4 t = (T - N * N.dot(T)).norm();
207+ t[3] = sign;
208+
209+ Tangents[a] = vec4(t);
210+ Tangents[b] = vec4(t);
211+ Tangents[c] = vec4(t);
212+ }
213+ }
214+}
215+
216+
217+// Normalize the normals
218+void Model3D::AdjustNormals(int NormalType, float SmoothThreshold)
219+{
220+ // Copy in normal sources for processing
221+ if (!NormSources.empty())
222+ {
223+ Normals.resize(NormSources.size());
224+ objlist_copy(NormBase(),NormSrcBase(),NormSources.size());
225+ }
226+
227+ // Which kind of special processing?
228+ switch(NormalType)
229+ {
230+ case None:
231+ Normals.clear();
232+ break;
233+
234+ case Original:
235+ case Reversed:
236+ default:
237+ // Normalize
238+ for (unsigned k=0; k<Normals.size()/3; k++)
239+ NormalizeNormal(&Normals[3*k]);
240+ break;
241+
242+ case ClockwiseSide:
243+ case CounterclockwiseSide:
244+ // The really interesting stuff
245+ {
246+ // First, create a list of per-polygon normals
247+ size_t NumPolys = NumVI()/3;
248+ vector<FlaggedVector> PerPolygonNormalList(NumPolys);
249+
250+ GLushort *IndxPtr = VIBase();
251+ for (unsigned k=0; k<NumPolys; k++)
252+ {
253+ // The three vertices:
254+ GLfloat *P0 = &Positions[3*(*(IndxPtr++))];
255+ GLfloat *P1 = &Positions[3*(*(IndxPtr++))];
256+ GLfloat *P2 = &Positions[3*(*(IndxPtr++))];
257+ // The two in-polygon vectors:
258+ GLfloat P01[3];
259+ P01[0] = P1[0] - P0[0];
260+ P01[1] = P1[1] - P0[1];
261+ P01[2] = P1[2] - P0[2];
262+ GLfloat P02[3];
263+ P02[0] = P2[0] - P0[0];
264+ P02[1] = P2[1] - P0[1];
265+ P02[2] = P2[2] - P0[2];
266+ // Those vectors' normal:
267+ FlaggedVector& PPN = PerPolygonNormalList[k];
268+ PPN.Vec[0] = P01[1]*P02[2] - P01[2]*P02[1];
269+ PPN.Vec[1] = P01[2]*P02[0] - P01[0]*P02[2];
270+ PPN.Vec[2] = P01[0]*P02[1] - P01[1]*P02[0];
271+ PPN.Flag = NormalizeNormal(PPN.Vec);
272+ }
273+
274+ // Create a list of per-vertex normals
275+ size_t NumVerts = Positions.size()/3;
276+ vector<FlaggedVector> PerVertexNormalList(NumVerts);
277+ objlist_clear(&PerVertexNormalList[0],NumVerts);
278+ IndxPtr = VIBase();
279+ for (unsigned k=0; k<NumPolys; k++)
280+ {
281+ FlaggedVector& PPN = PerPolygonNormalList[k];
282+ for (unsigned c=0; c<3; c++)
283+ {
284+ GLushort VertIndx = *(IndxPtr++);
285+ GLfloat *V = PerVertexNormalList[VertIndx].Vec;
286+ *(V++) += PPN.Vec[0];
287+ *(V++) += PPN.Vec[1];
288+ *(V++) += PPN.Vec[2];
289+ }
290+ }
291+
292+ // Normalize the per-vertex normals
293+ for (unsigned k=0; k<NumVerts; k++)
294+ {
295+ FlaggedVector& PVN = PerVertexNormalList[k];
296+ PVN.Flag = NormalizeNormal(PVN.Vec);
297+ }
298+
299+ // Find the variance of each of the per-vertex normals;
300+ // use that to decide whether to keep them unsplit;
301+ // this also needs counting up the number of polygons per vertex.
302+ vector<GLfloat> Variances(NumVerts);
303+ objlist_clear(&Variances[0],NumVerts);
304+ vector<short> NumPolysPerVert(NumVerts);
305+ objlist_clear(&NumPolysPerVert[0],NumVerts);
306+ IndxPtr = VIBase();
307+ for (unsigned k=0; k<NumPolys; k++)
308+ {
309+ FlaggedVector& PPN = PerPolygonNormalList[k];
310+ for (unsigned c=0; c<3; c++)
311+ {
312+ GLushort VertIndx = *(IndxPtr++);
313+ FlaggedVector& PVN = PerVertexNormalList[VertIndx];
314+ if (PVN.Flag)
315+ {
316+ GLfloat *V = PVN.Vec;
317+ GLfloat D0 = *(V++) - PPN.Vec[0];
318+ GLfloat D1 = *(V++) - PPN.Vec[1];
319+ GLfloat D2 = *(V++) - PPN.Vec[2];
320+ Variances[VertIndx] += (D0*D0 + D1*D1 + D2*D2);
321+ NumPolysPerVert[VertIndx]++;
322+ }
323+ }
324+ }
325+
326+ // Decide whether to split each vertex;
327+ // if the flag is "true", a vertex is not to be split
328+ for (unsigned k=0; k<NumVerts; k++)
329+ {
330+ // Vertices without contributions will automatically have
331+ // their flags be false, as a result of NormalizeNormal()
332+ unsigned NumVertPolys = NumPolysPerVert[k];
333+ if (NumVertPolys > 0 && PerVertexNormalList[k].Flag)
334+ PerVertexNormalList[k].Flag =
335+ sqrt(Variances[k]/NumVertPolys) <= SmoothThreshold;
336+ }
337+
338+ // The vertex flags are now set for whether to use that vertex's normal;
339+ // re-count the number of polygons per vertex.
340+ // Use NONE for unsplit ones
341+ objlist_clear(&NumPolysPerVert[0],NumVerts);
342+ IndxPtr = VIBase();
343+ for (unsigned k=0; k<NumPolys; k++)
344+ {
345+ for (unsigned c=0; c<3; c++)
346+ {
347+ GLushort VertIndx = *(IndxPtr++);
348+ FlaggedVector& PVN = PerVertexNormalList[VertIndx];
349+ if (PVN.Flag)
350+ NumPolysPerVert[VertIndx] = NONE;
351+ else
352+ NumPolysPerVert[VertIndx]++;
353+ }
354+ }
355+
356+ // Construct a polygon-association list; this will indicate
357+ // which polygons are associated with each of the resulting instances
358+ // of a split vertex (unsplit: NONE).
359+ // NumPolysPerVert will be recycled as a counter list,
360+ // after being used to construct a cumulative index-in-list array.
361+ // Finding that list will be used to find how many new vertices there are.
362+ vector<short> IndicesInList(NumVerts);
363+ short IndxInList = 0;
364+ for (unsigned k=0; k<NumVerts; k++)
365+ {
366+ IndicesInList[k] = IndxInList;
367+ FlaggedVector& PVN = PerVertexNormalList[k];
368+ IndxInList += PVN.Flag ? 1 : NumPolysPerVert[k];
369+ }
370+ GLushort NewNumVerts = IndxInList;
371+ vector<short> VertexPolygons(NewNumVerts);
372+ objlist_clear(&NumPolysPerVert[0],NumVerts);
373+
374+ // In creating that list, also remap the triangles' vertices
375+ GLushort *VIPtr = VIBase();
376+ for (unsigned k=0; k<NumPolys; k++)
377+ {
378+ for (unsigned c=0; c<3; c++)
379+ {
380+ GLushort VertIndx = *VIPtr;
381+ GLushort NewVertIndx = IndicesInList[VertIndx];
382+ FlaggedVector& PVN = PerVertexNormalList[VertIndx];
383+ if (PVN.Flag)
384+ {
385+ NumPolysPerVert[VertIndx] = NONE;
386+ VertexPolygons[NewVertIndx] = NONE;
387+ }
388+ else
389+ {
390+ NewVertIndx += (NumPolysPerVert[VertIndx]++);
391+ VertexPolygons[NewVertIndx] = k;
392+ }
393+ *VIPtr = NewVertIndx;
394+ VIPtr++;
395+ }
396+ }
397+
398+ // Split the vertices
399+ vector<GLfloat> NewPositions(3*NewNumVerts);
400+ vector<GLfloat> NewTxtrCoords;
401+ vector<GLfloat> NewNormals(3*NewNumVerts);
402+ vector<GLfloat> NewColors;
403+ vector<GLushort> NewVtxSrcIndices;
404+
405+ bool TCPresent = !TxtrCoords.empty();
406+ if (TCPresent) NewTxtrCoords.resize(2*NewNumVerts);
407+
408+ bool ColPresent = !Colors.empty();
409+ if (ColPresent) NewColors.resize(3*NewNumVerts);
410+
411+ bool VSPresent = !VtxSrcIndices.empty();
412+ if (VSPresent) NewVtxSrcIndices.resize(NewNumVerts);
413+
414+ // Use marching pointers to speed up the copy-over
415+ GLfloat *OldP = &Positions[0];
416+ GLfloat *NewP = &NewPositions[0];
417+ GLfloat *OldT = &TxtrCoords[0];
418+ GLfloat *NewT = &NewTxtrCoords[0];
419+ GLfloat *OldC = &Colors[0];
420+ GLfloat *NewC = &NewColors[0];
421+ GLushort *OldS = &VtxSrcIndices[0];
422+ GLushort *NewS = &NewVtxSrcIndices[0];
423+ GLfloat *NewN = &NewNormals[0];
424+ for (unsigned k=0; k<NumVerts; k++)
425+ {
426+ FlaggedVector& PVN = PerVertexNormalList[k];
427+ unsigned NumVertPolys = PVN.Flag ? 1 : NumPolysPerVert[k];
428+ for (unsigned c=0; c<NumVertPolys; c++)
429+ {
430+ GLfloat *OldPP = OldP;
431+ *(NewP++) = *(OldPP++);
432+ *(NewP++) = *(OldPP++);
433+ *(NewP++) = *(OldPP++);
434+ }
435+ if (TCPresent)
436+ {
437+ for (unsigned c=0; c<NumVertPolys; c++)
438+ {
439+ GLfloat *OldTP = OldT;
440+ *(NewT++) = *(OldTP++);
441+ *(NewT++) = *(OldTP++);
442+ }
443+ }
444+ if (ColPresent)
445+ {
446+ for (unsigned c=0; c<NumVertPolys; c++)
447+ {
448+ GLfloat *OldCP = OldC;
449+ *(NewC++) = *(OldCP++);
450+ *(NewC++) = *(OldCP++);
451+ *(NewC++) = *(OldCP++);
452+ }
453+ }
454+ if (VSPresent)
455+ {
456+ for (unsigned c=0; c<NumVertPolys; c++)
457+ *(NewS++) = *OldS;
458+ }
459+ if (PVN.Flag)
460+ {
461+ GLfloat *VP = PVN.Vec;
462+ *(NewN++) = *(VP++);
463+ *(NewN++) = *(VP++);
464+ *(NewN++) = *(VP++);
465+ }
466+ else
467+ {
468+ // A reference so that the incrementing can work on it
469+ short& IndxInList = IndicesInList[k];
470+ for (unsigned c=0; c<NumVertPolys; c++)
471+ {
472+ GLfloat *VP = PerPolygonNormalList[VertexPolygons[IndxInList++]].Vec;
473+ *(NewN++) = *(VP++);
474+ *(NewN++) = *(VP++);
475+ *(NewN++) = *(VP++);
476+ }
477+ }
478+
479+ // Advance!
480+ OldP += 3;
481+ if (TCPresent)
482+ OldT += 2;
483+ if (ColPresent)
484+ OldC += 3;
485+ if (VSPresent)
486+ OldS++;
487+
488+ }
489+ assert(OldP == &Positions[3*NumVerts]);
490+ assert(NewP == &NewPositions[3*NewNumVerts]);
491+ if (TCPresent)
492+ {
493+ assert(OldT == &TxtrCoords[2*NumVerts]);
494+ assert(NewT == &NewTxtrCoords[2*NewNumVerts]);
495+ }
496+ if (ColPresent)
497+ {
498+ assert(OldC == &Colors[3*NumVerts]);
499+ assert(NewC == &NewColors[3*NewNumVerts]);
500+ }
501+ if (VSPresent)
502+ {
503+ assert(OldS == &VtxSrcIndices[NumVerts]);
504+ assert(NewS == &NewVtxSrcIndices[NewNumVerts]);
505+ }
506+ assert(NewN == &NewNormals[3*NewNumVerts]);
507+
508+ // Accept the new vectors
509+ Positions.swap(NewPositions);
510+ TxtrCoords.swap(NewTxtrCoords);
511+ Normals.swap(NewNormals);
512+ Colors.swap(NewColors);
513+ VtxSrcIndices.swap(NewVtxSrcIndices);
514+ }
515+ break;
516+ }
517+
518+ // Now flip
519+ switch(NormalType)
520+ {
521+ case Reversed:
522+ case CounterclockwiseSide:
523+ {
524+ GLfloat *NormalPtr = NormBase();
525+ for (unsigned k=0; k<Normals.size(); k++)
526+ *(NormalPtr++) *= -1;
527+ }
528+ }
529+
530+ // Copy back out to the normal sources;
531+ // do the copying out if the vertices have sources,
532+ // which is the case for boned models.
533+ if (!VtxSources.empty())
534+ {
535+ size_t NormSize = Normals.size();
536+ if (NormSize > 0)
537+ {
538+ NormSources.resize(NormSize);
539+ objlist_copy(NormSrcBase(),NormBase(),NormSize);
540+ }
541+ else
542+ NormSources.clear();
543+ }
544+ else
545+ NormSources.clear();
546+}
547+
548+
549+// From the position data
550+void Model3D::FindBoundingBox()
551+{
552+ size_t NumVertices = Positions.size()/3;
553+ if (NumVertices > 0)
554+ {
555+ // Find the min and max of the positions:
556+ VecCopy(&Positions[0],BoundingBox[0]);
557+ VecCopy(&Positions[0],BoundingBox[1]);
558+ for (size_t i=1; i<NumVertices; i++)
559+ {
560+ GLfloat *Pos = &Positions[3*i];
561+ for (int ib=0; ib<3; ib++)
562+ {
563+ BoundingBox[0][ib] = MIN(BoundingBox[0][ib],Pos[ib]);
564+ BoundingBox[1][ib] = MAX(BoundingBox[1][ib],Pos[ib]);
565+ }
566+ }
567+ }
568+ else
569+ {
570+ // Clear the bounding box
571+ objlist_clear(BoundingBox[0],3);
572+ objlist_clear(BoundingBox[1],3);
573+ }
574+}
575+
576+
577+// For debugging
578+void Model3D::RenderBoundingBox(const GLfloat *EdgeColor, const GLfloat *DiagonalColor)
579+{
580+ GLfloat BBoxVertices[8][3];
581+
582+ // Binary-number arrangement of expanded vertices:
583+ for (int i1=0; i1<2; i1++)
584+ for (int i2=0; i2<2; i2++)
585+ for (int i3=0; i3<2; i3++)
586+ {
587+ int Indx = 4*i1 + 2*i2 + i3;
588+ BBoxVertices[Indx][0] = BoundingBox[i1][0];
589+ BBoxVertices[Indx][1] = BoundingBox[i2][1];
590+ BBoxVertices[Indx][2] = BoundingBox[i3][2];
591+ }
592+
593+ glDisable(GL_TEXTURE_2D);
594+ glDisableClientState(GL_COLOR_ARRAY);
595+ glEnableClientState(GL_VERTEX_ARRAY);
596+ glVertexPointer(3,GL_FLOAT,0,BBoxVertices[0]);
597+
598+ if (EdgeColor)
599+ {
600+ SglColor3fv(EdgeColor);
601+ const int NumEdgeVerts = 24;
602+ const unsigned short EdgeVerts[NumEdgeVerts] = {
603+ 0,1,
604+ 1,3,
605+ 3,2,
606+ 2,0,
607+
608+ 0,4,
609+ 1,5,
610+ 2,6,
611+ 3,7,
612+
613+ 4,5,
614+ 5,7,
615+ 7,6,
616+ 6,4
617+ };
618+ glDrawElements(GL_LINES,NumEdgeVerts,GL_UNSIGNED_SHORT,EdgeVerts);
619+ }
620+
621+ if (DiagonalColor)
622+ {
623+ SglColor3fv(DiagonalColor);
624+ const int NumDiagVerts = 24;
625+ const unsigned short DiagVerts[NumDiagVerts] = {
626+ 0,3,
627+ 1,2,
628+
629+ 0,5,
630+ 1,4,
631+
632+ 1,7,
633+ 3,5,
634+
635+ 3,6,
636+ 2,7,
637+
638+ 2,4,
639+ 0,6,
640+
641+ 4,7,
642+ 5,6
643+ };
644+ glDrawElements(GL_LINES,NumDiagVerts,GL_UNSIGNED_SHORT,DiagVerts);
645+ }
646+}
647+
648+void Model3D::BuildTrigTables()
649+{
650+ build_trig_tables();
651+}
652+
653+
654+void Model3D::BuildInverseVSIndices()
655+{
656+ if (VtxSrcIndices.empty()) return;
657+
658+ InverseVSIndices.resize(VtxSrcIndices.size());
659+ InvVSIPointers.resize(VtxSources.size()+1); // One extra member
660+
661+ // Use the pointers as temporary storage for the count
662+ objlist_clear(InvVSIPtrBase(),InvVSIPointers.size());
663+ for (vector<GLushort>::iterator VSI_Iter = VtxSrcIndices.begin();
664+ VSI_Iter < VtxSrcIndices.end();
665+ VSI_Iter++)
666+ InvVSIPointers[*VSI_Iter]++;
667+
668+ // Find the positions from the counts
669+ GLushort PtrSum = 0;
670+ for (vector<GLushort>::iterator IVP_Iter = InvVSIPointers.begin();
671+ IVP_Iter < InvVSIPointers.end();
672+ IVP_Iter++)
673+ {
674+ GLushort NewPtrSum = PtrSum + *IVP_Iter;
675+ *IVP_Iter = PtrSum;
676+ PtrSum = NewPtrSum;
677+ }
678+
679+ // Place the inverse indices
680+ for (unsigned k = 0; k<VtxSrcIndices.size(); k++)
681+ InverseVSIndices[InvVSIPointers[VtxSrcIndices[k]]++] = k;
682+
683+ // Push the pointer values forward in the list
684+ // since they'd become their next values in it.
685+ // The reverse iteration is necessary to avoid overwriting
686+ for (vector<GLushort>::iterator IVP_Iter = InvVSIPointers.end()-1;
687+ IVP_Iter > InvVSIPointers.begin();
688+ IVP_Iter--)
689+ {
690+ *IVP_Iter = *(IVP_Iter - 1);
691+ }
692+ InvVSIPointers[0] = 0;
693+}
694+
695+
696+// Neutral case: returns whether vertex-source data was used (present in animated models)
697+bool Model3D::FindPositions_Neutral(bool UseModelTransform)
698+{
699+ // Positions already there
700+ if (VtxSrcIndices.empty()) return false;
701+
702+ // Straight copy of the vertices:
703+
704+ size_t NumVertices = VtxSrcIndices.size();
705+ Positions.resize(3*NumVertices);
706+
707+ GLfloat *PP = PosBase();
708+ GLushort *IP = VtxSIBase();
709+
710+ size_t NumVtxSources = VtxSources.size();
711+
712+ if (UseModelTransform)
713+ {
714+ for (size_t k=0; k<NumVertices; k++, IP++, PP+=3)
715+ {
716+ size_t VSIndex = *IP;
717+ if (VSIndex >= 0 && VSIndex < NumVtxSources)
718+ {
719+ Model3D_VertexSource& VS = VtxSources[VSIndex];
720+ TransformPoint(PP,VS.Position,TransformPos);
721+ }
722+ else
723+ {
724+ GLfloat VP[3] = {0,0,0};
725+ TransformPoint(PP,VP,TransformPos);
726+ }
727+ }
728+ }
729+ else
730+ {
731+ for (size_t k=0; k<NumVertices; k++, IP++)
732+ {
733+ size_t VSIndex = *IP;
734+ if (VSIndex >= 0 && VSIndex < NumVtxSources)
735+ {
736+ Model3D_VertexSource& VS = VtxSources[VSIndex];
737+ GLfloat *VP = VS.Position;
738+ *(PP++) = *(VP++);
739+ *(PP++) = *(VP++);
740+ *(PP++) = *(VP++);
741+ }
742+ else
743+ {
744+ *(PP++) = 0;
745+ *(PP++) = 0;
746+ *(PP++) = 0;
747+ }
748+ }
749+ }
750+
751+ // Copy in the normals
752+ Normals.resize(NormSources.size());
753+
754+ if (UseModelTransform)
755+ {
756+ GLfloat *NormPtr = NormBase();
757+ GLfloat *NormBasePtr = NormSrcBase();
758+ size_t NumNorms = NormSources.size()/3;
759+ for (size_t k=0; k<NumNorms; k++, NormPtr+=3, NormBasePtr+=3)
760+ {
761+ TransformVector(NormPtr, NormBasePtr, TransformNorm);
762+ }
763+ }
764+ else
765+ {
766+ objlist_copy(NormBase(),NormSrcBase(),NormSources.size());
767+ }
768+
769+ return true;
770+}
771+
772+// Frame case
773+bool Model3D::FindPositions_Frame(bool UseModelTransform,
774+ GLshort FrameIndex, GLfloat MixFrac, GLshort AddlFrameIndex)
775+{
776+ // Bad inputs: do nothing and return false
777+
778+ if (Frames.empty()) return false;
779+
780+ size_t NumBones = Bones.size();
781+ if (FrameIndex < 0 || NumBones*FrameIndex >= Frames.size()) return false;
782+
783+ if (InverseVSIndices.empty()) BuildInverseVSIndices();
784+
785+ size_t NumVertices = VtxSrcIndices.size();
786+ Positions.resize(3*NumVertices);
787+
788+ // Set sizes:
789+ BoneMatrices.resize(NumBones);
790+ BoneStack.resize(NumBones);
791+
792+ // Find which frame; remember that frame data comes in [NumBones] sets
793+ Model3D_Frame *FramePtr = &Frames[NumBones*FrameIndex];
794+ Model3D_Frame *AddlFramePtr = &Frames[NumBones*AddlFrameIndex];
795+
796+ // Find the individual-bone transformation matrices:
797+ for (size_t ib=0; ib<NumBones; ib++)
798+ FindBoneTransform(BoneMatrices[ib],Bones[ib],
799+ FramePtr[ib],MixFrac,AddlFramePtr[ib]);
800+
801+ // Find the cumulative-bone transformation matrices:
802+ int StackIndx = -1;
803+ size_t Parent = UNONE;
804+ for (unsigned int ib=0; ib<NumBones; ib++)
805+ {
806+ Model3D_Bone& Bone = Bones[ib];
807+
808+ // Do the pop-push with the stack
809+ // to get the bone's parent bone
810+ if (TEST_FLAG(Bone.Flags,Model3D_Bone::Pop))
811+ {
812+ if (StackIndx >= 0)
813+ Parent = BoneStack[StackIndx--];
814+ else
815+ Parent = UNONE;
816+ }
817+ if (TEST_FLAG(Bone.Flags,Model3D_Bone::Push))
818+ {
819+ StackIndx = MAX(StackIndx,-1);
820+ BoneStack[++StackIndx] = Parent;
821+ }
822+
823+ // Do the transform!
824+ if (Parent != UNONE)
825+ {
826+ Model3D_Transform Res;
827+ TMatMultiply(Res,BoneMatrices[Parent],BoneMatrices[ib]);
828+ obj_copy(BoneMatrices[ib],Res);
829+ }
830+
831+ // Default: parent of next bone is current bone
832+ Parent = ib;
833+ }
834+
835+ bool NormalsPresent = !NormSources.empty();
836+ if (NormalsPresent) Normals.resize(NormSources.size());
837+
838+ for (unsigned ivs=0; ivs<VtxSources.size(); ivs++)
839+ {
840+ Model3D_VertexSource& VS = VtxSources[ivs];
841+ GLfloat Position[3];
842+
843+ if (VS.Bone0 >= 0)
844+ {
845+ Model3D_Transform& T0 = BoneMatrices[VS.Bone0];
846+ TransformPoint(Position,VS.Position,T0);
847+
848+ if (NormalsPresent)
849+ {
850+ for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
851+ {
852+ int Indx = 3*InverseVSIndices[iv];
853+ TransformVector(NormBase() + Indx, NormSrcBase() + Indx, T0);
854+ }
855+ }
856+
857+ GLfloat Blend = VS.Blend;
858+ if (VS.Bone1 >= 0 && Blend != 0)
859+ {
860+ Model3D_Transform& T1 = BoneMatrices[VS.Bone1];
861+ GLfloat PosExtra[3];
862+ GLfloat PosDiff[3];
863+ TransformPoint(PosExtra,VS.Position,T1);
864+ VecSub(PosExtra,Position,PosDiff);
865+ VecScalarMultTo(PosDiff,Blend);
866+ VecAddTo(Position,PosDiff);
867+
868+ if (NormalsPresent)
869+ {
870+ for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
871+ {
872+ int Indx = 3*InverseVSIndices[iv];
873+ GLfloat NormExtra[3];
874+ GLfloat NormDiff[3];
875+ GLfloat *OrigNorm = NormSrcBase() + Indx;
876+ GLfloat *Norm = NormBase() + Indx;
877+ TransformVector(NormExtra,OrigNorm,T1);
878+ VecSub(NormExtra,Norm,NormDiff);
879+ VecScalarMultTo(NormDiff,Blend);
880+ VecAddTo(Norm,NormDiff);
881+ }
882+ }
883+ }
884+ }
885+ else // The assumed root bone (identity transformation)
886+ {
887+ VecCopy(VS.Position,Position);
888+ if (NormalsPresent)
889+ {
890+ for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
891+ {
892+ int Indx = 3*InverseVSIndices[iv];
893+ VecCopy(NormSrcBase() + Indx, NormBase() + Indx);
894+ }
895+ }
896+ }
897+
898+ // Copy found position into vertex array!
899+ for (int iv=InvVSIPointers[ivs]; iv<InvVSIPointers[ivs+1]; iv++)
900+ VecCopy(Position,PosBase() + 3*InverseVSIndices[iv]);
901+ }
902+
903+ if (UseModelTransform)
904+ {
905+ GLfloat *PP = PosBase();
906+ for (size_t k=0; k<Positions.size()/3; k++, PP+=3)
907+ {
908+ GLfloat Position[3];
909+ TransformPoint(Position,PP,TransformPos);
910+ VecCopy(Position,PP);
911+ }
912+ GLfloat *NP = NormBase();
913+ for (size_t k=0; k<Normals.size()/3; k++, NP+=3)
914+ {
915+ GLfloat Normal[3];
916+ TransformVector(Normal,NP,TransformNorm);
917+ VecCopy(Normal,NP);
918+ }
919+ }
920+
921+ return true;
922+}
923+
924+// Returns 0 for out-of-range sequence
925+GLshort Model3D::NumSeqFrames(GLshort SeqIndex)
926+{
927+ if (SeqFrmPointers.empty()) return 0;
928+ if ((SeqIndex < 0) || (SeqIndex >= GLshort(SeqFrmPointers.size()))) return 0;
929+
930+ return (SeqFrmPointers[SeqIndex+1] - SeqFrmPointers[SeqIndex]);
931+}
932+
933+bool Model3D::FindPositions_Sequence(bool UseModelTransform, GLshort SeqIndex,
934+ GLshort FrameIndex, GLfloat MixFrac, GLshort AddlFrameIndex)
935+{
936+ // Bad inputs: do nothing and return false
937+
938+ GLshort NumSF = NumSeqFrames(SeqIndex);
939+ if (NumSF <= 0) return false;
940+
941+ if (FrameIndex < 0 || FrameIndex >= NumSF) return false;
942+
943+ Model3D_Transform TSF;
944+
945+ Model3D_SeqFrame& SF = SeqFrames[SeqFrmPointers[SeqIndex] + FrameIndex];
946+
947+ if (MixFrac != 0 && AddlFrameIndex != FrameIndex)
948+ {
949+ if (AddlFrameIndex < 0 || AddlFrameIndex >= NumSF) return false;
950+
951+ Model3D_SeqFrame& ASF = SeqFrames[SeqFrmPointers[SeqIndex] + AddlFrameIndex];
952+ FindFrameTransform(TSF,SF,MixFrac,ASF);
953+
954+ if (!FindPositions_Frame(false,SF.Frame,MixFrac,ASF.Frame)) return false;
955+ }
956+ else
957+ {
958+ if (!FindPositions_Frame(false,SF.Frame)) return false;
959+ FindFrameTransform(TSF,SF,0,SF);
960+ }
961+
962+ Model3D_Transform TTot;
963+ if (UseModelTransform)
964+ TMatMultiply(TTot,TransformPos,TSF);
965+ else
966+ obj_copy(TTot,TSF);
967+
968+ size_t NumVerts = Positions.size()/3;
969+ GLfloat *Pos = PosBase();
970+ for (size_t iv=0; iv<NumVerts; iv++)
971+ {
972+ GLfloat NewPos[3];
973+ TransformPoint(NewPos,Pos,TTot);
974+ VecCopy(NewPos,Pos);
975+ Pos += 3;
976+ }
977+
978+ bool NormalsPresent = !NormSources.empty();
979+ if (NormalsPresent)
980+ {
981+ // OK, since the bones don't change bulk
982+ if (UseModelTransform)
983+ TMatMultiply(TTot,TransformNorm,TSF);
984+ else
985+ obj_copy(TTot,TSF);
986+
987+ GLfloat *Norm = NormBase();
988+ for (size_t iv=0; iv<NumVerts; iv++)
989+ {
990+ GLfloat NewNorm[3];
991+ TransformVector(NewNorm,Norm,TTot);
992+ VecCopy(NewNorm,Norm);
993+ Norm += 3;
994+ }
995+ }
996+
997+ return true;
998+}
999+
1000+
1001+void Model3D_Transform::Identity()
1002+{
1003+ obj_clear(*this);
1004+ M[0][0] = M[1][1] = M[2][2] = 1;
1005+}
1006+
1007+
1008+static int16 InterpolateAngle(int16 Angle, GLfloat MixFrac, int16 AddlAngle)
1009+{
1010+ if (MixFrac != 0 && AddlAngle != Angle)
1011+ {
1012+ int16 AngleDiff = NORMALIZE_ANGLE(AddlAngle - Angle);
1013+ if (AngleDiff >= HALF_CIRCLE) AngleDiff -= FULL_CIRCLE;
1014+ Angle += int16(MixFrac*AngleDiff);
1015+ }
1016+ return NORMALIZE_ANGLE(Angle);
1017+}
1018+
1019+
1020+// Bone and Frame (positions, angles) -> Transform Matrix
1021+static void FindFrameTransform(Model3D_Transform& T,
1022+ Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame)
1023+{
1024+ T.Identity();
1025+
1026+ // First, do rotations:
1027+ short Angle;
1028+
1029+ // Right-to-left; in the right order for Dim3 (and Tomb Raider)
1030+
1031+ // Z:
1032+ Angle = InterpolateAngle(Frame.Angles[2],MixFrac,AddlFrame.Angles[2]);
1033+
1034+ if (Angle != 0)
1035+ {
1036+ GLfloat C = TrigNorm*cosine_table[Angle];
1037+ GLfloat S = TrigNorm*sine_table[Angle];
1038+
1039+ for (int ic=0; ic<3; ic++)
1040+ {
1041+ GLfloat X = T.M[0][ic];
1042+ GLfloat Y = T.M[1][ic];
1043+ GLfloat XR = X*C - Y*S;
1044+ GLfloat YR = X*S + Y*C;
1045+ T.M[0][ic] = XR;
1046+ T.M[1][ic] = YR;
1047+ }
1048+ }
1049+
1050+ // X:
1051+ Angle = InterpolateAngle(Frame.Angles[0],MixFrac,AddlFrame.Angles[0]);
1052+
1053+ if (Angle != 0)
1054+ {
1055+ GLfloat C = TrigNorm*cosine_table[Angle];
1056+ GLfloat S = TrigNorm*sine_table[Angle];
1057+
1058+ for (int ic=0; ic<3; ic++)
1059+ {
1060+ GLfloat X = T.M[1][ic];
1061+ GLfloat Y = T.M[2][ic];
1062+ GLfloat XR = X*C - Y*S;
1063+ GLfloat YR = X*S + Y*C;
1064+ T.M[1][ic] = XR;
1065+ T.M[2][ic] = YR;
1066+ }
1067+ }
1068+
1069+ // Y:
1070+ Angle = InterpolateAngle(Frame.Angles[1],MixFrac,AddlFrame.Angles[1]);
1071+
1072+ if (Angle != 0)
1073+ {
1074+ GLfloat C = TrigNorm*cosine_table[Angle];
1075+ GLfloat S = TrigNorm*sine_table[Angle];
1076+
1077+ for (int ic=0; ic<3; ic++)
1078+ {
1079+ GLfloat X = T.M[2][ic];
1080+ GLfloat Y = T.M[0][ic];
1081+ GLfloat XR = X*C - Y*S;
1082+ GLfloat YR = X*S + Y*C;
1083+ T.M[2][ic] = XR;
1084+ T.M[0][ic] = YR;
1085+ }
1086+ }
1087+
1088+ // Set up overall translate:
1089+ GLfloat *FrameOfst = Frame.Offset;
1090+ if (MixFrac != 0)
1091+ {
1092+ GLfloat *AddlFrameOfst = AddlFrame.Offset;
1093+ for (int ic=0; ic<3; ic++)
1094+ T.M[ic][3] = FrameOfst[ic] + MixFrac*(AddlFrameOfst[ic]-FrameOfst[ic]);
1095+ }
1096+ else
1097+ {
1098+ for (int ic=0; ic<3; ic++)
1099+ T.M[ic][3] = FrameOfst[ic];
1100+ }
1101+}
1102+
1103+static void FindBoneTransform(Model3D_Transform& T, Model3D_Bone& Bone,
1104+ Model3D_Frame& Frame, GLfloat MixFrac, Model3D_Frame& AddlFrame)
1105+{
1106+ FindFrameTransform(T,Frame,MixFrac,AddlFrame);
1107+
1108+ // Set up overall translate:
1109+ GLfloat *BonePos = Bone.Position;
1110+ for (int ic=0; ic<3; ic++)
1111+ T.M[ic][3] += BonePos[ic] - ScalarProd(T.M[ic],BonePos);
1112+}
1113+
1114+
1115+// Res = A * B, in that order
1116+static void TMatMultiply(Model3D_Transform& Res, Model3D_Transform& A, Model3D_Transform& B)
1117+{
1118+ // Multiply the rotation parts
1119+ for (int i=0; i<3; i++)
1120+ for (int j=0; j<3; j++)
1121+ {
1122+ GLfloat Sum = 0;
1123+ for (int k=0; k<3; k++)
1124+ Sum += A.M[i][k]*B.M[k][j];
1125+
1126+ Res.M[i][j] = Sum;
1127+ }
1128+
1129+ // Now the translation part
1130+ for (int i=0; i<3; i++)
1131+ {
1132+ GLfloat Sum = 0;
1133+ for (int k=0; k<3; k++)
1134+ Sum += A.M[i][k]*B.M[k][3];
1135+
1136+ Res.M[i][3] = A.M[i][3] + Sum;
1137+ }
1138+}
1139+
1140+
1141+#endif // def HAVE_OPENGL
--- marathon/trunk/Source_Files/ModelView/ModelRenderer.cpp (revision 526)
+++ marathon/trunk/Source_Files/ModelView/ModelRenderer.cpp (revision 527)
@@ -1,222 +1,222 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- Renders 3D-model objects;
21-
22- Created by Loren Petrich, July 18, 2001
23-*/
24-
25-#include <string.h>
26-#include <stdlib.h>
27-
28-#include "cseries.h"
29-
30-#ifdef HAVE_OPENGL
31-
32-#ifdef __WIN32__
33-#include <windows.h>
34-#endif
35-
36-#include "ModelRenderer.h"
37-#include <algorithm>
38-
39-void ModelRenderer::Render(Model3D& Model, ModelRenderShader *Shaders, int NumShaders,
40- int NumSeparableShaders, bool Use_Z_Buffer)
41-{
42- if (NumShaders <= 0) return;
43- if (!Shaders) return;
44- if (Model.Positions.empty()) return;
45- glEnableClientState(GL_VERTEX_ARRAY);
46- glVertexPointer(3,GL_FLOAT,0,Model.PosBase());
47-
48- // Effective number of separable shaders when the Z-buffer is absent: none
49- // (the Z-buffer enables separability).
50- // Editing this arg is OK since it was called by value.
51- if (!Use_Z_Buffer) NumSeparableShaders = 0;
52-
53- // Optimization: skip depth sorting, render, and quit
54- // if all shaders are separable.
55- if (NumSeparableShaders >= NumShaders)
56- {
57- for (int q=0; q<NumShaders; q++)
58- {
59- SetupRenderPass(Model,Shaders[q]);
60- glDrawElements(GL_TRIANGLES,(GLsizei)Model.NumVI(),GL_UNSIGNED_SHORT,Model.VIBase());
61- }
62- return;
63- }
64-
65- // If some of the shaders are nonseparable, then the polygons have to be depth-sorted.
66- // The separable ones will also use that depth-sorting, out of coding convenience.
67- // OpenGL != PowerVR
68- // (which can store polygons and depth-sort them in its hardware)
69-
70- // Find the centroids:
71- size_t NumTriangles = Model.NumVI()/3;
72- IndexedCentroidDepths.resize(NumTriangles);
73-
74- GLushort *VIPtr = Model.VIBase();
75- for (unsigned short k=0; k<NumTriangles; k++)
76- {
77- GLfloat Sum[3] = {0, 0, 0};
78- for (int v=0; v<3; v++)
79- {
80- GLfloat *Pos = &Model.Positions[3*(*VIPtr)];
81- Sum[0] += Pos[0];
82- Sum[1] += Pos[1];
83- Sum[2] += Pos[2];
84- VIPtr++;
85- }
86- IndexedCentroidDepths[k].index = k;
87- IndexedCentroidDepths[k].depth =
88- Sum[0]*ViewDirection[0] + Sum[1]*ViewDirection[1] + Sum[2]*ViewDirection[2];
89- }
90-
91- // Sort!
92- std::sort(IndexedCentroidDepths.begin(), IndexedCentroidDepths.end());
93-
94- // Optimization: a single nonseparable shader can be rendered as if it was separable,
95- // though it must still be depth-sorted.
96- if (NumSeparableShaders == NumShaders - 1)
97- NumSeparableShaders++;
98-
99- for (int q=0; q<NumSeparableShaders; q++)
100- {
101- // Need to do this only once
102- if (q == 0)
103- {
104- SortedVertIndices.resize(Model.NumVI());
105- GLushort *DestTriangle = &SortedVertIndices[0];
106- for (size_t k=0; k<NumTriangles; k++)
107- {
108- GLushort *SourceTriangle = &Model.VertIndices[3*IndexedCentroidDepths[k].index];
109- // Copy-over unrolled for speed
110- *(DestTriangle++) = *(SourceTriangle++);
111- *(DestTriangle++) = *(SourceTriangle++);
112- *(DestTriangle++) = *(SourceTriangle++);
113- }
114- }
115-
116- // Separable-shader optimization: render in one swell foop
117- SetupRenderPass(Model,Shaders[q]);
118-
119- // Go!
120- glDrawElements(GL_TRIANGLES,(GLsizei)Model.NumVI(),GL_UNSIGNED_SHORT,&SortedVertIndices[0]);
121- }
122-
123- if (NumSeparableShaders < NumShaders)
124- {
125- // Multishader case: each triangle separately
126- for (size_t k=0; k<NumTriangles; k++)
127- {
128- GLushort *Triangle = &Model.VertIndices[3*IndexedCentroidDepths[k].index];
129- for (int q=NumSeparableShaders; q<NumShaders; q++)
130- {
131- SetupRenderPass(Model,Shaders[q]);
132- glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_SHORT,Triangle);
133- }
134- }
135- }
136-}
137-
138-
139-/* TODO: sRGB-correct model colors. This needs to be done in the loader. The
140- lighting colors are already sRGB-corrected. -SB */
141-void ModelRenderer::SetupRenderPass(Model3D& Model, ModelRenderShader& Shader)
142-{
143- assert(Shader.TextureCallback);
144-
145- // Do textured rendering
146- if (!Model.TxtrCoords.empty() && TEST_FLAG(Shader.Flags,Textured))
147- {
148- glEnable(GL_TEXTURE_2D);
149- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
150- glTexCoordPointer(2,GL_FLOAT,0,Model.TCBase());
151- }
152- else
153- {
154- glDisable(GL_TEXTURE_2D);
155- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
156- }
157-
158- // Check whether to use external lighting
159- if (Shader.LightingCallback && !Model.Normals.empty() && TEST_FLAG(Shader.Flags,ExtLight))
160- {
161- size_t NumVerts = Model.Positions.size()/3;
162- size_t NumCPlanes = TEST_FLAG(Shader.Flags,EL_SemiTpt) ? 4 : 3;
163- size_t NumCValues = NumCPlanes*NumVerts;
164- ExtLightColors.resize(NumCValues);
165-
166- Shader.LightingCallback(Shader.LightingCallbackData,
167- NumVerts, Model.NormBase(),Model.PosBase(),&ExtLightColors[0]);
168-
169- if (!Model.Colors.empty() && TEST_FLAG(Shader.Flags,Colored))
170- {
171- GLfloat *ExtColorPtr = &ExtLightColors[0];
172- GLfloat *ColorPtr = Model.ColBase();
173- if (NumCPlanes == 3)
174- {
175- for (size_t k=0; k<NumCValues; k++, ExtColorPtr++, ColorPtr++)
176- (*ExtColorPtr) *= (*ColorPtr);
177- }
178- else if (NumCPlanes == 4)
179- {
180- for (size_t k=0; k<NumVerts; k++)
181- {
182- for (int chn=0; chn<3; chn++)
183- {
184- (*ExtColorPtr) *= (*ColorPtr);
185- ExtColorPtr++, ColorPtr++;
186- }
187- // Nothing happens to the alpha channel
188- ExtColorPtr++;
189- }
190- }
191- }
192-
193- glEnableClientState(GL_COLOR_ARRAY);
194- glColorPointer((GLint)NumCPlanes,GL_FLOAT,0,&ExtLightColors[0]);
195- }
196- else
197- {
198- // Do colored rendering
199- if (!Model.Colors.empty() && TEST_FLAG(Shader.Flags,Colored))
200- {
201- // May want to recover the currently-set color and do the same kind
202- // of treatment as above
203- glEnableClientState(GL_COLOR_ARRAY);
204- glColorPointer(3,GL_FLOAT,0,Model.ColBase());
205- }
206- else
207- glDisableClientState(GL_COLOR_ARRAY);
208- }
209-
210- // Do whatever texture management is necessary
211- Shader.TextureCallback(Shader.TextureCallbackData);
212-}
213-
214-
215-void ModelRenderer::Clear()
216-{
217- IndexedCentroidDepths.clear();
218- SortedVertIndices.clear();
219- ExtLightColors.clear();
220-}
221-
222-#endif // def HAVE_OPENGL
1+/*
2+
3+ Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4+ and the "Aleph One" developers.
5+
6+ This program is free software; you can redistribute it and/or modify
7+ it under the terms of the GNU General Public License as published by
8+ the Free Software Foundation; either version 3 of the License, or
9+ (at your option) any later version.
10+
11+ This program is distributed in the hope that it will be useful,
12+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+ GNU General Public License for more details.
15+
16+ This license is contained in the file "COPYING",
17+ which is included with this source code; it is available online at
18+ http://www.gnu.org/licenses/gpl.html
19+
20+ Renders 3D-model objects;
21+
22+ Created by Loren Petrich, July 18, 2001
23+*/
24+
25+#include <string.h>
26+#include <stdlib.h>
27+
28+#include "cseries.h"
29+
30+#ifdef HAVE_OPENGL
31+
32+#ifdef __WIN32__
33+#include <windows.h>
34+#endif
35+
36+#include "ModelRenderer.h"
37+#include <algorithm>
38+
39+void ModelRenderer::Render(Model3D& Model, ModelRenderShader *Shaders, int NumShaders,
40+ int NumSeparableShaders, bool Use_Z_Buffer)
41+{
42+ if (NumShaders <= 0) return;
43+ if (!Shaders) return;
44+ if (Model.Positions.empty()) return;
45+ glEnableClientState(GL_VERTEX_ARRAY);
46+ glVertexPointer(3,GL_FLOAT,0,Model.PosBase());
47+
48+ // Effective number of separable shaders when the Z-buffer is absent: none
49+ // (the Z-buffer enables separability).
50+ // Editing this arg is OK since it was called by value.
51+ if (!Use_Z_Buffer) NumSeparableShaders = 0;
52+
53+ // Optimization: skip depth sorting, render, and quit
54+ // if all shaders are separable.
55+ if (NumSeparableShaders >= NumShaders)
56+ {
57+ for (int q=0; q<NumShaders; q++)
58+ {
59+ SetupRenderPass(Model,Shaders[q]);
60+ glDrawElements(GL_TRIANGLES,(GLsizei)Model.NumVI(),GL_UNSIGNED_SHORT,Model.VIBase());
61+ }
62+ return;
63+ }
64+
65+ // If some of the shaders are nonseparable, then the polygons have to be depth-sorted.
66+ // The separable ones will also use that depth-sorting, out of coding convenience.
67+ // OpenGL != PowerVR
68+ // (which can store polygons and depth-sort them in its hardware)
69+
70+ // Find the centroids:
71+ size_t NumTriangles = Model.NumVI()/3;
72+ IndexedCentroidDepths.resize(NumTriangles);
73+
74+ GLushort *VIPtr = Model.VIBase();
75+ for (unsigned short k=0; k<NumTriangles; k++)
76+ {
77+ GLfloat Sum[3] = {0, 0, 0};
78+ for (int v=0; v<3; v++)
79+ {
80+ GLfloat *Pos = &Model.Positions[3*(*VIPtr)];
81+ Sum[0] += Pos[0];
82+ Sum[1] += Pos[1];
83+ Sum[2] += Pos[2];
84+ VIPtr++;
85+ }
86+ IndexedCentroidDepths[k].index = k;
87+ IndexedCentroidDepths[k].depth =
88+ Sum[0]*ViewDirection[0] + Sum[1]*ViewDirection[1] + Sum[2]*ViewDirection[2];
89+ }
90+
91+ // Sort!
92+ std::sort(IndexedCentroidDepths.begin(), IndexedCentroidDepths.end());
93+
94+ // Optimization: a single nonseparable shader can be rendered as if it was separable,
95+ // though it must still be depth-sorted.
96+ if (NumSeparableShaders == NumShaders - 1)
97+ NumSeparableShaders++;
98+
99+ for (int q=0; q<NumSeparableShaders; q++)
100+ {
101+ // Need to do this only once
102+ if (q == 0)
103+ {
104+ SortedVertIndices.resize(Model.NumVI());
105+ GLushort *DestTriangle = &SortedVertIndices[0];
106+ for (size_t k=0; k<NumTriangles; k++)
107+ {
108+ GLushort *SourceTriangle = &Model.VertIndices[3*IndexedCentroidDepths[k].index];
109+ // Copy-over unrolled for speed
110+ *(DestTriangle++) = *(SourceTriangle++);
111+ *(DestTriangle++) = *(SourceTriangle++);
112+ *(DestTriangle++) = *(SourceTriangle++);
113+ }
114+ }
115+
116+ // Separable-shader optimization: render in one swell foop
117+ SetupRenderPass(Model,Shaders[q]);
118+
119+ // Go!
120+ glDrawElements(GL_TRIANGLES,(GLsizei)Model.NumVI(),GL_UNSIGNED_SHORT,&SortedVertIndices[0]);
121+ }
122+
123+ if (NumSeparableShaders < NumShaders)
124+ {
125+ // Multishader case: each triangle separately
126+ for (size_t k=0; k<NumTriangles; k++)
127+ {
128+ GLushort *Triangle = &Model.VertIndices[3*IndexedCentroidDepths[k].index];
129+ for (int q=NumSeparableShaders; q<NumShaders; q++)
130+ {
131+ SetupRenderPass(Model,Shaders[q]);
132+ glDrawElements(GL_TRIANGLES,3,GL_UNSIGNED_SHORT,Triangle);
133+ }
134+ }
135+ }
136+}
137+
138+
139+/* TODO: sRGB-correct model colors. This needs to be done in the loader. The
140+ lighting colors are already sRGB-corrected. -SB */
141+void ModelRenderer::SetupRenderPass(Model3D& Model, ModelRenderShader& Shader)
142+{
143+ assert(Shader.TextureCallback);
144+
145+ // Do textured rendering
146+ if (!Model.TxtrCoords.empty() && TEST_FLAG(Shader.Flags,Textured))
147+ {
148+ glEnable(GL_TEXTURE_2D);
149+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
150+ glTexCoordPointer(2,GL_FLOAT,0,Model.TCBase());
151+ }
152+ else
153+ {
154+ glDisable(GL_TEXTURE_2D);
155+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
156+ }
157+
158+ // Check whether to use external lighting
159+ if (Shader.LightingCallback && !Model.Normals.empty() && TEST_FLAG(Shader.Flags,ExtLight))
160+ {
161+ size_t NumVerts = Model.Positions.size()/3;
162+ size_t NumCPlanes = TEST_FLAG(Shader.Flags,EL_SemiTpt) ? 4 : 3;
163+ size_t NumCValues = NumCPlanes*NumVerts;
164+ ExtLightColors.resize(NumCValues);
165+
166+ Shader.LightingCallback(Shader.LightingCallbackData,
167+ NumVerts, Model.NormBase(),Model.PosBase(),&ExtLightColors[0]);
168+
169+ if (!Model.Colors.empty() && TEST_FLAG(Shader.Flags,Colored))
170+ {
171+ GLfloat *ExtColorPtr = &ExtLightColors[0];
172+ GLfloat *ColorPtr = Model.ColBase();
173+ if (NumCPlanes == 3)
174+ {
175+ for (size_t k=0; k<NumCValues; k++, ExtColorPtr++, ColorPtr++)
176+ (*ExtColorPtr) *= (*ColorPtr);
177+ }
178+ else if (NumCPlanes == 4)
179+ {
180+ for (size_t k=0; k<NumVerts; k++)
181+ {
182+ for (int chn=0; chn<3; chn++)
183+ {
184+ (*ExtColorPtr) *= (*ColorPtr);
185+ ExtColorPtr++, ColorPtr++;
186+ }
187+ // Nothing happens to the alpha channel
188+ ExtColorPtr++;
189+ }
190+ }
191+ }
192+
193+ glEnableClientState(GL_COLOR_ARRAY);
194+ glColorPointer((GLint)NumCPlanes,GL_FLOAT,0,&ExtLightColors[0]);
195+ }
196+ else
197+ {
198+ // Do colored rendering
199+ if (!Model.Colors.empty() && TEST_FLAG(Shader.Flags,Colored))
200+ {
201+ // May want to recover the currently-set color and do the same kind
202+ // of treatment as above
203+ glEnableClientState(GL_COLOR_ARRAY);
204+ glColorPointer(3,GL_FLOAT,0,Model.ColBase());
205+ }
206+ else
207+ glDisableClientState(GL_COLOR_ARRAY);
208+ }
209+
210+ // Do whatever texture management is necessary
211+ Shader.TextureCallback(Shader.TextureCallbackData);
212+}
213+
214+
215+void ModelRenderer::Clear()
216+{
217+ IndexedCentroidDepths.clear();
218+ SortedVertIndices.clear();
219+ ExtLightColors.clear();
220+}
221+
222+#endif // def HAVE_OPENGL
--- marathon/trunk/Source_Files/ModelView/WavefrontLoader.h (revision 526)
+++ marathon/trunk/Source_Files/ModelView/WavefrontLoader.h (revision 527)
@@ -1,38 +1,38 @@
1-/*
2-
3- Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4- and the "Aleph One" developers.
5-
6- This program is free software; you can redistribute it and/or modify
7- it under the terms of the GNU General Public License as published by
8- the Free Software Foundation; either version 3 of the License, or
9- (at your option) any later version.
10-
11- This program is distributed in the hope that it will be useful,
12- but WITHOUT ANY WARRANTY; without even the implied warranty of
13- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14- GNU General Public License for more details.
15-
16- This license is contained in the file "COPYING",
17- which is included with this source code; it is available online at
18- http://www.gnu.org/licenses/gpl.html
19-
20- Alias|Wavefront Object Loader
21-
22- By Loren Petrich, June 16, 2001
23-*/
24-#ifndef WAVEFRONT_LOADER
25-#define WAVEFRONT_LOADER
26-
27-#include <stdio.h>
28-#include "Model3D.h"
29-#include "FileHandler.h" <