home *** CD-ROM | disk | FTP | other *** search
/ NEXT Generation 27 / NEXT27.iso / pc / demos / emperor / dx3.exe / SDK / SAMPLES / DS3DVIEW / RLDS3D.CPP < prev    next >
C/C++ Source or Header  |  1996-09-04  |  81KB  |  2,160 lines

  1. /************************************************************************************************************
  2.  
  3.   Direct3DRM and DirectSound3D interface designed for the Viewer Sample Application
  4.  
  5.   (c) 1996 Microsoft Corporation
  6.  
  7. ************************************************************************************************************/
  8.  
  9.  
  10. #include "rlds3d.h"
  11. #include "ds3dvi.h"     // "Internal" include stuff, like some structures
  12.  
  13. // For resource symbols
  14. #include "resource.h"
  15.  
  16. #include "d3drmwin.h"
  17.  
  18. #include <windows.h>
  19. #include <windowsx.h>
  20.  
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <malloc.h>
  24. #include <math.h>
  25. #include <direct.h>
  26.  
  27. #include <objbase.h>
  28. #include <initguid.h>
  29.  
  30. #include "rodcone.h"
  31. #include "file.h"
  32.  
  33. #include <mmsystem.h>
  34. #include "dsound.h"
  35. #include "dsutil3d.h"
  36.  
  37.  
  38. /*
  39. ** This is an interface between the viewer and the RL/DS3D APIs which provides the
  40. **   functionality required by the viewer in simplified form.
  41. **
  42. ** Note: This is not an object-oriented API since there should only be need for one
  43. **   copy at a time and this way C++ to C conversions should be fairly easy
  44. **
  45. ** DESCRIPTION: The user interfaces the 3D world and objects therein by selecting an item
  46. **   with the mouse (on the screen) and performing operations on that item.  The user will
  47. **   also be able to perform operations on the camera and a few global operations applied
  48. **   to all objects.
  49. **
  50. ** USAGE: Create the interface using RLDS3D_Initialize() (returns FALSE if not created)
  51. **   and remove it using RLDS3D_Deinitialize().  The Render() functionality draws the world
  52. **   on the screen.  Other functionality as described.
  53. */
  54.  
  55. /*
  56. ***********************************  GLOBALS  *************************************
  57. */
  58.  
  59. /*
  60. ** VIEWER LPDIRECT3DRMFRAME APPDATA STRUCTURE
  61. **
  62. ** Pointers to this class are placed (as necessary) in the appdata fields for LPDIRECT3DRMFRAMEs.  Any
  63. **   necessary data to be attached to frames should be added to this class
  64. */
  65.  
  66. class FRAMEAPPDATA {
  67. public:
  68.         // Sound buffer interfaces (the standard one and the 3D interface)
  69.         LPDIRECTSOUNDBUFFER Sound;
  70.         LPDIRECTSOUND3DBUFFER i3DSound;
  71.  
  72.         // Whether or not this frame is currently in orbit (and thus contained in a temporary frame)
  73.         BOOL bOrbiting;
  74.         // Whether or not this frame is a bullet (and thus has a bullet callback and dialog)
  75.         BOOL bBullet;
  76.         // A window handle associated with the orbiting/bullet state which controls speed, etc.
  77.         HWND hDlg;
  78.  
  79.         FRAMEAPPDATA() : Sound(NULL), i3DSound(NULL), bOrbiting(FALSE), bBullet(FALSE), hDlg(0) {};
  80.         ~FRAMEAPPDATA() { 
  81.                 if (i3DSound) i3DSound->Release();
  82.                 if (Sound) Sound->Release(); 
  83.         };
  84. };
  85. typedef FRAMEAPPDATA* LPFRAMEAPPDATA;
  86.  
  87. // Interface to the D3DRM functions
  88. LPDIRECT3DRM lpD3DRM = NULL;
  89.  
  90. // Pointer to the Directsound API
  91. LPDIRECTSOUND lpDS = NULL;
  92.  
  93. // Clipper for the DDraw surface
  94. LPDIRECTDRAWCLIPPER lpDDClipper = NULL;
  95.  
  96. // Handle to the API's parent window (passed in Initialize)
  97. HWND    hwndParent;
  98.  
  99. // Handle to our instance
  100. HANDLE  hinst;
  101.  
  102. AppInfo* info;
  103.  
  104. /*
  105. ** Globals used for editing what is in the D3DRM world
  106. */
  107.  
  108. // Selected frame (contains visual currently selected by user)
  109. LPDIRECT3DRMFRAME sFrame = NULL;
  110.  
  111. // Whether or not editing box should be shown around the selected object
  112. BOOL showBoxes = FALSE;
  113.  
  114. // Currently selected visual (if one is selected)
  115. static LPDIRECT3DRMMESHBUILDER sVisual = NULL;
  116.  
  117. // Currently selected light (if one is selected)
  118. static LPDIRECT3DRMLIGHT sLight = NULL;
  119.  
  120. // Box/Speaker cone to go around the selected item (if boxes are on and if there IS a speaker)
  121. static LPDIRECT3DRMMESH selectionBox = NULL;
  122. static LPDIRECT3DRMMESH selectionSpeaker = NULL;
  123.  
  124. // Clipboard frame and visual allowing cutting and pasting
  125. LPDIRECT3DRMFRAME clipboardFrame = NULL;
  126. LPDIRECT3DRMVISUAL clipboardVisual = NULL;
  127.  
  128. /*
  129. ** Globals for DirectSound3D
  130. */
  131.  
  132. // Listener information (connected to the camera)
  133. LPDIRECTSOUND3DLISTENER lp3DListenerInfo;
  134.  
  135. // Primary Buffer (we don't REALLY need a pointer to it but we keep it just in case)
  136. LPDIRECTSOUNDBUFFER lpDSBuff;
  137.  
  138. /*
  139. ************************  Internal function declarations  ****************************
  140. */
  141.  
  142. // These are not part of the interface and are just used by the RLDS3D functions
  143. static BOOL CreateDevice(HWND win, AppInfo* info);
  144. HRESULT loadTextures(char *name, void *arg, LPDIRECT3DRMTEXTURE *tex);
  145. char* LSTRRCHR( const char* lpString, int bChar );
  146. static void PlaceMesh(LPDIRECT3DRMMESHBUILDER mesh, AppInfo *info);
  147. static BOOL CreateScene(AppInfo* info);
  148. static BOOL RebuildDevice(HWND win, AppInfo* info, int width, int height);
  149. static LPDIRECT3DRMMESHBUILDER makeBox(D3DRMBOX*);
  150. void SelectVisual(LPDIRECT3DRMMESHBUILDER visual, LPDIRECT3DRMFRAME frame);
  151. int ChooseNewColor(HWND, D3DCOLOR*);
  152. void RemoveSoundRecord(LPDIRECT3DRMFRAME owner);
  153. void releaseSoundCallback(LPDIRECT3DRMFRAME frame);
  154. void StopOrbiting(LPDIRECT3DRMFRAME stopme);
  155. void UpdateConeVisual(void);
  156.  
  157. /*
  158. ***********************  Error checker functions  ************************************
  159. */
  160.  
  161. /*
  162. ** These are wrapped around DS or D3DRM calls to appropriately catch errors and handle them.
  163. **
  164. ** The generalized cases are coded, callers can pass forced reactions to errors by using the force_critical variables
  165. */
  166.  
  167. /*
  168. ** In this code the general philosophy for when to use the viewer is when requesting anything from something outside the
  169. **   program.  Thus, asking a frame which we know exists to accept an added visual doesn't get checked, but trying to create
  170. **   a new frame (which tries to allocate memory) is checked in case we've run out of free memory.
  171. */
  172.  
  173. BOOL D3DRM_SUCCEED(HRESULT result, int force_critical, char* info) {
  174.         if (result == D3DRM_OK) return TRUE;
  175.         // Could be 0 for non-critical, 1 for non-critical but reports it, or 2 for critical (notify and quit)
  176.         int priority = 0;
  177.         char* error_string;
  178.         switch (result) {
  179.                 case D3DRMERR_BADALLOC:                 error_string = "Out of memory";                                                         priority = 2; break;
  180.                 case D3DRMERR_BADDEVICE:                error_string = "Device is not compatible with renderer";        priority = 2; break;
  181.                 case D3DRMERR_BADFILE:                  error_string = "Data file is corrupt";                                          priority = 2; break;
  182.                 case D3DRMERR_BADMAJORVERSION:  error_string = "Bad DLL major version";                                         priority = 2; break;
  183.                 case D3DRMERR_BADMINORVERSION:  error_string = "Bad DLL minor version";                                         priority = 2; break;
  184.                 case D3DRMERR_BADOBJECT:                error_string = "Object expected in argument";                           priority = 2; break;
  185.                 case D3DRMERR_BADTYPE:                  error_string = "Bad argument type passed";                                      priority = 1; break;
  186.                 case D3DRMERR_BADVALUE:                 error_string = "Bad argument value passed";                                     priority = 1; break;
  187.                 case D3DRMERR_FACEUSED:                 error_string = "Face already used in a mesh";                           priority = 1; break;
  188.                 case D3DRMERR_FILENOTFOUND:             error_string = "File cannot be opened";                                         priority = 1; break;
  189.                 case D3DRMERR_NOTDONEYET:               error_string = "Unimplemented function called";                         priority = 1; break;
  190.                 case D3DRMERR_NOTFOUND:                 error_string = "Object not found in specified place";           priority = 1; break;
  191.                 case D3DRMERR_UNABLETOEXECUTE:  error_string = "Unable to carry out procedure";                         priority = 2; break;
  192.                 default:                                                error_string = "D3DRM Error: Unable to continue";                       priority = 2; break;
  193.         }
  194.  
  195.         int ret;
  196.         if (force_critical >= 0) priority = force_critical;
  197.         if (priority == 1) {
  198.             ret = MessageBox(hwndParent, error_string, "D3DRM Warning", MB_APPLMODAL|MB_ICONWARNING|MB_OK);
  199.         }
  200.         else if (priority == 2) {
  201.             ret = MessageBox(hwndParent, error_string, "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK);
  202.                 PostMessage(hwndParent, WM_CLOSE,0,0);
  203.         }
  204.         return FALSE;
  205. }
  206.  
  207. /*
  208. ** DS isn't vital for the viewer to run so error messages don't force a quit
  209. */
  210.  
  211. BOOL DS3D_SUCCEED(HRESULT result, int force_critical, char* info) {
  212.         if (result == DS_OK) return TRUE;
  213.         // Could be 0 for no message, 1 for non-critical (simply needs to report it), or 2 for critical (notify and quit)
  214.         int priority = 0;
  215.         char* error_string = NULL;
  216.         switch (result) {
  217.                 case DSERR_ALLOCATED:                   error_string = "Requested resources already in use";                    priority = 1; break;
  218.                 case DSERR_ALREADYINITIALIZED:  error_string = "Object already initialized";                                    priority = 0; break;
  219.                 case DSERR_BADFORMAT:                   error_string = "Wave format not supported";                                             priority = 0; break;
  220.                 case DSERR_BUFFERLOST:                  error_string = "Buffer lost and must be restored";                              priority = 0; break;
  221.                 case DSERR_CONTROLUNAVAIL:              error_string = "Control requested not available";                               priority = 0; break;
  222.                 case DSERR_GENERIC:                             error_string = "Undetermined error";                                                    priority = 1; break;
  223.                 case DSERR_INVALIDCALL:                 error_string = "Invalid call for object's current state";               priority = 0; break;
  224.                 case DSERR_INVALIDPARAM:                error_string = "Invalid parameters passed to object";                   priority = 0; break;
  225.                 case DSERR_NOAGGREGATION:               error_string = "Object does not support aggregation";                   priority = 1; break;
  226.                 case DSERR_NODRIVER:                    error_string = "No sound driver available";                                             priority = 1; break;
  227. //              case DSERR_NOINTERFACE:                 error_string = "Requested COM interface not available";                 priority = 0; break;
  228.                 case DSERR_OUTOFMEMORY:                 error_string = "Out of memory";                                                                 priority = 1; break;
  229.                 case DSERR_PRIOLEVELNEEDED:             error_string = "Caller does not have required priority level";  priority = 0; break;
  230. //              case DSERR_UNINITIALIZED:               error_string = "DirectSound not initialized";                                   priority = 1; break;
  231.                 case DSERR_UNSUPPORTED:                 error_string = "Unsupported function called";                                   priority = 0; break;
  232.                 default:                                                error_string = "Undetermined error";                                                    priority = 1; break;
  233.         }
  234.  
  235.         int ret;
  236.         if (force_critical >= 0) priority = force_critical;
  237.         if (priority == 1) {
  238.             ret = MessageBox(hwndParent, error_string, "DS3D Warning", MB_APPLMODAL|MB_ICONWARNING|MB_OK);
  239.         }
  240.         else if (priority == 2) {
  241.             ret = MessageBox(hwndParent, error_string, "DS3D Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK);
  242.                 PostMessage(hwndParent, WM_CLOSE,0,0);
  243.         }
  244.         return FALSE;
  245. }
  246.  
  247.  
  248.  
  249.  
  250. /*
  251. ************************  INITIALIZATION/DEINITIALIZATION ****************************
  252. */
  253.  
  254. /*
  255. ** Initialize will attach itself to the passed window.  Call initialize after
  256. **  getting a handle for your window but before you display it.
  257. **
  258. ** Initialize will initialize the D3DRM API and return false if it fails.  It will
  259. **  also attempt to initialize a DirectSound3D API, but failing this does not
  260. **  justify a failed Initialize (since the 3D sound isn't a necessary part of
  261. **  the viewer)
  262. */
  263.  
  264. BOOL RLDS3D_Initialize(HWND hwndPW, HANDLE this_inst) {
  265.  
  266.         if (!hwndPW) return FALSE;
  267.  
  268.         hinst = this_inst;
  269.         hwndParent = hwndPW;
  270.  
  271.         // D3DRM API INIT - we force non-critical warnings here because the initialization returns a boolean for success
  272.         
  273.         // Call Direct3DRMCreate to try to make a D3DRM object
  274.         if (!D3DRM_SUCCEED(Direct3DRMCreate(&lpD3DRM), 1)) return FALSE;
  275.  
  276.  
  277.                 if (!D3DRM_SUCCEED(DirectDrawCreateClipper(0, &lpDDClipper, NULL),1)) {
  278.                                         lpD3DRM->Release();
  279.                         return FALSE;
  280.                 }
  281.     
  282.         if (!D3DRM_SUCCEED(lpDDClipper->SetHWnd(0, hwndParent),1)) {
  283.                 lpDDClipper->Release();
  284.                 lpD3DRM->Release();
  285.         return FALSE;
  286.     }
  287.  
  288.  
  289.     // Set up D3DRM information structure
  290.     info = (AppInfo*) malloc(sizeof(AppInfo));
  291.         if (!info) {
  292.                 lpDDClipper->Release();
  293.                 lpD3DRM->Release();
  294.                 return FALSE;
  295.         }
  296.  
  297.     info->model = D3DCOLOR_MONO;
  298.  
  299.     // Calls our internal function to create a viewport and device and attach it to the window (CreateDevice in misc. functionality)
  300.         // (also fills in the global information structure "info" for what D3DRM can do and is currently doing)
  301.         if (CreateDevice(hwndParent, info) == FALSE) {
  302.                 lpDDClipper->Release();
  303.                 lpD3DRM->Release();
  304.                 return FALSE;
  305.         }
  306.         
  307.         // DIRECTSOUND 3D INIT - note, if this fails the entire initialization is still OK.
  308.  
  309.         // Description for our primary buffer creation
  310.         DSBUFFERDESC dsbd;
  311.  
  312.     int ret = IDRETRY;
  313.  
  314.         // Try to create the Directsound objects until we either do it, are told to ignore it, or are told to abort
  315.         while (ret == IDRETRY) {
  316.                 // Create the directsound object
  317.                 if (DS3D_SUCCEED(DirectSoundCreate(NULL, &lpDS, NULL))) {
  318.                         // Set cooperative level
  319.                         if (DS3D_SUCCEED(lpDS->SetCooperativeLevel(hwndParent, DSSCL_PRIORITY))) {
  320.                                 // Create a primary buffer so we can query for a 3D Listener interface
  321.                                 memset(&dsbd, 0, sizeof(DSBUFFERDESC));
  322.                                 dsbd.dwSize = sizeof(DSBUFFERDESC);
  323.                                 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
  324.                                 if (DS3D_SUCCEED(lpDS->CreateSoundBuffer(&dsbd, &lpDSBuff, NULL))) {
  325.                                         
  326.                                         // Make the primary 44.1 KHz so that it sounds better
  327.                                         WAVEFORMATEX wfx;
  328.                                         wfx.wFormatTag = WAVE_FORMAT_PCM;
  329.                                         wfx.nChannels = 2;
  330.                                         wfx.nSamplesPerSec = 44100;
  331.                                         wfx.nAvgBytesPerSec = 44100*2*2;
  332.                                         wfx.nBlockAlign = 4;
  333.                                         wfx.wBitsPerSample = 16;
  334.                                         wfx.cbSize = 0;
  335.                                         lpDSBuff->SetFormat(&wfx);
  336.  
  337.                                         // Get the 3D listener information (error currently ignored)
  338.                                         if (DS3D_SUCCEED(lpDSBuff->QueryInterface(IID_IDirectSound3DListener, (void**) &lp3DListenerInfo))) {
  339.                                                 lp3DListenerInfo->SetDopplerFactor(D3DVAL(100.0), DS3D_IMMEDIATE);
  340.                                         }
  341.                                         else {
  342.                                                 // Failed to get listener info
  343.                                                 lpDSBuff->Release();
  344.                                                 lpDS->Release();
  345.                                                 lpDS = NULL;
  346.                                         }
  347.                                 }
  348.                                 else {
  349.                                         // Failed to create a primary buffer
  350.                                         lpDS->Release();
  351.                                         lpDS = NULL;
  352.                                 }
  353.                         }
  354.                         else {
  355.                                 // Failed to set cooperative level
  356.                                 lpDS->Release();
  357.                                 lpDS = NULL;
  358.                         }
  359.                 }
  360.  
  361.                 // Warn that we could create the DirectSound object
  362.                 if (!lpDS) {
  363.                         ret = MessageBox(hwndParent, "DirectSound 3D could not initialize", "Warning", MB_APPLMODAL|MB_ICONWARNING|MB_ABORTRETRYIGNORE);
  364.                         if (ret == IDABORT) {
  365.                                 lpDDClipper->Release();
  366.                                 lpD3DRM->Release();
  367.                                 return FALSE;
  368.                             }
  369.                 }
  370.                 else ret = IDOK;
  371.         }
  372.         return TRUE;
  373. }
  374.  
  375. /*
  376. ** Deinitializes
  377. */
  378.  
  379. void RLDS3D_Deinitialize() {
  380.         // Releases the DSound interface... this is very important!
  381.         if (lpDS != NULL) {
  382.                 lpDSBuff->Release();
  383.                 lpDS->Release();
  384.         }
  385.         info->dev->Release();
  386.         lpDDClipper->Release();
  387.         lpD3DRM->Release();
  388.         free(info);
  389. }
  390.  
  391. /*
  392. ********************************************  ADDING/REMOVING/EDITING OBJECTS  *********************************
  393. */
  394.  
  395. /*
  396. ** Loads XOF file into the RL world (with textures)
  397. */
  398.  
  399. void RLDS3D_LoadXOF(char* file) {
  400.     if (!file) return;
  401.         LPDIRECT3DRMMESHBUILDER builder = NULL;
  402.         if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) {
  403.                 if (builder->Load(file, NULL, D3DRMLOAD_FROMFILE, loadTextures, NULL) != D3DRM_OK) {
  404.             MessageBox(hwndParent, "Unable to load file", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONEXCLAMATION|MB_OK);
  405.                         builder->Release();
  406.                         return;
  407.                 }
  408.                 PlaceMesh(builder, info);
  409.                 builder->Release();
  410.         }
  411. }
  412.  
  413. /*
  414. ** Sets/Gets whether or not boxes are shown around selected item
  415. */
  416.  
  417. BOOL RLDS3D_GetBoxes(void) {
  418.         return showBoxes;
  419. }
  420.  
  421. void RLDS3D_SetBoxes(BOOL new_val) {
  422.         showBoxes = new_val;
  423.         // Re-selects the currently selected visual so that the box is generated/destroyed around it
  424.         RLDS3D_UpdateSelectionBox();
  425. }
  426.  
  427. /*
  428. ** Updates the bounding box around the selected visual (this could be done using a render callback function to compare the
  429. **   frame's scaling and transform functions instead)
  430. */
  431.  
  432. void RLDS3D_UpdateSelectionBox(void) {
  433.         // When we select this visual, we destroy any existing box and create a new one around it if showBoxes is true.
  434.         SelectVisual(sVisual, sFrame);
  435. }
  436.  
  437. /*
  438. ** Deselects the currently selected 3D visual.
  439. */
  440.  
  441. void RLDS3D_DeselectVisual()
  442. {
  443.         // Removes the bounding box from around it if it's there
  444.     if (sFrame && selectionBox) {
  445.         sFrame->DeleteVisual(selectionBox);
  446.         sFrame->DeleteVisual(selectionSpeaker);
  447.     }
  448.                 
  449.         
  450.     sFrame = NULL;
  451.     sLight = NULL;
  452.     sVisual = NULL;
  453.     selectionBox = NULL;
  454.     selectionSpeaker = NULL;
  455. }
  456.  
  457. /*
  458. ** Given coordinates it selects the first visual under those coordinates in the window's viewport
  459. */
  460.  
  461. void RLDS3D_FindAndSelectVisual(int x, int y, LPBOOL changed) {
  462.     LPDIRECT3DRMVISUAL visual;
  463.     LPDIRECT3DRMFRAME frame;
  464.     LPDIRECT3DRMPICKEDARRAY picked;
  465.     LPDIRECT3DRMFRAMEARRAY frames;
  466.     LPDIRECT3DRMMESHBUILDER mesh;
  467.     LPDIRECT3DRMVIEWPORT view = info->view;
  468.  
  469.         LPDIRECT3DRMFRAME oldframe = sFrame;
  470.         
  471.     /*
  472.      * Make sure we don't try to select the selection box of the current
  473.      * selection.
  474.      */
  475.     RLDS3D_DeselectVisual();
  476.  
  477.     view->Pick(x, y, &picked);
  478.     if (picked)
  479.     {   if (picked->GetSize())
  480.         {
  481.                         // Get the top-level visual
  482.             picked->GetPick(0, &visual, &frames, 0);
  483.             // The frames that contain the visual are placed into a framearray in heiarchical order, take the
  484.             //   last one (the one most closely associated with the visual)
  485.             frames->GetElement(frames->GetSize() - 1, &frame);
  486.             // We can only select meshes so we query the visual to make sure it is one
  487.             if (D3DRM_SUCCEED(visual->QueryInterface(IID_IDirect3DRMMeshBuilder, (void **) &mesh), 0))
  488.             {   
  489.                 // If we're clicking on an orbiting frame then we need to stop it.
  490.                 StopOrbiting(frame);
  491.                 SelectVisual(mesh, frame);
  492.                 mesh->Release();
  493.             }
  494.             frame->Release();
  495.             frames->Release();
  496.             visual->Release();
  497.         }
  498.         picked->Release();
  499.     }
  500.         if (changed) {
  501.                 if (sFrame == oldframe) *changed = FALSE; else *changed = TRUE;
  502.         }
  503. }
  504.  
  505. /*
  506. ** Cuts the current selection to the clipboard
  507. */
  508.  
  509. void RLDS3D_CutVisual()
  510. {
  511.     LPDIRECT3DRMFRAME frame;
  512.  
  513.     if (clipboardFrame)
  514.     clipboardFrame->Release();
  515.  
  516.     if (sFrame)
  517.     {   clipboardFrame = sFrame;
  518.         clipboardVisual = sVisual;
  519.                 
  520.         // If a 3D sound is attached, remove it (sounds not carried to clipboard)
  521.                 RemoveSoundRecord(clipboardFrame);
  522.                 RLDS3D_DeselectVisual();
  523.  
  524.         clipboardFrame->AddRef();
  525.         clipboardFrame->GetParent(&frame);
  526.         if (frame) {
  527.             frame->DeleteChild(clipboardFrame);
  528.             frame->Release();
  529.         }
  530.     }
  531. }
  532.  
  533. /*
  534. ** Copies the current selection to the clipboard
  535. */
  536.  
  537. void RLDS3D_CopyVisual()
  538. {
  539.     LPDIRECT3DRMFRAME frame;
  540.  
  541.     if (sFrame)
  542.     {
  543.                 // Things could really foul up if the clones aren't created so we make sure they are
  544.         if (!D3DRM_SUCCEED(sFrame->Clone(0, IID_IDirect3DRMFrame, (void **) &clipboardFrame))) {
  545.                         // If we've failed, we must make sure the clipboard is empty!
  546.                         clipboardVisual->Release();
  547.                         clipboardVisual = NULL;
  548.                         return;
  549.                 }
  550.  
  551.         if (!D3DRM_SUCCEED(sVisual->Clone(0, IID_IDirect3DRMVisual, (void **) &clipboardVisual))) {
  552.                         // If we've failed, we must make sure the clipboard is empty!
  553.                         clipboardFrame->Release();
  554.                         clipboardFrame = NULL;
  555.                         return;
  556.                 }
  557.  
  558.                 // If a 3D sound is attached, remove it (sounds not carried to clipboard)
  559.                 RemoveSoundRecord(clipboardFrame);
  560.  
  561.         clipboardFrame->AddVisual(clipboardVisual);
  562.                 clipboardVisual->Release();
  563.  
  564.         clipboardFrame->GetParent(&frame);
  565.         if (frame) {
  566.                         frame->DeleteChild(clipboardFrame);
  567.                         frame->Release();
  568.                 }
  569.     }
  570. }
  571.  
  572. /*
  573. ** Pastes the current selection to the window
  574. */
  575.  
  576. void RLDS3D_PasteVisual()
  577. {
  578.     if (clipboardFrame)
  579.     {
  580.         LPDIRECT3DRMFRAME frame;
  581.         LPDIRECT3DRMVISUAL visual;
  582.  
  583.         if (!D3DRM_SUCCEED(clipboardFrame->Clone(0, IID_IDirect3DRMFrame, (void **) &frame))) return;
  584.         if (!D3DRM_SUCCEED(clipboardVisual->Clone(0, IID_IDirect3DRMVisual, (void **) &visual))) {
  585.                         frame->Release();
  586.                         return;
  587.                 }
  588.  
  589.         frame->AddVisual(visual);
  590.         info->scene->AddChild(frame);
  591.                 visual->Release();
  592.                 frame->Release();
  593.     }
  594. }
  595.  
  596. /*
  597. ** Deletes the current selection from the world without copying to the clipboard
  598. */
  599.  
  600. void RLDS3D_DeleteVisual()
  601. {
  602.     if (sFrame) {
  603.         LPDIRECT3DRMFRAME parent, frame;
  604.         // Make a copy of the selected frame ('cause deselecting it will make it inaccessible)
  605.         frame = sFrame;
  606.         // If a 3D sound is attached, remove it
  607.         RemoveSoundRecord(frame);
  608.  
  609.         // Deselect the frame (removes bounding box, etc, also sets sFrame to NULL)
  610.         RLDS3D_DeselectVisual();        
  611.         if (frame->GetAppData()) delete (FRAMEAPPDATA*)frame->GetAppData();
  612.                 frame->GetParent(&parent);
  613.         if (parent) {
  614.                 parent->DeleteChild(frame);
  615.                 parent->Release();
  616.         }
  617.     }
  618. }
  619.  
  620. /*
  621. ** Add a directional light
  622. */
  623.  
  624. void RLDS3D_AddDirectionalLight() {
  625.     LPDIRECT3DRMMESHBUILDER builder;
  626.     LPDIRECT3DRMLIGHT light;
  627.     LPDIRECT3DRMFRAME frame;
  628.  
  629.     if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) {
  630.                 if (D3DRM_SUCCEED(builder->Load("camera.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) {
  631.                         builder->SetQuality(D3DRMRENDER_UNLITFLAT);
  632.                         if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) {
  633.                                 if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) {
  634.                                         frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0));
  635.                                         frame->AddVisual(builder);
  636.                                         frame->AddLight(light);
  637.                                         frame->Release();
  638.                                 }
  639.                                 light->Release();
  640.                         }
  641.                 }
  642.                 builder->Release();
  643.         }
  644. }
  645.  
  646. /*
  647. ** Add a parallel point light
  648. */
  649.  
  650. void RLDS3D_AddParallelPointLight() {
  651.     LPDIRECT3DRMMESHBUILDER builder;
  652.     LPDIRECT3DRMLIGHT light;
  653.     LPDIRECT3DRMFRAME frame;
  654.  
  655.     if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) {
  656.             if (D3DRM_SUCCEED(builder->Load("sphere2.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) {
  657.                         builder->SetQuality(D3DRMRENDER_UNLITFLAT);
  658.                         builder->Scale(D3DVAL(0.2), D3DVAL(0.2), D3DVAL(0.2));
  659.                         if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) {
  660.                                 if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) {
  661.                                         frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0));
  662.                                         frame->AddVisual(builder);
  663.                                         frame->AddLight(light);
  664.                                         frame->Release();
  665.                                 }
  666.                                 light->Release();
  667.                         }
  668.                 }
  669.                 builder->Release();
  670.         }
  671. }
  672.  
  673. /*
  674. ** Add Point Light
  675. */
  676.  
  677. void RLDS3D_AddPointLight() {
  678.     LPDIRECT3DRMMESHBUILDER builder;
  679.     LPDIRECT3DRMLIGHT light;
  680.     LPDIRECT3DRMFRAME frame;
  681.  
  682.     if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) {
  683.                 if (D3DRM_SUCCEED(builder->Load("sphere2.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) {
  684.                         builder->SetQuality(D3DRMRENDER_UNLITFLAT);
  685.                         builder->Scale(D3DVAL(0.2), D3DVAL(0.2), D3DVAL(0.2));
  686.                         if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_POINT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) {
  687.                                 if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) {
  688.                                         frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0));
  689.                                         frame->AddVisual(builder);
  690.                                         frame->AddLight(light);
  691.                                         frame->Release();
  692.                                 }
  693.                                 light->Release();
  694.                         }
  695.                 }
  696.                 builder->Release();
  697.         }
  698. }
  699.  
  700. /*
  701. ** Add a spotlight
  702. */
  703.  
  704. void RLDS3D_AddSpotlight() {
  705.     LPDIRECT3DRMMESHBUILDER builder;
  706.     LPDIRECT3DRMLIGHT light;
  707.     LPDIRECT3DRMFRAME frame;
  708.  
  709.         if (D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&builder))) {
  710.             if (D3DRM_SUCCEED(builder->Load("camera.x", NULL, D3DRMLOAD_FROMFILE, NULL, NULL))) {
  711.                         builder->SetQuality(D3DRMRENDER_UNLITFLAT);
  712.                         if (D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_SPOT, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light))) {
  713.                                 if (D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) {
  714.                                         frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(10.0));
  715.                                         frame->AddVisual(builder);
  716.                                         frame->AddLight(light);
  717.                                         frame->Release();
  718.                                 }
  719.                                 light->Release();
  720.                         }
  721.                 }
  722.                 builder->Release();
  723.         }
  724. }
  725.  
  726.  
  727. /*
  728. ***********************************  OBJECT MOTION/SCALING/COLORING  ****************************************
  729. */
  730.  
  731. /*
  732. ** Chooses a new color and sets the selected object to it... handles the selection of the color, etc.
  733. */
  734.  
  735. void RLDS3D_SetSelColour(void) {
  736.     if (!sFrame) return;
  737.     LPDIRECT3DRMMESHBUILDER mesh;
  738.  
  739.     if (!D3DRM_SUCCEED(sVisual->QueryInterface(IID_IDirect3DRMMeshBuilder, (void**) &mesh),0)) return;
  740.  
  741.     if (sLight)
  742.     {
  743.         D3DCOLOR c = sLight->GetColor();
  744.  
  745.         if (ChooseNewColor(hwndParent, &c)) {
  746.             mesh->SetColor(c);
  747.             sLight->SetColor(c);
  748.         }
  749.     } else {
  750.         D3DCOLOR c;
  751.  
  752.         if (mesh->GetFaceCount()) {
  753.             LPDIRECT3DRMFACEARRAY faces;
  754.             LPDIRECT3DRMFACE face;
  755.             mesh->GetFaces(&faces);
  756.             faces->GetElement(0, &face);
  757.             c = face->GetColor();
  758.             face->Release();
  759.             faces->Release();
  760.         } else
  761.             c = D3DRMCreateColorRGB(D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
  762.  
  763.         if (ChooseNewColor(hwndParent, &c))
  764.             mesh->SetColor(c);
  765.     }
  766.  
  767.     mesh->Release();
  768. }
  769.  
  770.  
  771. /*
  772. ** Moves the camera relative to itself by providing scalars to multiply against the CAMERA-RELATIVE unit vectors
  773. **   forwards/up/right.
  774. */
  775.  
  776. void RLDS3D_SetCamVelRelToCam(D3DVALUE forward, D3DVALUE up, D3DVALUE right) {
  777.         D3DVECTOR vDir, vUp, vRight;
  778.         info->camera->GetOrientation(info->scene, &vDir, &vUp);
  779.         // Cross the UP vector and the RIGHT vector to get the FORWARDS vector
  780.         D3DRMVectorCrossProduct(&vRight, &vUp, &vDir);
  781.         info->camera->SetVelocity(info->scene, vDir.x*forward + vUp.x*up + vRight.x*right,
  782.                                    vDir.y*forward + vUp.y*up + vRight.y*right,
  783.                                    vDir.z*forward + vUp.z*up + vRight.z*right,
  784.                                                                    TRUE);
  785. }
  786.  
  787. /*
  788. ** Rotates the camera around its three axis
  789. **
  790. ** forward_axis is roll, up_axis is yaw, right_axis is pitch
  791. */
  792.  
  793. void RLDS3D_SetCamRotForward(D3DVALUE forward_axis) {
  794.         info->camera->SetRotation(info->camera, 0.0f, 0.0f, 1.0f, forward_axis);
  795. }
  796.  
  797. void RLDS3D_SetCamRotUp(D3DVALUE up_axis) {
  798.         info->camera->SetRotation(info->camera, 0.0f, 1.0f, 0.0f, up_axis);
  799. }
  800.  
  801. void RLDS3D_SetCamRotRight(D3DVALUE right_axis) {
  802.         info->camera->SetRotation(info->camera, 1.0f, 0.0f, 0.0f, right_axis);
  803. }
  804.  
  805. /*
  806. ** Scales the currently selected object by the scale factors...
  807. */
  808.  
  809. void RLDS3D_ScaleSelected(D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) {
  810.         if (!sFrame) return;
  811.         if (!sVisual) return;
  812.         RLDS3D_StopOrbitSelected();
  813.         sVisual->Scale(sx, sy, sz);
  814.         RLDS3D_UpdateSelectionBox();
  815. }
  816.  
  817. /*
  818. ** Moves the currently selected object in the 3D world relative to the camera
  819. */
  820.  
  821. void RLDS3D_SetSelectedVelRelToCam(D3DVALUE forward, D3DVALUE up, D3DVALUE right) {
  822.         if (!sFrame) return;
  823.         RLDS3D_StopOrbitSelected();
  824.         D3DVECTOR vDir, vUp, vRight;
  825.         info->camera->GetOrientation(info->scene, &vDir, &vUp);
  826.         D3DRMVectorCrossProduct(&vRight, &vUp, &vDir);
  827.         sFrame->SetVelocity(info->scene, vDir.x*forward + vUp.x*up + vRight.x*right,
  828.                                                            vDir.y*forward + vUp.y*up + vRight.y*right,
  829.                                                            vDir.z*forward + vUp.z*up + vRight.z*right,
  830.                                                            TRUE);
  831. }       
  832.  
  833. /*
  834. ** Rotates the currently selected object relative to the camera's frame when passed
  835. **   a vector for the axis and an angle of rotation.
  836. ** This is useful because the AXIS which is specified is relative to what appears on the screen with it's origin at the camera
  837. **   (ie: (0,0,1) will be an axis straight into the screen and thus things will spin around the centre of the screen)
  838. */
  839.  
  840. void RLDS3D_SetSelectedRotRelToCam(D3DVALUE AxisX, D3DVALUE AxisY, D3DVALUE AxisZ, D3DVALUE angle) {
  841.         if (!sFrame) return;
  842.         RLDS3D_StopOrbitSelected();
  843.         sFrame->SetRotation(info->camera, AxisX, AxisY, AxisZ, angle);
  844. }
  845.  
  846. /*
  847. ** Moves the currently selected object by x/y pixels on the screen from it's relative position.  Allows a user
  848. **   to drag objects around the screen using the mouse.  Assumes that the distance from the frame to the screen
  849. **   will remain constant
  850. */
  851.  
  852. void RLDS3D_MoveSelectedPosByScreenCoords(double delta_x, double delta_y) {
  853.         if (!sFrame) return;
  854.         RLDS3D_StopOrbitSelected();
  855.         D3DVECTOR p1;
  856.     D3DRMVECTOR4D p2;
  857.     sFrame->GetPosition(info->scene, &p1);
  858.         info->view->Transform(&p2, &p1);
  859.         // returned value is in homogenous coords so we need to multiply by w to get vector coords
  860.     p2.x += D3DMultiply(D3DVAL((D3DVALUE)delta_x), p2.w);
  861.     p2.y += D3DMultiply(D3DVAL((D3DVALUE)delta_y), p2.w);
  862.     info->view->InverseTransform(&p1, &p2);
  863.     sFrame->SetPosition(info->scene, p1.x, p1.y, p1.z);
  864. }
  865.  
  866. void RLDS3D_GetDistanceFactor(D3DVALUE *temp) {
  867.         lp3DListenerInfo->GetDistanceFactor(temp);
  868. }
  869.  
  870. void RLDS3D_GetDopplerFactor(D3DVALUE *temp) {
  871.         lp3DListenerInfo->GetDopplerFactor(temp);
  872. }
  873.  
  874. void RLDS3D_GetRolloffFactor(D3DVALUE *temp) {
  875.         lp3DListenerInfo->GetRolloffFactor(temp);
  876. }
  877.  
  878. void RLDS3D_SetDistanceFactor(D3DVALUE temp) {
  879.         lp3DListenerInfo->SetDistanceFactor(temp, DS3D_IMMEDIATE);
  880. }
  881.  
  882. void RLDS3D_SetDopplerFactor(D3DVALUE temp) {
  883.         lp3DListenerInfo->SetDopplerFactor(temp, DS3D_IMMEDIATE);
  884. }
  885.  
  886. void RLDS3D_SetRolloffFactor(D3DVALUE temp) {
  887.         lp3DListenerInfo->SetRolloffFactor(temp, DS3D_IMMEDIATE);
  888. }
  889.  
  890. void RLDS3D_CommitDeferredSettings(void) {
  891.         lp3DListenerInfo->CommitDeferredSettings();
  892. }
  893.  
  894. BOOL RLDS3D_SoundSelected(void) {
  895.         if (!lpDS) return FALSE;
  896.         if (!sFrame) return FALSE;
  897.         if (!sFrame->GetAppData()) return FALSE;
  898.         if (!((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound) return FALSE;
  899.         return TRUE;
  900. }
  901.  
  902. void RLDS3D_GetSelConeAngles(LPDWORD inner, LPDWORD outer) {
  903.         if (!RLDS3D_SoundSelected) return;
  904.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetConeAngles(inner, outer);
  905. }
  906.  
  907. void RLDS3D_GetSelConeOutsideVolume(LPLONG temp) {
  908.         if (!RLDS3D_SoundSelected) return;
  909.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetConeOutsideVolume(temp);
  910. }
  911.  
  912. void RLDS3D_GetSelMinimumDistance(D3DVALUE *temp) {
  913.         if (!RLDS3D_SoundSelected) return;
  914.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetMinDistance(temp);
  915. }
  916.  
  917. void RLDS3D_GetSelMaximumDistance(D3DVALUE *temp) {
  918.         if (!RLDS3D_SoundSelected) return;
  919.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->GetMaxDistance(temp);
  920. }
  921.         
  922. void RLDS3D_SetSelConeAngles(DWORD inner, DWORD outer) {
  923.         if (!RLDS3D_SoundSelected) return;
  924.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetConeAngles(inner, outer, DS3D_IMMEDIATE);
  925.         UpdateConeVisual();
  926. }
  927.  
  928. void RLDS3D_SetSelConeOutsideVolume(LONG temp) {
  929.         if (!RLDS3D_SoundSelected) return;
  930.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetConeOutsideVolume(temp, DS3D_IMMEDIATE);
  931. }
  932.  
  933. void RLDS3D_SetSelMinimumDistance(D3DVALUE temp) {
  934.         if (!RLDS3D_SoundSelected) return;
  935.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetMinDistance(temp, DS3D_IMMEDIATE);
  936. }
  937.  
  938. void RLDS3D_SetSelMaximumDistance(D3DVALUE temp) {
  939.         if (!RLDS3D_SoundSelected) return;
  940.         ((LPFRAMEAPPDATA)(sFrame->GetAppData()))->i3DSound->SetMaxDistance(temp, DS3D_IMMEDIATE);
  941. }
  942.  
  943.  
  944.  
  945.  
  946.  
  947. /*
  948. ** Orbit control windows have a pointer to the orbiting frame attached to them.  The frame, in return, has the handle of the window in it's
  949. ** frameappdata.  When the window is used (the value modified/Stop Orbit button hit) the window modifies the frame and itself appropriately.
  950. ** When the frame is modified (ie: Destroyed) it passes messages to the window appropriately.
  951. */
  952.  
  953. /*
  954. ** Structure to store info about an orbit
  955. */
  956.  
  957. struct ORBITDATA {
  958.         LPDIRECT3DRMFRAME orbit_frame;
  959.         LPDIRECT3DRMFRAME child_frame;
  960.         D3DVECTOR axis; // Axis around which the orbit is going
  961.         D3DVALUE speed; // Speed (angles per second)
  962. };
  963.  
  964. typedef ORBITDATA* LPORBITDATA;
  965.  
  966. /*
  967. ** Structure to store info about a bullet, used by both the dialog box and the callback function
  968. */
  969.  
  970. struct BULLETDATA {
  971.         LPDIRECT3DRMFRAME bullet_frame;
  972.         D3DVECTOR vStartPosition;
  973.         D3DVECTOR vDirection;
  974.         D3DVALUE fSpeed;
  975.         D3DVALUE fTime;
  976. };
  977.  
  978. typedef BULLETDATA* LPBULLETDATA;
  979.  
  980. static void CDECL bulletCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delta) {
  981.         LPBULLETDATA binfo = (LPBULLETDATA)arg;
  982.         if (binfo->fTime <= D3DVAL(0.0)) {
  983.                 binfo->fTime = D3DVAL(10.0); // Ten seconds, 5 for before and 5 for after.
  984.                 D3DVALUE mult = D3DDivide(D3DMultiply(binfo->fTime, binfo->fSpeed), D3DVAL(-2.0)); // Calculate the displacement multiplier for calculating starting position
  985.                 obj->SetPosition(info->scene, binfo->vStartPosition.x + D3DMultiply(mult, binfo->vDirection.x),
  986.                                                   binfo->vStartPosition.y + D3DMultiply(mult, binfo->vDirection.y),
  987.                                                                           binfo->vStartPosition.z + D3DMultiply(mult, binfo->vDirection.z));
  988.         }
  989.         else binfo->fTime -= delta;
  990. }
  991.  
  992.  
  993. /*
  994. ** Stops the passed frame from orbiting or from bulleting
  995. */
  996.  
  997. void StopOrbiting(LPDIRECT3DRMFRAME stopme) {
  998.         // If it isn't orbiting we don't need to stop it!
  999.         if (!stopme) return;
  1000.         if (!stopme->GetAppData()) return;
  1001.         FRAMEAPPDATA* data = (FRAMEAPPDATA*)stopme->GetAppData();
  1002.         if (data->bOrbiting) {
  1003.                 if (data->hDlg) {
  1004.                         // We explicitly call EndDialog on the orbit's control without sending it any message.  As a result we have
  1005.                         //   to grab it's associated orbitdata and delete it ourselves.
  1006.                         LPORBITDATA orbit_data = (LPORBITDATA)GetWindowLong(data->hDlg, DWL_USER);
  1007.                         if (orbit_data) delete orbit_data;
  1008.                         EndDialog(data->hDlg, TRUE);
  1009.                 }
  1010.  
  1011.                 // Get its parent
  1012.                 LPDIRECT3DRMFRAME parent;
  1013.                 stopme->GetParent(&parent);
  1014.                 
  1015.                 // Put this frame back into the scene frame (this assumes that's where it came from)
  1016.                 info->scene->AddChild(stopme);
  1017.                 
  1018.                 // The parent frame of an orbiting visual is only around to make the object orbit, so we don't need it
  1019.                 parent->Release();
  1020.                 data->bOrbiting = FALSE;
  1021.                 data->hDlg = 0;
  1022.         }
  1023.         if (data->bBullet) {
  1024.                 if (data->hDlg) {
  1025.                         LPBULLETDATA bullet_data = (LPBULLETDATA)GetWindowLong(data->hDlg, DWL_USER);
  1026.                         stopme->SetPosition(info->scene, bullet_data->vStartPosition.x, bullet_data->vStartPosition.y, bullet_data->vStartPosition.z);
  1027.                         if (bullet_data) delete bullet_data;
  1028.                         EndDialog(data->hDlg, TRUE);
  1029.                         stopme->DeleteMoveCallback(bulletCallback, bullet_data);
  1030.                 }
  1031.                 stopme->SetVelocity(info->scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0), TRUE);
  1032.                 data->bBullet = FALSE;
  1033.                 data->hDlg = 0;
  1034.         }
  1035. }
  1036.  
  1037. void RLDS3D_StopOrbitSelected() {
  1038.         StopOrbiting(sFrame);
  1039. }
  1040.  
  1041. /*
  1042. ** Windows procedure for the orbit dialog box
  1043. */  
  1044.  
  1045. BOOL CALLBACK OrbitDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam)
  1046. {
  1047.     lparam = lparam;
  1048.  
  1049.         LPORBITDATA my_data = (ORBITDATA*)GetWindowLong(win, DWL_USER);
  1050.         char lpszTS[100];
  1051.  
  1052.     switch (msg)
  1053.     {
  1054.     case WM_INITDIALOG:
  1055.                 // We've set it up to pass in the pointer to our case-specific data (through WM_INITDIALOG)
  1056.                 // so we tuck it away for later use.  This points to the ORBITDATA structure associated with
  1057.                 // this dialog box
  1058.                 SetWindowLong(win, DWL_USER, lparam);
  1059.                 sprintf(lpszTS, "%f", ((LPORBITDATA)lparam)->speed);
  1060.                 SendDlgItemMessage(win, IDC_ORBIT, WM_SETTEXT, 0, (LPARAM) lpszTS);
  1061.                 SendDlgItemMessage(win, IDC_ORBIT, EM_SETLIMITTEXT, 100, 0);
  1062.         return TRUE;
  1063.  
  1064.     case WM_COMMAND:
  1065.                 {
  1066.                         if (wparam == IDOK) {
  1067.                                 // Stopping the orbit will end the dialog for us.
  1068.                                 StopOrbiting(my_data->child_frame);
  1069.                                 return TRUE;
  1070.                         }
  1071.                         else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_ORBIT) {
  1072.                                 // Get string length
  1073.                                 int stringlength = SendDlgItemMessage(win, IDC_ORBIT, EM_LINELENGTH, 0, 0);
  1074.                                 // Check to make sure the string isn't too long - atof accepts up to 100 chars
  1075.                                 if (stringlength > 99) return TRUE;
  1076.                                 lpszTS[0] = (char)stringlength;
  1077.                                 // Get string
  1078.                                 SendDlgItemMessage(win, IDC_ORBIT, EM_GETLINE, 0, (LPARAM) lpszTS);
  1079.                                 lpszTS[stringlength] = 0;
  1080.                                 // Store speed and set new rotation
  1081.                                 my_data->speed = D3DVAL(atof(lpszTS));
  1082.                                 my_data->orbit_frame->SetRotation(info->scene, my_data->axis.x, my_data->axis.y, my_data->axis.z, my_data->speed);
  1083.                                 return TRUE;
  1084.                         }
  1085.                         else return FALSE;
  1086.                 }               
  1087.         break;
  1088.         }
  1089.         return FALSE;
  1090. }
  1091.  
  1092. /*
  1093. ** Windows proc for the bullet dialog box
  1094. */
  1095.  
  1096. BOOL CALLBACK BulletDlgProc(HWND win, UINT msg, WPARAM wparam, LPARAM lparam)
  1097. {
  1098.     lparam = lparam;
  1099.  
  1100.         LPBULLETDATA my_data = (BULLETDATA*)GetWindowLong(win, DWL_USER);
  1101.         char lpszTS[100];
  1102.  
  1103.     switch (msg)
  1104.     {
  1105.     case WM_INITDIALOG:
  1106.                 // We've set it up to pass in the pointer to our case-specific data (through WM_INITDIALOG)
  1107.                 // so we tuck it away for later use.  This points to the ORBITDATA structure associated with
  1108.                 // this dialog box
  1109.                 SetWindowLong(win, DWL_USER, lparam);
  1110.                 sprintf(lpszTS, "%f", ((LPBULLETDATA)lparam)->fSpeed);
  1111.                 SendDlgItemMessage(win, IDC_BULLET, WM_SETTEXT, 0, (LPARAM) lpszTS);
  1112.                 SendDlgItemMessage(win, IDC_BULLET, EM_SETLIMITTEXT, 100, 0);
  1113.         return TRUE;
  1114.  
  1115.     case WM_COMMAND:
  1116.                 {
  1117.                         if (wparam == IDOK) {
  1118.                                 // Stopping the orbit will end the dialog for us.
  1119.                                 StopOrbiting(my_data->bullet_frame);
  1120.                                 return TRUE;
  1121.                         }
  1122.                         else if (HIWORD(wparam) == EN_UPDATE && LOWORD(wparam) == IDC_BULLET) {
  1123.                                 // Get string length
  1124.                                 int stringlength = SendDlgItemMessage(win, IDC_BULLET, EM_LINELENGTH, 0, 0);
  1125.                                 // Check to make sure the string isn't too long - atof accepts up to 100 chars
  1126.                                 if (stringlength > 99) return TRUE;
  1127.                                 lpszTS[0] = (char)stringlength;
  1128.                                 // Get string
  1129.                                 SendDlgItemMessage(win, IDC_BULLET, EM_GETLINE, 0, (LPARAM) lpszTS);
  1130.                                 lpszTS[stringlength] = 0;
  1131.  
  1132.                                 // Store speed and set
  1133.                                 my_data->fSpeed = D3DVAL(atof(lpszTS));
  1134.                                 my_data->bullet_frame->SetVelocity(info->scene, D3DMultiply(my_data->vDirection.x, my_data->fSpeed),
  1135.                                                                     D3DMultiply(my_data->vDirection.y, my_data->fSpeed), 
  1136.                                                                     D3DMultiply(my_data->vDirection.z, my_data->fSpeed),
  1137.                                                                                                                                         TRUE);
  1138.                                 return TRUE;
  1139.                         }
  1140.                         else return FALSE;
  1141.                 }               
  1142.         break;
  1143.         }
  1144.         return FALSE;
  1145. }
  1146.  
  1147.  
  1148.  
  1149. /*
  1150. ** Orbits the currently selected object around the camera... creates a frame which is centred at the camera and contains
  1151. **   the selected frame, and then rotates that frame around the camera
  1152. */
  1153.  
  1154. ULONG orbit_num = 1;
  1155.  
  1156. void RLDS3D_OrbitSelected(void) {
  1157.         if (!sFrame) return;
  1158.         StopOrbiting(sFrame);
  1159.         FRAMEAPPDATA* tempdat;
  1160.         
  1161.         // Need to create orbit data, if not then can't orbit
  1162.         LPORBITDATA orbitdat = new ORBITDATA;
  1163.         if (!orbitdat) return;
  1164.  
  1165.         if (!sFrame->GetAppData()) {
  1166.                 tempdat = new FRAMEAPPDATA();
  1167.                 if (!tempdat) return;
  1168.                 sFrame->SetAppData((ULONG)(tempdat));
  1169.         }
  1170.         else {
  1171.                 tempdat = (LPFRAMEAPPDATA)sFrame->GetAppData();
  1172.         }
  1173.  
  1174.         // Create a frame.  Centre it around the camera.  Add the selected frame.  Rotate it!
  1175.         LPDIRECT3DRMFRAME orbiting_frame;
  1176.  
  1177.         if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &orbiting_frame))) return;
  1178.  
  1179.         orbiting_frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
  1180.         orbiting_frame->SetOrientation(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0));
  1181.         orbiting_frame->AddChild(sFrame);
  1182.  
  1183.         D3DVECTOR vDir, vUp;
  1184.         info->camera->GetOrientation(info->scene, &vDir, &vUp);
  1185.  
  1186.         // Store orbit info
  1187.         orbitdat->orbit_frame = orbiting_frame;
  1188.         orbitdat->child_frame = sFrame;
  1189.         orbitdat->speed = D3DVAL(1.0);
  1190.         orbitdat->axis.x = vUp.x;
  1191.         orbitdat->axis.y = vUp.y;
  1192.         orbitdat->axis.z = vUp.z;
  1193.  
  1194.         orbiting_frame->SetRotation(info->scene, vUp.x, vUp.y, vUp.z, orbitdat->speed);
  1195.  
  1196.         // Set the boolean in the frame's appinfo to tell everyone it's orbiting
  1197.         tempdat->bOrbiting = TRUE;
  1198.         
  1199.         // Create a dialog box and orbitinfo so that this thing has something to control it
  1200.         HWND mydial = CreateDialogParam(hinst, "OrbitBox", hwndParent, (int(__stdcall*)(void))OrbitDlgProc, (LONG)orbitdat);
  1201.         if (mydial) {
  1202.                 char name_string[50];
  1203.                 sprintf(name_string, "Orbit #%i", orbit_num++);
  1204.                 SetWindowText(mydial, name_string);
  1205.                 ShowWindow(mydial, SW_SHOW);
  1206.                 UpdateWindow(mydial);
  1207.                 tempdat->hDlg = mydial;
  1208.         }
  1209. }
  1210.  
  1211. /*
  1212. ** Bullet the currently selected object.  Bullets have two properties: Speed and bullet-time.  The bullet passes through it's original
  1213. **   position along the path defined by the viewer's original viewpoint (it heads towards the viewer).  It takes 5 seconds to get to the
  1214. **   viewer and five seconds once past the viewer, and continues doing thus until the bullet is stopped.  Speed is controlled by the user.
  1215. */
  1216.  
  1217. ULONG bullet_num = 1;
  1218.  
  1219. void RLDS3D_BulletSelected(void) {
  1220.         if (!sFrame) return;
  1221.         StopOrbiting(sFrame);
  1222.  
  1223.         FRAMEAPPDATA* tempdat;
  1224.         if (!sFrame->GetAppData()) {
  1225.                 tempdat = new FRAMEAPPDATA();
  1226.                 if (!tempdat) return;
  1227.                 sFrame->SetAppData((ULONG)(tempdat));
  1228.         }
  1229.         else {
  1230.                 tempdat = (LPFRAMEAPPDATA)sFrame->GetAppData();
  1231.         }
  1232.  
  1233.         // Create a new bullet object and attach our callback to the frame (it will release itself and put itself back
  1234.         //   into position when it's done!)
  1235.  
  1236.         LPBULLETDATA bs = new BULLETDATA;
  1237.         if (!bs) return;
  1238.  
  1239.         sFrame->GetPosition(info->scene, &(bs->vStartPosition));
  1240.  
  1241.         D3DVECTOR vDir, vUp;
  1242.         info->camera->GetOrientation(info->scene, &vDir, &vUp);
  1243.         // velocity heading towards camera
  1244.         bs->vDirection.x = -vDir.x;
  1245.         bs->vDirection.y = -vDir.y;
  1246.         bs->vDirection.z = -vDir.z;
  1247.  
  1248.         bs->bullet_frame = sFrame;
  1249.         bs->fTime = D3DVAL(10.0); // Ten seconds, 5 for before and 5 for after.
  1250.         bs->fSpeed = D3DVAL(10.0);
  1251.         
  1252.         D3DVALUE mult = D3DDivide(D3DMultiply(bs->fTime, bs->fSpeed), D3DVAL(-2.0)); // Calculate the displacement multiplier for calculating starting position
  1253.         
  1254.         sFrame->SetPosition(info->scene, bs->vStartPosition.x + D3DMultiply(mult, bs->vDirection.x),
  1255.                                              bs->vStartPosition.y + D3DMultiply(mult, bs->vDirection.y),
  1256.                                                                      bs->vStartPosition.z + D3DMultiply(mult, bs->vDirection.z));
  1257.  
  1258.         sFrame->SetVelocity(info->scene, D3DMultiply(bs->vDirection.x, bs->fSpeed),
  1259.                              D3DMultiply(bs->vDirection.y, bs->fSpeed),
  1260.                              D3DMultiply(bs->vDirection.z, bs->fSpeed),
  1261.                                                          TRUE);
  1262.  
  1263.         sFrame->AddMoveCallback(bulletCallback, (void*)bs);
  1264.         
  1265.         // Set the boolean in the frame's appinfo to tell everyone it's orbiting
  1266.         tempdat->bBullet = TRUE;
  1267.  
  1268.         // Create a dialog box and orbitinfo so that this thing has something to control it
  1269.         HWND mydial = CreateDialogParam(hinst, "BulletBox", hwndParent, (int(__stdcall*)(void))BulletDlgProc, (LONG)bs);
  1270.         if (mydial) {
  1271.                 char name_string[50];
  1272.                 sprintf(name_string, "Bullet #%i", bullet_num++);
  1273.                 SetWindowText(mydial, name_string);
  1274.                 ShowWindow(mydial, SW_SHOW);
  1275.                 UpdateWindow(mydial);
  1276.                 tempdat->hDlg = mydial;
  1277.         }
  1278. }
  1279.  
  1280. /*
  1281. **********************************  DIRECTSOUND 3D FUNCTIONS  *****************************************
  1282. */
  1283.  
  1284. /*
  1285. ** The RLDS3D interface allows you to attach sound(s) to the selected object which can then be played and
  1286. **   whose 3D audio position will update according to the RL object's position
  1287. **
  1288. ** Some functionality is applied to all sounds, and as a result, the RLDS3D interface keeps a record of
  1289. **   existing sounds and which frames they are attached to.  Removal of a frame and/or a sound updates these
  1290. **   records
  1291. */
  1292.  
  1293. /*
  1294. ** CALLBACKS - update position as 3D objects move around
  1295. */
  1296.  
  1297.  
  1298. // The listener callback is attached to the camera.  This is done in CreateScene()
  1299. static void CDECL listenerCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delta)
  1300. {
  1301.         arg = arg;
  1302.         if (!lpDS) return;
  1303.         D3DVECTOR rlvCameraInfo, rlvCameraUp;
  1304.  
  1305.         info->camera->GetPosition(info->scene, &rlvCameraInfo);
  1306.         lp3DListenerInfo->SetPosition(rlvCameraInfo.x, rlvCameraInfo.y,
  1307.                                       rlvCameraInfo.z, DS3D_DEFERRED);
  1308.  
  1309.         info->camera->GetOrientation(info->scene, &rlvCameraInfo, &rlvCameraUp);
  1310.         lp3DListenerInfo->SetOrientation(rlvCameraInfo.x, rlvCameraInfo.y,
  1311.                                          rlvCameraInfo.z, rlvCameraUp.x,
  1312.                                          rlvCameraUp.y, rlvCameraUp.z, DS3D_DEFERRED);
  1313.  
  1314.         info->camera->GetVelocity(info->scene, &rlvCameraInfo, TRUE);
  1315.         lp3DListenerInfo->SetVelocity(rlvCameraInfo.x, rlvCameraInfo.y,
  1316.                                       rlvCameraInfo.z, DS3D_DEFERRED);
  1317.  
  1318.         lp3DListenerInfo->CommitDeferredSettings();
  1319. }
  1320.  
  1321.  
  1322. // Sounds are updated whenever their frame moves using Render Callbacks.  This is the callback function.
  1323. static void CDECL soundCallback(LPDIRECT3DRMFRAME obj, void* arg, D3DVALUE delay)
  1324. {
  1325.         arg = arg;
  1326.         LPDIRECT3DRMFRAME tFrame = (LPDIRECT3DRMFRAME)obj;
  1327.         if (!lpDS) return;
  1328.         // Get the sound from the frame's app data
  1329.         LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)tFrame->GetAppData();
  1330.         if (!lpAppDat) return;
  1331.         // Get the 3D sound buffer and remove the sound callback if it's NULL since it shouldn't exist
  1332.         if (!lpAppDat->Sound || !lpAppDat->i3DSound) {
  1333.                 releaseSoundCallback(tFrame);
  1334.                 return;
  1335.         }
  1336.         D3DVECTOR rlvVisualInfo, rlvVisualUp;
  1337.  
  1338.         tFrame->GetPosition(info->scene, &rlvVisualInfo);
  1339.         lpAppDat->i3DSound->SetPosition(rlvVisualInfo.x, rlvVisualInfo.y,
  1340.                                         rlvVisualInfo.z, DS3D_DEFERRED);
  1341.  
  1342.         tFrame->GetOrientation(info->scene, &rlvVisualInfo, &rlvVisualUp);
  1343.         lpAppDat->i3DSound->SetConeOrientation(rlvVisualInfo.x, rlvVisualInfo.y,
  1344.                                                rlvVisualInfo.z, DS3D_DEFERRED);
  1345.  
  1346.         tFrame->GetVelocity(info->scene, &rlvVisualInfo, TRUE);
  1347.         lpAppDat->i3DSound->SetVelocity(rlvVisualInfo.x, rlvVisualInfo.y,
  1348.                                         rlvVisualInfo.z, DS3D_DEFERRED);
  1349.  
  1350.         lp3DListenerInfo->CommitDeferredSettings();
  1351. }
  1352.  
  1353. // This adds a sound callback function to a frame
  1354. void setSoundCallback(LPDIRECT3DRMFRAME frame) {
  1355.         frame->AddMoveCallback(soundCallback, NULL);
  1356. }
  1357.  
  1358. // Removes a sound callback from a frame
  1359. void releaseSoundCallback(LPDIRECT3DRMFRAME frame) {
  1360.         frame->DeleteMoveCallback(soundCallback, NULL);
  1361. }
  1362.  
  1363. /*
  1364. ** Sound records - keeps track of which frames have sounds associated with them and
  1365. **   adds/removes the sounds as required.
  1366. */
  1367.  
  1368. struct SoundRecord {
  1369.         LPDIRECT3DRMFRAME  lpFrame;
  1370.         SoundRecord* next;
  1371. };
  1372.  
  1373. SoundRecord* top = NULL;
  1374.  
  1375. BOOL Recurse_Remove(LPDIRECT3DRMFRAME owner, SoundRecord* record) {
  1376.         if (!record) return FALSE;
  1377.         if (Recurse_Remove(owner, record->next)) {
  1378.                 SoundRecord* temp = record->next;
  1379.                 record->next = temp->next;
  1380.                 delete temp;
  1381.         }
  1382.         if (owner == record->lpFrame) return TRUE;
  1383.         return FALSE;
  1384. }
  1385.  
  1386. // Removes a frame from the records
  1387. void RemoveSoundRecord(LPDIRECT3DRMFRAME owner) {
  1388.         if (!owner) return;
  1389.  
  1390.         // Don't bother deleting frames without appdata and a sound associated with them
  1391.         // because they SHOULDN'T be on the list.
  1392.         if (!owner->GetAppData()) return;
  1393.         LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(owner->GetAppData());
  1394.         if (!data->Sound) return;
  1395.         
  1396.         // Release the sound and remove the soundrecord from the list.
  1397.         data->i3DSound->Release();
  1398.         data->i3DSound = NULL;
  1399.         data->Sound->Release();
  1400.         data->Sound = NULL;
  1401.         releaseSoundCallback(owner);
  1402.         
  1403.         if (Recurse_Remove(owner, top)) {
  1404.                 SoundRecord* temp = top;
  1405.                 top = top->next;
  1406.                 delete temp;
  1407.         }
  1408. }
  1409.  
  1410. // Adds a frame to the records
  1411. void AddSoundRecord(LPDIRECT3DRMFRAME owner, char* sound_filename) {
  1412.         if (!lpDS) return;
  1413.         if (!owner) return;
  1414.         if (!sound_filename) return;
  1415.         
  1416.         // Removes all previous sounds from this frame
  1417.         RemoveSoundRecord(owner);
  1418.         
  1419.         // Create frame data for this frame if it hasn't already been done
  1420.         LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(owner->GetAppData());
  1421.         if (!data) {
  1422.                 data = new FRAMEAPPDATA();
  1423.                 if (!data) return;
  1424.                 owner->SetAppData((ULONG)data);
  1425.         }
  1426.         
  1427.         // Create the sound and attach it to the frame
  1428.         data->Sound = DSLoad3DSoundBuffer(lpDS, sound_filename);
  1429.         if (!data->Sound) return; 
  1430.         
  1431.         // Query to get the 3D interface, destroy the sound buffer if it's not available...
  1432.         data->Sound->QueryInterface(IID_IDirectSound3DBuffer, (void**)&data->i3DSound);
  1433.         if (!data->i3DSound) {
  1434.                 data->Sound->Release();
  1435.                 data->Sound = NULL;
  1436.                 return;
  1437.         }
  1438.  
  1439.         // Set the minimum distance at which the sound's amplitude should decay.
  1440.         data->i3DSound->SetMinDistance((D3DVALUE)10.0, DS3D_IMMEDIATE);
  1441.  
  1442.         setSoundCallback(owner);
  1443.  
  1444.         SoundRecord* temp = new SoundRecord;
  1445.         temp->next = top;
  1446.         top = temp;
  1447.         top->lpFrame = owner;
  1448. }
  1449.  
  1450. /*
  1451. ** Functionality for users
  1452. */
  1453.  
  1454. // Stops all the sounds in the world.  (Actually, only in the local RL world.)
  1455. // Runs through the records of all the sounds and stops them.
  1456. void RLDS3D_StopAllSounds() {
  1457.         if (!lpDS) return;
  1458.         SoundRecord* temp = top;
  1459.         while (temp) {
  1460.                 LPFRAMEAPPDATA data = (LPFRAMEAPPDATA)(temp->lpFrame->GetAppData());
  1461.                 if (data) {
  1462.                         if (data->Sound) data->Sound->Stop();
  1463.                 }
  1464.                 temp = temp->next;
  1465.         }
  1466. }
  1467.  
  1468. // Removes all sounds in the world
  1469. void RLDS3D_RemoveAllSounds() {
  1470.         if (!lpDS) return;
  1471.         while (top) RemoveSoundRecord(top->lpFrame);
  1472.                 UpdateConeVisual();
  1473. }
  1474.  
  1475. // Plays the sound associated with the currently selected object
  1476. void RLDS3D_PlaySound(BOOL bIsLooping) {
  1477.         if (!sFrame) return;
  1478.         if (!lpDS) return;
  1479.         LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)sFrame->GetAppData();
  1480.         if (lpAppDat) {
  1481.                 if (lpAppDat->Sound) {
  1482.                         lpAppDat->Sound->Stop();
  1483.                         lpAppDat->Sound->SetCurrentPosition(0);
  1484.                         if (bIsLooping) {
  1485.                                 lpAppDat->Sound->Play(0,0,DSBPLAY_LOOPING);
  1486.                         }
  1487.                         else {
  1488.                                 lpAppDat->Sound->Play(0,0,0);
  1489.                         }
  1490.                 }
  1491.         }
  1492. }
  1493.  
  1494. // Stops the sound associated with the currently selected object
  1495. void RLDS3D_StopSelectedSound() {
  1496.         if (!sFrame) return;
  1497.         if (!lpDS) return;
  1498.         LPFRAMEAPPDATA lpAppDat = (LPFRAMEAPPDATA)sFrame->GetAppData();
  1499.         if (lpAppDat) {
  1500.                 if (lpAppDat->Sound) {
  1501.                         lpAppDat->Sound->Stop();
  1502.                 }
  1503.         }
  1504. }
  1505.  
  1506. // Removes the sound from the currently selected object
  1507. void RLDS3D_RemoveSound() {
  1508.         RLDS3D_StopSelectedSound();
  1509.         if (!sFrame) return;
  1510.         if (!lpDS) return;
  1511.         RemoveSoundRecord(sFrame);
  1512.                 UpdateConeVisual();
  1513. }
  1514.  
  1515. // Attaches a sound (filename provided) to the selected frame
  1516. void RLDS3D_AttachSound(char* filename) {
  1517.         // Removes the sound attached to the currently selected item (if it exists)
  1518.         RLDS3D_RemoveSound();
  1519.         if (!sFrame) return;
  1520.         if (!lpDS) return;
  1521.         AddSoundRecord(sFrame, filename);
  1522.                 UpdateConeVisual();
  1523. }
  1524.  
  1525. /*
  1526. *************************************  MISC. MAINTENANCE FUNCTIONS  **************************************
  1527. */
  1528.  
  1529. /*
  1530. ** Allows external users access to the RL Device to deal with Windows-related issues
  1531. **   (See case WM_ACTIVATE: and case WM_PAINT: in viewer source for examples of HandleActivate() and HandlePaint())
  1532. ** Design note: This was done to save time from the conversion from the old version of the viewer rather than
  1533. **   having RLDS3D_HandleActivate(), etc.
  1534. */
  1535.  
  1536. LPDIRECT3DRMDEVICE RLDS3D_WinDevice() {
  1537.         if (!info) return NULL;
  1538.         return info->dev;
  1539. }
  1540.  
  1541. /*
  1542. ** Handles activation messages from the window
  1543. */
  1544.  
  1545. void RLDS3D_HandleActivate(WPARAM wparam) {
  1546.         if (!info || !info->dev) return;
  1547.         LPDIRECT3DRMWINDEVICE windev;
  1548.         if (D3DRM_SUCCEED(info->dev->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &windev))) {
  1549.                 windev->HandleActivate(wparam);
  1550.                 windev->Release();
  1551.         }
  1552. }
  1553.  
  1554. /*
  1555. ** Handles paint messages from the window - the paintstruct which BeginPaint has been called on is
  1556. **   passed to it
  1557. */
  1558.  
  1559. void RLDS3D_HandlePaint(PAINTSTRUCT* ps) {
  1560.         if (!info) return;
  1561.         LPDIRECT3DRMWINDEVICE windev;
  1562.         if (D3DRM_SUCCEED(info->dev->QueryInterface(IID_IDirect3DRMWinDevice, (void **) &windev))) {
  1563.                 windev->HandlePaint(ps->hdc);
  1564.                 windev->Release();
  1565.         }
  1566. }
  1567.  
  1568. /*
  1569. ** Tells whether or not sound is initialized
  1570. */
  1571.  
  1572. BOOL RLDS3D_SoundInitialized() {
  1573.         if (lpDS) return TRUE;
  1574.         return FALSE;
  1575. }
  1576.  
  1577. /*
  1578. ** Tells whether or not a frame is selected
  1579. */
  1580.  
  1581. BOOL RLDS3D_FrameSelected() {
  1582.         if (sFrame) return TRUE;
  1583.         return FALSE;
  1584. }
  1585.  
  1586. /*
  1587. ** Render the scene into the viewport.
  1588. */
  1589.  
  1590. void RLDS3D_Render(D3DVALUE time_delta)
  1591. {
  1592.     // When the WM_SIZE message passes 0's as size to the ResizeViewport we know that it's minimized, in which case we don't render it.
  1593.     if (info->bMinimized == TRUE) return;
  1594.     D3DRM_SUCCEED(info->scene->Move(time_delta));
  1595.     D3DRM_SUCCEED(info->view->Clear());
  1596.     D3DRM_SUCCEED(info->view->Render(info->scene));
  1597.     D3DRM_SUCCEED(info->dev->Update());
  1598. }
  1599.  
  1600.  
  1601. /*
  1602.  * Resize the viewport and device when the window size changes.
  1603.  */
  1604. void RLDS3D_ResizeViewport(int width, int height)
  1605. {
  1606.     int view_width = info->view->GetWidth();
  1607.     int view_height = info->view->GetHeight();
  1608.     int dev_width = info->dev->GetWidth();
  1609.     int dev_height = info->dev->GetHeight();
  1610.  
  1611.     if (!(width && height)) {
  1612.             info->bMinimized = TRUE;
  1613.             return;
  1614.  
  1615.     }
  1616.     else info->bMinimized = FALSE;
  1617.  
  1618.     if (view_width == width && view_height == height)
  1619.         return;
  1620.         else info->bMinimized = FALSE;
  1621.     
  1622.     if (width <= dev_width && height <= dev_height) {
  1623.         info->view->Release();
  1624.         D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, width, height, &info->view));
  1625.         info->view->SetBack(D3DVAL(400.0));
  1626.     }
  1627.  
  1628.     int ret;
  1629.     if (!RebuildDevice(hwndParent, info, width, height)) {
  1630.         ret = MessageBox(hwndParent, "Unable to create Direct3D device", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK);
  1631.         PostMessage(hwndParent, WM_CLOSE,0,0);
  1632.     };
  1633. }
  1634.  
  1635. /*
  1636. ** Sets/Gets the polygon fill mode
  1637. */
  1638.  
  1639. D3DRMFILLMODE RLDS3D_GetPolygonFillMode(void) {
  1640.     return (D3DRMFILLMODE)(info->dev->GetQuality() & D3DRMFILL_MASK);
  1641. }
  1642.  
  1643. void RLDS3D_SetPolygonFillMode(D3DRMFILLMODE quality) {
  1644.     D3DRMRENDERQUALITY oldq = info->dev->GetQuality();
  1645.     oldq = (oldq & ~D3DRMFILL_MASK) | quality;
  1646.     info->dev->SetQuality(oldq);
  1647. }
  1648.  
  1649. /*
  1650. ** Sets/Gets the polygon shading mode
  1651. */
  1652.  
  1653. D3DRMSHADEMODE RLDS3D_GetPolygonShadeMode(void) {
  1654.         return (D3DRMSHADEMODE)(info->dev->GetQuality() & D3DRMSHADE_MASK);
  1655. }
  1656.  
  1657. void RLDS3D_SetPolygonShadeMode(D3DRMSHADEMODE quality) {
  1658.         D3DRMRENDERQUALITY oldq = info->dev->GetQuality();
  1659.         oldq = (oldq & ~D3DRMSHADE_MASK) | quality;
  1660.         info->dev->SetQuality(oldq);
  1661. }
  1662.  
  1663.  
  1664. /*
  1665. ** Sets/Gets the color model for the viewport (RGB or mono (256-color-based))
  1666. */
  1667.  
  1668. D3DRMCOLORMODEL RLDS3D_GetColourModel(void) {
  1669.         return info->model;
  1670. }
  1671.  
  1672. void RLDS3D_SetColourModel(D3DRMCOLORMODEL model) {
  1673.     info->model = model;
  1674.     int ret;
  1675.     if (!RebuildDevice(hwndParent, info, info->dev->GetWidth(), info->dev->GetHeight())) {
  1676.         ret = MessageBox(hwndParent, "Unable to selected Direct3D device", "D3DRM Fatal Error", MB_APPLMODAL|MB_ICONSTOP|MB_OK);
  1677.         PostMessage(hwndParent, WM_CLOSE,0,0);
  1678.     }
  1679. }
  1680.  
  1681. /*
  1682. ** Sets/Gets whether or not lighting is on
  1683. */
  1684.  
  1685. BOOL RLDS3D_GetLighting(void) {
  1686.     D3DRMLIGHTMODE mode = (D3DRMLIGHTMODE)(info->dev->GetQuality() & D3DRMLIGHT_MASK);
  1687.     if (mode == D3DRMLIGHT_ON) return TRUE;
  1688.     return FALSE;
  1689. }
  1690.  
  1691. void RLDS3D_SetLighting(BOOL new_val) {
  1692.     D3DRMRENDERQUALITY qual = info->dev->GetQuality() & ~D3DRMLIGHT_MASK;
  1693.     if (new_val) qual |= D3DRMLIGHT_ON; else qual |= D3DRMLIGHT_OFF;
  1694.     info->dev->SetQuality(qual);
  1695. }
  1696.  
  1697. /*
  1698. ** Sets/Gets whether or not dithering is on
  1699. */
  1700.  
  1701. BOOL RLDS3D_GetDither(void) {
  1702.     return info->dev->GetDither();
  1703. }
  1704.  
  1705. void RLDS3D_SetDither(BOOL dither) {
  1706.     info->dev->SetDither(dither);
  1707. }
  1708.  
  1709. /*
  1710. ** Sets/Gets texture quality (only relevant for RGB modes)
  1711. */
  1712.  
  1713. D3DRMTEXTUREQUALITY RLDS3D_GetTextureQuality(void) {
  1714.     return info->dev->GetTextureQuality();
  1715. }
  1716.  
  1717. void RLDS3D_SetTextureQuality(D3DRMTEXTUREQUALITY new_quality) {
  1718.     info->dev->SetTextureQuality(new_quality);
  1719. }
  1720.  
  1721.  
  1722. /*
  1723. *************************************  INTERNAL FUNCTIONS (Not part of API)  ********************************
  1724. */
  1725.  
  1726. /*
  1727. ** Given a bounding box this generates a visual representation of it using rods and cones
  1728. */
  1729.  
  1730. static LPDIRECT3DRMMESHBUILDER makeBox(D3DRMBOX* box)
  1731. {
  1732.     LPDIRECT3DRMMESHBUILDER mesh;
  1733.     static D3DVECTOR zero = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)};
  1734.     static D3DVECTOR dir = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)};
  1735.     D3DVECTOR a, b;
  1736.  
  1737.     if (!D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&mesh))) return NULL;
  1738.  
  1739.     dir.z = box->max.z + D3DVAL(1.0);
  1740.     AddRod(mesh, D3DVAL(0.05), zero, dir);
  1741.     a = dir;
  1742.     a.z += D3DVAL(0.6);
  1743.     AddCone(mesh, D3DVAL(0.2), dir, a);
  1744.     a = box->min;
  1745.     b = a;
  1746.     b.y = box->max.y;
  1747.     AddRod(mesh, D3DVAL(0.05), a, b);
  1748.     a = b; b.x = box->max.x;
  1749.     AddRod(mesh, D3DVAL(0.05), a, b);
  1750.     a = b; b.y = box->min.y;
  1751.     AddRod(mesh, D3DVAL(0.05), a, b);
  1752.     a = b; b.x = box->min.x;
  1753.     AddRod(mesh, D3DVAL(0.05), a, b);
  1754.     a = b; b.z = box->max.z;
  1755.     AddRod(mesh, D3DVAL(0.05), a, b);
  1756.     a = b; b.x = box->max.x;
  1757.     AddRod(mesh, D3DVAL(0.05), a, b);
  1758.     a = b; b.y = box->max.y;
  1759.     AddRod(mesh, D3DVAL(0.05), a, b);
  1760.     a = b; b.x = box->min.x;
  1761.     AddRod(mesh, D3DVAL(0.05), a, b);
  1762.     a = b; b.y = box->min.y;
  1763.     AddRod(mesh, D3DVAL(0.05), a, b);
  1764.     b.y = box->max.y; a = b; b.z = box->min.z;
  1765.     AddRod(mesh, D3DVAL(0.05), a, b);
  1766.     a = b = box->max; b.z = box->min.z;
  1767.     AddRod(mesh, D3DVAL(0.05), a, b);
  1768.     a.y = box->min.y; b = a; b.z = box->min.z;
  1769.     AddRod(mesh, D3DVAL(0.05), a, b);
  1770.                 
  1771.     if (!D3DRM_SUCCEED(mesh->SetColor(D3DRMCreateColorRGB(D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0))))) {
  1772.         mesh->Release();
  1773.         return NULL;
  1774.     }
  1775.     return mesh;
  1776. }
  1777.  
  1778. /*
  1779. ** Given a box, this creates a mesh in the shape of a 16-sided speaker cone aimed forward with requested angle
  1780. */
  1781.  
  1782. #define CONE_POINTS 16
  1783. #define pi 3.14159
  1784.  
  1785. static LPDIRECT3DRMMESHBUILDER makeSpeaker(D3DRMBOX* box, D3DVALUE in_angle) {
  1786.     if (!box) return NULL;
  1787.     
  1788.     D3DVALUE angle = in_angle / D3DVAL(2.0); 
  1789.         DWORD* speaker_faces = new DWORD[CONE_POINTS*4+1];
  1790.     if (!speaker_faces) return NULL;
  1791.     memset(speaker_faces, 0, sizeof(DWORD[CONE_POINTS*4+1]));
  1792.  
  1793.     LPDIRECT3DRMMESHBUILDER mesh;
  1794.     if (!D3DRM_SUCCEED(lpD3DRM->CreateMeshBuilder(&mesh))) {
  1795.         delete speaker_faces;
  1796.         return NULL;
  1797.     }
  1798.     
  1799.     static D3DVECTOR zero = {D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0)};
  1800.  
  1801.     D3DVECTOR v[CONE_POINTS+1];
  1802.  
  1803.     // center of the cone
  1804.     v[CONE_POINTS] = zero;
  1805.  
  1806.     int looper;
  1807.  
  1808.     // Angle along XZ plane which is rotated CONE_POINT times to form cone
  1809.     D3DVECTOR base_angle;
  1810.     base_angle.z = (box->max.z + D3DVAL(2.0)) * (D3DVALUE)cos(angle * pi / 180.0);
  1811.     base_angle.x = (box->max.z + D3DVAL(2.0)) * (D3DVALUE)sin(angle * pi / 180.0);
  1812.     base_angle.y = D3DVAL(0.0);
  1813.     
  1814.     for (looper=0; looper<CONE_POINTS; looper++) {
  1815.         v[looper].z = base_angle.z;
  1816.         v[looper].x = base_angle.x * (D3DVALUE)cos((looper*2*pi)/CONE_POINTS);
  1817.         v[looper].y = base_angle.x * (D3DVALUE)sin((looper*2*pi)/CONE_POINTS);
  1818.         speaker_faces[looper*4] = 3;
  1819.         speaker_faces[looper*4+1] = looper % CONE_POINTS;
  1820.         speaker_faces[looper*4+2] = (looper + 1) % CONE_POINTS;
  1821.         speaker_faces[looper*4+3] = CONE_POINTS;
  1822.     }
  1823.  
  1824.     v[CONE_POINTS] = zero;
  1825.  
  1826.     if (!D3DRM_SUCCEED(mesh->AddFaces(CONE_POINTS+1, v, 0, NULL, speaker_faces, NULL))) {
  1827.         delete speaker_faces;
  1828.         mesh->Release();
  1829.         return NULL;
  1830.     }
  1831.  
  1832.     for (looper=0; looper<CONE_POINTS; looper++) {
  1833.         speaker_faces[looper*4+2] = looper % CONE_POINTS;
  1834.         speaker_faces[looper*4+1] = (looper + 1) % CONE_POINTS;
  1835.     }
  1836.  
  1837.     if (!D3DRM_SUCCEED(mesh->AddFaces(CONE_POINTS+1, v, 0, NULL, speaker_faces, NULL))) {
  1838.         delete speaker_faces;
  1839.         mesh->Release();
  1840.         return NULL;
  1841.     }
  1842.     
  1843.     delete speaker_faces;
  1844.  
  1845.     if (!D3DRM_SUCCEED(mesh->SetColor(D3DRMCreateColorRGB(D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0))))) {
  1846.         mesh->Release();
  1847.         return NULL;
  1848.     }
  1849.  
  1850.     if (!D3DRM_SUCCEED(mesh->SetQuality((mesh->GetQuality() & ~D3DRMSHADE_MASK) | D3DRMSHADE_FLAT))) {
  1851.         mesh->Release();
  1852.         return NULL;
  1853.     }
  1854.  
  1855.     return mesh;
  1856. }
  1857.  
  1858. /*
  1859. ** Selects the given visual inside the given frame
  1860. */
  1861.  
  1862. void UpdateConeVisual(void) {
  1863.         if (!sFrame) return;
  1864.         if (!sFrame->GetAppData()) return;
  1865.     LPFRAMEAPPDATA fd = (LPFRAMEAPPDATA)sFrame->GetAppData();
  1866.         if (!fd->i3DSound) return;
  1867.         if (showBoxes && sVisual)
  1868.         {   D3DRMBOX box;
  1869.             LPDIRECT3DRMMESHBUILDER builder;
  1870.                 sFrame->DeleteVisual(selectionSpeaker);
  1871.             sVisual->GetBox(&box);
  1872.                         DWORD temp, outer;
  1873.                         fd->i3DSound->GetConeAngles(&temp, &outer);
  1874.             builder = makeSpeaker(&box, D3DVAL(temp));
  1875.             builder->CreateMesh(&selectionSpeaker);
  1876.             sFrame->AddVisual(selectionSpeaker);
  1877.             selectionSpeaker->Release();
  1878.                         builder->Release();
  1879.                 }
  1880.  
  1881. }
  1882.  
  1883. void SelectVisual(LPDIRECT3DRMMESHBUILDER visual, LPDIRECT3DRMFRAME frame) {
  1884.     RLDS3D_DeselectVisual();
  1885.     sVisual = visual;
  1886.     sFrame = frame;
  1887.  
  1888.     if (sVisual)
  1889.     {   LPDIRECT3DRMLIGHTARRAY lights;
  1890.  
  1891.         sLight = 0;
  1892.         sFrame->GetLights(&lights);
  1893.         if (lights)
  1894.         {   if (lights->GetSize())
  1895.             {   lights->GetElement(0, &sLight);
  1896.                 sLight->Release(); /* reinstate reference count */
  1897.             }
  1898.             lights->Release();
  1899.         }
  1900.  
  1901.         if (showBoxes && visual)
  1902.         {   D3DRMBOX box;
  1903.             LPDIRECT3DRMMESHBUILDER builder;
  1904.  
  1905.             sVisual->GetBox(&box);
  1906.             builder = makeBox(&box);
  1907.             builder->CreateMesh(&selectionBox);
  1908.             sFrame->AddVisual(selectionBox);
  1909.             selectionBox->Release();
  1910.                         builder->Release();
  1911.                         UpdateConeVisual();
  1912.         }
  1913.     }
  1914. }
  1915.  
  1916. LPGUID
  1917. FindDevice(D3DCOLORMODEL cm)
  1918. {
  1919.     LPDIRECTDRAW lpDD;
  1920.     LPDIRECT3D lpD3D;
  1921.     D3DFINDDEVICESEARCH search;
  1922.     static D3DFINDDEVICERESULT result;
  1923.     HRESULT error;
  1924.  
  1925.     if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK)
  1926.         return NULL;
  1927.  
  1928.     if (lpDD->QueryInterface(IID_IDirect3D, (void**) &lpD3D) != DD_OK) {
  1929.         lpDD->Release();
  1930.         return NULL;
  1931.     }
  1932.     
  1933.     memset(&search, 0, sizeof search);
  1934.     search.dwSize = sizeof search;
  1935.     search.dwFlags = D3DFDS_COLORMODEL;
  1936.     search.dcmColorModel = (cm == D3DCOLOR_MONO) ? D3DCOLOR_MONO : D3DCOLOR_RGB;
  1937.  
  1938.     memset(&result, 0, sizeof result);
  1939.     result.dwSize = sizeof result;
  1940.  
  1941.     error = lpD3D->FindDevice(&search, &result);
  1942.  
  1943.     lpD3D->Release();
  1944.     lpDD->Release();
  1945.  
  1946.     if (error != D3D_OK)
  1947.         return NULL;
  1948.     else
  1949.         return &result.guid;
  1950. }
  1951.  
  1952. /*
  1953.  * Create the device and viewport.
  1954.  */
  1955.  
  1956. static BOOL CreateDevice(HWND win, AppInfo* info)
  1957. {
  1958.     RECT r;
  1959.     int bpp;
  1960.     HDC hdc;
  1961.  
  1962.     GetClientRect(win, &r);
  1963.     if (!D3DRM_SUCCEED(lpD3DRM->CreateDeviceFromClipper(lpDDClipper, NULL, r.right, r.bottom, &info->dev))) return FALSE;
  1964.         
  1965.         hdc = GetDC(win);
  1966.     bpp = GetDeviceCaps(hdc, BITSPIXEL);
  1967.     ReleaseDC(win, hdc);
  1968.     switch (bpp)
  1969.     {
  1970.     case 1:
  1971.         info->dev->SetShades(4);
  1972.         lpD3DRM->SetDefaultTextureShades(4);
  1973.         break;
  1974.     case 16:
  1975.         info->dev->SetShades(32);
  1976.         lpD3DRM->SetDefaultTextureColors(64);
  1977.         lpD3DRM->SetDefaultTextureShades(32);
  1978.         info->dev->SetDither(FALSE);
  1979.         break;
  1980.     case 24:
  1981.         info->dev->SetShades(256);
  1982.         lpD3DRM->SetDefaultTextureColors(64);
  1983.         lpD3DRM->SetDefaultTextureShades(256);
  1984.         info->dev->SetDither(FALSE);
  1985.         break;
  1986.     default:
  1987.         info->dev->SetDither(FALSE);
  1988.     }
  1989.     if (!CreateScene(info)) {
  1990.                 info->dev->Release();
  1991.                 return FALSE;
  1992.         }
  1993.     if (!D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, info->dev->GetWidth(), info->dev->GetHeight(), &info->view))) {
  1994.                 info->dev->Release();
  1995.                 return FALSE;
  1996.         }
  1997.     info->view->SetBack(D3DVAL(5000.0));
  1998.         return TRUE;
  1999. }
  2000.  
  2001. /*
  2002.  * Creates a simple scene and adds it to the main scene
  2003.  */
  2004.  
  2005. static BOOL CreateScene(AppInfo* info)
  2006. {
  2007.     LPDIRECT3DRMFRAME light;
  2008.     LPDIRECT3DRMLIGHT light1, light2;
  2009.  
  2010.     // Note that if something fails, we don't bother freeing up everything we've created... the caller to CreateScene should destroy
  2011.     //   the lpD3DRM object and that should happily release everything created with it.
  2012.     // Also note that, since we're just the viewer, if there's a critical error we pass a quit message with our error message since
  2013.     //   we'd want to quit if the initialize failed anyways...
  2014.  
  2015.     if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(NULL, &info->scene))) return FALSE;
  2016.     if (!D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &light1))) return FALSE;
  2017.     if (!D3DRM_SUCCEED(lpD3DRM->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.1), D3DVAL(0.1), D3DVAL(0.1), &light2))) return FALSE;
  2018.     if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &light))) return FALSE;
  2019.  
  2020.     light->SetPosition(info->scene, D3DVAL(2.0), D3DVAL(2.0), D3DVAL(5.0));
  2021.     light->SetOrientation(info->scene, D3DVAL(-1.0), D3DVAL(-1.0), D3DVAL(1.0), D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0));
  2022.     light->AddLight(light1);
  2023.     info->scene->AddLight(light2);
  2024.  
  2025.     if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &info->camera))) return FALSE;
  2026.     info->camera->SetPosition(info->scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));
  2027.     // Add a callback to the camera's frame so that the listener is updated with the camera
  2028.     info->camera->AddMoveCallback(listenerCallback, NULL);
  2029.  
  2030.     light->Release(), light1->Release(), light2->Release();
  2031.     return TRUE;
  2032. }
  2033.  
  2034. /*
  2035.  * Regenerate the device if the color model changes or the window size
  2036.  * changes.
  2037.  */
  2038. static BOOL RebuildDevice(HWND win, AppInfo* info, int width, int height)
  2039. {
  2040.     int old_dither = info->dev->GetDither();
  2041.     D3DRMRENDERQUALITY old_quality = info->dev->GetQuality();
  2042.     int old_shades = info->dev->GetShades();
  2043.  
  2044.     info->view->Release();
  2045.     info->dev->Release();
  2046.     
  2047.     LPGUID guid = FindDevice(info->model);
  2048.  
  2049.     if (!guid) return FALSE;
  2050.  
  2051.     if (!D3DRM_SUCCEED(lpD3DRM->CreateDeviceFromClipper(lpDDClipper, guid, width, height, &info->dev))) return FALSE;
  2052.  
  2053.     info->dev->SetDither(old_dither);
  2054.     info->dev->SetQuality(old_quality);
  2055.     info->dev->SetShades(old_shades);
  2056.     width = info->dev->GetWidth();
  2057.     height = info->dev->GetHeight();
  2058.     if (!D3DRM_SUCCEED(lpD3DRM->CreateViewport(info->dev, info->camera, 0, 0, width, height, &info->view))) return FALSE;
  2059.     info->view->SetBack(D3DVAL(400.0));
  2060.         return TRUE;
  2061. }
  2062.  
  2063. /*
  2064.  * Place an object in front of the camera.
  2065.  */
  2066. static void PlaceMesh(LPDIRECT3DRMMESHBUILDER mesh, AppInfo *info)
  2067. {
  2068.     LPDIRECT3DRMFRAME frame;
  2069.  
  2070.     if (!D3DRM_SUCCEED(lpD3DRM->CreateFrame(info->scene, &frame))) return;
  2071.     frame->AddVisual(mesh);
  2072.     frame->SetPosition(info->camera, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(15.0));
  2073.     frame->Release();
  2074. }
  2075.  
  2076. HRESULT loadTextures(char *name, void *arg, LPDIRECT3DRMTEXTURE *tex)
  2077. {
  2078.     char* ext = LSTRRCHR(name, (int)'.');
  2079.  
  2080.     if (ext && !lstrcmpi(ext, ".ppm"))
  2081.         if (D3DRM_SUCCEED(lpD3DRM->LoadTexture(name, tex))) return 0;
  2082.     return -1;
  2083. }
  2084.  
  2085. /*
  2086. ** Finds the last occurance of bChar in a null-terminated string, good for finding a pointer to the extension of a filename
  2087. */
  2088. char* LSTRRCHR( const char* lpString, int bChar )
  2089. {
  2090.     if( lpString != NULL )
  2091.     {
  2092.         const char*     lpBegin;
  2093.  
  2094.         lpBegin = lpString;
  2095.  
  2096.         while( *lpString != 0 )
  2097.         {
  2098.             lpString++;
  2099.         }
  2100.  
  2101.         while( 1 )
  2102.         {
  2103.             if( *lpString == bChar )
  2104.             {
  2105.                 return (char*)lpString;
  2106.             }
  2107.             
  2108.             if( lpString == lpBegin )
  2109.             {
  2110.                 break;
  2111.             }
  2112.  
  2113.             lpString--;
  2114.         }
  2115.     }
  2116.  
  2117.     return NULL;
  2118. } /* LSTRRCHR */
  2119.  
  2120.  
  2121. /*
  2122. ** Strange little function to pick a color from a table using standardized Windows stuff
  2123. */
  2124.  
  2125. int ChooseNewColor(HWND win, D3DCOLOR* current)
  2126. {
  2127.     CHOOSECOLOR cc;
  2128.     COLORREF clr;
  2129.     COLORREF aclrCust[16];
  2130.     int i;
  2131.  
  2132.     for (i = 0; i < 16; i++)
  2133.         aclrCust[i] = RGB(255, 255, 255);
  2134.  
  2135.     clr =
  2136.         RGB
  2137.         (   (int) (255 * D3DRMColorGetRed(*current)),
  2138.             (int) (255 * D3DRMColorGetGreen(*current)),
  2139.             (int) (255 * D3DRMColorGetBlue(*current))
  2140.         );
  2141.  
  2142.     memset(&cc, 0, sizeof(CHOOSECOLOR));
  2143.     cc.lStructSize = sizeof(CHOOSECOLOR);
  2144.     cc.hwndOwner = win;
  2145.     cc.rgbResult = clr;
  2146.     cc.lpCustColors = aclrCust;
  2147.     cc.Flags = CC_RGBINIT|CC_FULLOPEN;
  2148.  
  2149.     if (ChooseColor(&cc))
  2150.     {   *current =
  2151.             D3DRMCreateColorRGB
  2152.             (   D3DVAL(GetRValue(cc.rgbResult) / D3DVAL(255.0)),
  2153.                 D3DVAL(GetGValue(cc.rgbResult) / D3DVAL(255.0)),
  2154.                 D3DVAL(GetBValue(cc.rgbResult) / D3DVAL(255.0))
  2155.             );
  2156.         return TRUE;
  2157.     }
  2158.     else return FALSE;
  2159. }
  2160.