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