1://////////////////////////////////////////////////////////////////////////////
2://
3:// CMOTHERBOARD.CPP
4:// Implementation file for CMotherboard, CWhisp classes
5://
6:// - CMotherboard defines a 3D fly-through of a motherboard
7:// - CWhip is a whisp of light used in the motherboard scene
8:// - Tabs set at 3.  (Tronster prefers real tabs, Moby Disk prefers spaces.)
9://
10://////////////////////////////////////////////////////////////////////////////
11:
12:#pragma warning(disable:4786)
13:#include <stdlib.h>                             // Header has rand()
14:#include "CMotherboard.h"
15:#include "loaders3d.h"                          // GeoGL: 3SO file loader
16:#include "tesselate3d.h"                        // GeoGL: Object tesselator
17:
18:// Define in CMotherboard header for manual control
19:#ifdef MANUAL_CONTROL
20:#include <iostream>                             // Manual control supports console output
21:#endif MANUAL_CONTROL
22:
23:// Import geoGL and STL namespaces
24:using namespace geoGL;
25:using namespace std;
26:
27:// User interface control settings
28:#define MOTIONSPEED  0.5f                       // Speed of manual control
29:
30:// Nifty variations on appearance
31:#define FLUXINESS       0.75f                   // How much the glowing dots waver around
32:#define WHISP_SIZE      (5.0f/3.0f)             // Size of whispy lights
33:#define SOCKET_XSIZE    15.0f                   // Size of CPU socket
34:#define SOCKET_YSIZE    15.0f
35:#define LIGHT_HOVER_Z   10                      // Height of lighting light ball above 
-->motherboard
36:
37:// RGB colors defining "background" of mindware microchip logo
38:#define BACK_GRAY    128
39:#define BACK_RED     128
40:#define BACK_GREEN   128
41:#define BACK_BLUE    128
42:
43:// Light map settings
44:#define LIGHT_FWD_DIST  25
45:#define LIGHT_PWR2      6
46:#define LIGHT_DIM       (1<<LIGHT_PWR2)
47:#define LIGHT_SIZE      10.0f
48:#define PHONG           64
49:
50:// Times in MS for start and duration of events
51:#define GLOWSTART    65000                         // Glowing starts
52:#define GLOWTIME     4000
53:#define CHIPSTART    (GLOWSTART+5000)              // Chip starts falling
54:#define CHIPTIME     5000
55:#define ANIMSTART    (CHIPSTART+CHIPTIME+GLOWTIME) // Chip logo animates
56:#define ANIMTIME     5000
57:#define FADESTART    (ANIMSTART+5000)              // Fadeout
58:#define FADETIME     3000
59:#define FINALSTART   (FADESTART+FADETIME+3000)
60:#define FINALTIME    1500
61:
62:/******************************** Non-members *********************************/
63:
64:// Random floating point number 0..1
65:static inline float frand01() { return rand() / static_cast<float>(RAND_MAX); }
66:// Random floating point number -1..1
67:static inline float frand11() { return (rand()/ static_cast<float>(RAND_MAX)) * 2 - 1; }
68:
69:// Generic min/max routines
70:template <class T> static inline T TMAX(T T1, T T2) { return (T1>T2 ? T1 : T2); }
71:template <class T> static inline T TMIN(T T1, T T2) { return (T1<T2 ? T1 : T2); }
72:
73://////////////////////////////////////////////////////////////////////////////
74://
75:// Compute animation factor 0..1
76://    - Uses the current & start times and durations to compute a number 0..1
77://    
78://
79:// RETURNS: 0   = start of effect or before effect
80://          0-1 = lienar progression between start/end time
81://          1   = end of effect or after effect
82://
83://////////////////////////////////////////////////////////////////////////////
84:static float calcFactor(unsigned int nElapsedTime, unsigned int nStartTime, unsigned int 
-->nDuration)
85:{
86:   if (nElapsedTime<nStartTime)              return 0;
87:   if (nElapsedTime>nStartTime+nDuration)    return 1;
88:   return static_cast<float>(nElapsedTime-nStartTime) / nDuration;
89:}
90:
91://////////////////////////////////////////////////////////////////////////////
92://
93:// Load a bitmap texture
94://    - Load image using mediaduke (PNG/BMP/whatever)
95://    - Create mipmapped texture
96://    - Add to geoGL texture list (so object loader can access them)
97://
98:// ARGS:    mediaDuke,     Mediaduke object for image loading
99://          filename,      name+ext of image file to load
100://          textureName,   unique string name of texture for use by object loader
101://
102:// RETURNS: texture ID number
103://
104://////////////////////////////////////////////////////////////////////////////
105:GLuint CMotherboard::loadGLTexture(
106:      md::CmediaDuke &mediaDuke, char *filename, char *textureName/*=NULL*/)
107:{
108:   GLuint      nTexture;                  // Texture ID number
109:   md::Cimage  textureImage;              // Mediaduke image
110:
111:   // Load image, return on failure
112:   if (!mediaDuke.read(filename,textureImage))
113:   {
114:      char temp[200];
115:      sprintf(temp,"Unable to read \"%s\"",filename);
116:      throw temp;
117:   }
118:      throwMessage("Unable to read \"%s\"",filename);
119:
120:   // Cimage create an opengl texture - returns -1 on error
121:   nTexture = textureImage.makeGLTexture(m_oEnvInfo.glWantMipmap, m_oEnvInfo.glWantLinear);
122:
123:   // Unable to create texture?  Throw an error
124:   if (nTexture<=0) {
125:      char temp[200];
126:      sprintf(temp,"Unable to create texture \"%s\"",filename);
127:      throw temp;
128:   }
129:   if (nTexture<=0)
130:      throwMessage("Unable to create textures \"%s\"",filename);
131:
132:   // Add texture to map using unique name.  GeoGL object loader can now use this texture.
133:   if (textureName)
134:      mapTextures[string(textureName)] = nTexture;
135:
136:   // Return texture id
137:   return nTexture;
138:}
139:
140:/******************************** CDemoEffect *********************************/
141:
142:
143://////////////////////////////////////////////////////////////////////////////
144://
145:// Construct motherboard effect
146://    - Store a reference to the environment information
147://
148://////////////////////////////////////////////////////////////////////////////
149:CMotherboard::CMotherboard(CEnvInfo *oEnvInfo) : m_oEnvInfo(*oEnvInfo) {}
150:
151:
152://////////////////////////////////////////////////////////////////////////////
153://
154:// Destroy the motherboard
155://   - Has Tronster's cool destructor tracking (for memory leaks)
156://
157://////////////////////////////////////////////////////////////////////////////
158:CMotherboard::~CMotherboard()
159:{ 
160:   MSG("~CMotherboard()")
161:   unInit();
162:   MSGEND
163:}
164:
165:
166://////////////////////////////////////////////////////////////////////////////
167://
168:// Start the motherboard effect
169://    - Called by demo just before first frame
170://    - Apply opengl settings that other effect may have overridden
171://    - Begin time counter
172://
173:// RETURNS: true on success (always)
174://
175://////////////////////////////////////////////////////////////////////////////
176:bool CMotherboard::start()
177:{
178:   initGLstuff();             // Initialize opengl settings
179:
180:   // Setup view frustum
181:   glMatrixMode(GL_PROJECTION);
182:   glLoadIdentity();
183:   gluPerspective(45.0f,static_cast<float>(m_oEnvInfo.nWinWidth)/m_oEnvInfo.nWinHeight,2,270.0f);
-->
184:   frustum.init  (45.0f,static_cast<float>(m_oEnvInfo.nWinWidth)/m_oEnvInfo.nWinHeight,2,270.0f);
-->
185:   // force 4/3 aspect ratio even if the window is stretched
186:   gluPerspective(45.0f, 4.0f/3.0f, 2,270.0f);
187:   frustum.init  (45.0f, 4.0f/3.0f, 2,270.0f);
188:   glMatrixMode(GL_MODELVIEW);
189:
190:   // Save start time, and start the fly through
191:   nTimeStart = m_oEnvInfo.getMusicTime();
192:   flight.start();
193:   flight.start(0);
194:
195:   // Doesn't bother to check for errors, but it should
196:   return true;
197:}
198:
199://////////////////////////////////////////////////////////////////////////////
200://
201:// Stop the motherboard effect
202://    - Clear any lighting and weird stuff so other effects are happy
203://
204:// RETURNS: true on success (always)
205://
206://////////////////////////////////////////////////////////////////////////////
207:bool CMotherboard::stop()
208:{
209:   // Disable light attenuation
210:   glLightf(GL_LIGHT0+light0.nLightID,GL_LINEAR_ATTENUATION,0);
211:
212:   // Turn off all our lights
213:   light0.off();
214:   lightSocket.off();
215:
216:   // Doesn't bother to check for errors, but it should
217:   return true;
218:}
219:
220://////////////////////////////////////////////////////////////////////////////
221://
222:// Initialize and load the motherboard
223://    - Precompute quality values
224://    - Load objects
225://    - Initialize manual control if applicable
226://
227:// RETURNS: true on success (always)
228://
229://////////////////////////////////////////////////////////////////////////////
230:bool CMotherboard::init()
231:{
232:   // Grab demo quality and scale things according to it
233:   // It can range 0..10..infinity
234:   int nQuality = m_oEnvInfo.nDemoQuality;
235:
236:   // Number of whisps to display at each quality setting (0..10)
237:   static const float num_Whispy[11] = {32,66,100,134,166,200,280,400,530,660,800};
238:   static const int num_Whispy[11] = {32,66,100,134,166,200,280,400,530,660,800};
239:   if (nQuality<11)
240:      nWhispy = num_Whispy[nQuality];        // Normal range (0..10) lookup in table
241:   else
242:      nWhispy = num_Whispy[10]*nQuality/10;  // Above 10, scale lineaarly beyond...
243:      nWhispy = num_Whispy[10]*nQuality/10;  // Above 10, scale linearly beyond...
244:
245:   // How much to tesselate objects
246:   //   - The BIGGER this is the LESS tesselated
247:   //   - The SMALLER this is, the MORE tesselated
248:   //   - This factor will actually refer to the length of the longest side in a
249:   //       tesselated object.  This results in a very nonlinear growth in # of sides!
250:   if (nQuality<=2)
251:      fTesselationFactor = 1000;             // Insanely large, never tesselate
252:   else
253:      fTesselationFactor = 5.0f/nQuality;    // Use quality to determine tesselation
254:
255:   // Determine bump map to use
256:   // <=5 -- no bump map
257:   // 6,7 -- 256x256 bump map
258:   // 8+  -- 512x512 bump map
259:   if (nQuality>=8) m_szBumpFile = "mb_bump512.png"; else
260:   if (nQuality>=6) m_szBumpFile = "mb_bump256.png"; else
261:                    m_szBumpFile = NULL;
262:   
263:
264:   // Initialize objects, textures, camera...
265:   initObjects();
266:
267:   // Initialize the light map tables for bump map (if doing bump map)
268:   if (m_szBumpFile)
269:      initLight();
270:
271:   // If under manual control, set the start position
272:   #ifdef MANUAL_CONTROL
273:   motion  = zero3D;
274:   rotation= zero3D;
275:   #endif
276:
277:   // Doesn't bother to check for errors, but it should
278:   return true;
279:}
280:
281://////////////////////////////////////////////////////////////////////////////
282://
283:// Uninitialize
284://    - Unload objects, textures, etc.  Generally free memory.
285://
286:// RETURNS: true on success (always)
287://
288://////////////////////////////////////////////////////////////////////////////
289:// ???WHG??? Add methods to cleanup geometry3d classes
290:bool CMotherboard::unInit()
291:{
292:   // Doesn't bother to check for errors, but it should
293:   return true;
294:}
295:
296://////////////////////////////////////////////////////////////////////////////
297://
298:// Load all textures
299://    - Load PNG files using mediaduke object (available through CEnvInfo)
300://    - Create procedural textures (for animating mindware logo)
301://
302://////////////////////////////////////////////////////////////////////////////
303:void CMotherboard::loadGLTextures()
304:{
305:   // Load generic PNG textures in the "smart" texture IDs
306:   text_cap32        = loadGLTexture(m_oEnvInfo.oMedia,"cap32.png", "cap32");
307:   text_res32        = loadGLTexture(m_oEnvInfo.oMedia,"res32.png", "res32");
308:   text_circuit      = loadGLTexture(m_oEnvInfo.oMedia,"chip2.png", "circuit");
309:   text_flare1       = loadGLTexture(m_oEnvInfo.oMedia,"flare1.png", "flare1");
310:   text_flare3       = loadGLTexture(m_oEnvInfo.oMedia,"flare3.png", "flare3");
311:   text_mbmap        = loadGLTexture(m_oEnvInfo.oMedia,"mb_map.png", "mb_map");
312:   text_socket       = loadGLTexture(m_oEnvInfo.oMedia,"socket.png", "socket");
313:   text_slot         = loadGLTexture(m_oEnvInfo.oMedia,"slot.png",   "slot");
314:   text_metal1       = loadGLTexture(m_oEnvInfo.oMedia,"metal.png",  "metal1");
315:   text_microchip1   = loadGLTexture(m_oEnvInfo.oMedia,"microchip1.png", "microchip1");
316:   text_microchip2   = loadGLTexture(m_oEnvInfo.oMedia,"mindwarethreesomecoma2.png","microchip2")
-->;
317:   text_greenchip    = loadGLTexture(m_oEnvInfo.oMedia,"creditschip.png","greenchip");
318:
319:   //////////////// Custom mindware procedural texture ////////////////
320:   // This is a texture that starts out blank, but will get drawn on as the effect
321:   // progresses.  So we load the final image (img_mindware) and hold on to it.  Then 
322:   // another image of matching dimensions is created.  This image is made into a texture
323:   // that will be used on the actual object.
324:   
325:   // Load mindware logo 
326:   if (!m_oEnvInfo.oMedia.read("mindware.png",img_mindware))
327:   {
328:      char temp[200];
329:      sprintf(temp,"Unable to read \"%s\"","mindware.png");
330:      throw temp;
331:   }
332:      throwMessage("Unable to read \"%s\"","mindware.png");
333:
334:   // Convert to RGB
335:   //   - This would be done automatically if we created a texture out of it
336:   if (img_mindware.palette)
337:      img_mindware.makeDataRGB();
338:
339:   // Create RGB image to draw on, then fill with neutral color
340:   img_mindware0.create(img_mindware.x,img_mindware.y,3);
341:   memset(img_mindware0.data, BACK_GRAY, img_mindware0.x*img_mindware0.y*3);
342:
343:   // Make image into a texture - throw error if problem arises
344:   text_mindware = img_mindware0.makeGLTexture(m_oEnvInfo.glWantLinear, m_oEnvInfo.glWantLinear);
-->
345:
346:   if (text_mindware<=0) {
347:      char temp[200];
348:      sprintf(temp,"Unable to create procedural texture mindware0");
349:      throw temp;
350:   }
351:   if (text_mindware<=0)
352:      throwMessage("Unable to create procedural texture mindware0");
353:
354:   ///////////////////// Custom mindware lightmap /////////////////////
355:   // Load motherboard bump map -- if we are using one
356:   if (m_szBumpFile)
357:   {
358:      if (!m_oEnvInfo.oMedia.read(m_szBumpFile,img_bump))
359:      {
360:         char temp[200];
361:         sprintf(temp,"Unable to read \"%s\"",m_szBumpFile);
362:         throw temp;
363:      }
364:         throwMessage("Unable to read \"%s\"",m_szBumpFile);
365:
366:      // Verify bump map is 8-bit
367:      if (img_bump.bytesPerPixel != 1)
368:         throw "Bump map should be 256 color grayscale!";
369:         throwMessage("Bump map should be 256 color grayscale!");
370:
371:      // Create RGB image to draw on, then fill with neutral color
372:      img_lightMap.create(img_bump.x,img_bump.y,3);
373:      memset(img_lightMap.data, 0, img_lightMap.x*img_lightMap.y*3);
374:
375:      // Make image into a texture - throw error if problem arises
376:      text_lightMap = img_lightMap.makeGLTexture(m_oEnvInfo.glWantLinear, m_oEnvInfo.
-->glWantLinear);
377:
378:      if (text_lightMap<=0) {
379:         char temp[200];
380:         sprintf(temp,"Unable to create procedural texture text_lightMap");
381:         throw temp;
382:      }
383:      if (text_lightMap<=0)
384:         throwMessage("Unable to create procedural texture text_lightMap");
385:   } else
386:      text_lightMap = 0; // Done bump map
387:
388:
389:   //////////////// Light beam 1D procedural texture ////////////////
390:   // And you thought 1D textures were useless...
391:   // Then light beam does not strike on the background color - only on texels
392:   // that are going to change.  To do this, the light beam uses a 1D texture
393:   // as a "mask" for where to draw a line, and where not to draw it
394:
395:   // Create a "1D" image 
396:   // The width is same as the logo, but the height is 1
397:   img_animate.create(img_mindware.x,1,3);
398:   for (int zz=0; zz<img_animate.x; zz++) {
399:      img_animate.data[zz*3+0] = zz;
400:      img_animate.data[zz*3+1] = 0;
401:      img_animate.data[zz*3+2] = zz;
402:   }
403:
404:   // Allocate Texture ID using GeoGL "smart" texture object
405:   text_animate.create();
406:
407:   // Create a 1D texture 
408:   // -  MediaDuke doesn't have a nice function to do this for me
409:
410:   // Bind 1D texture
411:   glBindTexture(GL_TEXTURE_2D, 0);
412:   glBindTexture(GL_TEXTURE_1D, text_animate);
413:
414:   // Use linear interpolation, mipmaps would be silly
415:   glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,m_oEnvInfo.glWantLinear);
416:   glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,m_oEnvInfo.glWantLinear);
417:
418:   // Create the texture
419:   glTexImage1D(GL_TEXTURE_1D,0,3,img_animate.x,0,
420:                     GL_RGB,GL_UNSIGNED_BYTE,img_animate.data);
421:
422:   // Release the 1D texture binding 
423:   glBindTexture(GL_TEXTURE_1D, 0);
424:}
425:
426://////////////////////////////////////////////////////////////////////////////
427://
428:// Create circular light
429://    - Computes simple light into img_light image
430://
431://////////////////////////////////////////////////////////////////////////////
432:void CMotherboard::initLight()
433:{
434:   // Create a circular light
435:   img_light.create(LIGHT_DIM,LIGHT_DIM,1);
436:   for (int j=0; j<LIGHT_DIM; j++)
437:   {
438:      for (int i=0; i<LIGHT_DIM; i++)
439:      {
440:         float dist = (LIGHT_DIM/2-i)*(LIGHT_DIM/2-i) + (LIGHT_DIM/2-j)*(LIGHT_DIM/2-j);
441:         if (fabsf(dist)>1)
442:            dist = sqrtf(dist);
443:         int c = (int)(LIGHT_SIZE*dist);  //???WHG Random deviation + (rand()%7)-3;
444:         if (c<0)    c = 0;
445:         if (c>255)  c = 255;
446:         img_light.data[(j<<LIGHT_PWR2)+i] = 255-c;
447:      }
448:   }
449:}
450:
451://////////////////////////////////////////////////////////////////////////////
452://
453:// Set light attenuation
454://    - Some sucky opengl drivers don't support this.  Maybe we need an option
455://      to disable it.
456://
457://////////////////////////////////////////////////////////////////////////////
458:void CMotherboard::setAttenuation()
459:{
460:   // For those not versed in light attenuation:
461:   //   Attenuation causes light to affect distant objects less than near ones
462:   //   It can be constant, linear, or quadratic.
463:   //   Just a slight linear falloff allows a bit of realizm and cloaks
464:   //   far away details(and artifacts) until nearby
465:
466:   // Maybe GeoGL's Light3D class should support this
467:   glLightf(GL_LIGHT0+light0.nLightID,GL_LINEAR_ATTENUATION,0.05f);
468:}
469:
470://////////////////////////////////////////////////////////////////////////////
471://
472:// Apply opengl settings, fog, attenuation, light...
473://    - Use glEnable to setup things that other demo effects may have changed
474://    - Setup the lights and the ambient light
475://
476://////////////////////////////////////////////////////////////////////////////
477:void CMotherboard::initGLstuff()                            // All Setup For OpenGL Goes Here
478:{
479:// OpenGL setup
480:   glEnable(GL_DEPTH_TEST);                     // Enables Depth Testing
481:   glEnable(GL_CULL_FACE);                      // Cull back faces
482:   glDisable(GL_NORMALIZE);                     // GeoGL creates unit normals at load
483:   glShadeModel(GL_SMOOTH);                     // Enable Smooth Shading
484:   glDepthFunc(GL_LEQUAL);                      // Allows blends to be drawn over objects
485:   glEnable(GL_LIGHTING);                       // Lighting is key...
486:
487:   setAttenuation();                            // Set light attenuation
488:
489:// Light setup
490:
491:   // Disable any lights left around by other effects
492:   for (int i=0; i<1; i++)
493:      glDisable(GL_LIGHT0+i);
494:
495:   // No scene ambient light - the individual lights handle this
496:   Light3D::setSceneAmbient(fRGBA(0,0,0,0));
497:
498:   // Light at viewer is on at full white
499:   light0.setLight(1.0f,1.0f,1.0f,1);
500:   light0.position(0.0f,0.0f,0.0f);
501:   light0.on();
502:
503:   // Light at CPU socket comes later
504:   lightSocket.off();
505:}
506:
507://////////////////////////////////////////////////////////////////////////////
508://
509:// Initialize openGL objects
510://    - Load objects with GeoGL object loader
511://    - Customize texture, light, position, transparency
512://
513://////////////////////////////////////////////////////////////////////////////
514:void CMotherboard::initObjects()
515:{
516:   // Load fly through path
517:   flight.Load("data.md/flythrough.dat");
518:
519:   // Load all textures before objects
520:   loadGLTextures();
521:
522:   // ABOUT GEOGL OBJECT LOADING
523:   // - Objects are loaded at the origin.  The motherboard is on the XY plane, so
524:   //   other objects are placed above it (+z) at various positions
525:   // - Objects may be tesselated so the triangle-mesh is more detailed.  This can
526:   //   aid lighting effects (particularly specular light) but eats memory+T&L time
527:   // - When objects are loaded, GeoGL will use the texture and light settings
528:   // specified in the 3SO file.  Some objects are "generic" (cube, plane) and
529:   // need to have the texture, color, etc. set manually.  Custom objects load
530:   // exactly as needed (resistor, CPU chip)
531:
532:   // COMPUTER CASE: load, scale, position
533:   // - It is a 2-sided cube (2-sided else inside is culled)
534:   // - Metal texture is specified in the 3SO file
535:   computer = Load3SO("data.md/cube2s.3so",NULL,false);
536:   computer.rescale(80,120,40);
537:   computer.compile();
538:
539:   // MOTHERBOARD: load, tesselate, scale, position
540:   // - Tesselated since lighting must be computed at more just the 4 corners!
541:   // - Uses large, detailed 512x512 texture
542:   motherboard = Load3SO("data.md/mb_map.3so",NULL,false);   
543:   motherboard = splitObj(motherboard,0.5f);
544:   motherboard.rescale(70,80,1);
545:   motherboard.position(0,-20,-37);
546:   motherboard.compile();
547:
548:   // CPU SOCKET: load, scale, position
549:   // - Uses simple texture showing holes in socket
550:   socket = Load3SO("data.md/socket.3so",NULL,false);
551:   socket.rescale(SOCKET_XSIZE,SOCKET_YSIZE,0.8);
552:   socket.position = motherboard.position + fVector3D(0,49,0.8);
553:   socket.compile();
554:
555:   // SOCKET HANDLE: load, scale, position, orient, 
556:   // - Reorient with the motherboard
557:   socketHandle = Load3SO("data.md/handle.3so",NULL,false);
558:   socketHandle.rescale(1,0.5f,36.5f);
559:   socketHandle.position = motherboard.position + fVector3D(21.5f,33.5f,0);
560:   socketHandle.direction(0,-90,0);
561:   socketHandle.compile();
562:
563:   // CPU CHIP: load, scale, position, set texture
564:   // - Created from a generic cube so the texture must be set
565:   // - Size is slightly smaller than the socket
566:   CPUchip = Load3SO("data.md/cube.3so",NULL,false);
567:   CPUchip.rescale(SOCKET_XSIZE - 0.2f,SOCKET_YSIZE - 0.2f,0.5);
568:   CPUchip.position = socket.position + fVector3D(0,0,100);
569:   FOROBJSET(CPUchip,chipobj)
570:      chipobj.nTextureID = text_mindware;
571:   ENDFOROBJSET
572:   CPUchip.compile();
573:
574:   // COOLCHIP: load, tesselate, scale, position
575:   // - Tesselated for nice specular highlight
576:   // - Created from a generic cube so the texture must be set
577:   // - Play with the default lighting too
578:   // - This used to be a big green chip, now it shows the credits
579:   coolchip = Load3SO("data.md/cube.3so",NULL,false);
580:   coolchip = splitObj(coolchip,0.75f*fTesselationFactor);
581:   coolchip.rescale(11,10,0.5);
582:   coolchip.position = motherboard.position + fVector3D(7,13,0.6);
583:   FOROBJSET(coolchip,chipobj)
584:      chipobj.nTextureID = text_greenchip;
585:      chipobj.color(0.6f,1.0f,0.6f);
586:      chipobj.setLight(0.15f,1.0f,0,1.5f,10);
587:      chipobj.bSpecularBlend = true;            // Apply 2-pass specular blend
588:      chipobj.bDrawSmooth = true;
589:   ENDFOROBJSET
590:   coolchip.compile();
591:
592:   // CAPACITOR TABLE
593:   // - This is the coordinates of all the capacitors on the motherboard
594:   fVector3D cap_pos[num_Cap] = {
595:      fVector3D(-45,50,0), fVector3D(-45,44,0), fVector3D(-45,38,0), 
596:      fVector3D(-45,32,0), fVector3D(-45,26,0),
597:
598:      fVector3D(-39,-6,0), fVector3D(-38.5,-12,0),
599:      fVector3D(-35,-32,0), fVector3D(-6,-33,0),
600:      fVector3D(-12,-44,0), fVector3D(7,-53,0),
601:      fVector3D(30,-44,0), fVector3D(34,-13,0),
602:      fVector3D(30,68,0), fVector3D(21,69,0),
603:      fVector3D(16,68,0), fVector3D(3,68,0),
604:      fVector3D(-1.5,68.5,0), fVector3D(-9,68.2,0)
605:   };
606:
607:   // - This is the orientation of all the capacitors on the motherboard
608:   fVector3D cap_dir[num_Cap] = {
609:      fVector3D(0,270,10), fVector3D(4,320,6), fVector3D(-1,130,0), 
610:      fVector3D(11,200,7),  fVector3D(0,110,0),
611:      fVector3D(20,360,5), fVector3D(-19,210,0),
612:      fVector3D(0,140,-8),   fVector3D(7,90,-13),
613:      fVector3D(-5,110,3),  fVector3D(12,120,0),
614:      fVector3D(15,60,-1),  fVector3D(0,70,0),
615:      fVector3D(-3,280,0), fVector3D(2,150,-7),
616:      fVector3D(13,40,4),  fVector3D(0,10,0),
617:      fVector3D(-16,80,0),  fVector3D(13,256,2)
618:   };
619:
620:   // MAIN CAPACITOR: load, tesselate, scale, position, reorient
621:   // - Tesselated for specular highlight
622:   ObjectSet3D cap_main = Load3SO("data.md/cap.3so",NULL,false);
623:   cap_main = splitObj(cap_main,0.75f*fTesselationFactor);
624:   cap_main.rescale(2.0f,1.5f,2.0f);
625:   cap_main.direction(90,0,0);
626:   cap_main.position = motherboard.position + fVector3D(0,0,1.5f*1.5f);
627:   cap_main.objects[0].bSpecularBlend = true;
628:
629:   // Loop through capacitor table and create a capacitor for each position
630:   // - Copies of the original capacitor object (share vertices)
631:   // - Position & orientation from the above tables
632:   // - Random colors, 50% are specular
633:   for (int nCap=0; nCap<num_Cap; nCap++) {
634:      capacitor[nCap] = cap_main;                  // Copy original
635:      capacitor[nCap].position +=cap_pos[nCap];    // Add to position, direction
636:      capacitor[nCap].direction+=cap_dir[nCap];
637:
638:      // Every other one
639:      if (nCap % 2==0)
640:      {
641:         // Is given a random color change of about 50%
642:         Object3D &obj = capacitor[nCap].objects[0];
643:         obj.diffuse.r = obj.diffuse.r*(frand01()/2 + 0.5f);
644:         obj.diffuse.g = obj.diffuse.g*(frand01()/2 + 0.5f);
645:         obj.diffuse.b = obj.diffuse.b*(frand01()/2 + 0.5f);
646:      }
647:      // And every other one is specular
648:      cap_main.objects[0].bSpecularBlend = (nCap % 2==0);
649:      capacitor[nCap].compile();
650:   }
651:
652:   // RESISTOR TABLE
653:   // - This is the coordinates of all the capacitors on the motherboard
654:   fVector3D res_pos[num_Res] = {
655:      fVector3D(-55,68,0), fVector3D(-55,65,0),
656:      fVector3D(-55,62,0), fVector3D(-55,59,0),
657:   };
658:
659:   // - This is the orientation of all the capacitors on the motherboard
660:   fVector3D res_dir[num_Res] = {
661:      fVector3D(0,0,90), fVector3D(0,0,90),
662:      fVector3D(0,0,90), fVector3D(0,0,90),
663:   };
664:
665:   // MAIN RESISTOR: load, scale, position
666:   // - Tesselated for specular highlight
667:   ObjectSet3D res_main = Load3SO("data.md/res.3so",NULL,false);
668:   res_main.position = motherboard.position + fVector3D(0,0,1.0f);
669:   res_main.compile();
670:
671:   // Loop through resistor table and create a resistor for each position
672:   // - Copies of the original resistor object (share vertices)
673:   // - Position & orientation from the above tables
674:   for (int nRes=0; nRes<num_Res; nRes++) {
675:      resistor[nRes] = res_main;
676:      resistor[nRes].position +=res_pos[nRes];
677:      resistor[nRes].direction+=res_dir[nRes];
678:   }
679:
680:   // MAIN PCI SLOT: load, tesselate, scale, position
681:   // - Created from a generic cube so the texture must be set
682:   PCI[0] = Load3SO("data.md/slot.3so",NULL,false);
683:   PCI[0] = splitObj(PCI[0],1*fTesselationFactor);
684:   PCI[0].rescale(28,2,2);
685:   PCI[0].position = motherboard.position + fVector3D(-13,-27,1);
686:   PCI[0].compile();
687:
688:   // Loop creating PCI slots, next to each other
689:   // - Each idential
690:   for (int nPCI=1; nPCI<num_PCI; nPCI++) {
691:      PCI[nPCI] = PCI[0];
692:      PCI[nPCI].position+=fVector3D(0,-11*nPCI,0);
693:   }
694:
695:   // AGP SLOT: load, tesselate, scale, position
696:   // - Uses PCI slot object, but is a lighter color
697:   AGP = Load3SO("data.md/slot.3so",NULL,false);
698:   AGP = splitObj(AGP,1*fTesselationFactor);
699:   AGP.rescale(24,2,2);
700:   AGP.position= PCI[0].position + fVector3D(13,12,0);
701:   FOROBJSET(AGP,obj)
702:      obj.color(1,0.85f,0.48f);
703:      obj.setLight(0.1,0.4,0,0,0);
704:   ENDFOROBJSET
705:   AGP.compile();
706:
707:   // ISA SLOT : load, tesselate, scale, position
708:   // - Uses PCI slot object, but is a darker color
709:   ISA = Load3SO("data.md/slot.3so",NULL,false);
710:   ISA = splitObj(ISA,1*fTesselationFactor);
711:   ISA.rescale(45,2,2);
712:   ISA.position= PCI[num_PCI-1].position + fVector3D(7,-6,0);
713:   FOROBJSET(ISA,obj)
714:      obj.diffuse = PCI[0].objects[0].diffuse / 4;
715:   ENDFOROBJSET
716:   ISA.compile();
717:
718:   // MAIN DIMM SLOT: load, tesselate, scale, position
719:   // - Uses PCI slot object, but is darker, thinner, shorter
720:   ObjectSet3D DIMM0 = Load3SO("data.md/slot.3so",NULL,false);
721:   DIMM0 = splitObj(DIMM0,1*fTesselationFactor);
722:   DIMM0.rescale(17,1,1);
723:   DIMM0.direction = fVector3D(0,0,90);
724:   DIMM0.position = motherboard.position+fVector3D(47,54,0.5f);
725:   FOROBJSET(DIMM0,obj)
726:      obj.diffuse = PCI[0].objects[0].diffuse / 4;
727:   ENDFOROBJSET
728:   DIMM0.compile();
729:
730:   // DIMM SLOTS: create 3 dimm slots (each pair forms one memory slot)
731:   DIMM[0] = DIMM0;     DIMM[0].position += fVector3D( 0,  0,0);
732:   DIMM[1] = DIMM0;     DIMM[1].position += fVector3D( 0,-34,0);
733:   DIMM[2] = DIMM0;     DIMM[2].position += fVector3D(-3,  0,0);
734:   DIMM[3] = DIMM0;     DIMM[3].position += fVector3D(-3,-34,0);
735:   DIMM[4] = DIMM0;     DIMM[4].position += fVector3D(-6,  0,0);
736:   DIMM[5] = DIMM0;     DIMM[5].position += fVector3D(-6,-34,0);
737:
738:   // RAM CHIP: load, tesselate, scale, position
739:   // - Created from a generic cube so the texture must be set
740:   RAM = Load3SO("data.md/cube.3so",NULL,false);
741:   RAM.rescale(0.4f,33,7);
742:   RAM.position = motherboard.position+fVector3D(47,37,3.5f);
743:   FOROBJSET(RAM,obj)
744:      obj.nTextureID = text_circuit;
745:   ENDFOROBJSET
746:   RAM.compile();
747:
748:   // SOME MICROCHIPS: load, scale, position, orientation
749:   // - Created from a generic cube so the texture must be set
750:   microchip[0] = Load3SO("data.md/chip.3so",NULL,false);
751:   microchip[0].rescale(11,1,5);
752:   microchip[0].direction = fVector3D(90,90,180);
753:   microchip[0].position = motherboard.position + fVector3D(-47,-62,1);
754:   microchip[0].compile();
755:
756:   microchip[1] = Load3SO("data.md/chip.3so",NULL,false);
757:   microchip[1].objects[0].nTextureID = text_microchip2;
758:   microchip[1].rescale(6,1,3.5f);
759:   microchip[1].position = motherboard.position + fVector3D(-48,-44,1);
760:   microchip[1].direction = fVector3D(90,0,180);
761:   microchip[1].compile();
762:
763:   microchip[2] = Load3SO("data.md/chip.3so",NULL,false);
764:   microchip[2].rescale(5,0.75f,2.5f);
765:   microchip[2].position = motherboard.position + fVector3D(30,-5,0.75f);
766:   microchip[2].direction = fVector3D(90,0,180);
767:   microchip[2].compile();
768:
769:   // LIGHT BEAM: load, scale, position, orientation
770:   // - This is the light beam that "etches" onto the mindware chip
771:   // - Created from a generic plane so many things are changed 
772:   lightBeam = Load3SO("data.md/plane.3so",NULL,false);
773:   lightBeam.rescale(20,20,20);
774:   lightBeam.direction(90,-45,0);
775:   lightBeam.position = socket.position + fVector3D(-15,-15,20);
776:   FOROBJSET(lightBeam,obj)
777:      obj.color = fwhite;
778:      obj.bTransparency = true;              // Oooh, transparency!
779:      obj.nTextureType = GL_TEXTURE_1D;      // And a 1D texture!
780:      obj.nTextureID = text_animate;         // A procedural one!
781:      obj.ambient(0,0,0,1);                  // These settings make 50% transparency
782:      obj.diffuse(0,0,0,0.5f);               // They are magic, so don't muck with them
783:      obj.specular(0,0,0,1);
784:      obj.emission(1,1,1,1);
785:   ENDFOROBJSET
786:
787:   // EXPLODING LIGHT FLARE: load, scale, position
788:   // - This object explodes out from the socket when the glowing starts
789:   // - Created from a generic plane and heavily modified
790:   flare = Load3SO("data.md/plane.3so",NULL,false);
791:   flare.position = motherboard.position + fVector3D(0,49,0.8);
792:   FOROBJSET(flare,flareobj)
793:      flareobj.color(1,1,1,1);
794:      flareobj.bTransparency = true;
795:      flareobj.nTextureID = text_flare3;
796:      flareobj.ambient(0,0,0,1);
797:      flareobj.diffuse(0,0,0,0);
798:      flareobj.specular(0,0,0,1);
799:      flareobj.emission(1,1,1,1);
800:   ENDFOROBJSET
801:
802:   motherboardBump = motherboard;
803:   FOROBJSET(motherboardBump,obj)
804:      obj.color(1,1,1,1);
805:      obj.bTransparency = true;
806:      obj.nTextureID = text_lightMap;
807:      obj.ambient(0,0,0,1);
808:      obj.diffuse(0,0,0,0.5f);
809:      obj.specular(0,0,0,1);
810:      obj.emission(1,1,1,1);
811:   ENDFOROBJSET
812:
813:   // GLOWING COLUMN: load, scale, position, orientation
814:   // - Created from a generic cube so attributes are changed
815:   // - GlowingOld is the original cube object and scale
816:   // - Glowing is an object that will change shape and size
817:   glowing    = Load3SO("data.md/cube.3so",NULL,false);  // Load twice instead of copying
818:   glowingOld = Load3SO("data.md/cube.3so",NULL,false);  // So they don't share vertices
819:   glowing.rescale(14.9f,1.0f,14.9f);
820:   glowingOld.rescale(14.9f,1.0f,14.9f);
821:   glowing.direction(90,0,0);
822:   glowing.position = motherboard.position + fVector3D(0,49,1.8);
823:   // Transparent, no texture
824:   FOROBJSET(glowing,obj)
825:      obj.color(1,1,1,1);
826:      obj.ambient(0,0,0,0);
827:      obj.diffuse(0,0,0,0.0);
828:      obj.specular(0,0,0,0);
829:      obj.bSpecularBlend = false;
830:      obj.emission(1,1,1,1);
831:      obj.bTransparency = true;
832:      obj.nTextureID = 0;
833:   ENDFOROBJSET
834:
835:   // MAIN WHISP: load, scale, position, orientation
836:   // - Created from a generic plane so attributes are changed
837:   ObjectSet3D whispy_main = Load3SO("data.md/plane.3so",NULL,false);
838:   whispy_main.position = motherboard.position + fVector3D(0,49,5000);
839:   whispy_main.direction(45,0,0);
840:   whispy_main.rescale(WHISP_SIZE,WHISP_SIZE,WHISP_SIZE);
841:   FOROBJSET(whispy_main,obj)
842:      obj.color(1,1,1,1);
843:      obj.bTransparency = true;
844:      obj.nTextureID = text_flare1;
845:      obj.ambient(0,0,0,1);
846:      obj.diffuse(0,0,0,0);
847:      obj.specular(0,0,0,1);
848:      obj.emission(1,1,1,1);
849:   ENDFOROBJSET
850:
851:   // Create a multitude of whisp objects that are waay to high to be seen
852:   // - since they are so high above the chip, they will get reset right away
853:   // - Scale the # of whisps according to the quality factor
854:   whispy.refnew(nWhispy);
855:   whispyData.refnew(nWhispy);
856:   for (int nWhisp=0; nWhisp<nWhispy; nWhisp++)
857:   {
858:      whispy[nWhisp] = whispy_main;          // Copy original, share vertices
859:      whispyData[nWhisp].basePos.z = 5000;   // Position way up high
860:   }
861:
862:   // All objects are now ready to be rendered
863:}
864:
865://////////////////////////////////////////////////////////////////////////////
866://
867:// Call a visible object
868://    - Checks to see if the object is in the view frustum
869://    - Only draws the object if it is 
870://    - Call only works on compiled objects
871://
872:// ARGS:    obj,           GeoGL "ObjectSet3D" to call
873://
874:// RETURNS: true if object visible, false if not
875://////////////////////////////////////////////////////////////////////////////
876:bool CMotherboard::drawVisibleObject(ObjectSet3D &obj)
877:{
878:   if (frustum.visible(obj))
879:   {
880:      obj.call();
881:      return true;
882:   } else
883:      return false;
884:}
885:
886:void CMotherboard::drawBump(int lx1, int ly1)
887:{
888:   int i, j, px, py, x, y, offs, c;
889:   int bump_w = img_lightMap.x;
890:   int bump_h = img_lightMap.y;
891:
892:   offs = bump_w;
893:   int lx = -lx1 + LIGHT_DIM/2;   // lx1 ranges 0..LIGHT_DIM.  Change to -LIGHT_DIM/2 to 
-->+LIGHT_DIM/2
894:   int ly = -ly1 + LIGHT_DIM/2;   // ly1 ranges 0..LIGHT_DIM.  Change to -LIGHT_DIM/2 to 
-->+LIGHT_DIM/2
895:   for (j=1; j<bump_h; j++)
896:   {
897:      img_lightMap.data[offs*3 + 0] = 0;
898:      img_lightMap.data[offs*3 + 1] = 0;
899:      img_lightMap.data[offs*3 + 2] = 0;
900:      offs++;
901:      for (i=1; i<bump_w; i++)
902:      {
903:         // Calculate the slope for each pixels
904:         px = i + img_bump.data[offs-1] - img_bump.data[offs];
905:         py = j + img_bump.data[offs-bump_w] - img_bump.data[offs];
906:
907:         // Lookup the first light map to have to light intensisy of this point
908:         x = px + lx;
909:         y = py + ly;
910:         if ((y>=0) && (y<LIGHT_DIM) && (x>=0) && (x<LIGHT_DIM))
911:            c = img_light.data[(y<<LIGHT_PWR2)+x];
912:         else
913:            c = 0;
914:
915:         // Do not underflow or overflow
916:         if (c<0) c = 0;
917:         if (c>255) c = 255;
918:
919:         img_lightMap.data[offs*3 + 0 ] = c;
920:         img_lightMap.data[offs*3 + 1 ] = c;
921:         img_lightMap.data[offs*3 + 2 ] = c;
922:
923:         offs++;
924:      }
925:   }
926:
927:   // Only update changed portions of the light map image
928:   // This could be MUCH more optimal
929:   int top, bott, subOffset;
930:
931:   top = TMAX<int>(ly1 - LIGHT_DIM/2,0);                 // Top of area affected by light
932:   bott= TMIN<int>(ly1 + LIGHT_DIM/2,img_lightMap.y-1);  // Bottom of area affected by light
933:   subOffset = top*img_lightMap.x*3;                     // Offset of top
934:
935:   // Update the light map - only update the portion that is affected
936:   // ???WHG 1) This still updates all the way across
937:   // ???WHG 2) This should leave trails since we did not include the area from the previous 
-->frame!
938:   glBindTexture(GL_TEXTURE_2D, text_lightMap);
939:   glTexSubImage2D(GL_TEXTURE_2D, 0, 0, top, img_lightMap.x,bott-top+1, 
940:                       GL_RGB, GL_UNSIGNED_BYTE,img_lightMap.data+subOffset);
941:   glBindTexture(GL_TEXTURE_2D, 0);
942:
943:}
944:
945://////////////////////////////////////////////////////////////////////////////
946://
947:// Advance the light map effect
948://    - This computes a bump map for the motherboard
949://    - It is centered around the light in front of the viewer
950://
951://////////////////////////////////////////////////////////////////////////////
952:#define MB_X0  (-70 - 0)
953:#define MB_X1  (+70 - 0)
954:#define MB_Y0  (-80 - 20)
955:#define MB_Y1  (+80 - 20)
956:#define MB_X   (MB_X1 - MB_X0)
957:#define MB_Y   (MB_Y1 - MB_Y0)
958:
959:void CMotherboard::advanceLightMap()
960:{
961:   // Exit if we are not doing bump mapping
962:   if (!m_szBumpFile)
963:      return;
964:
965:   int lx,ly;
966:   fVector2D lightPos;
967:
968:   // Compute position of light, projected onto MB, range 0..1
969:   lightPos.x = (light0.position.x - MB_X0) / MB_X;
970:   lightPos.y = (light0.position.y - MB_Y0) / MB_Y;
971:
972:   // Clamp in case it goes off the MB
973:   if (lightPos.x<0) lightPos.x = 0; else
974:   if (lightPos.x>1) lightPos.x = 1;
975:   if (lightPos.y<0) lightPos.y = 0; else
976:   if (lightPos.y>1) lightPos.y = 1;
977:
978:   // Scale it to the resolution of the light map
979:   lx = lightPos.x * img_lightMap.x;
980:   ly = lightPos.y * img_lightMap.y;
981:   lx = static_cast<int>(lightPos.x * img_lightMap.x);
982:   ly = static_cast<int>(lightPos.y * img_lightMap.y);
983:
984:   // Bump map not changed, return
985:   if (lx==lx0 && ly==ly0)
986:      return;
987:   lx0 = lx;
988:   ly0 = ly;
989:
990:   drawBump(lx,ly);
991:}
992:
993://////////////////////////////////////////////////////////////////////////////
994://
995:// Advance the animation for the mindware chip
996://    - Computes the procedural texture img_mindware and sends it to opengl
997://    - Computes the procedural 1D texture for the "etching" light beam
998://
999:// ARGS:    fAnimateFactor,   0..1 (0 is starting, 1 is completed)
1000://////////////////////////////////////////////////////////////////////////////
1001:void CMotherboard::advanceChipTexture(float fAnimateFactor)
1002:{
1003:   static const int nAhead     = 50;                // # of pixels ahead for white glow
1004:   static const int nWhiteness = 5;                 // Brightness factor for these pixels
1005:
1006:   // The image appears on the chip coming from one corner moving to another.
1007:   // This is commonly called a "diagonal wipe"
1008:   // Just ahead of the wipe, the pixels-to-be glow white, and fade down to the
1009:   // proper color.  This only happens on non-background pixels.
1010:   //
1011:   // In addition, a glowing beam carves out the sections that are being drawn.
1012:   // The beam fires at the glowing points that are ahead of the wiped pixels.
1013:   //   - The wipe cannot depend on previous frames (due to frameskip)
1014:   //   - A 1D texture must be plotted with the section the beam carves
1015:   //   - The texture must be NxN, RGB8
1016:   //   - The 1D texture must be Nx1
1017:   //   - "Diagonals" range 0..2N
1018:   //   - The diagonal of a pixel at (x,y) in an NxN texture is (x+y)
1019:
1020:   md::Cimage &image0 = img_mindware0;        // Working image to draw on
1021:   md::Cimage &image  = img_mindware;         // Final image to draw from
1022:
1023:   int nCurrDiag = fAnimateFactor * (image.x+image.y);   // Current diagonal at full color
1024:   int nCurrDiag = static_cast<int>(
1025:                     fAnimateFactor * (image.x+image.y));// Current diagonal at full color
1026:   int nGlowDiag = nCurrDiag + nAhead;                   // Current glowing diagonal
1027:   int nOffset   = 0;                                    // Offset of (x,y) pixel in texture
1028:
1029:   // Erase the carved 1D texture.  This is filled in as we go
1030:   memset(img_animate.data,0,img_animate.x*3);
1031:
1032:   // Loop through texture
1033:   for (int y=0; y<image.y; y++)
1034:   {
1035:      for (int x=0; x<image.x; x++)
1036:      {
1037:         int nDiag     = x+y;                      // Current diagonal (0..x+y)
1038:         nOffset += 3;                             // Update offset
1039:
1040:         // Is this diagonal on or before the current diagonal?
1041:         if (nDiag <= nCurrDiag)
1042:         {
1043:            // Then set the color to the final color
1044:            image0.data[nOffset+0] = image.data[nOffset+0];
1045:            image0.data[nOffset+1] = image.data[nOffset+1];
1046:            image0.data[nOffset+2] = image.data[nOffset+2];
1047:         } else
1048:
1049:         // Is this diagonal between the current diagonal and the white glowing diagonal?
1050:         if (nDiag <= nCurrDiag + nAhead)
1051:         {
1052:            // Only glow pixels that are not "background" gray
1053:            if (image.data[nOffset+0]!=BACK_RED || 
1054:                image.data[nOffset+1]!=BACK_GREEN ||
1055:                image.data[nOffset+2]!=BACK_BLUE)
1056:            {
1057:               // Glow far ahead pixels white, fade down to proper color
1058:
1059:               int nWhite = (nDiag - nCurrDiag) * nWhiteness;     // Whiteness factor
1060:
1061:               // Add the whiteness to the pixel color
1062:               image0.data[nOffset+0] = TMIN(image.data[nOffset+0] + nWhite,255);
1063:               image0.data[nOffset+1] = TMIN(image.data[nOffset+1] + nWhite,255);
1064:               image0.data[nOffset+2] = TMIN(image.data[nOffset+2] + nWhite,255);
1065:
1066:               // Is this the brightest glowing diagonal?
1067:               if (nDiag == nCurrDiag + nAhead)
1068:               {
1069:                  // Yes it is - this is where the 1D carving glow strikes
1070:                  // This pixel is glowing, so we add it to the mask.  This is a mess.
1071:
1072:                  int nLen, nVal;
1073:
1074:                  // Need: 1) The length of the current diagonal (0..N)
1075:                  //       2) The coordinate to decide the offset along that diagonal
1076:                  if (nDiag <= img_animate.x)
1077:                    { nLen = nDiag; nVal = x; }
1078:                  else
1079:                    { nLen = 2*img_animate.x - nDiag; nVal = img_animate.x - y; }
1080:
1081:                  // Compute the offset (-N/2..N/2) along the diagonal
1082:                  // Then compute the pixel in the 1D image that it corresponds to
1083:                  int nOffset = nLen/2 - nVal;
1084:                  int nPixel  = img_animate.x/2 - nOffset;
1085:
1086:                  // I think it is going out of range by 1 sometimes, due to the rounding
1087:                  if (nPixel>=0 && nPixel<img_animate.x)
1088:                  {
1089:                     // Compute offset, set to white
1090:                     int z = nPixel * 3;
1091:                     img_animate.data[z+0] = 255;
1092:                     img_animate.data[z+1] = 255;
1093:                     img_animate.data[z+2] = 255;
1094:                  }
1095:               } // end 1D texture at brightest diagonal
1096:            } // end any pixel that is not the background color
1097:         } // end which diagonal
1098:
1099:         // Update offset
1100:         nOffset += 3;
1101:
1102:      } // end X
1103:   } // end Y
1104:
1105:   // Wow, now lets move the plane onto the appropriate diagonal
1106:   // We compute the position of the texture's diagonal in 3D space
1107:   float fGlowDiagonal = (float)(nCurrDiag+nAhead)/(image.x+image.y);
1108:   lightBeam.position = 
1109:      socket.position + 
1110:      fVector3D(-SOCKET_XSIZE+SOCKET_XSIZE*2*fGlowDiagonal,
1111:                -SOCKET_YSIZE+SOCKET_YSIZE*2*fGlowDiagonal, 20);
1112:
1113:   // Update the chip drawing
1114:   // Use glTexSubImage2D to update a texture
1115:   glBindTexture(GL_TEXTURE_2D, text_mindware);
1116:   glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img_mindware0.x,img_mindware0.y, 
1117:                       GL_RGB, GL_UNSIGNED_BYTE,img_mindware0.data);
1118:   glBindTexture(GL_TEXTURE_2D, 0);
1119:
1120:   // Update the light beam
1121:   // Use glTexSubImage1D to update a texture
1122:   glBindTexture(GL_TEXTURE_1D, text_animate);
1123:   glTexSubImage1D(GL_TEXTURE_1D, 0, 0, img_animate.x, 
1124:                       GL_RGB, GL_UNSIGNED_BYTE,img_animate.data);
1125:   glBindTexture(GL_TEXTURE_1D, 0);
1126:
1127:}
1128:
1129://////////////////////////////////////////////////////////////////////////////
1130://
1131:// Render the current frame
1132://    - Draws everything as computed in last advanceFrame, updates nothing
1133://
1134:// RETURN:  true on success (always)
1135://////////////////////////////////////////////////////////////////////////////
1136:bool CMotherboard::renderFrame()
1137:{
1138:   // These are used as backups when fading the light out
1139:   fRGBA l0Ambient,l0Diffuse,l0Specular; int l0Shininess;
1140:   fRGBA lsAmbient,lsDiffuse,lsSpecular; int lsShininess;
1141:
1142:   // Camera
1143:   glMatrixMode(GL_MODELVIEW);         // Reset modelview matrix
1144:   glLoadIdentity();
1145:   flight.cam.execute();               // Apply camera transform
1146:   frustum.setFrustum(flight.cam);     // Rotate view frustum according to camera
1147:
1148:   // If we are in fadeout mode, fade the lights to a lower level
1149:   if (nElapsedTime>=FADESTART) {
1150:      // Calculate fade factor from 0..1
1151:      float fFadeFactor = calcFactor(nElapsedTime,FADESTART,FADETIME);
1152:
1153:      // Save light settings
1154:      l0Ambient = light0.ambient;      lsAmbient = lightSocket.ambient;
1155:      l0Diffuse = light0.diffuse;      lsDiffuse = lightSocket.diffuse;
1156:      l0Specular= light0.specular;     lsSpecular= lightSocket.specular;
1157:      l0Shininess=light0.shininess;    lsShininess=lightSocket.shininess;
1158:
1159:      // Fade out scaled light settings according to fade factor computed above
1160:      fFadeFactor = 1-fFadeFactor;
1161:      light0.ambient = l0Ambient*fFadeFactor;
1162:      light0.diffuse = l0Diffuse*fFadeFactor;
1163:      light0.specular= l0Specular*fFadeFactor;
1164:      lightSocket.ambient = lsAmbient*fFadeFactor;
1165:      lightSocket.diffuse = lsDiffuse*fFadeFactor;
1166:      lightSocket.specular= lsSpecular*fFadeFactor;
1167:
1168:      // If we are in the final fadeout, fade the saved lights too, not just the copies
1169:      if (nElapsedTime>=FINALSTART)
1170:      {
1171:         float fRate = (1.0f/30.0f) * (FINALTIME / 1000.0f);      // Speed of final fade down
1172:         l0Ambient  -= fwhite*fRate*m_oEnvInfo.fFrameFactor;    lsAmbient  -= 
-->fwhite*fRate*m_oEnvInfo.fFrameFactor;
1173:         l0Diffuse  -= fwhite*fRate*m_oEnvInfo.fFrameFactor;    lsDiffuse  -= 
-->fwhite*fRate*m_oEnvInfo.fFrameFactor;
1174:         l0Specular -= fwhite*fRate*m_oEnvInfo.fFrameFactor;    lsSpecular -= 
-->fwhite*fRate*m_oEnvInfo.fFrameFactor;
1175:         l0Shininess-= fRate*m_oEnvInfo.fFrameFactor;           lsShininess-= fRate*m_oEnvInfo.
-->fFrameFactor;
1176:      }
1177:   }
1178:   ////////// BEGIN STUFF THAT WILL BE AFFECTED BY THE FIRST FADEOUT //////////
1179:
1180:   // Apply light position & color
1181:   light0.executeLight();
1182:   lightSocket.executeLight();
1183:
1184:   // Draw static objects
1185:   drawVisibleObject(computer);              // Computer case            
1186:   drawVisibleObject(motherboard);           // Motherboard plane            
1187:   drawVisibleObject(coolchip);              // Big chip near CPU socket (credits on it)
1188:   drawVisibleObject(RAM);                   // Memory chip
1189:   drawVisibleObject(AGP);                   // AGP, ISA slots
1190:   drawVisibleObject(ISA);
1191:
1192:   // Multiple static objects
1193:   int nObject;
1194:   for (nObject=0; nObject<num_Res; nObject++)  drawVisibleObject(resistor[nObject]);
1195:   for (nObject=0; nObject<num_Cap; nObject++)  drawVisibleObject(capacitor[nObject]);
1196:   for (nObject=0; nObject<num_PCI; nObject++)  drawVisibleObject(PCI[nObject]);
1197:   for (nObject=0; nObject<num_DIMM; nObject++) drawVisibleObject(DIMM[nObject]);
1198:   for (nObject=0; nObject<num_Chip; nObject++) drawVisibleObject(microchip[nObject]);
1199:
1200:   /////////// END STUFF THAT WILL BE AFFECTED BY THE FIRST FADEOUT ////////////
1201:
1202:   // Okay, now there are a few things that we want drawn even if the light is out
1203:   // So restore the light
1204:   if (nElapsedTime>=FADESTART) {
1205:      // Bring back light from saved values
1206:      light0.ambient = l0Ambient;
1207:      light0.diffuse = l0Diffuse;
1208:      light0.specular= l0Specular;
1209:      lightSocket.ambient = lsAmbient;
1210:      lightSocket.diffuse = lsDiffuse;
1211:      lightSocket.specular= lsSpecular;
1212:
1213:      // Re-apply the lights
1214:      light0.executeLight();
1215:      lightSocket.executeLight();
1216:   }
1217:
1218:   // Static objects not affected by the first fade out
1219:   drawVisibleObject(socketHandle);          // Movable handle to CPU socket
1220:   drawVisibleObject(socket);                // CPU socket            
1221:   drawVisibleObject(CPUchip);               // CPU chip            
1222:
1223:   // Transparent objects, effects
1224:   // - Disable writing to the depth buffer
1225:   glDepthMask(GL_FALSE);
1226:
1227:   // Draw flare of light that grows out from the CPU socket, along the motherboard
1228:   flare.execute();
1229:
1230:   // Draw light beam etching mindware logo onto CPU chip
1231:   // - Only if the time is right
1232:   if (nElapsedTime>ANIMSTART && nElapsedTime<ANIMSTART+ANIMTIME)
1233:      lightBeam.execute();
1234:
1235:   // Draw glowing column of light & the little whisps
1236:   // - Only if the time is right
1237:   if (nElapsedTime>GLOWSTART && nElapsedTime<CHIPSTART+CHIPTIME)
1238:   {
1239:      // Draw the column
1240:      glowing.execute();
1241:      // Draw the whisps
1242:      for (nObject=0; nObject<nWhispy; nObject++)
1243:         whispy[nObject].execute();
1244:   }
1245:
1246:   // Draw bump map on motherboard
1247:   if (m_szBumpFile)
1248:      motherboardBump.execute();
1249:
1250:   /* Glowing sphere
1251:   /*
1252:      -- This is commented out by popular demand.  I really liked it because you could see
1253:         what was causing the light.  But no matter what I did, it looked lame.  So it is
1254:         removed.
1255:   glPushMatrix();
1256:      // No texture, enable additive blending
1257:      glBindTexture(GL_TEXTURE_2D,0);
1258:      glEnable(GL_BLEND);
1259:      glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1260:      glTranslatef(light0.position.x,light0.position.y,light0.position.z);
1261:
1262:      // White, vary 40%-60% transparency
1263:      glMaterialfv(GL_FRONT,GL_AMBIENT,fRGBA(0,0,0,1));
1264:      glMaterialfv(GL_FRONT,GL_DIFFUSE,fRGBA(0,0,0,frand01()*0.2f + 0.4f));
1265:      glMaterialfv(GL_FRONT,GL_SPECULAR,fRGBA(0,0,0,1));
1266:      glMaterialfv(GL_FRONT,GL_EMISSION,fRGBA(1,1,1,1));
1267:
1268:      // Draw sphere
1269:      GLUquadricObj *qoSphere = gluNewQuadric();
1270:      gluQuadricTexture(qoSphere, GL_FALSE);
1271:      gluQuadricDrawStyle(qoSphere, GLU_FILL);
1272:      gluSphere(qoSphere, 0.5f, 10, 10);
1273:      gluDeleteQuadric(qoSphere);
1274:
1275:      glDisable(GL_BLEND);
1276:   glPopMatrix();
1277:   */
1278:
1279:   // Draw debugging info if in debug mode
1280:   #ifdef _DEBUG
1281:   if (m_oEnvInfo.bShowDebugInfo)
1282:   {
1283:      glMaterialfv(GL_FRONT,GL_EMISSION,white);
1284:      m_oEnvInfo.OglDebug.printf(0,48,0,"start: %d, frame: %d",nTimeStart,flight.nFrame);
1285:      glMaterialfv(GL_FRONT,GL_EMISSION,black);
1286:      glMaterialfv(GL_FRONT,GL_EMISSION,fwhite);
1287:      m_oEnvInfo.OglDebug.printf(0,48,0,"start: %d, frame: %d",nTimeStart,flight.getFrame());
1288:      glMaterialfv(GL_FRONT,GL_EMISSION,fblack);
1289:   }
1290:   #endif
1291:
1292:   // Re-enable the depth mask
1293:   glDepthMask(GL_TRUE);
1294:
1295:   // This is unneeded
1296:   glFlush();
1297:
1298:   return true;
1299:}
1300:
1301://////////////////////////////////////////////////////////////////////////////
1302://
1303:// Advance the animation for the motherboard effect
1304://    - Computes the procedural texture img_mindware and sends it to opengl
1305://    - Computes the procedural 1D texture for the "etching" light beam
1306://
1307:// RETURNS:    true for success (always)
1308://////////////////////////////////////////////////////////////////////////////
1309:bool CMotherboard::advanceFrame()
1310:{
1311:   // Update elapsed time
1312:   nElapsedTime = m_oEnvInfo.getMusicTime() - nTimeStart;
1313:
1314:   // Apply motion set during manual control
1315:#ifdef MANUAL_CONTROL
1316:   flight.cam.position += motion;               // Apply movement, rotation vectors
1317:   flight.cam.rotateDeg(rotation);
1318:   motion  = motion/2;                          // Slow down motion, rotation
1319:   rotation= rotation/2;
1320:#else
1321:   // Tell the fly-through the new time - it will update position accordingly
1322:   flight.setCamera(nElapsedTime);
1323:#endif
1324:
1325:   // CPU chip falling
1326:   if (nElapsedTime>=CHIPSTART)
1327:   {
1328:      // Compute factor 0..1, and new chip position
1329:      float fChipFactor = calcFactor(nElapsedTime,CHIPSTART,CHIPTIME);
1330:      CPUchip.position = socket.position + fVector3D(0,0,100*(1-fChipFactor)+1);
1331:   }
1332:
1333:   // Do glowing socket stuff
1334:   // (do this after the chip falls because glowingBurst() references the chip position)
1335:   if (nElapsedTime>=GLOWSTART && nElapsedTime<CHIPSTART+CHIPTIME) {
1336:      // Glowing factor 0...1
1337:      float fGlowFactor = calcFactor(nElapsedTime,GLOWSTART,GLOWTIME);
1338:
1339:      // Drawing the burst of light
1340:      advanceGlowingBurst(fGlowFactor);
1341:
1342:      // Drawing the whisps of light
1343:      for (int i=0; i<nWhispy; i++) {
1344:         if (whispy[i].position.z >= CPUchip.position.z - WHISP_SIZE*2) {
1345:            whispyData[i].reset();
1346:            whispy[i].objects[0].emission(0.3f,frand01()+0.3f,frand01()+0.3f,1);
1347:         }
1348:         whispyData[i].advance(m_oEnvInfo.fFrameFactor);
1349:         whispy[i].objects[0].diffuse(0,0,0,whispyData[i].bright);
1350:         whispy[i].position = motherboard.position + fVector3D(0,49,0) + whispyData[i].fluxPos;
1351:      }
1352:   }
1353:
1354:   // Socket handle returns after chip falls
1355:   if (nElapsedTime>=CHIPSTART+CHIPTIME) {
1356:      float fSockFactor = calcFactor(nElapsedTime,CHIPSTART+CHIPTIME,GLOWTIME);
1357:
1358:      socketHandle.direction(0,fSockFactor*-90,0);
1359:   }
1360:
1361:   // Chip logo appears
1362:   if (nElapsedTime>=ANIMSTART) {
1363:      float fAnimFactor = calcFactor(nElapsedTime,ANIMSTART,ANIMTIME);
1364:
1365:      advanceChipTexture(fAnimFactor);
1366:   }
1367:
1368:   // Light source is 10 units ahead of the camera position
1369:   light0.position = flight.cam.position + flight.cam.getForward() * LIGHT_FWD_DIST;
1370:   light0.position.z = motherboard.position.z + LIGHT_HOVER_Z;
1371:
1372:   // Update light map
1373:   advanceLightMap();
1374:   return true;
1375:};
1376:
1377://////////////////////////////////////////////////////////////////////////////
1378://
1379:// Animate glowing from CPU socket
1380://   - Advance the column of light from the CPU socket
1381://   - Enlarge the flare from the CPU socket
1382://
1383:// ARGS:    fGlowFactor,   0..1 (0 is starting, 1 is completed)
1384://////////////////////////////////////////////////////////////////////////////
1385:void CMotherboard::advanceGlowingBurst(float fGlowFactor)
1386:{
1387:   Object3D &glowObj = glowing.objects[0];      // Working column of light
1388:   Object3D &glowOld = glowingOld.objects[0];   // Original column of light
1389:
1390:   // First half of flare is fast
1391:   if (fGlowFactor<0.50f) {
1392:      if (fGlowFactor>0.01f)                 // Don't scale below a certain size
1393:         flare.scale(fGlowFactor*1000);      //  or it goofs up
1394:      flare.objects[0].diffuse(0,0,0,fGlowFactor*2);
1395:   } else
1396:   // Second half of flare is really fast
1397:   if (fGlowFactor<1.00f) {
1398:      flare.scale(500 + (fGlowFactor-0.5f)*2500);
1399:      flare.objects[0].diffuse(0,0,0,(1-fGlowFactor)*2);
1400:   }
1401:   // Rotate the flare for effect
1402:   flare.direction = fVector3D(0,0,fGlowFactor*360);
1403:
1404:   // Apply the light coming from the socket
1405:   // - This yellowish light floats up from the motherboard to just above the socket
1406:   // - It flickers randomly, and only affects very nearby objects (due to attenuation)
1407:   lightSocket.on();
1408:   lightSocket.setLight(fGlowFactor,fGlowFactor,fGlowFactor,64,fRGBA(1,1,0));
1409:   lightSocket.position = motherboard.position + fVector3D(0,49,1 + fGlowFactor*5);
1410:   glLightf(GL_LIGHT0+lightSocket.nLightID,GL_LINEAR_ATTENUATION,0.05f+frand01()*0.025f);
1411:
1412:   // Turn the socket handle
1413:   socketHandle.direction(0,fGlowFactor*90 - 90,0);
1414:
1415:   // The glowing column of light has the alpha value randomly fluctuate by 10%
1416:   // - It grows upward (z+) from the original position
1417:   glowObj.diffuse(0,0,0,fGlowFactor/4+frand01()*0.1f);
1418:   float fHeight = CPUchip.position.z - glowing.position.z - 1;
1419:   glowObj.points[2].y = glowOld.points[2].y + fGlowFactor*fHeight;
1420:   glowObj.points[3].y = glowOld.points[3].y + fGlowFactor*fHeight;
1421:   glowObj.points[6].y = glowOld.points[6].y + fGlowFactor*fHeight;
1422:   glowObj.points[7].y = glowOld.points[7].y + fGlowFactor*fHeight;
1423:}
1424:
1425:/********************************** CWhisp ***********************************/
1426:
1427://////////////////////////////////////////////////////////////////////////////
1428://
1429:// Reset a whisp
1430://    - Move to a random position at the base of the socket
1431://    - Pick random speeds and motions
1432://////////////////////////////////////////////////////////////////////////////
1433:void CWhisp::reset()
1434:{
1435:   // Place somewhere within the socket, directly on the motherboard
1436:   basePos = fVector3D(frand11()*SOCKET_XSIZE, frand11()*SOCKET_YSIZE, 0);
1437:   // Reset flux animation factor
1438:   fluxCnt = 0;
1439:   // Pick a vertical motion speed and a speed for fluxiness
1440:   zSpeed  = frand01() * 1.0f + 0.4f;
1441:   rSpeed  = frand01() * 0.3f + 0.1f;
1442:
1443:   // Pick a direction 0 - 360 degrees for the particle to fluctuate
1444:   float fluxDeg = rand() % 360;
1445:   fluxDir(sinf(DtoR(fluxDeg)), cosf(DtoR(fluxDeg)), 0);
1446:   // Brightness starts at fully transparent
1447:   bright = 0.0f;
1448:}
1449:
1450://////////////////////////////////////////////////////////////////////////////
1451://
1452:// Advance the animation for a CWhisp
1453://    - Moves object position, updates brightness
1454://
1455:// ARGS:    fFrameFactor,   0..1 (0 is starting, 1 is completed)
1456://////////////////////////////////////////////////////////////////////////////
1457:void CWhisp::advance(float fFrameFactor)
1458:{
1459:   // Move whisp according to whispy speed and frame rate
1460:   basePos.z += zSpeed * fFrameFactor;                // Move along z axis
1461:   fluxCnt   += rSpeed * fFrameFactor;                // Swirl along xy axis
1462:
1463:   // Final position is base position + fluxing position * fluxiness
1464:   fluxPos = basePos + fluxDir * sinf(fluxCnt) * FLUXINESS;
1465:
1466:   // Make brighter, unless at max brightness
1467:   if (bright>=1)
1468:      bright = 1;
1469:   else
1470:      bright += zSpeed * 0.05f * fFrameFactor;
1471:}
1472:
1473:/*** This will be replaced with something else later
1474:
1475:???WHG??? Implement array of pressed keys in CVidGLDerive
1476:
1477:bool CMotherboard::keyEvent(int nKey, bool bPress)
1478:{
1479:   if (!bPress)
1480:      return true;
1481:
1482:   Camera3D &cam = flight.cam;
1483:
1484:   switch(nKey) {
1485:      case VK_ESCAPE:
1486:         endGL();
1487:         break;
1488:
1489:      case '0':      flight.start(); break;
1490:#ifdef _DEBUG
1491:      case '1':      std::cout << cam.position.x << ","
1492:                               << cam.position.y << ","
1493:                               << cam.position.z << std::endl;
1494:                     std::cout << cam.getDirection().x << ","
1495:                               << cam.getDirection().y << ","
1496:                               << cam.getDirection().z << ","
1497:                               << cam.getDirection().w << std::endl;
1498:                     break;
1499:#endif
1500:
1501:      case 'W':      motion += cam.getForward()*MOTIONSPEED;  break;
1502:      case 'S':      motion -= cam.getForward()*MOTIONSPEED;  break;
1503:      case 'A':      motion += cam.getSideways()*MOTIONSPEED; break;
1504:      case 'D':      motion -= cam.getSideways()*MOTIONSPEED; break;
1505:      case 'Q':      motion += cam.getUpward()*MOTIONSPEED;   break;
1506:      case 'Z':      motion -= cam.getUpward()*MOTIONSPEED;   break;
1507:
1508:      case VK_UP:    rotation.x -= 1.0f; break;
1509:      case VK_DOWN:  rotation.x += 1.0f; break;
1510:      case VK_RIGHT: rotation.y += 1.0f; break;
1511:      case VK_LEFT:  rotation.y -= 1.0f; break;
1512:      case VK_HOME  :rotation.z -= 1.0f; break;
1513:      case VK_PRIOR :rotation.z += 1.0f; break;
1514:   }
1515:
1516:   return true;
1517:}
1518:
1519:***/
1520:

Statistics:

Unchanged lines: 1391
Deleted lines: 98
Inserted lines: 30
Lines in diff: 1519
Lines in first text: 1489
Lines in second text: 1421