改行コード修正
@@ -0,0 +1,1063 @@ | ||
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 | + Model-Viewer Test Shell, by Loren Petrich, June 10, 2001 | |
21 | + This uses GLUT to create an OpenGL context for displaying a 3D model, | |
22 | + in order to test model-reading code | |
23 | +*/ | |
24 | + | |
25 | +#include <math.h> | |
26 | +#include <string.h> | |
27 | +#include <ctype.h> | |
28 | +#if defined (__APPLE__) && defined (__MACH__) | |
29 | +# include <OpenGL/gl.h> | |
30 | +# include <OpenGL/glu.h> | |
31 | +# include <OpenGL/glut.h> | |
32 | +# include <Movies.h> | |
33 | +#elif defined mac | |
34 | +# include <gl.h> | |
35 | +# include <glu.h> | |
36 | +# include <glut.h> | |
37 | +# include <Movies.h> | |
38 | +#else | |
39 | +# include <GL/gl.h> | |
40 | +# include <GL/glu.h> | |
41 | +# include <GL/glut.h> | |
42 | +#endif | |
43 | +#include "cseries.h" | |
44 | +#include "FileHandler.h" | |
45 | +#include "ImageLoader.h" | |
46 | +#include "ModelRenderer.h" | |
47 | +#include "Dim3_Loader.h" | |
48 | +#include "QD3D_Loader.h" | |
49 | +#include "StudioLoader.h" | |
50 | +#include "WavefrontLoader.h" | |
51 | + | |
52 | + | |
53 | +// Test for Macintosh-specific stuff: Quicktime (for loading images) | |
54 | +// and Navigation Services (fancy dialog boxes) | |
55 | + | |
56 | +bool HasQuicktime = false; | |
57 | +bool HasNavServices = false; | |
58 | +void InitMacServices() | |
59 | +{ | |
60 | + // Cribbed from shell_macintosh.cpp | |
61 | + long response; | |
62 | + OSErr error; | |
63 | + | |
64 | + error= Gestalt(gestaltQuickTime, &response); | |
65 | + if(!error) | |
66 | + { | |
67 | + /* No error finding QuickTime. Check for ICM so we can use Standard Preview */ | |
68 | + error= Gestalt(gestaltCompressionMgr, &response); | |
69 | + if(!error) | |
70 | + { | |
71 | + // Now initialize Quicktime's Movie Toolbox, | |
72 | + // since the soundtrack player will need it | |
73 | + error = EnterMovies(); | |
74 | + if (!error) { | |
75 | + HasQuicktime = true; | |
76 | + } | |
77 | + } | |
78 | + } | |
79 | + | |
80 | + if ((void*)NavLoad != (void*)kUnresolvedCFragSymbolAddress) | |
81 | + { | |
82 | + NavLoad(); | |
83 | + HasNavServices = true; | |
84 | + } | |
85 | +} | |
86 | + | |
87 | +bool machine_has_quicktime() | |
88 | +{ | |
89 | + return HasQuicktime; | |
90 | +} | |
91 | + | |
92 | +bool machine_has_nav_services() | |
93 | +{ | |
94 | + return HasNavServices; | |
95 | +} | |
96 | + | |
97 | +void global_idle_proc() {} // Do nothing | |
98 | + | |
99 | +// GLUT window ID in case there is more than one GLUT window; | |
100 | +// however, Apple GLUT does not like more than one such window, | |
101 | +// so this program will be one-window-only for now. | |
102 | +int MainWindowID; | |
103 | + | |
104 | +void ResizeMainWindow(int _Width, int _Height); | |
105 | + | |
106 | + | |
107 | +// Model and skin objects; | |
108 | +// a model is saved in case it is edited, as when vertices are split | |
109 | +Model3D Model, SavedModel; | |
110 | +ImageDescriptor Image; | |
111 | +bool ImageSemitransparent = false; | |
112 | +ModelRenderer Renderer; | |
113 | +ModelRenderShader Shaders[2]; | |
114 | + | |
115 | +// For loading models and skins | |
116 | +enum { | |
117 | + ModelWavefront = 1, | |
118 | + ModelStudio, | |
119 | + Model_QD3D, | |
120 | + Model_Dim3_First, | |
121 | + Model_Dim3_Rest | |
122 | +}; | |
123 | +const int LoadSkinItem = 1; | |
124 | + | |
125 | +// When the model or the skin is changed, then do this | |
126 | +void InvalidateTexture(); | |
127 | + | |
128 | +void LoadModelAction(int ModelType) | |
129 | +{ | |
130 | + // Get the model file | |
131 | + FileSpecifier File; | |
132 | + bool Success = false; | |
133 | + | |
134 | + // Specialize the dialog for what kind of model file | |
135 | + switch(ModelType) | |
136 | + { | |
137 | + case ModelWavefront: | |
138 | + set_typecode(_typecode_patch,'TEXT'); | |
139 | + if (File.ReadDialog(_typecode_patch,"Model Type: Alias|Wavefront")) | |
140 | + Success = LoadModel_Wavefront(File, Model); | |
141 | + break; | |
142 | + case ModelStudio: | |
143 | + // No canonical MacOS assignment of this model type | |
144 | + if (File.ReadDialog(_typecode_unknown,"Model Type: 3D Studio Max")) | |
145 | + Success = LoadModel_Studio(File, Model); | |
146 | + break; | |
147 | + case Model_QD3D: | |
148 | + set_typecode(_typecode_patch,'3DMF'); | |
149 | + if (File.ReadDialog(_typecode_patch,"Model Type: QuickDraw 3D")) | |
150 | + Success = LoadModel_QD3D(File, Model); | |
151 | + break; | |
152 | + case Model_Dim3_First: | |
153 | + set_typecode(_typecode_patch,'TEXT'); | |
154 | + if (File.ReadDialog(_typecode_patch,"Model Type: Dim3 (First Part)")) | |
155 | + Success = LoadModel_Dim3(File, Model, LoadModelDim3_First); | |
156 | + break; | |
157 | + case Model_Dim3_Rest: | |
158 | + set_typecode(_typecode_patch,'TEXT'); | |
159 | + if (File.ReadDialog(_typecode_patch,"Model Type: Dim3 (Later Parts)")) | |
160 | + Success = LoadModel_Dim3(File, Model,LoadModelDim3_Rest); | |
161 | + break; | |
162 | + } | |
163 | + | |
164 | + if (!Success) Model.Clear(); | |
165 | + | |
166 | + glutSetWindow(MainWindowID); | |
167 | + char Name[256]; | |
168 | + File.GetName(Name); | |
169 | + glutSetWindowTitle(Name); | |
170 | + | |
171 | + // Neutral positions will always be initially present. | |
172 | + // Assume that a boned model already has a bounding box; | |
173 | + // most boneless (static) models do not have such bounding boxes. | |
174 | + if (Model.Bones.empty()) | |
175 | + Model.FindBoundingBox(); | |
176 | + | |
177 | + // Just in case they are off... | |
178 | + Model.NormalizeNormals(); | |
179 | + | |
180 | + ResizeMainWindow(glutGet(GLUT_WINDOW_WIDTH),glutGet(GLUT_WINDOW_HEIGHT)); | |
181 | + | |
182 | + // Dump stats | |
183 | + printf("Vertex Positions: %d\n",Model.Positions.size()/3); | |
184 | + printf("Txtr Coords: %d\n",Model.TxtrCoords.size()/2); | |
185 | + printf("Normals: %d\n",Model.Normals.size()/3); | |
186 | + printf("Colors: %d\n",Model.Colors.size()/3); | |
187 | + printf("Triangles: %d\n",Model.VertIndices.size()/3); | |
188 | + printf("Bounding Box\n"); | |
189 | + for (int c=0; c<3; c++) | |
190 | + printf("%9f %9f\n",Model.BoundingBox[0][c],Model.BoundingBox[1][c]); | |
191 | + | |
192 | + SavedModel = Model; | |
193 | + | |
194 | + // Invalidate the current texture, so it will be reloaded for the model | |
195 | + InvalidateTexture(); | |
196 | +} | |
197 | + | |
198 | +// Z-Buffering | |
199 | +bool Use_Z_Buffer = true; | |
200 | + | |
201 | +// Bounding box? | |
202 | +bool Show_Bounding_Box = false; | |
203 | + | |
204 | +// Texture management | |
205 | +bool TxtrIsPresent = false; | |
206 | +GLuint TxtrID; | |
207 | + | |
208 | +// External lighting? | |
209 | +bool Use_Light = false; | |
210 | + | |
211 | +// Model overall transform? | |
212 | +bool Use_Model_Transform = false; | |
213 | + | |
214 | +// How many rendering passes | |
215 | +int NumRenderPasses = 1; | |
216 | + | |
217 | +// What to render: neutral, frames, or sequences? And which of each? | |
218 | +enum { | |
219 | + ShowAnim_Neutral, | |
220 | + ShowAnim_Frames, | |
221 | + ShowAnim_Sequences | |
222 | +} ShowAnim = ShowAnim_Neutral; | |
223 | + | |
224 | +int ThisFrame = 0; | |
225 | +int ThisSeq = 0; | |
226 | + | |
227 | +// Also the mixture between the current and next frames | |
228 | +float MixFrac = 0; | |
229 | + | |
230 | + | |
231 | +void InvalidateTexture() | |
232 | +{ | |
233 | + if (TxtrIsPresent) | |
234 | + { | |
235 | + glDeleteTextures(1,&TxtrID); | |
236 | + TxtrIsPresent = false; | |
237 | + } | |
238 | +} | |
239 | + | |
240 | + | |
241 | +void LoadSkinAction(int SkinType) | |
242 | +{ | |
243 | + // Get the skin file | |
244 | + FileSpecifier File; | |
245 | + | |
246 | + bool Success = false; | |
247 | + if (File.ReadDialog(_typecode_unknown,"Skin Image")) | |
248 | + Success = LoadImageFromFile(Image,File,SkinType); | |
249 | + if (!Success) return; | |
250 | + | |
251 | + const int BufferSize = 256; | |
252 | + char Buffer[BufferSize]; | |
253 | + File.GetName(Buffer); | |
254 | + printf("Read skin file %s\n",Buffer); | |
255 | + switch(SkinType) | |
256 | + { | |
257 | + case ImageLoader_Colors: | |
258 | + ImageSemitransparent = false; // Initially, no nonzero transparency | |
259 | + printf("For colors\n"); | |
260 | + break; | |
261 | + | |
262 | + case ImageLoader_Opacity: | |
263 | + ImageSemitransparent = true; // Nonzero transparency | |
264 | + printf("For opacity\n"); | |
265 | + break; | |
266 | + } | |
267 | + | |
268 | + // Invalidate the current texture | |
269 | + InvalidateTexture(); | |
270 | + | |
271 | + glutPostRedisplay(); | |
272 | +} | |
273 | + | |
274 | + | |
275 | +float VertexSplitThreshold = 0; | |
276 | + | |
277 | + | |
278 | +void NormalTypeAction(int NormAction) | |
279 | +{ | |
280 | + Model = SavedModel; | |
281 | + Model.AdjustNormals(NormAction,VertexSplitThreshold); | |
282 | + glutPostRedisplay(); | |
283 | +} | |
284 | + | |
285 | + | |
286 | +void ShaderCallback(void *Data) | |
287 | +{ | |
288 | + (void)(Data); | |
289 | + if (!Image.IsPresent()) return; | |
290 | + | |
291 | + // Setting up the textures each time | |
292 | + if (TxtrIsPresent) | |
293 | + glBindTexture(GL_TEXTURE_2D,TxtrID); | |
294 | + else | |
295 | + { | |
296 | + // Load the texture | |
297 | + glGenTextures(1,&TxtrID); | |
298 | + glBindTexture(GL_TEXTURE_2D,TxtrID); | |
299 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
300 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | |
301 | + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |
302 | + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, | |
303 | + Image.GetWidth(), Image.GetHeight(), | |
304 | + GL_RGBA, GL_UNSIGNED_BYTE, | |
305 | + Image.GetPixelBasePtr()); | |
306 | + TxtrIsPresent = true; | |
307 | + } | |
308 | +} | |
309 | + | |
310 | + | |
311 | +// Needed for external lighting; | |
312 | +// the first index is the color channel, | |
313 | +// while the second index is in two parts, | |
314 | +// the first three (0-2) multipled by the normal vector in dot-product fasion | |
315 | +// and the fourth (3) added | |
316 | + | |
317 | +GLfloat ExternalLight[3][4]; | |
318 | + | |
319 | + | |
320 | +void LightingCallback(void *Data, unsigned long NumVerts, GLfloat *Normals, GLfloat *Positions, GLfloat *Colors) | |
321 | +{ | |
322 | + (void)(Data); | |
323 | + | |
324 | +// Attempting to keep CB's code on hand | |
325 | +#if 0 | |
326 | + | |
327 | + // Register usage: | |
328 | + // mm0/mm1: ExternalLight[0] (4 components) | |
329 | + // mm2/mm3: ExternalLight[1] (4 components) | |
330 | + // mm4/mm5: ExternalLight[2] (4 components) | |
331 | + // mm6: Normal[0]/Normal[1] | |
332 | + // mm7: Normal[2]/1.0 | |
333 | + | |
334 | + GLfloat tmp[2] = {0.0, 1.0}; | |
335 | + __asm__ __volatile__(" | |
336 | + femms\n | |
337 | + movq 0x00(%3),%%mm0\n | |
338 | + movq 0x08(%3),%%mm1\n | |
339 | + movq 0x10(%3),%%mm2\n | |
340 | + movq 0x18(%3),%%mm3\n | |
341 | + movq 0x20(%3),%%mm4\n | |
342 | + movq 0x28(%3),%%mm5\n | |
343 | + movl 8(%0),%%eax\n | |
344 | + movl %%eax,(%4)\n | |
345 | + 0:\n | |
346 | + prefetch 192(%0)\n | |
347 | + movq (%0),%%mm6\n | |
348 | + movq (%4),%%mm7\n | |
349 | + pfmul %%mm0,%%mm6\n | |
350 | + pfmul %%mm1,%%mm7\n | |
351 | + pfacc %%mm6,%%mm7\n | |
352 | + pfacc %%mm6,%%mm7\n | |
353 | + movd %%mm7,(%1)\n | |
354 | + movq (%0),%%mm6\n | |
355 | + movq (%4),%%mm7\n | |
356 | + pfmul %%mm2,%%mm6\n | |
357 | + pfmul %%mm3,%%mm7\n | |
358 | + pfacc %%mm6,%%mm7\n | |
359 | + pfacc %%mm6,%%mm7\n | |
360 | + movd %%mm7,4(%1)\n | |
361 | + movq (%0),%%mm6\n | |
362 | + movq (%4),%%mm7\n | |
363 | + pfmul %%mm4,%%mm6\n | |
364 | + pfmul %%mm5,%%mm7\n | |
365 | + pfacc %%mm6,%%mm7\n | |
366 | + pfacc %%mm6,%%mm7\n | |
367 | + movd %%mm7,8(%1)\n | |
368 | + \n | |
369 | + add $0x0c,%0\n | |
370 | + movl 8(%0),%%eax\n | |
371 | + movl %%eax,(%4)\n | |
372 | + add $0x0c,%1\n | |
373 | + dec %2\n | |
374 | + jne 0b\n | |
375 | + femms\n" | |
376 | + : | |
377 | + : "g" (Normals), "g" (ColorPtr), "g" (NumVerts), "g" (ExternalLight), "g" (&tmp) | |
378 | + : "eax", "memory" | |
379 | + ); | |
380 | + | |
381 | +#else | |
382 | + | |
383 | + GLfloat *el0 = ExternalLight[0], *el1 = ExternalLight[1], *el2 = ExternalLight[2]; | |
384 | + for (int k=0; k<NumVerts; k++) | |
385 | + { | |
386 | + GLfloat N0 = *(Normals++); | |
387 | + GLfloat N1 = *(Normals++); | |
388 | + GLfloat N2 = *(Normals++); | |
389 | + *(Colors++) = | |
390 | + el0[0]*N0 + | |
391 | + el0[1]*N1 + | |
392 | + el0[2]*N2 + | |
393 | + el0[3]; | |
394 | + *(Colors++) = | |
395 | + el1[0]*N0 + | |
396 | + el1[1]*N1 + | |
397 | + el1[2]*N2 + | |
398 | + el1[3]; | |
399 | + *(Colors++) = | |
400 | + el2[0]*N0 + | |
401 | + el2[1]*N1 + | |
402 | + el2[2]*N2 + | |
403 | + el2[3]; | |
404 | + } | |
405 | + | |
406 | +#endif | |
407 | +} | |
408 | + | |
409 | + | |
410 | +// Callback for drawing that window | |
411 | +void DrawMainWindow() | |
412 | +{ | |
413 | + // No smearing allowed! | |
414 | + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
415 | + | |
416 | + // Render the model | |
417 | + if (!Model.Positions.empty()) | |
418 | + { | |
419 | + // In the absence of a skin... | |
420 | + const GLfloat FalseColors[12][3] = | |
421 | + { | |
422 | + { 1, 0, 0}, | |
423 | + { 1, 0.5, 0}, | |
424 | + { 1, 1, 0}, | |
425 | + | |
426 | + {0.5, 1, 0}, | |
427 | + { 0, 1, 0}, | |
428 | + { 0, 1, 0.5}, | |
429 | + | |
430 | + { 0, 1, 1}, | |
431 | + { 0, 0.5, 1}, | |
432 | + { 0, 0, 1}, | |
433 | + | |
434 | + {0.5, 0, 1}, | |
435 | + { 1, 0, 1}, | |
436 | + { 1, 0, 0.5} | |
437 | + }; | |
438 | + | |
439 | + // Replace the colors if there isn't an already-present color array | |
440 | + int NumColors = Model.Positions.size()/3; | |
441 | + if (Model.Colors.size() != 3*NumColors) | |
442 | + { | |
443 | + Model.Colors.resize(3*NumColors); | |
444 | + for (int k=0; k<NumColors; k++) | |
445 | + { | |
446 | + const GLfloat *FalseColor = FalseColors[k%12]; | |
447 | + GLfloat *Color = &Model.Colors[3*k]; | |
448 | + for (int c=0; c<3; c++) | |
449 | + Color[c] = FalseColor[c]; | |
450 | + } | |
451 | + } | |
452 | + | |
453 | + // Extract the view direction from the matrix | |
454 | + // Since it is pure rotation, transpose = inverse | |
455 | + GLfloat ModelViewMatrix[16]; | |
456 | + glGetFloatv(GL_MODELVIEW_MATRIX,ModelViewMatrix); | |
457 | + for (int k=0; k<3; k++) | |
458 | + Renderer.ViewDirection[k] = - ModelViewMatrix[4*k + 2]; | |
459 | + | |
460 | + // Compose a set of external lights: | |
461 | + for (int c=0; c<3; c++) | |
462 | + { | |
463 | + for (int k=0; k<3; k++) | |
464 | + ExternalLight[c][k] = 0.5*ModelViewMatrix[4*k + c]; | |
465 | + ExternalLight[c][3] = 0; | |
466 | + } | |
467 | + | |
468 | + if (Use_Z_Buffer) | |
469 | + glEnable(GL_DEPTH_TEST); | |
470 | + else | |
471 | + glDisable(GL_DEPTH_TEST); | |
472 | + glDepthFunc(GL_LEQUAL); | |
473 | + // glEnable(GL_CULL_FACE); | |
474 | + // glCullFace(GL_BACK); | |
475 | + // glFrontFace(GL_CW); | |
476 | + | |
477 | + int NextFrame = 0; | |
478 | + switch(ShowAnim) | |
479 | + { | |
480 | + case ShowAnim_Neutral: | |
481 | + Model.FindPositions_Neutral(Use_Model_Transform); | |
482 | + break; | |
483 | + | |
484 | + case ShowAnim_Frames: | |
485 | + if (MixFrac != 0) | |
486 | + { | |
487 | + if ((NextFrame = ThisFrame + 1) >= Model.TrueNumFrames()) | |
488 | + NextFrame = 0; | |
489 | + } | |
490 | + Model.FindPositions_Frame(Use_Model_Transform,ThisFrame,MixFrac,NextFrame); | |
491 | + break; | |
492 | + | |
493 | + case ShowAnim_Sequences: | |
494 | + if (MixFrac != 0) | |
495 | + { | |
496 | + if ((NextFrame = ThisFrame + 1) >= Model.NumSeqFrames(ThisSeq)) | |
497 | + NextFrame = 0; | |
498 | + } | |
499 | + Model.FindPositions_Sequence(Use_Model_Transform,ThisSeq,ThisFrame,MixFrac,NextFrame); | |
500 | + break; | |
501 | + } | |
502 | + | |
503 | + // Draw the bounding box | |
504 | + const GLfloat EdgeColor[3] = {1,1,0}; | |
505 | + const GLfloat DiagColor[3] = {0,1,1}; | |
506 | + if (Show_Bounding_Box) Model.RenderBoundingBox(EdgeColor,DiagColor); | |
507 | + glColor3f(1,1,1); | |
508 | + | |
509 | + glEnable(GL_BLEND); | |
510 | + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); | |
511 | + unsigned int Flags = 0; | |
512 | + SET_FLAG(Flags,ModelRenderer::Textured,TxtrIsPresent); | |
513 | + SET_FLAG(Flags,ModelRenderer::Colored,!TxtrIsPresent); | |
514 | + SET_FLAG(Flags,ModelRenderer::ExtLight,Use_Light); | |
515 | + Shaders[1].Flags = Shaders[0].Flags = Flags; | |
516 | + int NumOpaqueRenderPasses = ImageSemitransparent ? NumRenderPasses : 0; | |
517 | + Renderer.Render(Model,Shaders,NumRenderPasses,NumOpaqueRenderPasses,Use_Z_Buffer); | |
518 | + } | |
519 | + | |
520 | + // All done -- ready to show | |
521 | + glutSwapBuffers(); | |
522 | +} | |
523 | + | |
524 | + | |
525 | +void ResizeMainWindow(int _Width, int _Height) | |
526 | +{ | |
527 | + if (!Model.Positions.empty()) | |
528 | + { | |
529 | + // For getting the aspect ratio straight | |
530 | + int MinDim = MAX(MIN(_Width,_Height),1); | |
531 | + | |
532 | + // Bounding-box "radius" | |
533 | + GLfloat BBRadius = 0; | |
534 | + for (int m=0; m<3; m++) | |
535 | + { | |
536 | + | |
537 | + GLfloat Dist = MAX(-Model.BoundingBox[0][m],Model.BoundingBox[1][m]); | |
538 | + BBRadius += Dist*Dist; | |
539 | + } | |
540 | + BBRadius = sqrt(BBRadius); | |
541 | + | |
542 | + GLfloat XDim = BBRadius*_Width/MinDim; | |
543 | + GLfloat YDim = BBRadius*_Height/MinDim; | |
544 | + GLfloat ZDim = 2*BBRadius; | |
545 | + | |
546 | + glMatrixMode(GL_PROJECTION); | |
547 | + glLoadIdentity(); | |
548 | + glOrtho(-XDim,XDim,-YDim,YDim,-ZDim,ZDim); | |
549 | + glutPostRedisplay(); | |
550 | + // glTranslatef(0.6*BBRadius,0.3*BBRadius,0); | |
551 | + } | |
552 | +} | |
553 | + | |
554 | +// Perform a "view side" rotate of the modelview matrix; | |
555 | +// rotations are normally applied to the matrix's "model side" | |
556 | +static void ViewSideRotate(GLfloat Angle, GLfloat X, GLfloat Y, GLfloat Z) | |
557 | +{ | |
558 | + GLfloat Matrix[16]; | |
559 | + glMatrixMode(GL_MODELVIEW); | |
560 | + glGetFloatv(GL_MODELVIEW_MATRIX,Matrix); | |
561 | + glLoadIdentity(); | |
562 | + glRotatef(Angle,X,Y,Z); | |
563 | + glMultMatrixf(Matrix); | |
564 | +} | |
565 | + | |
566 | +// Convenient standard value: 1/4 of a right angel | |
567 | +const GLfloat RotationAngle = 22.5; | |
568 | + | |
569 | +const char ESCAPE = 0x1b; | |
570 | + | |
571 | +void KeyInMainWindow(unsigned char key, int x, int y) | |
572 | +{ | |
573 | + | |
574 | + key = toupper(key); | |
575 | + | |
576 | + switch(key) | |
577 | + { | |
578 | + case '6': | |
579 | + ViewSideRotate(RotationAngle,0,1,0); | |
580 | + glutPostRedisplay(); | |
581 | + break; | |
582 | + | |
583 | + case '4': | |
584 | + ViewSideRotate(-RotationAngle,0,1,0); | |
585 | + glutPostRedisplay(); | |
586 | + break; | |
587 | + | |
588 | + case '8': | |
589 | + ViewSideRotate(RotationAngle,1,0,0); | |
590 | + glutPostRedisplay(); | |
591 | + break; | |
592 | + | |
593 | + case '2': | |
594 | + ViewSideRotate(-RotationAngle,1,0,0); | |
595 | + glutPostRedisplay(); | |
596 | + break; | |
597 | + | |
598 | + case '9': | |
599 | + ViewSideRotate(RotationAngle,0,0,-1); | |
600 | + glutPostRedisplay(); | |
601 | + break; | |
602 | + | |
603 | + case '7': | |
604 | + ViewSideRotate(-RotationAngle,0,0,-1); | |
605 | + glutPostRedisplay(); | |
606 | + break; | |
607 | + | |
608 | + case '0': | |
609 | + glMatrixMode(GL_MODELVIEW); | |
610 | + glLoadIdentity(); | |
611 | + glutPostRedisplay(); | |
612 | + break; | |
613 | + | |
614 | + case 'Z': | |
615 | + Use_Z_Buffer = !Use_Z_Buffer; | |
616 | + if (Use_Z_Buffer) | |
617 | + printf("Z-Buffer On\n"); | |
618 | + else | |
619 | + printf("Z-Buffer Off\n"); | |
620 | + glutPostRedisplay(); | |
621 | + break; | |
622 | + | |
623 | + case 'B': | |
624 | + Show_Bounding_Box = !Show_Bounding_Box; | |
625 | + if (Show_Bounding_Box) | |
626 | + printf("Bounding Box On\n"); | |
627 | + else | |
628 | + printf("Bounding Box Off\n"); | |
629 | + glutPostRedisplay(); | |
630 | + break; | |
631 | + | |
632 | + case 'S': | |
633 | + if (NumRenderPasses == 1) | |
634 | + NumRenderPasses = 2; | |
635 | + else | |
636 | + NumRenderPasses = 1; | |
637 | + printf("Render passes: %d\n",NumRenderPasses); | |
638 | + glutPostRedisplay(); | |
639 | + break; | |
640 | + | |
641 | + case 'L': | |
642 | + Use_Light = !Use_Light; | |
643 | + if (Use_Light) | |
644 | + printf("Light On\n"); | |
645 | + else | |
646 | + printf("Light Off\n"); | |
647 | + glutPostRedisplay(); | |
648 | + break; | |
649 | + | |
650 | + case 'T': | |
651 | + Use_Model_Transform = !Use_Model_Transform; | |
652 | + if (Use_Model_Transform) | |
653 | + printf("Model Transform On\n"); | |
654 | + else | |
655 | + printf("Model Transform Off\n"); | |
656 | + glutPostRedisplay(); | |
657 | + break; | |
658 | + | |
659 | + case 'P': | |
660 | + switch(ShowAnim) | |
661 | + { | |
662 | + case ShowAnim_Neutral: | |
663 | + ShowAnim = ShowAnim_Frames; | |
664 | + if (Model.Frames.empty()) | |
665 | + ShowAnim = ShowAnim_Neutral; | |
666 | + break; | |
667 | + | |
668 | + case ShowAnim_Frames: | |
669 | + ShowAnim = ShowAnim_Sequences; | |
670 | + if (Model.Frames.empty()) | |
671 | + ShowAnim = ShowAnim_Neutral; | |
672 | + else if (Model.SeqFrames.empty()) | |
673 | + ShowAnim = ShowAnim_Neutral; | |
674 | + break; | |
675 | + | |
676 | + case ShowAnim_Sequences: | |
677 | + ShowAnim = ShowAnim_Neutral; | |
678 | + break; | |
679 | + } | |
680 | + | |
681 | + switch(ShowAnim) | |
682 | + { | |
683 | + case ShowAnim_Neutral: | |
684 | + printf("Neutral\n"); | |
685 | + break; | |
686 | + case ShowAnim_Frames: | |
687 | + ThisFrame = 0; | |
688 | + printf("Frame %d\n",ThisFrame); | |
689 | + break; | |
690 | + case ShowAnim_Sequences: | |
691 | + ThisSeq = 0; | |
692 | + ThisFrame = 0; | |
693 | + printf("Sequence %d %d\n",ThisSeq,ThisFrame); | |
694 | + break; | |
695 | + } | |
696 | + | |
697 | + glutPostRedisplay(); | |
698 | + break; | |
699 | + | |
700 | + case 'M': | |
701 | + if ((MixFrac += 0.25) > 1) MixFrac = 0; | |
702 | + break; | |
703 | + | |
704 | + case '[': | |
705 | + case '{': | |
706 | + if (--ThisSeq < 0) | |
707 | + ThisSeq = Model.TrueNumSeqs() - 1; | |
708 | + ThisFrame = 0; | |
709 | + printf("Sequence %d %d\n",ThisSeq,ThisFrame); | |
710 | + glutPostRedisplay(); | |
711 | + break; | |
712 | + | |
713 | + case ']': | |
714 | + case '}': | |
715 | + if (++ThisSeq >= Model.TrueNumSeqs()) | |
716 | + ThisSeq = 0; | |
717 | + ThisFrame = 0; | |
718 | + printf("Sequence %d %d\n",ThisSeq,ThisFrame); | |
719 | + glutPostRedisplay(); | |
720 | + break; | |
721 | + | |
722 | + case '-': | |
723 | + case '_': | |
724 | + switch(ShowAnim) | |
725 | + { | |
726 | + case ShowAnim_Frames: | |
727 | + if (--ThisFrame < 0) | |
728 | + ThisFrame = Model.TrueNumFrames()-1; | |
729 | + printf("Frame %d\n",ThisFrame); | |
730 | + break; | |
731 | + | |
732 | + case ShowAnim_Sequences: | |
733 | + if (--ThisFrame < 0) | |
734 | + ThisFrame = Model.NumSeqFrames(ThisSeq)-1; | |
735 | + printf("Sequence %d %d\n",ThisSeq,ThisFrame); | |
736 | + break; | |
737 | + } | |
738 | + | |
739 | + glutPostRedisplay(); | |
740 | + break; | |
741 | + | |
742 | + case '=': | |
743 | + case '+': | |
744 | + switch(ShowAnim) | |
745 | + { | |
746 | + case ShowAnim_Frames: | |
747 | + if (++ThisFrame >= Model.TrueNumFrames()) | |
748 | + ThisFrame = 0; | |
749 | + printf("Frame %d\n",ThisFrame); | |
750 | + break; | |
751 | + | |
752 | + case ShowAnim_Sequences: | |
753 | + if (++ThisFrame >= Model.NumSeqFrames(ThisSeq)) | |
754 | + ThisFrame = 0; | |
755 | + printf("Sequence %d %d\n",ThisSeq,ThisFrame); | |
756 | + break; | |
757 | + } | |
758 | + | |
759 | + glutPostRedisplay(); | |
760 | + break; | |
761 | + } | |
762 | +} | |
763 | + | |
764 | +void SpecialInMainWindow(int key, int x, int y) | |
765 | +{ | |
766 | + switch(key) | |
767 | + { | |
768 | + case GLUT_KEY_RIGHT: | |
769 | + ViewSideRotate(RotationAngle,0,1,0); | |
770 | + glutPostRedisplay(); | |
771 | + break; | |
772 | + | |
773 | + case GLUT_KEY_LEFT: | |
774 | + ViewSideRotate(-RotationAngle,0,1,0); | |
775 | + glutPostRedisplay(); | |
776 | + break; | |
777 | + | |
778 | + case GLUT_KEY_UP: | |
779 | + ViewSideRotate(RotationAngle,1,0,0); | |
780 | + glutPostRedisplay(); | |
781 | + break; | |
782 | + | |
783 | + case GLUT_KEY_DOWN: | |
784 | + ViewSideRotate(-RotationAngle,1,0,0); | |
785 | + glutPostRedisplay(); | |
786 | + break; | |
787 | + } | |
788 | +} | |
789 | + | |
790 | + | |
791 | +// Create checked/unchecked menu item | |
792 | +char *CheckedText = "*"; | |
793 | +char *UncheckedText = "_"; | |
794 | + | |
795 | +void DoMenuItemChecking(int MenuItem, char *ItemText, int Selector, int Value) { | |
796 | + char TextBuffer[256]; | |
797 | + if (Selector == Value) | |
798 | + strcpy(TextBuffer,CheckedText); | |
799 | + else | |
800 | + strcpy(TextBuffer,UncheckedText); | |
801 | + strcat(TextBuffer," "); | |
802 | + strcat(TextBuffer,ItemText); | |
803 | + glutChangeToMenuEntry(MenuItem,TextBuffer,Value); | |
804 | +} | |
805 | + | |
806 | +// Separate from NormalTypeAction to be below the checking/unchecking mechanism | |
807 | + | |
808 | +enum | |
809 | +{ | |
810 | + VS_00, | |
811 | + VS_01, | |
812 | + VS_03, | |
813 | + VS_10, | |
814 | + VS_30 | |
815 | +}; | |
816 | + | |
817 | +int VertexSplitThresholdIndex = VS_00; | |
818 | + | |
819 | +int VertexSplitMenu; | |
820 | + | |
821 | +void UpdateVertexSplitMenu() | |
822 | +{ | |
823 | + glutSetMenu(VertexSplitMenu); | |
824 | + int Indx = 1; | |
825 | + DoMenuItemChecking(Indx++,"0",VertexSplitThresholdIndex,VS_00); | |
826 | + DoMenuItemChecking(Indx++,"0.1",VertexSplitThresholdIndex,VS_01); | |
827 | + DoMenuItemChecking(Indx++,"0.3",VertexSplitThresholdIndex,VS_03); | |
828 | + DoMenuItemChecking(Indx++,"1.0",VertexSplitThresholdIndex,VS_10); | |
829 | + DoMenuItemChecking(Indx++,"3.0",VertexSplitThresholdIndex,VS_30); | |
830 | + | |
831 | + switch(VertexSplitThresholdIndex) | |
832 | + { | |
833 | + case VS_00: | |
834 | + VertexSplitThreshold = 0.0; | |
835 | + break; | |
836 | + case VS_01: | |
837 | + VertexSplitThreshold = 0.1; | |
838 | + break; | |
839 | + case VS_03: | |
840 | + VertexSplitThreshold = 0.3; | |
841 | + break; | |
842 | + case VS_10: | |
843 | + VertexSplitThreshold = 1.0; | |
844 | + break; | |
845 | + case VS_30: | |
846 | + VertexSplitThreshold = 3.0; | |
847 | + break; | |
848 | + } | |
849 | +} | |
850 | + | |
851 | + | |
852 | +void VertexSplitAction(int c) | |
853 | +{ | |
854 | + VertexSplitThresholdIndex = c; | |
855 | + UpdateVertexSplitMenu(); | |
856 | +} | |
857 | + | |
858 | + | |
859 | + | |
860 | +// Background-color stuff: designed for use with GLUT color picker | |
861 | +int RedIndx = 0, GreenIndx = 0, BlueIndx = 0; | |
862 | +const int CIndxRange = 5; | |
863 | +const float CVals[CIndxRange] = {0, 0.25, 0.5, 0.75, 1}; | |
864 | + | |
865 | +void SetBackgroundColor() | |
866 | +{ | |
867 | + // static GLfloat Red = CVals[RedIndx]; | |
868 | + // static GLfloat Green = CVals[GreenIndx]; | |
869 | + // static GLfloat Blue = CVals[BlueIndx]; | |
870 | + // static GLfloat Alpha = 1; | |
871 | + // glClearColor(Red,Green,Blue,Alpha); | |
872 | + // glClearColor(CVals[RedIndx],CVals[GreenIndx],CVals[BlueIndx],1); | |
873 | +} | |
874 | + | |
875 | +// Update the color-selection menus | |
876 | + | |
877 | +void UpdateColorMenu(int ColorIndx) { | |
878 | + int Indx = 1; | |
879 | + DoMenuItemChecking(Indx++,"0",ColorIndx,0); | |
880 | + DoMenuItemChecking(Indx++,"0.25",ColorIndx,1); | |
881 | + DoMenuItemChecking(Indx++,"0.5",ColorIndx,2); | |
882 | + DoMenuItemChecking(Indx++,"0.75",ColorIndx,3); | |
883 | + DoMenuItemChecking(Indx++,"1",ColorIndx,4); | |
884 | +} | |
885 | + | |
886 | +int RedMenu; | |
887 | + | |
888 | +void UpdateRedMenu() { | |
889 | + glutSetMenu(RedMenu); | |
890 | + UpdateColorMenu(RedIndx); | |
891 | +} | |
892 | + | |
893 | +int GreenMenu; | |
894 | + | |
895 | +void UpdateGreenMenu() { | |
896 | + glutSetMenu(GreenMenu); | |
897 | + UpdateColorMenu(GreenIndx); | |
898 | +} | |
899 | + | |
900 | +int BlueMenu; | |
901 | + | |
902 | +void UpdateBlueMenu() { | |
903 | + glutSetMenu(BlueMenu); | |
904 | + UpdateColorMenu(BlueIndx); | |
905 | +} | |
906 | + | |
907 | +// Sets the window's background color | |
908 | + | |
909 | +void SetRedSelection(int c) { | |
910 | + RedIndx = c; | |
911 | + UpdateRedMenu(); | |
912 | + SetBackgroundColor(); | |
913 | + glutPostRedisplay(); | |
914 | +} | |
915 | + | |
916 | +void SetGreenSelection(int c) { | |
917 | + GreenIndx = c; | |
918 | + UpdateGreenMenu(); | |
919 | + SetBackgroundColor(); | |
920 | + glutPostRedisplay(); | |
921 | +} | |
922 | + | |
923 | +void SetBlueSelection(int c) { | |
924 | + BlueIndx = c; | |
925 | + UpdateBlueMenu(); | |
926 | + SetBackgroundColor(); | |
927 | + glutPostRedisplay(); | |
928 | +} | |
929 | + | |
930 | +void SetColorSelection(int c) { | |
931 | + // Nothing | |
932 | + (void)(c); | |
933 | +} | |
934 | + | |
935 | +// Top-level action function | |
936 | + | |
937 | +void RightButtonAction(int c) {} | |
938 | + | |
939 | +// Preset display-window side: | |
940 | +const int MainWindowWidth = 512, MainWindowHeight = 512; | |
941 | + | |
942 | +int main(int argc, char **argv) | |
943 | +{ | |
944 | + // Setting up for using frames and sequences | |
945 | + Model3D::BuildTrigTables(); | |
946 | + | |
947 | + // For debugging of use of overall model transforms: inverts the model | |
948 | + for (int k=0; k<3; k++) | |
949 | + Model.TransformPos.M[k][k] = Model.TransformNorm.M[k][k] = -1; | |
950 | + | |
951 | + // Must be up here | |
952 | + glutInit(&argc, argv); | |
953 | + | |
954 | + // See if QT and NavSvcs (MacOS) are present | |
955 | + InitMacServices(); | |
956 | + | |
957 | + // Direct debug output to the console: | |
958 | + SetDebugOutput_Wavefront(stdout); | |
959 | + SetDebugOutput_Studio(stdout); | |
960 | + SetDebugOutput_QD3D(stdout); | |
961 | + SetDebugOutput_Dim3(stdout); | |
962 | + | |
963 | + // Set up shader object | |
964 | + Shaders[0].Flags = 0; | |
965 | + Shaders[0].TextureCallback = ShaderCallback; | |
966 | + Shaders[0].LightingCallback = LightingCallback; | |
967 | + Shaders[1].Flags = 0; | |
968 | + Shaders[1].TextureCallback = ShaderCallback; | |
969 | + Shaders[1].LightingCallback = LightingCallback; | |
970 | + | |
971 | + // Set up for creating the main window | |
972 | + glutInitWindowSize(MainWindowWidth,MainWindowHeight); | |
973 | + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH); | |
974 | + SetBackgroundColor(); | |
975 | + | |
976 | + // Actually create that window | |
977 | + MainWindowID = glutCreateWindow("Pfhormz"); | |
978 | + | |
979 | + // Set its main callbacks | |
980 | + glutDisplayFunc(DrawMainWindow); | |
981 | + glutReshapeFunc(ResizeMainWindow); | |
982 | + glutKeyboardFunc(KeyInMainWindow); | |
983 | + glutSpecialFunc(SpecialInMainWindow); | |
984 | + | |
985 | + // Create model and skin menu items | |
986 | + int ModelMenu = glutCreateMenu(LoadModelAction); | |
987 | + glutAddMenuEntry("Alias-Wavefront...",ModelWavefront); | |
988 | + glutAddMenuEntry("3D Studio Max...",ModelStudio); | |
989 | + glutAddMenuEntry("QuickDraw 3D...",Model_QD3D); | |
990 | + glutAddMenuEntry("Dim3 [First]...",Model_Dim3_First); | |
991 | + glutAddMenuEntry("Dim3 [Rest]...",Model_Dim3_Rest); | |
992 | + | |
993 | + int SkinMenu = glutCreateMenu(LoadSkinAction); | |
994 | + glutAddMenuEntry("Colors...",ImageLoader_Colors); | |
995 | + glutAddMenuEntry("Opacity...",ImageLoader_Opacity); | |
996 | + | |
997 | + // Create normal-modification menu items | |
998 | + int NormalTypeMenu = glutCreateMenu(NormalTypeAction); | |
999 | + glutAddMenuEntry("None",Model3D::None); | |
1000 | + glutAddMenuEntry("Originals",Model3D::Original); | |
1001 | + glutAddMenuEntry("Reversed Originals",Model3D::Reversed); | |
1002 | + glutAddMenuEntry("Clockwise Sides",Model3D::ClockwiseSide); | |
1003 | + glutAddMenuEntry("Counterclockwise Sides",Model3D::CounterclockwiseSide); | |
1004 | + | |
1005 | + // Create a vertex-splitting-threshold menu item | |
1006 | + VertexSplitMenu = glutCreateMenu(VertexSplitAction); | |
1007 | + glutAddMenuEntry("0.0",VS_00); | |
1008 | + glutAddMenuEntry("0.1",VS_01); | |
1009 | + glutAddMenuEntry("0.3",VS_03); | |
1010 | + glutAddMenuEntry("1.0",VS_10); | |
1011 | + glutAddMenuEntry("3.0",VS_30); | |
1012 | + UpdateVertexSplitMenu(); | |
1013 | + | |
1014 | + // Create a GLUT Color Picker | |
1015 | + // LP: suppressed because glClearColor() is broken | |
1016 | + #ifdef USE_BACKGROUND_COLOR | |
1017 | + // Create color-component submenus | |
1018 | + RedMenu = glutCreateMenu(SetRedSelection); | |
1019 | + glutAddMenuEntry("0",0); | |
1020 | + glutAddMenuEntry("0.25",1); | |
1021 | + glutAddMenuEntry("0.5",2); | |
1022 | + glutAddMenuEntry("0.75",3); | |
1023 | + glutAddMenuEntry("1",4); | |
1024 | + UpdateRedMenu(); | |
1025 | + | |
1026 | + GreenMenu = glutCreateMenu(SetGreenSelection); | |
1027 | + glutAddMenuEntry("0",0); | |
1028 | + glutAddMenuEntry("0.25",1); | |
1029 | + glutAddMenuEntry("0.5",2); | |
1030 | + glutAddMenuEntry("0.75",3); | |
1031 | + glutAddMenuEntry("1",4); | |
1032 | + UpdateGreenMenu(); | |
1033 | + | |
1034 | + BlueMenu = glutCreateMenu(SetBlueSelection); | |
1035 | + glutAddMenuEntry("0",0); | |
1036 | + glutAddMenuEntry("0.25",1); | |
1037 | + glutAddMenuEntry("0.5",2); | |
1038 | + glutAddMenuEntry("0.75",3); | |
1039 | + glutAddMenuEntry("1",4); | |
1040 | + UpdateBlueMenu(); | |
1041 | + | |
1042 | + // Create background-color submenu | |
1043 | + int ColorMenu = glutCreateMenu(SetColorSelection); | |
1044 | + glutAddSubMenu("Red", RedMenu); | |
1045 | + glutAddSubMenu("Green", GreenMenu); | |
1046 | + glutAddSubMenu("Blue", BlueMenu); | |
1047 | + #endif | |
1048 | + | |
1049 | + // Create right-button menu: | |
1050 | + int RightButtonMenu = glutCreateMenu(RightButtonAction); | |
1051 | + glutAddSubMenu("Load Model...", ModelMenu); | |
1052 | + glutAddSubMenu("Load Skin...", SkinMenu); | |
1053 | + glutAddSubMenu("Normal Type", NormalTypeMenu); | |
1054 | + glutAddSubMenu("Vertex-Split Threshold", VertexSplitMenu); | |
1055 | + #ifdef USE_BACKGROUND_COLOR | |
1056 | + glutAddSubMenu("Background Color", ColorMenu); | |
1057 | + #endif | |
1058 | + glutAttachMenu(GLUT_RIGHT_BUTTON); | |
1059 | + | |
1060 | + glutMainLoop(); | |
1061 | + | |
1062 | + return 0; | |
1063 | +} |