Subversion Repositories Games.Chess Giants

Rev

Rev 164 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. // render.cpp
  2.  
  3. // thanks ChaotikMind for optimizing the engine a bit :)
  4.  
  5. #include "common.h"
  6.  
  7.  
  8. // note: DirectX requires a C++ compiler.
  9. #include "DirectX9/Include/d3d9.h"
  10. #include "DirectX9/Include/d3dx9.h"
  11.  
  12. // include the Direct3D library files
  13. #pragma comment (lib, "DirectX9/Lib/x86/d3d9.lib")
  14. #pragma comment (lib, "DirectX9/Lib/x86/d3dx9.lib")
  15.  
  16.  
  17. // local definitions
  18. #define TEXTBG_NONE    0
  19. #define TEXTBG_CENTER (1 << 0)
  20. #define TEXTBG_LEFT   (1 << 1)
  21. #define TEXTBG_RIGHT  (1 << 2)
  22. #define TEXTBG_TOP    (1 << 3)
  23. #define TEXTBG_BOTTOM (1 << 4)
  24. #define TEXTBG_ALL    (TEXTBG_CENTER | TEXTBG_LEFT | TEXTBG_RIGHT | TEXTBG_TOP | TEXTBG_BOTTOM)
  25.  
  26.  
  27. // handy macro to print a chatter channel reply
  28. #define PRINT_CCREPLY(ccreply) \
  29. { \
  30.    if (ccreply->channelname[0] != 0) \
  31.    { \
  32.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  33.                       L"Papyrus", 8.0f, false, false, (ccreply)->color, TEXTBG_CENTER | TEXTBG_LEFT, &rect, \
  34.                       L"["); \
  35.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  36.                       L"Papyrus", 8.0f, false, false, RGBA_TO_RGBACOLOR (159, 159, 159, RGBACOLOR_ALPHA ((ccreply)->color)), TEXTBG_CENTER, &rect, \
  37.                       (ccreply)->channelname); \
  38.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  39.                       L"Papyrus", 8.0f, false, false, (ccreply)->color, TEXTBG_CENTER, &rect, \
  40.                       L"] "); /* closing bracket + non-breakable space */ \
  41.    } \
  42.    else \
  43.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  44.                       L"Papyrus", 8.0f, false, false, (ccreply)->color, TEXTBG_CENTER | TEXTBG_LEFT, &rect, \
  45.                       L" "); \
  46.    if (ccreply->nickname[0] != 0) \
  47.    { \
  48.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  49.                       L"Papyrus", 8.0f, false, false, RGBA_TO_RGBACOLOR (159, 159, 159, RGBACOLOR_ALPHA ((ccreply)->color)), TEXTBG_CENTER, &rect, \
  50.                       (ccreply)->nickname); \
  51.       Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  52.                       L"Papyrus", 8.0f, false, false, (ccreply)->color, TEXTBG_CENTER, &rect, \
  53.                       L": "); /* colon + non-breakable space */ \
  54.    } \
  55.    Render_wprintf (rect.right * 100.0f / initial_width, 100 - combinedheight_percent, (initial_width - rect.right) * 100.0f / initial_width, ALIGN_LEFT, ALIGN_TOP, ALIGN_LEFT, \
  56.                    L"Papyrus", 8.0f, false, false, (ccreply)->color, TEXTBG_CENTER | TEXTBG_RIGHT, &rect, \
  57.                    ((ccreply)->text != NULL ? (ccreply)->text : L"")); /* CC text (or empty string) */ \
  58. }
  59.  
  60.  
  61. // handy macro to draw a GUI button
  62. #define DRAW_BUTTON_IF_NEEDED(button) \
  63. { \
  64.    if ((button).state != 0) \
  65.    { \
  66.       Render_DrawSprite (&sprites[(button).sprite_index], (button).left, (button).top, (button).width, (button).height, ((button).state == 2 ? 0xFF : 0x7F)); \
  67.       if ((button).text[0] != 0) \
  68.          Render_wprintf ((button).left + (button).width / 2.0f, (button).top + (button).height / 2.0f, (button).width, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, \
  69.          (button).font_face, (button).font_sizepct, (button).is_bold, (button).is_italic, D3DCOLOR_ARGB (255, 255, 255, /*alpha*/ ((button).state == 2 ? 0xFF : 0x7F)), TEXTBG_NONE, NULL, (button).text); \
  70.    } \
  71. }
  72.  
  73.  
  74. // handy macro to draw a GUI text
  75. #define DRAW_TEXT_IF_NEEDED(text) \
  76. { \
  77.    if ((text).is_displayed) \
  78.    { \
  79.       Render_wprintf ((text).xpos_percent, (text).ypos_percent, \
  80.                       (text).maxwidth_percent, (text).horizontal_align, (text).vertical_align, (text).text_align, (text).font_face, (text).font_sizepct, (text).is_bold, (text).is_italic, \
  81.                       ((text).want_fade ? \
  82.                        RGBACOLOR_SETALPHA ((text).color, \
  83.                                            (((text).appear_time + (text).disappear_time) * 0.5f > current_time ? \
  84.                                            /* fading in  */ (int) FadeFloat (0, RGBACOLOR_ALPHA ((text).color), (text).appear_time, (text).appear_time + 0.5f) : \
  85.                                            /* fading out */ (int) FadeFloat (RGBACOLOR_ALPHA ((text).color), 0, (text).disappear_time - 3.0f, (text).disappear_time))) : \
  86.                        (text).color), TEXTBG_NONE, \
  87.                       NULL, (text).buffer); \
  88.       if ((text).disappear_time < current_time) \
  89.          (text).is_displayed = false; \
  90.    } \
  91. }
  92.  
  93.  
  94. #pragma pack(push,1)
  95.  
  96. // definition for a vector
  97. typedef struct vector_s
  98. {
  99.    float x; // X component
  100.    float y; // Y component
  101.    float z; // Z component
  102. } vector_t;
  103.  
  104.  
  105. // definition for a texture coordinates pair
  106. typedef struct texcoord_s
  107. {
  108.    float u; // X coordinate of the texture point this vertex corresponds to
  109.    float v; // Y coordinate of the texture point this vertex corresponds to
  110. } texcoord_t;
  111.  
  112.  
  113. // definition for a vertex (must be in that order for Direct3D)
  114. typedef struct vertex_s
  115. {
  116.    vector_t position; // position in space
  117.    vector_t normal; // coordinates of the unary normal vector of the plane this vertex is on (for illumination)
  118.    texcoord_t texcoord; // coordinates of the texture point this vertex corresponds to
  119. } vertex_t;
  120.  
  121. #pragma pack(pop)
  122.  
  123.  
  124. // definition for a reflected object (qsort array element to sort reflections by distance)
  125. typedef struct reflectedobject_s
  126. {
  127.    sceneobject_t *object; // pointer to the scene object
  128.    float distance; // distance to viewer camera
  129. } reflectedobject_t;
  130.  
  131.  
  132. // definition for a material (light reflection type)
  133. typedef struct material_s
  134. {
  135.    wchar_t name[32]; // material name
  136.    float ambient; // ambient reflection value ranging from 0 to 1
  137.    float diffuse; // diffuse reflection value ranging from 0 to 1
  138.    float emissive; // emissive reflection value ranging from 0 to 1
  139.    float specular; // specular reflection value ranging from 0 to 1
  140.    float shininess; // shininess (specular factor)
  141.    float transparency; // transparency value ranging from 0 (fully transparent) to 1 (opaque)
  142. } material_t;
  143.  
  144.  
  145. // definition for a mesh
  146. typedef struct mesh_s
  147. {
  148.    unsigned long hash; // basic content hash, to avoid duplicates
  149.    unsigned long vertex_format;
  150.    IDirect3DVertexBuffer9 *d3dvertices; // handled opaquely by Direct3D
  151.    int vertice_size;
  152.    int vertice_count;
  153.    bool is_indexed; // set to TRUE if this mesh has an index buffer
  154.    IDirect3DIndexBuffer9 *d3dindices; // handled opaquely by Direct3D
  155.    int indice_size;
  156.    int indice_count;
  157. } mesh_t;
  158.  
  159.  
  160. // definition for a texture
  161. typedef struct texture_s
  162. {
  163.    unsigned long hash; // basic content hash, to avoid duplicates
  164.    int width;
  165.    int height;
  166.    IDirect3DTexture9 *d3dtexture;
  167. } texture_t;
  168.  
  169.  
  170. // definition for a font
  171. typedef struct font_s
  172. {
  173.    unsigned long pathname_hash;
  174.    ID3DXFont *d3dfont;
  175. } font_t;
  176.  
  177.  
  178. // definition for a sprite
  179. typedef struct sprite_s
  180. {
  181.    unsigned long hash; // basic content hash, to avoid duplicates
  182.    ID3DXSprite *d3dsprite;
  183.    int texture_index;
  184. } sprite_t;
  185.  
  186.  
  187. // global variables used in this module only
  188. static IDirect3D9 *d3d = NULL; // our Direct3D interface
  189. static IDirect3DDevice9 *d3ddev = NULL; // the device class
  190.  
  191. static material_t *materials = NULL;
  192. static int material_count = 0;
  193. static texture_t *textures = NULL;
  194. static int texture_count = 0;
  195. static mesh_t *meshes = NULL;
  196. static int mesh_count = 0;
  197. static font_t *fonts = NULL;
  198. static int font_count = 0;
  199. static sprite_t *sprites = NULL;
  200. static int sprite_count = 0;
  201. static const float fov_value = 45.0f; // field of view width, in degrees
  202. static const float viewdist_near = 1.0f; // nearest view plane distance
  203. static const float viewdist_far = 200.0f; // farthest view plane distance
  204. static int initial_width = 0; // initial width of the render surface, in pixels (typically equal to the max displayable size)
  205. static int initial_height = 0; // initial height of the render surface, in pixels (typically equal to the max displayable size)
  206. static float current_width = 0.0f; // current width of the client area on which the render surface is rendered, in pixels
  207. static float current_height = 0.0f; // current height of the client area on which the render surface is rendered, in pixels
  208. static D3DCOLOR ambient_light;
  209. static vector_t camera_position;
  210. static vector_t scene_lookatpoint = { 0.0f, 0.0f, 0.0f };
  211. static const vector_t upwards_direction = { 0.0f, 0.0f, 1.0f };
  212. static int best_supported_filter;
  213. static bool is_fsaa_supported = false;
  214.  
  215. static wchar_t printf_buffer[0xffff];
  216.  
  217.  
  218. // prototypes of functions used in this module only
  219. static int Render_LoadFont (const wchar_t *font_name, float font_sizepct, bool is_bold, bool is_italic);
  220. static bool Render_LoadMesh_Obj (mesh_t *mesh, const wchar_t *objfile_pathname);
  221. static void Render_DrawSceneObjectReflection (sceneobject_t *sceneobject);
  222. static void Render_DrawSceneObject (sceneobject_t *sceneobject);
  223. static void Render_DrawSceneTile (sceneobject_t *sceneobject);
  224. static void Render_DrawSprite (sprite_t *sprite, float x_percent, float y_percent, float width_percent, float height_percent, int alpha);
  225. static int Render_GetTextBoundaries (float maxwidth_percent, wchar_t *font_face, float font_sizepct, bool is_bold, bool is_italic, wchar_t *text, RECT *rect);
  226. static void Render_wprintf (float x_percent, float y_percent, float maxwidth_percent, int horiz_align, int vert_align, int text_align, wchar_t *font_face, float font_sizepct, bool want_bold, bool want_italic, unsigned long color_rgba, int background_flags, RECT *out_rect, const wchar_t *fmt, ...);
  227. static float DistanceToCamera (float x, float y, float z);
  228. static float FadeFloat (float from, float to, float start_time, float end_time);
  229. static unsigned long HashString (const wchar_t *string_buffer);
  230. static unsigned long HashFile (const wchar_t *file_pathname);
  231. static void ResolveWildcard (wchar_t *file_pathname, wchar_t *extensions_separated_by_bars);
  232. static int SortReflectedObjects (const void *object1, const void *object2);
  233.  
  234.  
  235. bool Render_Init (const wchar_t *fmt, ...)
  236. {
  237.    // this function sets up and initializes Direct3D
  238.  
  239.    static wchar_t splashscreen_pathname[MAX_PATH];
  240.    static wchar_t *default_materialname = L"default";
  241.    va_list argptr;
  242.  
  243.    D3DCAPS9 device_capabilities;
  244.    unsigned long behaviour_flags;
  245.    unsigned long multisample_quality_count;
  246.    D3DPRESENT_PARAMETERS d3dpp;
  247.    wchar_t errorfile_path[MAX_PATH];
  248.    wchar_t line_buffer[256];
  249.    material_t material;
  250.    HRESULT ret;
  251.    RECT rect;
  252.    RECT viewport_rect;
  253.    FILE *fp;
  254.  
  255.    // concatenate all the arguments in one string
  256.    va_start (argptr, fmt);
  257.    wvsprintf (splashscreen_pathname, fmt, argptr);
  258.    va_end (argptr);
  259.  
  260.    // create the Direct3D interface
  261.    d3d = Direct3DCreate9 (D3D_SDK_VERSION);
  262.  
  263.    // get hardware capabilities
  264.    if (FAILED (d3d->GetDeviceCaps (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &device_capabilities)))
  265.    {
  266.       MessageBox (NULL, LOCALIZE (L"Error_CouldNotCreateD3DDevGetDeviceCapsFailed"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  267.       terminate_everything = true; // this is a fatal error
  268.       return (false);
  269.    }
  270.  
  271.    // grab info from that and adjust our D3D settings
  272.    best_supported_filter = (device_capabilities.RasterCaps & D3DPRASTERCAPS_ANISOTROPY ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR);
  273.    behaviour_flags = (device_capabilities.VertexProcessingCaps != 0 ? D3DCREATE_HARDWARE_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING);
  274.  
  275.    // see if simple (non-maskable) full-scene antialiasing is supported and in how many quality flavors
  276.    if (SUCCEEDED (d3d->CheckDeviceMultiSampleType (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, D3DMULTISAMPLE_NONMASKABLE, &multisample_quality_count)) && (multisample_quality_count > 0))
  277.       is_fsaa_supported = true; // remember FSAA is supported
  278.  
  279.    memset (&d3dpp, 0, sizeof (d3dpp)); // clear out the struct for use
  280.    d3dpp.Windowed = true; // always windowed (because we can't display dialog boxes in fullscreen mode)
  281.    d3dpp.BackBufferCount = 1;
  282.    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // discard old frames
  283.    d3dpp.hDeviceWindow = hMainWnd; // set the window to be used by Direct3D
  284.    d3dpp.EnableAutoDepthStencil = true; // enable Z-buffer and stencil buffer
  285.    d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; // D3DFMT_D15S1 15 bits should be enough to store each pixel's Z depth
  286.    if (is_fsaa_supported)
  287.    {
  288.       d3dpp.MultiSampleType = D3DMULTISAMPLE_NONMASKABLE; // use multisampling (full scene antialiasing) if supported
  289.       d3dpp.MultiSampleQuality = multisample_quality_count - 1; // use the best available multisample quality (FIXME: not working)
  290.    }
  291.    else
  292.    {
  293.       d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; // do not use multisampling (full scene antialiasing)
  294.       d3dpp.MultiSampleQuality = 0;
  295.    }
  296.    /*{ // this FSAA code isn't working any better
  297.       int start = D3DMULTISAMPLE_2_SAMPLES;
  298.       int end = D3DMULTISAMPLE_16_SAMPLES;
  299.       for (int n = start; n < end; n++)
  300.       {
  301.          D3DMULTISAMPLE_TYPE mst = (D3DMULTISAMPLE_TYPE) n;
  302.          DWORD quality = 0;
  303.          if (SUCCEEDED (d3d->CheckDeviceMultiSampleType (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, true, mst, &quality)))
  304.          {
  305.             d3dpp.MultiSampleType = mst; // use multisampling (full scene antialiasing) if supported
  306.             d3dpp.MultiSampleQuality = D3DMULTISAMPLE_2_SAMPLES;// quality - 1; // use the best available multisample quality
  307.          }
  308.       }
  309.    }*/
  310.  
  311.    // get main window's current rectangle and full screen size
  312.    GetWindowRect (hMainWnd, &rect);
  313.  
  314.    // create a device class using this information and the info from the d3dpp stuct. Resize the window to the max displayable
  315.    // size before creating the DirectX device, so as to guarantee the viewport will never be upscaled in case of window resize.
  316.    SetWindowPos (hMainWnd, NULL, rect.left, rect.top, GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN), SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOREPOSITION | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOCOPYBITS | SWP_NOACTIVATE);
  317.    if (FAILED (ret = d3d->CreateDevice (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, behaviour_flags, &d3dpp, &d3ddev)))
  318.    {
  319.       MessageBox (NULL, LOCALIZE (L"Error_CouldNotCreateD3DDevCreateDeviceFailed"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  320.  
  321.       // on error, write a log file on the user's desktop
  322.       errorfile_path[0] = 0;
  323.       SHGetSpecialFolderPath (NULL, errorfile_path, CSIDL_DESKTOP, true);
  324.       wcscat_s (errorfile_path, WCHAR_SIZEOF (errorfile_path), L"\\Chess Giants error.log");
  325.       _wfopen_s (&fp, errorfile_path, L"w, ccs=UNICODE");
  326.       if (fp != NULL)
  327.       {
  328.          fwprintf (fp, L"device_capabilities.DeviceType = %d\n", device_capabilities.DeviceType);
  329.          fwprintf (fp, L"device_capabilities.AdapterOrdinal = %ul\n", device_capabilities.AdapterOrdinal);
  330.          fwprintf (fp, L"device_capabilities.Caps = %ul\n", device_capabilities.Caps);
  331.          fwprintf (fp, L"device_capabilities.Caps2 = %ul\n", device_capabilities.Caps2);
  332.          fwprintf (fp, L"device_capabilities.Caps3 = %ul\n", device_capabilities.Caps3);
  333.          fwprintf (fp, L"device_capabilities.PresentationIntervals = %ul\n", device_capabilities.PresentationIntervals);
  334.          fwprintf (fp, L"device_capabilities.CursorCaps = %ul\n", device_capabilities.CursorCaps);
  335.          fwprintf (fp, L"device_capabilities.DevCaps = %ul\n", device_capabilities.DevCaps);
  336.          fwprintf (fp, L"device_capabilities.PrimitiveMiscCaps = %ul\n", device_capabilities.PrimitiveMiscCaps);
  337.          fwprintf (fp, L"device_capabilities.RasterCaps = %ul\n", device_capabilities.RasterCaps);
  338.          fwprintf (fp, L"device_capabilities.ZCmpCaps = %ul\n", device_capabilities.ZCmpCaps);
  339.          fwprintf (fp, L"device_capabilities.SrcBlendCaps = %ul\n", device_capabilities.SrcBlendCaps);
  340.          fwprintf (fp, L"device_capabilities.DestBlendCaps = %ul\n", device_capabilities.DestBlendCaps);
  341.          fwprintf (fp, L"device_capabilities.AlphaCmpCaps = %ul\n", device_capabilities.AlphaCmpCaps);
  342.          fwprintf (fp, L"device_capabilities.ShadeCaps = %ul\n", device_capabilities.ShadeCaps);
  343.          fwprintf (fp, L"device_capabilities.TextureCaps = %ul\n", device_capabilities.TextureCaps);
  344.          fwprintf (fp, L"device_capabilities.TextureFilterCaps = %ul\n", device_capabilities.TextureFilterCaps);
  345.          fwprintf (fp, L"device_capabilities.CubeTextureFilterCaps = %ul\n", device_capabilities.CubeTextureFilterCaps);
  346.          fwprintf (fp, L"device_capabilities.VolumeTextureFilterCaps = %ul\n", device_capabilities.VolumeTextureFilterCaps);
  347.          fwprintf (fp, L"device_capabilities.TextureAddressCaps = %ul\n", device_capabilities.TextureAddressCaps);
  348.          fwprintf (fp, L"device_capabilities.VolumeTextureAddressCaps = %ul\n", device_capabilities.VolumeTextureAddressCaps);
  349.          fwprintf (fp, L"device_capabilities.LineCaps = %ul\n", device_capabilities.LineCaps);
  350.          fwprintf (fp, L"device_capabilities.MaxTextureWidth = %ul\n", device_capabilities.MaxTextureWidth);
  351.          fwprintf (fp, L"device_capabilities.MaxTextureHeight = %ul\n", device_capabilities.MaxTextureHeight);
  352.          fwprintf (fp, L"device_capabilities.MaxVolumeExtent = %ul\n", device_capabilities.MaxVolumeExtent);
  353.          fwprintf (fp, L"device_capabilities.MaxTextureRepeat = %ul\n", device_capabilities.MaxTextureRepeat);
  354.          fwprintf (fp, L"device_capabilities.MaxTextureAspectRatio = %ul\n", device_capabilities.MaxTextureAspectRatio);
  355.          fwprintf (fp, L"device_capabilities.MaxAnisotropy = %ul\n", device_capabilities.MaxAnisotropy);
  356.          fwprintf (fp, L"device_capabilities.MaxVertexW = %f\n", device_capabilities.MaxVertexW);
  357.          fwprintf (fp, L"device_capabilities.GuardBandLeft = %f\n", device_capabilities.GuardBandLeft);
  358.          fwprintf (fp, L"device_capabilities.GuardBandTop = %f\n", device_capabilities.GuardBandTop);
  359.          fwprintf (fp, L"device_capabilities.GuardBandRight = %f\n", device_capabilities.GuardBandRight);
  360.          fwprintf (fp, L"device_capabilities.GuardBandBottom = %f\n", device_capabilities.GuardBandBottom);
  361.          fwprintf (fp, L"device_capabilities.ExtentsAdjust = %f\n", device_capabilities.ExtentsAdjust);
  362.          fwprintf (fp, L"device_capabilities.StencilCaps = %ul\n", device_capabilities.StencilCaps);
  363.          fwprintf (fp, L"device_capabilities.FVFCaps = %ul\n", device_capabilities.FVFCaps);
  364.          fwprintf (fp, L"device_capabilities.TextureOpCaps = %ul\n", device_capabilities.TextureOpCaps);
  365.          fwprintf (fp, L"device_capabilities.MaxTextureBlendStages = %ul\n", device_capabilities.MaxTextureBlendStages);
  366.          fwprintf (fp, L"device_capabilities.MaxSimultaneousTextures = %ul\n", device_capabilities.MaxSimultaneousTextures);
  367.          fwprintf (fp, L"device_capabilities.VertexProcessingCaps = %ul\n", device_capabilities.VertexProcessingCaps);
  368.          fwprintf (fp, L"device_capabilities.MaxActiveLights = %ul\n", device_capabilities.MaxActiveLights);
  369.          fwprintf (fp, L"device_capabilities.MaxUserClipPlanes = %ul\n", device_capabilities.MaxUserClipPlanes);
  370.          fwprintf (fp, L"device_capabilities.MaxVertexBlendMatrices = %ul\n", device_capabilities.MaxVertexBlendMatrices);
  371.          fwprintf (fp, L"device_capabilities.MaxVertexBlendMatrixIndex = %ul\n", device_capabilities.MaxVertexBlendMatrixIndex);
  372.          fwprintf (fp, L"device_capabilities.MaxPointSize = %f\n", device_capabilities.MaxPointSize);
  373.          fwprintf (fp, L"device_capabilities.MaxPrimitiveCount = %ul\n", device_capabilities.MaxPrimitiveCount);
  374.          fwprintf (fp, L"device_capabilities.MaxVertexIndex = %ul\n", device_capabilities.MaxVertexIndex);
  375.          fwprintf (fp, L"device_capabilities.MaxStreams = %ul\n", device_capabilities.MaxStreams);
  376.          fwprintf (fp, L"device_capabilities.MaxStreamStride = %ul\n", device_capabilities.MaxStreamStride);
  377.          fwprintf (fp, L"device_capabilities.VertexShaderVersion = %ul\n", device_capabilities.VertexShaderVersion);
  378.          fwprintf (fp, L"device_capabilities.MaxVertexShaderConst = %ul\n", device_capabilities.MaxVertexShaderConst);
  379.          fwprintf (fp, L"device_capabilities.PixelShaderVersion = %ul\n", device_capabilities.PixelShaderVersion);
  380.          fwprintf (fp, L"device_capabilities.PixelShader1xMaxValue = %f\n", device_capabilities.PixelShader1xMaxValue);
  381.          fwprintf (fp, L"device_capabilities.DevCaps2 = %ul\n", device_capabilities.DevCaps2);
  382.          fwprintf (fp, L"device_capabilities.MaxNpatchTessellationLevel = %f\n", device_capabilities.MaxNpatchTessellationLevel);
  383.          fwprintf (fp, L"device_capabilities.Reserved5 = %ul\n", device_capabilities.Reserved5);
  384.          fwprintf (fp, L"device_capabilities.MasterAdapterOrdinal = %ul\n", device_capabilities.MasterAdapterOrdinal);
  385.          fwprintf (fp, L"device_capabilities.AdapterOrdinalInGroup = %ul\n", device_capabilities.AdapterOrdinalInGroup);
  386.          fwprintf (fp, L"device_capabilities.NumberOfAdaptersInGroup = %ul\n", device_capabilities.NumberOfAdaptersInGroup);
  387.          fwprintf (fp, L"device_capabilities.DeclTypes = %ul\n", device_capabilities.DeclTypes);
  388.          fwprintf (fp, L"device_capabilities.NumSimultaneousRTs = %ul\n", device_capabilities.NumSimultaneousRTs);
  389.          fwprintf (fp, L"device_capabilities.StretchRectFilterCaps = %ul\n", device_capabilities.StretchRectFilterCaps);
  390.          fwprintf (fp, L"device_capabilities.VS20Caps.Caps = %ul\n", device_capabilities.VS20Caps.Caps);
  391.          fwprintf (fp, L"device_capabilities.VS20Caps.DynamicFlowControlDepth = %d\n", device_capabilities.VS20Caps.DynamicFlowControlDepth);
  392.          fwprintf (fp, L"device_capabilities.VS20Caps.NumTemps = %d\n", device_capabilities.VS20Caps.NumTemps);
  393.          fwprintf (fp, L"device_capabilities.VS20Caps.StaticFlowControlDepth = %d\n", device_capabilities.VS20Caps.StaticFlowControlDepth);
  394.          fwprintf (fp, L"device_capabilities.PS20Caps.Caps = %ul\n", device_capabilities.PS20Caps.Caps);
  395.          fwprintf (fp, L"device_capabilities.PS20Caps.DynamicFlowControlDepth = %d\n", device_capabilities.PS20Caps.DynamicFlowControlDepth);
  396.          fwprintf (fp, L"device_capabilities.PS20Caps.NumTemps = %d\n", device_capabilities.PS20Caps.NumTemps);
  397.          fwprintf (fp, L"device_capabilities.PS20Caps.StaticFlowControlDepth = %d\n", device_capabilities.PS20Caps.StaticFlowControlDepth);
  398.          fwprintf (fp, L"device_capabilities.PS20Caps.NumInstructionSlots = %d\n", device_capabilities.PS20Caps.NumInstructionSlots);
  399.          fwprintf (fp, L"device_capabilities.VertexTextureFilterCaps = %ul\n", device_capabilities.VertexTextureFilterCaps);
  400.          fwprintf (fp, L"device_capabilities.MaxVShaderInstructionsExecuted = %ul\n", device_capabilities.MaxVShaderInstructionsExecuted);
  401.          fwprintf (fp, L"device_capabilities.MaxPShaderInstructionsExecuted = %ul\n", device_capabilities.MaxPShaderInstructionsExecuted);
  402.          fwprintf (fp, L"device_capabilities.MaxVertexShader30InstructionSlots = %ul\n", device_capabilities.MaxVertexShader30InstructionSlots);
  403.          fwprintf (fp, L"device_capabilities.MaxPixelShader30InstructionSlots = %ul\n", device_capabilities.MaxPixelShader30InstructionSlots);
  404.          fwprintf (fp, L"========\n");
  405.          fwprintf (fp, L"(guessed) behaviour_flags = %ul\n", behaviour_flags);
  406.          fwprintf (fp, L"========\n");
  407.          fwprintf (fp, L"d3dpp.MultiSampleType = %d\n", d3dpp.MultiSampleType);
  408.          fwprintf (fp, L"d3dpp.MultiSampleQuality = %d\n", d3dpp.MultiSampleQuality);
  409.          fwprintf (fp, L"========\n");
  410.          fwprintf (fp, L"d3d->CreateDevice() returned %d\n", (int) ret);
  411.          fclose (fp);
  412.  
  413.          MessageBox (NULL, LOCALIZE (L"Error_GameCouldNotStartPleaseSendLogToAuthor"), LOCALIZE (L"Information"), MB_ICONINFORMATION | MB_OK);
  414.       }
  415.       else
  416.          MessageBox (NULL, LOCALIZE (L"Error_CouldNotWriteToLogFile"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  417.  
  418.       terminate_everything = true; // this is a fatal error
  419.       return (false);
  420.    }
  421.  
  422.    // save the viewport's width and height
  423.    GetClientRect (hMainWnd, &viewport_rect);
  424.    initial_width = viewport_rect.right;
  425.    initial_height = viewport_rect.bottom;
  426.  
  427.    // now resize the window to its original size
  428.    SetWindowPos (hMainWnd, NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW);
  429.  
  430.    // set the texture parameters
  431.    d3ddev->SetSamplerState (0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); // wrap textures around their edges
  432.    d3ddev->SetSamplerState (0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
  433.    d3ddev->SetTextureStageState (0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // and modulate their alpha with the material's alpha
  434.    d3ddev->SetTextureStageState (0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
  435.    d3ddev->SetTextureStageState (0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
  436.  
  437.    // enable the Z buffer
  438.    d3ddev->SetRenderState (D3DRS_ZENABLE, true);
  439.  
  440.    // disable the stencil buffer
  441.    d3ddev->SetRenderState (D3DRS_STENCILENABLE, false);
  442.  
  443.    // normalize the face normals (if we don't, scaling will cause problems with lighting computations)
  444.    d3ddev->SetRenderState (D3DRS_NORMALIZENORMALS, true);
  445.  
  446.    // turn on alpha blending
  447.    d3ddev->SetRenderState (D3DRS_ALPHABLENDENABLE, true);
  448.    d3ddev->SetRenderState (D3DRS_BLENDOP, D3DBLENDOP_ADD);
  449.    d3ddev->SetRenderState (D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
  450.    d3ddev->SetRenderState (D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
  451.  
  452.    // use all of the materials' light reflection properties
  453.    d3ddev->SetRenderState (D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
  454.    d3ddev->SetRenderState (D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
  455.    d3ddev->SetRenderState (D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);
  456.    d3ddev->SetRenderState (D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
  457.    d3ddev->SetRenderState (D3DRS_COLORVERTEX, false);
  458.  
  459.    // enable 3D lighting
  460.    d3ddev->SetRenderState (D3DRS_LIGHTING, true);
  461.  
  462.    // load the splash screen sprite and display it
  463.    int bgsprite_index = Render_LoadSprite (splashscreen_pathname);
  464.    d3ddev->Clear (0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0);
  465.    d3ddev->BeginScene (); // begins the 3D scene
  466.    Render_DrawSprite (&sprites[bgsprite_index], 0.0f, 0.0f, 100.0f, 100.0f, 0xFF); // draw the background sprite
  467.    d3ddev->EndScene (); // ends the 3D scene
  468.    d3ddev->Present (NULL, NULL, NULL, NULL); // displays the created frame on the screen
  469.  
  470.    // open and parse the materials file and build the materials list
  471.    materials = NULL;
  472.    material_count = 0;
  473.    _wfopen_s (&fp, L"materials.cfg", L"r, ccs=UNICODE");
  474.    if (fp != NULL)
  475.    {
  476.       // read line per line...
  477.       while (fgetws (line_buffer, WCHAR_SIZEOF (line_buffer), fp) != NULL)
  478.       {
  479.          // can we read a complete material line ?
  480.          if (swscanf_s (line_buffer, L"\"%[^\"]\" %f %f %f %f %f %f", material.name, WCHAR_SIZEOF (material.name), &material.ambient, &material.diffuse, &material.emissive, &material.specular, &material.shininess, &material.transparency) == 7)
  481.          {
  482.             materials = (material_t *) SAFE_realloc (materials, material_count, material_count + 1, sizeof (material_t), false);
  483.             memcpy (&materials[material_count], &material, sizeof (material_t));
  484.             material_count++; // if so, append this new material to the materials array
  485.          }
  486.       }
  487.       fclose (fp); // finished, close the file
  488.    }
  489.    materials = (material_t *) SAFE_realloc (materials, material_count, material_count + 1, sizeof (material_t), false);
  490.    wcscpy_s (materials[material_count].name, WCHAR_SIZEOF (materials[material_count].name), default_materialname);
  491.    materials[material_count].ambient = 1.0f;
  492.    materials[material_count].diffuse = 1.0f;
  493.    materials[material_count].emissive = 0.0f;
  494.    materials[material_count].specular = 0.0f;
  495.    materials[material_count].shininess = 0.0f;
  496.    materials[material_count].transparency = 1.0f;
  497.    material_count++; // append a default material at the end of the array and we're all set
  498.  
  499.    return (true); // finished
  500. }
  501.  
  502.  
  503. void Render_Shutdown (void)
  504. {
  505.    // this function shuts down the Direct3D interfaces and releases the Direct3D COM objects
  506.  
  507.    int array_index;
  508.  
  509.    // close and release font data
  510.    SAFE_free ((void **) &fonts);
  511.    font_count = 0;
  512.  
  513.    // close and release sprite data
  514.    SAFE_free ((void **) &sprites);
  515.    sprite_count = 0;
  516.  
  517.    // close and release meshes data
  518.    if (meshes != NULL)
  519.    {
  520.       for (array_index = 0; array_index < mesh_count; array_index++)
  521.       {
  522.          if (meshes[array_index].d3dindices != NULL)
  523.             meshes[array_index].d3dindices->Release ();
  524.          meshes[array_index].d3dindices = NULL;
  525.  
  526.          if (meshes[array_index].d3dvertices != NULL)
  527.             meshes[array_index].d3dvertices->Release ();
  528.          meshes[array_index].d3dvertices = NULL;
  529.       }
  530.    }
  531.    meshes = NULL;
  532.    mesh_count = 0;
  533.  
  534.    // close and release texture data
  535.    if (textures != NULL)
  536.    {
  537.       for (array_index = 0; array_index < texture_count; array_index++)
  538.          if (textures[array_index].d3dtexture != NULL)
  539.             textures[array_index].d3dtexture->Release ();
  540.       SAFE_free ((void **) &textures);
  541.    }
  542.    texture_count = 0;
  543.  
  544.    // close and release materials data
  545.    SAFE_free ((void **) &materials);
  546.    material_count = 0;
  547.  
  548.    // close and release the 3D device
  549.    if (d3ddev != NULL)
  550.       d3ddev->Release ();
  551.    d3ddev = NULL;
  552.  
  553.    // close and release Direct3D
  554.    if (d3d != NULL)
  555.       d3d->Release ();
  556.    d3d = NULL;
  557.  
  558.    return; // finished
  559. }
  560.  
  561.  
  562. void Render_RenderFrame (scene_t *scene)
  563. {
  564.    // this is the function used to render a single frame
  565.  
  566.    static int framerate_value = 0;
  567.    static int framerate_count = 0;
  568.    static float framerate_time = 0;
  569.  
  570.    D3DXMATRIX scaling_matrix;
  571.    D3DXMATRIX translation_matrix;
  572.    D3DXMATRIX view_matrix; // the view transform matrix
  573.    D3DXMATRIX projection_matrix; // the projection transform matrix
  574.    D3DLIGHT9 dxlight;
  575.    RECT rect;
  576.    float angle;
  577.    float sin_pitch;
  578.    float sin_yaw;
  579.    float cos_pitch;
  580.    float cos_yaw;
  581.    int light_index;
  582.    int object_index;
  583.    int cchistory_index;
  584.    int cchistory_index2;
  585.    float combinedwidth_percent;
  586.    float combinedheight_percent;
  587.    light_t *light;
  588.    ccreply_t *ccreply;
  589.    sceneobject_t *sceneobject;
  590.    reflectedobject_t *reflectedobjects; // mallocated
  591.    int reflectedobject_count;
  592.    reflectedobject_t *otherobjects; // mallocated
  593.    int otherobject_count;
  594.  
  595.    if (terminate_everything)
  596.       return; // consistency check
  597.  
  598.    // get the device view port and save the actual width and height
  599.    GetClientRect (hMainWnd, &rect);
  600.    current_width = (float) rect.right; // they may differ from window width and window height
  601.    current_height = (float) rect.bottom; // because of title bars, menus, borders, etc.
  602.  
  603.    // clear the back buffer, the Z buffer and the stencil buffer
  604.    d3ddev->Clear (0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0);
  605.    d3ddev->BeginScene (); // begins the 3D scene
  606.  
  607.    ////////////////////////////////////////
  608.    // Setup scene lights
  609.  
  610.    // There are three types of light : ambient, diffuse and specular.
  611.    // Diffuse lights can be of type directional (sun), point (bulb) or spot (flashlight).
  612.  
  613.    // The attenuation formula for point lights is :
  614.    // Atten = 1 / (att0 + (att1 * d) + (att2 * d²))
  615.  
  616.    // In spot lights, Phi is the outer cone angle. Theta is the inner cone angle.
  617.  
  618.    // set the default lighting color
  619.    ambient_light = RGBACOLOR_TO_ARGBCOLOR (RGBACOLOR_FULLALPHA (theme->illum.ambient_light));
  620.    d3ddev->SetRenderState (D3DRS_AMBIENT, ambient_light);
  621.  
  622.    // for each light...
  623.    for (light_index = 0; light_index < theme->illum.light_count; light_index++)
  624.    {
  625.       light = &theme->illum.lights[light_index]; // quick access to light
  626.  
  627.       memset (&dxlight, 0, sizeof (dxlight)); // clear out the dxlight struct for use
  628.  
  629.       // set its type
  630.       if (light->type == LIGHT_DIRECTIONAL)
  631.          dxlight.Type = D3DLIGHT_DIRECTIONAL; // directional light (e.g, sun)
  632.       else if (light->type == LIGHT_POINT)
  633.          dxlight.Type = D3DLIGHT_POINT; // point light (e.g, light bulb)
  634.       else if (light->type == LIGHT_SPOT)
  635.          dxlight.Type = D3DLIGHT_SPOT; // spot light (e.g, flash light)
  636.       else
  637.       {
  638.          d3ddev->LightEnable (light_index, false); // unknown light ; turn off light #index
  639.          continue; // and proceed to the next one
  640.       }
  641.  
  642.       // set its parameters
  643.       dxlight.Diffuse = D3DXCOLOR (RGBACOLOR_TO_ARGBCOLOR (RGBACOLOR_FULLALPHA (light->color)));
  644.       dxlight.Specular = D3DXCOLOR (0xffffffff);
  645.       dxlight.Position.x = light->pos_x;
  646.       dxlight.Position.y = light->pos_y;
  647.       dxlight.Position.z = light->pos_z;
  648.       dxlight.Direction.x = light->direction_x;
  649.       dxlight.Direction.y = light->direction_y;
  650.       dxlight.Direction.z = light->direction_z;
  651.       dxlight.Range = light->range; // light won't be computed after this distance
  652.       dxlight.Attenuation0 = light->attenuation_constant; // constant attenuation, see formula
  653.       dxlight.Attenuation1 = light->attenuation_proportional; // proportional attenuation, see formula
  654.       dxlight.Attenuation2 = light->attenuation_square; // square attenuation, see formula
  655.       dxlight.Phi = light->cone_outer * TO_RADIANS; // outer spot cone
  656.       dxlight.Theta = light->cone_inner * TO_RADIANS; // inner spot cone
  657.  
  658.       d3ddev->SetLight (light_index, &dxlight); // send the light struct properties to light #index
  659.       d3ddev->LightEnable (light_index, true); // turn on light #index
  660.    }
  661.  
  662.    ////////////////////////////////
  663.    // View transform
  664.  
  665.    // update the point looked at
  666.    scene_lookatpoint.x = lookatpoint_x;
  667.    scene_lookatpoint.y = lookatpoint_y;
  668.  
  669.    // compute the sine and cosine of the pitch component
  670.    angle = current_pitch * TO_RADIANS;
  671.    sin_pitch = sinf (angle);
  672.    cos_pitch = cosf (angle);
  673.  
  674.    // compute the sine and cosine of the yaw component
  675.    angle = current_yaw * TO_RADIANS;
  676.    sin_yaw = sinf (angle);
  677.    cos_yaw = cosf (angle);
  678.  
  679.    // build the camera position
  680.    camera_position.x = scene_lookatpoint.x + (float) -(cos_pitch * cos_yaw) * current_distance;
  681.    camera_position.y = scene_lookatpoint.y + (float) -(cos_pitch * sin_yaw) * current_distance;
  682.    camera_position.z = (float) sin_pitch * current_distance;
  683.  
  684.    // set up a view matrix
  685.    D3DXMatrixLookAtLH (&view_matrix,
  686.                        (D3DXVECTOR3 *) &camera_position, // camera position
  687.                        (D3DXVECTOR3 *) &scene_lookatpoint, // look-at position
  688.                        (D3DXVECTOR3 *) &upwards_direction); // up direction
  689.  
  690.    // tell Direct3D about our matrix
  691.    d3ddev->SetTransform (D3DTS_VIEW, &view_matrix);
  692.  
  693.    /////////////////////////////////////
  694.    // Projection transform
  695.  
  696.    // set up a projection matrix
  697.    D3DXMatrixPerspectiveFovLH (&projection_matrix,
  698.                                fov_value * TO_RADIANS, // field of view width
  699.                                current_width / current_height, // aspect ratio
  700.                                viewdist_near, viewdist_far); // view plane distances
  701.  
  702.    // tell Direct3D about our matrix
  703.    d3ddev->SetTransform (D3DTS_PROJECTION, &projection_matrix);
  704.  
  705.    /////////////////////////////////////
  706.    // End of the transforms
  707.  
  708.    // if we want it, enable specular lighting
  709.    d3ddev->SetRenderState (D3DRS_SPECULARENABLE, options.want_specularlighting);
  710.  
  711.    // turn on texture filtering if needed
  712.    if (options.want_filtering)
  713.    {
  714.       d3ddev->SetSamplerState (0, D3DSAMP_MINFILTER, best_supported_filter);
  715.       d3ddev->SetSamplerState (0, D3DSAMP_MAGFILTER, best_supported_filter);
  716.       d3ddev->SetSamplerState (0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  717.  
  718.       // turn on fullscene antialiasing only if capable
  719.       d3ddev->SetRenderState (D3DRS_MULTISAMPLEANTIALIAS, is_fsaa_supported);
  720.    }
  721.    else
  722.    {
  723.       d3ddev->SetSamplerState (0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
  724.       d3ddev->SetSamplerState (0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
  725.       d3ddev->SetSamplerState (0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
  726.  
  727.       // turn off fullscene antialiasing
  728.       d3ddev->SetRenderState (D3DRS_MULTISAMPLEANTIALIAS, false);
  729.    }
  730.  
  731.    /////////////////////////////////////////////////////////////
  732.    // draw the background elements. No need for a Z buffer here.
  733.  
  734.    d3ddev->SetRenderState (D3DRS_ZENABLE, false); // disable depth testing
  735.  
  736.    // draw the background sprite, if any
  737.    if (scene->background_spriteindex >= 0)
  738.       Render_DrawSprite (&sprites[scene->background_spriteindex], 0.0f, 0.0f, 100.0f, 100.0f, 0xFF);
  739.  
  740.    // draw the table border
  741.    Render_DrawSceneObject (&scene->objects[0]);
  742.    Render_DrawSceneObject (&scene->objects[1]);
  743.  
  744.    // draw the table and build the stencil buffer at the same time
  745.    d3ddev->SetRenderState (D3DRS_STENCILENABLE, true); // enable the stencil buffer (i.e. the "frame" for drawing table reflections)
  746.    d3ddev->SetRenderState (D3DRS_STENCILFUNC, D3DCMP_ALWAYS); // instruct how to fill it
  747.    d3ddev->SetRenderState (D3DRS_STENCILPASS, D3DSTENCILOP_INCRSAT); // instruct how to fill it
  748.    Render_DrawSceneObject (&scene->objects[2]); // draw the table squares in the stencil buffer
  749.    d3ddev->SetRenderState (D3DRS_STENCILENABLE, false); // finished drawing the stencil buffer
  750.  
  751.    d3ddev->SetRenderState (D3DRS_ZENABLE, true); // re-enable depth testing
  752.  
  753.    ////////////////////////////////////////////////////////////
  754.    // draw the scene objects and their reflections on the table
  755.  
  756.    // start with the reflections if we want them, and if the table does it
  757.    if (options.want_reflections && (theme->reflection_alpha > 0))
  758.    {
  759.       // build an array of reflected objects with their distances
  760.       reflectedobjects = NULL;
  761.       reflectedobject_count = 0;
  762.       otherobjects = NULL;
  763.       otherobject_count = 0;
  764.  
  765.       // cycle through all parts and see which ones need to be reflected
  766.       for (object_index = 3; object_index < scene->object_count; object_index++)
  767.       {
  768.          sceneobject = &scene->objects[object_index]; // quick access to scene object
  769.  
  770.          // is this object a mesh AND it's above the ground ?
  771.          if ((sceneobject->mesh_index != -1) && (sceneobject->z > 0))
  772.          {
  773.             // yes it is. It thus needs to be reflected, so add it to the list
  774.             reflectedobjects = (reflectedobject_t *) SAFE_realloc (reflectedobjects, reflectedobject_count, reflectedobject_count + 1, sizeof (reflectedobject_t), false);
  775.             reflectedobjects[reflectedobject_count].object = sceneobject; // save scene object
  776.             reflectedobjects[reflectedobject_count].distance = DistanceToCamera (sceneobject->x, sceneobject->y, sceneobject->z);
  777.             reflectedobject_count++; // we have now one object more to reflect
  778.          }
  779.          else
  780.          {
  781.             // no it's not. It doesn't need to be reflected, so add it to the other list
  782.             otherobjects = (reflectedobject_t *) SAFE_realloc (otherobjects, otherobject_count, otherobject_count + 1, sizeof (reflectedobject_t), false);
  783.             otherobjects[otherobject_count].object = sceneobject; // save scene object
  784.             otherobjects[otherobject_count].distance = 0;
  785.             otherobject_count++; // we have now one object more to reflect
  786.          }
  787.       }
  788.  
  789.       // now sort them from farthest to closest and draw them in this order
  790.       qsort (reflectedobjects, reflectedobject_count, sizeof (reflectedobject_t), SortReflectedObjects);
  791.       for (object_index = 0; object_index < reflectedobject_count; object_index++)
  792.          Render_DrawSceneObjectReflection (reflectedobjects[object_index].object); // draw the reflection
  793.       for (object_index = 0; object_index < reflectedobject_count; object_index++)
  794.          Render_DrawSceneObject (reflectedobjects[object_index].object); // and draw the objects afterwards
  795.       for (object_index = 0; object_index < otherobject_count; object_index++)
  796.          Render_DrawSceneObject (otherobjects[object_index].object); // finally, draw the non-reflected objects
  797.  
  798.       SAFE_free ((void **) &reflectedobjects); // and free the reflected objects array
  799.       SAFE_free ((void **) &otherobjects); // and the non-reflected objects array
  800.    }
  801.    else
  802.       for (object_index = 3; object_index < scene->object_count; object_index++)
  803.          Render_DrawSceneObject (&scene->objects[object_index]); // else if no reflections, draw the objects, the Z-buffer will sort them
  804.  
  805.    // draw the overlay texture if required
  806.    if (scene->overlay_spriteindex >= 0)
  807.       Render_DrawSprite (&sprites[scene->overlay_spriteindex], 0.0f, 0.0f, 100.0f, 100.0f, 0x4F);
  808.  
  809.    ///////////////
  810.    // draw the GUI
  811.  
  812.    DRAW_BUTTON_IF_NEEDED (scene->gui.newgamebutton); // new game
  813.    DRAW_BUTTON_IF_NEEDED (scene->gui.opengamebutton); // open game
  814.  
  815.    // draw the arrows
  816.    DRAW_BUTTON_IF_NEEDED (scene->gui.llarrow); // "jump to beginning" arrow
  817.    DRAW_BUTTON_IF_NEEDED (scene->gui.larrow); // "previous move" arrow
  818.    DRAW_BUTTON_IF_NEEDED (scene->gui.rarrow); // "next move" arrow
  819.    DRAW_BUTTON_IF_NEEDED (scene->gui.rrarrow); // "jump to end" arrow
  820.    DRAW_TEXT_IF_NEEDED (scene->gui.arrow_text); // arrow text
  821.  
  822.    // draw the other GUI buttons
  823.    DRAW_BUTTON_IF_NEEDED (scene->gui.chatbutton); // chat button
  824.    DRAW_BUTTON_IF_NEEDED (scene->gui.gamesbutton); // games button
  825.    DRAW_BUTTON_IF_NEEDED (scene->gui.peoplebutton); // people button
  826.  
  827.    // does the parts pick line need to be displayed ?
  828.    if (scene->gui.is_partspick_displayed)
  829.    {
  830. #define PARTSIZE_PCT (100.0f / 13.0f)
  831.  
  832.       if      (scene->gui.partspick_selectedpart == 'P') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  0.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  833.       else if (scene->gui.partspick_selectedpart == 'R') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  1.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  834.       else if (scene->gui.partspick_selectedpart == 'N') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  2.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  835.       else if (scene->gui.partspick_selectedpart == 'B') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  3.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  836.       else if (scene->gui.partspick_selectedpart == 'Q') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  4.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  837.       else if (scene->gui.partspick_selectedpart == 'K') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  5.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  838.       else if (scene->gui.partspick_selectedpart == ' ') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  6.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  839.       else if (scene->gui.partspick_selectedpart == 'k') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  7.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  840.       else if (scene->gui.partspick_selectedpart == 'q') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  8.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  841.       else if (scene->gui.partspick_selectedpart == 'b') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex],  9.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  842.       else if (scene->gui.partspick_selectedpart == 'n') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex], 10.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  843.       else if (scene->gui.partspick_selectedpart == 'r') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex], 11.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  844.       else if (scene->gui.partspick_selectedpart == 'p') Render_DrawSprite (&sprites[theme->lastmovetarget_spriteindex], 12.0f * PARTSIZE_PCT, 0.0f, PARTSIZE_PCT, 11.0f, 0xFF);
  845.  
  846.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_PAWN]],       0.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'P') || (scene->gui.partspick_selectedpart == 'P')) ? 0xFF : 0x7F)); // white pawn
  847.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_ROOK]],       1.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'R') || (scene->gui.partspick_selectedpart == 'R')) ? 0xFF : 0x7F)); // white rook
  848.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_KNIGHT]],     2.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'N') || (scene->gui.partspick_selectedpart == 'N')) ? 0xFF : 0x7F)); // white knight
  849.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_BISHOP]],     3.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'B') || (scene->gui.partspick_selectedpart == 'B')) ? 0xFF : 0x7F)); // white bishop
  850.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_QUEEN]],      4.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'Q') || (scene->gui.partspick_selectedpart == 'Q')) ? 0xFF : 0x7F)); // white queen
  851.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_WHITE][PART_KING]],       5.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'K') || (scene->gui.partspick_selectedpart == 'K')) ? 0xFF : 0x7F)); // white king
  852.       Render_DrawSprite (&sprites[theme->lastmovesource_spriteindex],                6.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == ' ') || (scene->gui.partspick_selectedpart == ' ')) ? 0xFF : 0x7F)); // erase mark
  853.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_KING]],       7.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'k') || (scene->gui.partspick_selectedpart == 'k')) ? 0xFF : 0x7F)); // black king
  854.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_QUEEN]],      8.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'q') || (scene->gui.partspick_selectedpart == 'q')) ? 0xFF : 0x7F)); // black queen
  855.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_BISHOP]],     9.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'b') || (scene->gui.partspick_selectedpart == 'b')) ? 0xFF : 0x7F)); // black bishop
  856.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_KNIGHT]],    10.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'n') || (scene->gui.partspick_selectedpart == 'n')) ? 0xFF : 0x7F)); // black knight
  857.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_ROOK]],      11.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'r') || (scene->gui.partspick_selectedpart == 'r')) ? 0xFF : 0x7F)); // black rook
  858.       Render_DrawSprite (&sprites[theme->flatsprites[COLOR_BLACK][PART_PAWN]],      12.0f * PARTSIZE_PCT, 0, PARTSIZE_PCT, 11.0f, (((scene->gui.partspick_hoveredpart == 'p') || (scene->gui.partspick_selectedpart == 'p')) ? 0xFF : 0x7F)); // black pawn
  859.  
  860. #undef PARTSIZE_PCT
  861.    }
  862.  
  863.    // draw GUI texts
  864.    DRAW_TEXT_IF_NEEDED (scene->gui.comment_text); // move comments
  865.    DRAW_TEXT_IF_NEEDED (scene->gui.history_text); // game history
  866.    DRAW_TEXT_IF_NEEDED (scene->gui.clock_text); // game clock
  867.  
  868.    // draw the chatter channels text
  869.    Render_GetTextBoundaries (-1, L"Papyrus", 8.0f, false, false, L"a", &rect);
  870.    combinedheight_percent = (float) (rect.bottom * 100 / initial_height) * 1.14f; // get a string's height
  871.  
  872.    // cycle through all the chat strings...
  873.    ccreply = NULL;
  874.    for (cchistory_index = 0; cchistory_index < scene->gui.cchistory_count; cchistory_index++)
  875.    {
  876.       ccreply = &scene->gui.cchistory[cchistory_index]; // quick access to CC reply
  877.       if ((ccreply->text[0] != 0) && (ccreply->arrival_time + 60.0f > current_time))
  878.          break; // break at the first one that we should display
  879.    }
  880.  
  881.    // have we some to display ?
  882.    if (cchistory_index < scene->gui.cchistory_count)
  883.    {
  884.       // first, get the remaining text's combined height
  885.       for (cchistory_index2 = cchistory_index; cchistory_index2 < scene->gui.cchistory_count; cchistory_index2++)
  886.       {
  887.          ccreply = &scene->gui.cchistory[cchistory_index2]; // quick access to CC reply
  888.  
  889.          combinedwidth_percent = 1.0f;
  890.          if (ccreply->channelname[0] != 0)
  891.          {
  892.             Render_GetTextBoundaries (100 - combinedwidth_percent, L"Papyrus", 8.0f, false, false, L"[] : ", &rect);
  893.             combinedwidth_percent += (float) (rect.right * 100 / initial_width);
  894.             Render_GetTextBoundaries (100 - combinedwidth_percent, L"Papyrus", 8.0f, false, false, ccreply->channelname, &rect);
  895.             combinedwidth_percent += (float) (rect.right * 100 / initial_width);
  896.          }
  897.          if (ccreply->nickname[0] != 0)
  898.          {
  899.             Render_GetTextBoundaries (100 - combinedwidth_percent, L"Papyrus", 8.0f, false, false, ccreply->nickname, &rect);
  900.             combinedwidth_percent += (float) (rect.right * 100 / initial_width);
  901.          }
  902.          Render_GetTextBoundaries (100 - combinedwidth_percent, L"Papyrus", 8.0f, false, false, ccreply->text, &rect);
  903.          combinedheight_percent += (float) (rect.bottom * 100 / initial_height) * 1.14f; // add this string's height
  904.       }
  905.  
  906.       // now for each string remaining...
  907.       for (; cchistory_index < scene->gui.cchistory_count; cchistory_index++)
  908.       {
  909.          ccreply = &scene->gui.cchistory[cchistory_index]; // quick access to CC reply
  910.          rect.right = 10;
  911.          PRINT_CCREPLY (ccreply); // print CC reply on screen
  912.          combinedheight_percent -= (float) ((rect.bottom - rect.top) * 100 / initial_height) * 1.14f; // draw it from top of zone to bottom
  913.       }
  914.    }
  915.  
  916.    // are we online ?
  917.    if (Player_FindByType (PLAYER_INTERNET) != NULL)
  918.    {
  919.       ccreply = &scene->gui.entered_ccreply;
  920.       if (!scene->gui.is_entering_text)
  921.          ccreply->color = RGBACOLOR_SETALPHA (ccreply->color, RGBACOLOR_ALPHA (ccreply->color) / 6); // if there's no text being entered, fade CC reply a lot
  922.  
  923.       rect.right = 10;
  924.       PRINT_CCREPLY (ccreply); // print CC reply on screen
  925.    }
  926.  
  927.    // print GUI texts
  928.    DRAW_TEXT_IF_NEEDED (scene->gui.turn_text); // player turn's text
  929.    DRAW_TEXT_IF_NEEDED (scene->gui.central_text); // central notification zone text
  930.  
  931.    // if needed, print the spinning wheel
  932.    if (scene->gui.want_spinwheel)
  933.       Render_DrawSprite (&sprites[spinner_spriteindex[(int) (10.0f * current_time) % 12]], 47.0f, 46.0f, 6.0f, 8.0f, 255);
  934.  
  935.    // are we in demo mode ? if so, display the program name as a watermark
  936. #ifndef NO_REGISTRATION
  937.    if (!is_registered)
  938.       Render_wprintf (50.0f, 60.0f, 0.0f, ALIGN_CENTER, ALIGN_CENTER, ALIGN_CENTER, L"Papyrus", 4.0f, false, false, D3DCOLOR_RGBA (255, 255, 255, 255), TEXTBG_NONE,
  939.                       NULL, PROGRAM_NAME L"%s - " PROGRAM_URL L"\n- %d:%02d -", LOCALIZE (L"EvaluationMode"), (int) (DEMO_TIMEOUT - current_time) / 60, (int) (DEMO_TIMEOUT - current_time) % 60);
  940. #endif // !NO_REGISTRATION
  941.  
  942.    // this must be printed last, so as to overlay all the rest
  943.    if (want_framerate)
  944.       Render_wprintf (99.0f, 1.0f, 0.0f, ALIGN_RIGHT, ALIGN_TOP, ALIGN_CENTER, L"Papyrus", 6.0f, false, false, D3DCOLOR_RGBA (255, 255, 255, 255), TEXTBG_NONE, NULL,
  945.                       L"%d textures\n"
  946.                       L"%d meshes\n"
  947.                       L"%d fonts\n"
  948.                       L"%d sprites\n"
  949.                       L"%d fps", texture_count, mesh_count, font_count, sprite_count, framerate_value);
  950.  
  951.    // end 3D rendering on the back buffer
  952.    //////////////////////////////////////
  953.  
  954.    d3ddev->EndScene (); // ends the 3D scene
  955.    d3ddev->Present (NULL, NULL, hMainWnd, NULL); // displays the created frame on the screen
  956.  
  957.    // update the frame rate
  958.    if (framerate_time < current_time)
  959.    {
  960.       framerate_value = framerate_count;
  961.       framerate_count = 0;
  962.       framerate_time = current_time + 1.0f;
  963.    }
  964.    framerate_count++; // one frame more elapsed
  965.  
  966.    return; // finished
  967. }
  968.  
  969.  
  970. int Render_LoadMesh (const wchar_t *fmt, ...)
  971. {
  972.    // this function appends a new mesh in the global meshes buffer and returns its index
  973.  
  974.    static wchar_t meshfile_pathname[MAX_PATH];
  975.    unsigned long hash;
  976.    mesh_t *mesh;
  977.    int mesh_index;
  978.    va_list argptr;
  979.  
  980.    // concatenate all the arguments in one string
  981.    va_start (argptr, fmt);
  982.    wvsprintf (meshfile_pathname, fmt, argptr);
  983.    va_end (argptr);
  984.  
  985.    // resolve wildcards and get content hash
  986.    ResolveWildcard (meshfile_pathname, L".obj");
  987.    hash = HashFile (meshfile_pathname);
  988.    
  989.    // now cycle through all our loaded meshes and see if it's already loaded
  990.    for (mesh_index = 0; mesh_index < mesh_count; mesh_index++)
  991.       if (meshes[mesh_index].hash == hash)
  992.          return (mesh_index); // if we can find it, return its index so as not to load it twice
  993.  
  994.    // reallocate space to hold one mesh more
  995.    meshes = (mesh_t *) SAFE_realloc (meshes, mesh_count, mesh_count + 1, sizeof (mesh_t), false);
  996.    mesh = &meshes[mesh_count]; // quick access to the mesh we'll be working on
  997.  
  998.    // load the mesh
  999.    if (!Render_LoadMesh_Obj (mesh, meshfile_pathname))
  1000.       return (-1); // bomb out on error
  1001.  
  1002.    mesh->hash = hash; // save the hash
  1003.    mesh_count++; // we know now one mesh more
  1004.    return (mesh_count - 1); // return its index
  1005. }
  1006.  
  1007.  
  1008. int Render_LoadTexture (const wchar_t *fmt, ...)
  1009. {
  1010.    // this function appends a new texture in the global textures buffer and returns its index
  1011.  
  1012.    D3DSURFACE_DESC texture_description;
  1013.    wchar_t texturefile_pathname[MAX_PATH];
  1014.    static wchar_t *filename;
  1015.    unsigned long hash;
  1016.    int texture_index;
  1017.    va_list argptr;
  1018.  
  1019.    // concatenate all the arguments in one string
  1020.    va_start (argptr, fmt);
  1021.    wvsprintf (texturefile_pathname, fmt, argptr);
  1022.    va_end (argptr);
  1023.  
  1024.    // resolve wildcards and get content hash
  1025.    ResolveWildcard (texturefile_pathname, L".dds|.jpg|.jpeg|.png|.tga|.bmp");
  1026.    hash = HashFile (texturefile_pathname);
  1027.  
  1028.    // now cycle through all our loaded textures and see if it's already loaded
  1029.    for (texture_index = 0; texture_index < texture_count; texture_index++)
  1030.       if (textures[texture_index].hash == hash)
  1031.          return (texture_index); // if we can find it, return its index so as not to load it twice
  1032.  
  1033.    // reallocate space to hold one texture more
  1034.    textures = (texture_t *) SAFE_realloc (textures, texture_count, texture_count + 1, sizeof (texture_t), false);
  1035.  
  1036.    // ask Direct3D to prepare texture data
  1037.    if (FAILED (D3DXCreateTextureFromFile (d3ddev, texturefile_pathname, &textures[texture_count].d3dtexture)))
  1038.    {
  1039.       MessageBox (NULL, LOCALIZE (L"Error_UnableToAddTextureD3DXCreateTextureFromFileFailed"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  1040.       terminate_everything = true; // this is a fatal error
  1041.       return (-1); // bomb out on error
  1042.    }
  1043.  
  1044.    // get info on the newly loaded texture such as size etc.
  1045.    textures[texture_count].d3dtexture->GetLevelDesc (0, &texture_description);
  1046.    textures[texture_count].width = texture_description.Width; // save texture width (as loaded)
  1047.    textures[texture_count].height = texture_description.Height; // save texture height (as loaded)
  1048.    textures[texture_count].hash = hash; // save its hash
  1049.    texture_count++; // we know now one texture more
  1050.    return (texture_count - 1); // return its index
  1051. }
  1052.  
  1053.  
  1054. static int Render_LoadFont (const wchar_t *font_name, float font_sizepct, bool is_bold, bool is_italic)
  1055. {
  1056.    // this function appends a new font in the global fonts buffer and returns its index
  1057.  
  1058.    unsigned long pathname_hash;
  1059.    int font_index;
  1060.    int font_size;
  1061.  
  1062.    // convert font size to pixels
  1063.    font_size = (int) ((font_sizepct * (float) initial_height) / 100.0f);
  1064.  
  1065.    // first, get the hash of the requested pathname (include font size and weight parameters)
  1066.    pathname_hash = HashString (font_name);
  1067.    pathname_hash += 3 * (unsigned long) font_size + 2 * (unsigned long) is_bold + (unsigned long) is_italic;
  1068.  
  1069.    // now cycle through all our loaded fonts and see if it's already loaded
  1070.    for (font_index = 0; font_index < font_count; font_index++)
  1071.       if (fonts[font_index].pathname_hash == pathname_hash)
  1072.          return (font_index); // if we can find it, return its index so as not to load it twice
  1073.  
  1074.    // reallocate space to hold one font more
  1075.    fonts = (font_t *) SAFE_realloc (fonts, font_count, font_count + 1, sizeof (font_t), false);
  1076.  
  1077.    // create a Direct3D font object and record font data
  1078.    if (FAILED (D3DXCreateFont (d3ddev, font_size, // font height
  1079.                                0, // font width (0 means "use default width")
  1080.                                (is_bold ? FW_BOLD : FW_NORMAL), // font weight (bold, etc)
  1081.                                0, // miplevels
  1082.                                is_italic, // is italic
  1083.                                DEFAULT_CHARSET, // charset
  1084.                                OUT_DEFAULT_PRECIS, // precision
  1085.                                CLEARTYPE_QUALITY, // font quality (antialiased or not)
  1086.                                DEFAULT_PITCH | FF_DONTCARE, // font family
  1087.                                font_name, // font name
  1088.                                &fonts[font_count].d3dfont))) // and a pointer that will receive the font
  1089.    {
  1090.       MessageBox (NULL, LOCALIZE (L"Error_UnableToAddFontD3DXCreateFontFailed"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  1091.       terminate_everything = true; // this is a fatal error
  1092.       return (-1); // bomb out on error
  1093.    }
  1094.  
  1095.    fonts[font_count].pathname_hash = pathname_hash; // save its hash
  1096.    font_count++; // we know now one font more
  1097.    return (font_count - 1); // return its index
  1098. }
  1099.  
  1100.  
  1101. int Render_LoadSprite (const wchar_t *fmt, ...)
  1102. {
  1103.    // this function appends a new sprite in the global sprites buffer and returns its index
  1104.  
  1105.    wchar_t spritefile_pathname[MAX_PATH];
  1106.    unsigned long hash;
  1107.    int sprite_index;
  1108.    va_list argptr;
  1109.  
  1110.    // concatenate all the arguments in one string
  1111.    va_start (argptr, fmt);
  1112.    wvsprintf (spritefile_pathname, fmt, argptr);
  1113.    va_end (argptr);
  1114.  
  1115.    // resolve wildcards and get content hash
  1116.    ResolveWildcard (spritefile_pathname, L".dds|.jpg|.jpeg|.png|.tga|.bmp");
  1117.    hash = HashFile (spritefile_pathname);
  1118.  
  1119.    // now cycle through all our loaded sprites and see if it's already loaded
  1120.    for (sprite_index = 0; sprite_index < sprite_count; sprite_index++)
  1121.       if (sprites[sprite_index].hash == hash)
  1122.          return (sprite_index); // if we can find it, return its index so as not to load it twice
  1123.  
  1124.    // reallocate space to hold one sprite more
  1125.    sprites = (sprite_t *) SAFE_realloc (sprites, sprite_count, sprite_count + 1, sizeof (sprite_t), false);
  1126.  
  1127.    // ask Direct3D to prepare texture data
  1128.    if (FAILED (D3DXCreateSprite (d3ddev, &sprites[sprite_count].d3dsprite)))
  1129.    {
  1130.       MessageBox (NULL, LOCALIZE (L"Error_UnableToAddSpriteD3DXCreateSpriteFailed"), LOCALIZE (L"FatalError"), MB_ICONERROR | MB_OK);
  1131.       terminate_everything = true; // this is a fatal error
  1132.       return (-1); // bomb out on error
  1133.    }
  1134.    sprites[sprite_count].texture_index = Render_LoadTexture (spritefile_pathname); // register and save sprite texture
  1135.    sprites[sprite_count].hash = hash; // save its hash
  1136.  
  1137.    sprite_count++; // we know now one sprite more
  1138.    return (sprite_count - 1); // return its index
  1139. }
  1140.  
  1141.  
  1142. int Render_MaterialIndexOf (const wchar_t *material_name)
  1143. {
  1144.    // this function returns the index of the material in the global materials array which has the specified name
  1145.  
  1146.    int material_index;
  1147.  
  1148.    // cycle through all materials and look whether one with the specified name exists
  1149.    for (material_index = 0; material_index < material_count; material_index++)
  1150.       if (_wcsicmp (materials[material_index].name, material_name) == 0)
  1151.          return (material_index); // if we find one, return its index
  1152.  
  1153.    return (material_count - 1); // else return the index of the last material in list, which is the default material
  1154. }
  1155.  
  1156.  
  1157. void Render_MouseToFloor (short mouse_x, short mouse_y, float *floor_x, float *floor_y)
  1158. {
  1159.    // this function converts a mouse coordinates into floor coordinates by doing vector
  1160.    // projection on the floor plane from the eyepoint of the camera
  1161.  
  1162.    static D3DXPLANE floor_plane;
  1163.    static bool is_planefound = false;
  1164.  
  1165.    D3DXMATRIX projection_matrix;
  1166.    D3DXMATRIX view_matrix;
  1167.    D3DXMATRIX invertedview_matrix;
  1168.    float mouse_pitch;
  1169.    float mouse_yaw;
  1170.    vector_t v_lookat;
  1171.    vector_t v_intersection;
  1172.  
  1173.    // find the floor plane (only do it once)
  1174.    if (!is_planefound)
  1175.    {
  1176.       D3DXPlaneFromPointNormal (&floor_plane, (D3DXVECTOR3 *) &scene_lookatpoint, (D3DXVECTOR3 *) &upwards_direction);
  1177.       is_planefound = true; // once and for all, as this plane will never change
  1178.    }
  1179.  
  1180.    // get the current projection and view matrices, and invert the view matrix
  1181.    d3ddev->GetTransform (D3DTS_PROJECTION, &projection_matrix);
  1182.    d3ddev->GetTransform (D3DTS_VIEW, &view_matrix);
  1183.    D3DXMatrixInverse (&invertedview_matrix, NULL, &view_matrix);
  1184.  
  1185.    // convert the mouse coordinates to relative pitch and yaw
  1186.    mouse_pitch =  (((mouse_x * 2.0f) / current_width) - 1) / projection_matrix._11;
  1187.    mouse_yaw = -(((mouse_y * 2.0f) / current_height) - 1) / projection_matrix._22;
  1188.  
  1189.    // now build a matrix that will describe the mouse direction vector, add it to the camera position, and make it 200 times longer
  1190.    v_lookat.x = camera_position.x + (mouse_pitch * invertedview_matrix._11 + mouse_yaw * invertedview_matrix._21 + invertedview_matrix._31) * 200.0f;
  1191.    v_lookat.y = camera_position.y + (mouse_pitch * invertedview_matrix._12 + mouse_yaw * invertedview_matrix._22 + invertedview_matrix._32) * 200.0f;
  1192.    v_lookat.z = camera_position.z + (mouse_pitch * invertedview_matrix._13 + mouse_yaw * invertedview_matrix._23 + invertedview_matrix._33) * 200.0f;
  1193.  
  1194.    // and the intersection point with our ray
  1195.    D3DXPlaneIntersectLine ((D3DXVECTOR3 *) &v_intersection, &floor_plane, (D3DXVECTOR3 *) &camera_position, (D3DXVECTOR3 *) &v_lookat);
  1196.  
  1197.    // now fill the return values
  1198.    *floor_x = v_intersection.x;
  1199.    *floor_y = v_intersection.y;
  1200.  
  1201.    return; // finished
  1202. }
  1203.  
  1204.  
  1205. bool Render_IsMouseInBox (short mouse_x, short mouse_y, float x_percent, float y_percent, float width_percent, float height_percent)
  1206. {
  1207.    // helper function that returns whether the mouse coordinates are inside a given square
  1208.  
  1209.    float mousex_percent;
  1210.    float mousey_percent;
  1211.  
  1212.    // compute mouse coordinates in percents
  1213.    mousex_percent = (float) mouse_x * 100.0f / current_width;
  1214.    mousey_percent = (float) mouse_y * 100.0f / current_height;
  1215.  
  1216.    return ((mousex_percent >= x_percent) && (mousex_percent <= x_percent + width_percent)
  1217.            && (mousey_percent >= y_percent) && (mousey_percent <= y_percent + height_percent));
  1218. }
  1219.  
  1220.  
  1221. static bool Render_LoadMesh_Obj (mesh_t *mesh, const wchar_t *objfile_pathname)
  1222. {
  1223.    // this function loads a mesh from a Wavefront Object file (.obj)
  1224.  
  1225.    #define OBJ_INCREASE_OR_RESIZE(count,maxcount,increment,arrayptr,type,erase) { \
  1226.       (count)++; \
  1227.       if ((count) == (maxcount)) \
  1228.       { \
  1229.          (arrayptr) = (type *) SAFE_realloc ((arrayptr), (maxcount), (maxcount) + (increment), sizeof (type), erase); \
  1230.          (maxcount) += (increment); \
  1231.       } \
  1232.    }
  1233.    #define OBJ_CONVERT_INDEX(element,current_element_count) if ((element) < 0) (element) = (current_element_count) + (element); else (element)--;
  1234.    #define OBJ_GET_EXISTING_INDEX_OR_APPEND_VERTEX(uniquevertex) \
  1235.    { \
  1236.       hash = &hashtable[*((unsigned long *) &obj.vs[(uniquevertex).iv].x) & 0xFF]; \
  1237.       for (vertex_index = 0; vertex_index < hash->count; vertex_index++) \
  1238.          if ((memcmp (&vertices[hash->indices[vertex_index]].position, &obj.vs[(uniquevertex).iv], sizeof (vector_t)) == 0) \
  1239.              && (memcmp (&vertices[hash->indices[vertex_index]].normal, &obj.ns[(uniquevertex).in], sizeof (vector_t)) == 0) \
  1240.              && (memcmp (&vertices[hash->indices[vertex_index]].texcoord, &obj.tcs[(uniquevertex).itc], sizeof (texcoord_t)) == 0)) \
  1241.             break; \
  1242.       if (vertex_index == hash->count) \
  1243.       { \
  1244.          vertex_index = mesh->vertice_count; \
  1245.          current_vertex = &vertices[vertex_index]; \
  1246.          memset (current_vertex, 0, sizeof (vertex_t)); \
  1247.          memcpy (&current_vertex->position, &obj.vs[(uniquevertex).iv], sizeof (vector_t)); \
  1248.          if ((uniquevertex).in > -1) \
  1249.             memcpy (&current_vertex->normal, &obj.ns[(uniquevertex).in], sizeof (vector_t)); \
  1250.          if ((uniquevertex).itc > -1) \
  1251.             memcpy (&current_vertex->texcoord, &obj.tcs[(uniquevertex).itc], sizeof (texcoord_t)); \
  1252.          hash = &hashtable[*((unsigned long *) &current_vertex->normal.x) & 0xFF]; \
  1253.          hash->indices = (long *) SAFE_realloc (hash->indices, hash->count, hash->count + 1, sizeof (long), false); \
  1254.          hash->indices[hash->count] = vertex_index; \
  1255.          hash->count++; \
  1256.          mesh->vertice_count++; \
  1257.       } \
  1258.       else \
  1259.          vertex_index = hash->indices[vertex_index]; \
  1260.    }
  1261.  
  1262.    typedef struct obj_uniquevertex_s { long iv, in, itc; } obj_uniquevertex_t;
  1263.    typedef struct obj_face_s { obj_uniquevertex_t v1, v2, v3; } obj_face_t;
  1264.    typedef struct obj_hashbucket_s { int count; long *indices; /* mallocated */ } obj_hashbucket_t;
  1265.    typedef struct objfile_s
  1266.    {
  1267.       vector_t *vs; long v_count; long v_maxcount; // array mallocated to v_maxcount
  1268.       vector_t *ns; long n_count; long n_maxcount; // array mallocated to n_maxcount
  1269.       texcoord_t *tcs; long tc_count; long tc_maxcount; // array mallocated to tc_maxcount
  1270.       obj_face_t *fs; long f_count; long f_maxcount; // array mallocated to f_maxcount
  1271.    } objfile_t;
  1272.  
  1273.    static obj_hashbucket_t hashtable[256];
  1274.    objfile_t obj;
  1275.    obj_hashbucket_t *hash;
  1276.    obj_face_t *f;
  1277.    vertex_t *vertices; // mallocated
  1278.    unsigned long *indices; // mallocated
  1279.    vertex_t *current_vertex;
  1280.    unsigned long file_size;
  1281.    char *filedata; // mallocated
  1282.    char *fileptr;
  1283.    int vertex_index;
  1284.    int array_index;
  1285.    void *ptr_to;
  1286.    FILE *fp;
  1287.  
  1288.    // open the mesh file and read it as a whole
  1289.    _wfopen_s (&fp, objfile_pathname, L"rb");
  1290.    if (fp == NULL)
  1291.       return (false); // bomb out on error
  1292.    fseek (fp, 0, SEEK_END); // seek at end of file...
  1293.    file_size = ftell (fp); // ...read file length...
  1294.    fseek (fp, 0, SEEK_SET); // and rewind
  1295.    filedata = (char *) SAFE_malloc (file_size, sizeof (char), false); // mallocate space for data
  1296.    fread (filedata, file_size, 1, fp); // read file as a whole
  1297.    fclose (fp); // file is read, close it
  1298.  
  1299.    // allocate space for an arbitrary amount of vertices, texture coordinates, normals and faces
  1300.    memset (&obj, 0, sizeof (obj));
  1301.    obj.v_maxcount = 10000; obj.vs = (vector_t *) SAFE_malloc (obj.v_maxcount, sizeof (vector_t), false);
  1302.    obj.n_maxcount = 10000; obj.ns = (vector_t *) SAFE_malloc (obj.n_maxcount, sizeof (vector_t), false);
  1303.    obj.tc_maxcount = 10000; obj.tcs = (texcoord_t *) SAFE_malloc (obj.tc_maxcount, sizeof (texcoord_t), false);
  1304.    obj.f_maxcount = 5000; obj.fs = (obj_face_t *) SAFE_malloc (obj.f_maxcount, sizeof (obj_face_t), true); // zero out the faces array (IMPORTANT !)
  1305.  
  1306.    // read file line per line...
  1307.    fileptr = filedata - 1; // start parsing line after line
  1308.    while (fileptr != NULL)
  1309.    {
  1310.       fileptr++; // skip the line feed (or reach the first character, if it's the first pass)
  1311.  
  1312.       // is it a vertex-related line ?
  1313.       if (fileptr[0] == L'v')
  1314.       {
  1315.          // is it a vertex, a normal or a texture coordinate ?
  1316.          if ((fileptr[1] == L' ') && (sscanf_s (&fileptr[2], "%f %f %f", &obj.vs[obj.v_count].x, &obj.vs[obj.v_count].y, &obj.vs[obj.v_count].z) == 3))
  1317.             OBJ_INCREASE_OR_RESIZE (obj.v_count, obj.v_maxcount, 10000, obj.vs, vector_t, false) // one vertex more has been read
  1318.          else if ((fileptr[1] == L'n') && (sscanf_s (&fileptr[3], "%f %f %f", &obj.ns[obj.n_count].x, &obj.ns[obj.n_count].y, &obj.ns[obj.n_count].z) == 3))
  1319.             OBJ_INCREASE_OR_RESIZE (obj.n_count, obj.n_maxcount, 10000, obj.ns, vector_t, false) // one normal more has been read
  1320.          else if ((fileptr[1] == L't') && (sscanf_s (&fileptr[3], "%f %f", &obj.tcs[obj.tc_count].u, &obj.tcs[obj.tc_count].v) == 2))
  1321.             OBJ_INCREASE_OR_RESIZE (obj.tc_count, obj.tc_maxcount, 10000, obj.tcs, texcoord_t, false) // one texture coordinate more has been read
  1322.       }
  1323.  
  1324.       // else is it a face-related line ?
  1325.       else if (fileptr[0] == L'f')
  1326.       {
  1327.          // get a quick pointer to current face (note: it's been already blanked out by malloc())
  1328.          f = &obj.fs[obj.f_count];
  1329.  
  1330.          // is it a face with normals, a face without normals or a face without normals and texture coordinates ?
  1331.          if ((sscanf_s (&fileptr[2], "%d/%d/%d %d/%d/%d %d/%d/%d", &f->v1.iv, &f->v1.itc, &f->v1.in, &f->v2.iv, &f->v2.itc, &f->v2.in, &f->v3.iv, &f->v3.itc, &f->v3.in) == 9)
  1332.              || (sscanf_s (&fileptr[2], "%d/%d %d/%d %d/%d", &f->v3.iv, &f->v3.itc, &f->v2.iv, &f->v2.itc, &f->v3.iv, &f->v3.itc) == 6)
  1333.              || (sscanf_s (&fileptr[2], "%d %d %d", &f->v3.iv, &f->v2.iv, &f->v3.iv) == 6))
  1334.          {
  1335.             OBJ_CONVERT_INDEX (f->v1.iv, obj.v_count);
  1336.             OBJ_CONVERT_INDEX (f->v1.in, obj.n_count); // if no normal could be read, its index will be converted from 0 to -1
  1337.             OBJ_CONVERT_INDEX (f->v1.itc, obj.tc_count); // if no texcoord could be read, its index will be converted from 0 to -1
  1338.             OBJ_CONVERT_INDEX (f->v2.iv, obj.v_count);
  1339.             OBJ_CONVERT_INDEX (f->v2.in, obj.n_count); // if no normal could be read, its index will be converted from 0 to -1
  1340.             OBJ_CONVERT_INDEX (f->v2.itc, obj.tc_count); // if no texcoord could be read, its index will be converted from 0 to -1
  1341.             OBJ_CONVERT_INDEX (f->v3.iv, obj.v_count);
  1342.             OBJ_CONVERT_INDEX (f->v3.in, obj.n_count); // if no normal could be read, its index will be converted from 0 to -1
  1343.             OBJ_CONVERT_INDEX (f->v3.itc, obj.tc_count); // if no texcoord could be read, its index will be converted from 0 to -1
  1344.             OBJ_INCREASE_OR_RESIZE (obj.f_count, obj.f_maxcount, 5000, obj.fs, obj_face_t, true) // one face more has been read
  1345.          }
  1346.       }
  1347.  
  1348.       fileptr = strchr (fileptr, '\n'); // proceed to next line
  1349.    }
  1350.  
  1351.    // now build our final vertex and index list
  1352.    vertices = (vertex_t *) SAFE_malloc (3 * obj.f_count, sizeof (vertex_t), false); // mallocate for the max number of vertices we can have
  1353.    indices = (unsigned long *) SAFE_malloc (3 * obj.f_count, sizeof (unsigned long), false); // mallocate for the right amount of indices
  1354.  
  1355.    // t3h mighty l00p ^^ (builds vertex and index buffers)
  1356.    memset (hashtable, 0, sizeof (hashtable)); // wipe out the hashtable
  1357.    mesh->vertice_count = 0; // start with an unoptimized list
  1358.    for (array_index = 0; array_index < obj.f_count; array_index++)
  1359.    {
  1360.       f = &obj.fs[array_index]; // quick access to current face
  1361.       OBJ_GET_EXISTING_INDEX_OR_APPEND_VERTEX (f->v1);
  1362.       indices[3 * array_index + 0] = vertex_index;
  1363.       OBJ_GET_EXISTING_INDEX_OR_APPEND_VERTEX (f->v2);
  1364.       indices[3 * array_index + 1] = vertex_index;
  1365.       OBJ_GET_EXISTING_INDEX_OR_APPEND_VERTEX (f->v3);
  1366.       indices[3 * array_index + 2] = vertex_index;
  1367.    }
  1368.  
  1369.    // now create a correctly-sized DirectX vertex buffer and populate it
  1370.    mesh->vertex_format = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
  1371.    mesh->vertice_size = sizeof (vertex_t);
  1372.    d3ddev->CreateVertexBuffer (mesh->vertice_count * mesh->vertice_size, // length
  1373.                                D3DUSAGE_DYNAMIC, // usage
  1374.                                mesh->vertex_format, // vertex format
  1375.                                D3DPOOL_DEFAULT, // pool type
  1376.                                &mesh->d3dvertices, // pointer to the vertex buffer pointer
  1377.                                NULL); // shared handle
  1378.    mesh->d3dvertices->Lock (0, mesh->vertice_count * mesh->vertice_size, &ptr_to, D3DLOCK_DISCARD);
  1379.    memcpy (ptr_to, vertices, mesh->vertice_count * mesh->vertice_size);
  1380.    mesh->d3dvertices->Unlock ();
  1381.  
  1382.    // create a correctly-sized DirectX index buffer and populate it
  1383.    mesh->is_indexed = true; // remember that we're building an index buffer
  1384.    mesh->indice_count = obj.f_count * 3;
  1385.    mesh->indice_size = (mesh->indice_count <= (int) USHRT_MAX ? 2 : 4);
  1386.    d3ddev->CreateIndexBuffer (mesh->indice_count * mesh->indice_size, // length
  1387.                               D3DUSAGE_DYNAMIC, // usage
  1388.                               (mesh->indice_size == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32), // format (here, 16 or 32-bit)
  1389.                               D3DPOOL_DEFAULT, // pool type
  1390.                               &mesh->d3dindices, // pointer to the index buffer pointer
  1391.                               NULL); // shared handle
  1392.    mesh->d3dindices->Lock (0, mesh->indice_count * mesh->indice_size, &ptr_to, D3DLOCK_DISCARD);
  1393.    if (mesh->indice_size == 2)
  1394.       for (array_index = 0; array_index < mesh->indice_count; array_index++)
  1395.          ((unsigned short *) ptr_to)[array_index] = (unsigned short) indices[array_index];
  1396.    else
  1397.       memcpy (ptr_to, indices, mesh->indice_count * mesh->indice_size);
  1398.    mesh->d3dindices->Unlock ();
  1399.  
  1400.    // finished, free the temporary objects
  1401.    for (array_index = 0; array_index < sizeof (hashtable) / sizeof (obj_hashbucket_t); array_index++)
  1402.       SAFE_free ((void **) &hashtable[array_index].indices);
  1403.    SAFE_free ((void **) &indices);
  1404.    SAFE_free ((void **) &vertices);
  1405.    SAFE_free ((void **) &obj.vs);
  1406.    SAFE_free ((void **) &obj.ns);
  1407.    SAFE_free ((void **) &obj.tcs);
  1408.    SAFE_free ((void **) &obj.fs);
  1409.    SAFE_free ((void **) &filedata);
  1410.  
  1411.    return (true); // Wavefront Object successfully loaded, return TRUE
  1412.  
  1413.    #undef OBJ_GET_EXISTING_INDEX_OR_APPEND_VERTEX
  1414.    #undef OBJ_CONVERT_INDEX
  1415.    #undef OBJ_INCREASE_OR_RESIZE
  1416. }
  1417.  
  1418.  
  1419. static void Render_DrawSceneObjectReflection (sceneobject_t *sceneobject)
  1420. {
  1421.    // fast helper to draw a mesh at a specified location with certain pitch and yaw angles
  1422.  
  1423.    D3DXMATRIX rotation_matrix;
  1424.    D3DXMATRIX translation_matrix;
  1425.    D3DXMATRIX reflect_matrix;
  1426.    D3DXMATRIX scaling_matrix;
  1427.    material_t *material;
  1428.    D3DMATERIAL9 d3dmaterial;
  1429.    D3DXPLANE plane;
  1430.    mesh_t *mesh;
  1431.    mesh_t *tile_mesh;
  1432.    float alpha;
  1433.  
  1434.    // draw the reflection below this mesh
  1435.  
  1436.    // quick access to meshes
  1437.    mesh = &meshes[sceneobject->mesh_index];
  1438.    tile_mesh = &meshes[theme->tile_meshindex];
  1439.  
  1440.    // set the world transform at location
  1441.    D3DXPlaneFromPointNormal (&plane, (D3DXVECTOR3 *) &scene_lookatpoint, (D3DXVECTOR3 *) &upwards_direction);
  1442.    D3DXMatrixReflect (&reflect_matrix, &plane);
  1443.    D3DXMatrixRotationYawPitchRoll (&rotation_matrix, -sceneobject->pitch * TO_RADIANS, 0.0f, -sceneobject->yaw * TO_RADIANS);
  1444.    D3DXMatrixTranslation (&translation_matrix, sceneobject->x, sceneobject->y, -sceneobject->z);
  1445.    D3DXMatrixScaling (&scaling_matrix, sceneobject->scale, sceneobject->scale, 1.0f);
  1446.  
  1447.    // tell Direct3D about our matrix
  1448.    d3ddev->SetTransform (D3DTS_WORLD, &(reflect_matrix * scaling_matrix * rotation_matrix * translation_matrix));
  1449.  
  1450.    d3ddev->SetRenderState (D3DRS_STENCILENABLE, true); // enable the stencil buffer
  1451.    d3ddev->SetRenderState (D3DRS_STENCILFUNC, D3DCMP_LESS); // instruct how to fill the stencil buffer
  1452.    d3ddev->SetRenderState (D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); // instruct how to fill the stencil buffer
  1453.  
  1454.    // set the texture for this mesh
  1455.    if (sceneobject->texture_index != -1)
  1456.       d3ddev->SetTexture (0, textures[sceneobject->texture_index].d3dtexture);
  1457.    else
  1458.       d3ddev->SetTexture (0, NULL);
  1459.  
  1460.    // adjust the light reflection properties by setting the material
  1461.    if (sceneobject->material_index != -1)
  1462.       material = &materials[sceneobject->material_index]; // use the specified scene object material
  1463.    else
  1464.       material = &materials[material_count - 1]; // scene object material unspecified, use default material
  1465.    alpha = theme->reflection_alpha / 256.0f;
  1466.    d3dmaterial.Ambient = D3DXCOLOR (material->ambient, material->ambient, material->ambient, material->transparency * alpha); // Alpha value not used according to SDK
  1467.    d3dmaterial.Diffuse = D3DXCOLOR (material->diffuse, material->diffuse, material->diffuse, material->transparency * alpha);
  1468.    d3dmaterial.Emissive = D3DXCOLOR (material->emissive, material->emissive, material->emissive, material->transparency * alpha); // Alpha value not used according to SDK
  1469.    d3dmaterial.Specular = D3DXCOLOR (material->specular, material->specular, material->specular, material->transparency * alpha); // Alpha value not used according to SDK
  1470.    d3dmaterial.Power = material->shininess;
  1471.    d3ddev->SetMaterial (&d3dmaterial);
  1472.  
  1473.    // draw the mesh subset
  1474.    d3ddev->SetStreamSource (0, mesh->d3dvertices, 0, sizeof (vertex_t));
  1475.    d3ddev->SetFVF (mesh->vertex_format);
  1476.    d3ddev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW); // draw the faces backwards
  1477.    if (mesh->is_indexed)
  1478.    {
  1479.       d3ddev->SetIndices (mesh->d3dindices);
  1480.       d3ddev->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, mesh->vertice_count, 0, mesh->indice_count / 3);
  1481.    }
  1482.    else
  1483.       d3ddev->DrawPrimitive (D3DPT_TRIANGLELIST, 0, mesh->vertice_count / 3);
  1484.  
  1485.    d3ddev->SetRenderState (D3DRS_STENCILENABLE, false); // and disable the stencil buffer
  1486.  
  1487.    // now draw the simple shadow below this mesh
  1488.  
  1489.    // grab the tools we need in hand
  1490.    d3ddev->SetRenderState (D3DRS_ZENABLE, false); // disable the Z buffer
  1491.    d3ddev->SetRenderState (D3DRS_AMBIENT, D3DCOLOR_XRGB (255, 255, 255)); // raise ambient light
  1492.  
  1493.    // position the simple shadow sprite
  1494.    D3DXMatrixScaling (&scaling_matrix, max (sceneobject->simpleshadow_size, sceneobject->z / 5.0f), max (sceneobject->simpleshadow_size, sceneobject->z / 5.0f), 0.0f);
  1495.    D3DXMatrixTranslation (&translation_matrix, sceneobject->x, sceneobject->y, 0.0f);
  1496.    d3ddev->SetTransform (D3DTS_WORLD, &(scaling_matrix * translation_matrix));
  1497.  
  1498.    // adjust the light reflection properties by setting the material
  1499.    material = &materials[material_count - 1]; // use the default material
  1500.    d3dmaterial.Ambient = D3DXCOLOR (material->ambient, material->ambient, material->ambient, material->transparency);
  1501.    d3dmaterial.Diffuse = D3DXCOLOR (material->diffuse, material->diffuse, material->diffuse, material->transparency);
  1502.    d3dmaterial.Emissive = D3DXCOLOR (material->emissive, material->emissive, material->emissive, material->transparency);
  1503.    d3dmaterial.Specular = D3DXCOLOR (material->specular, material->specular, material->specular, material->transparency);
  1504.    d3dmaterial.Power = material->shininess;
  1505.    d3ddev->SetMaterial (&d3dmaterial);
  1506.  
  1507.    d3ddev->SetTexture (0, textures[theme->shadow_textureindex].d3dtexture); // select the texture we want
  1508.  
  1509.    // and then draw it
  1510.    d3ddev->SetStreamSource (0, tile_mesh->d3dvertices, 0, sizeof (vertex_t)); // set stream source
  1511.    d3ddev->SetFVF (tile_mesh->vertex_format); // select which vertex format we are using
  1512.    d3ddev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW); // draw the faces backwards
  1513.    if (tile_mesh->is_indexed)
  1514.    {
  1515.       d3ddev->SetIndices (tile_mesh->d3dindices);
  1516.       d3ddev->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, tile_mesh->vertice_count, 0, tile_mesh->indice_count / 3);
  1517.    }
  1518.    else
  1519.       d3ddev->DrawPrimitive (D3DPT_TRIANGLELIST, 0, tile_mesh->vertice_count / 3);
  1520.  
  1521.    // finished, reset ambient light to its previous value and enable the Z buffer back
  1522.    d3ddev->SetRenderState (D3DRS_AMBIENT, ambient_light);
  1523.    d3ddev->SetRenderState (D3DRS_ZENABLE, true);
  1524.  
  1525.    return; // finished
  1526. }
  1527.  
  1528.  
  1529. static void Render_DrawSceneObject (sceneobject_t *sceneobject)
  1530. {
  1531.    // fast helper to draw a mesh at a specified location with certain pitch and yaw angles
  1532.  
  1533.    D3DXMATRIX rotation_matrix;
  1534.    D3DXMATRIX translation_matrix;
  1535.    D3DXMATRIX scaling_matrix;
  1536.    material_t *material;
  1537.    D3DMATERIAL9 d3dmaterial;
  1538.    mesh_t *mesh;
  1539.  
  1540.    // is this object a tile (i.e, it has no mesh) ?
  1541.    if (sceneobject->mesh_index == -1)
  1542.    {
  1543.       Render_DrawSceneTile (sceneobject); // then draw it as a tile instead
  1544.       return; // and return
  1545.    }
  1546.  
  1547.    // quick access to mesh
  1548.    mesh = &meshes[sceneobject->mesh_index];
  1549.  
  1550.    // set the world transform at location
  1551.    D3DXMatrixRotationYawPitchRoll (&rotation_matrix, sceneobject->pitch * TO_RADIANS, 0.0f, sceneobject->yaw * TO_RADIANS);
  1552.    D3DXMatrixTranslation (&translation_matrix, sceneobject->x, sceneobject->y, sceneobject->z);
  1553.    D3DXMatrixScaling (&scaling_matrix, sceneobject->scale, sceneobject->scale, 1.0f);
  1554.  
  1555.    // tell Direct3D about our matrix
  1556.    d3ddev->SetTransform (D3DTS_WORLD, &(scaling_matrix * rotation_matrix * translation_matrix));
  1557.  
  1558.    // set the texture for this mesh
  1559.    if (sceneobject->texture_index != -1)
  1560.       d3ddev->SetTexture (0, textures[sceneobject->texture_index].d3dtexture);
  1561.    else
  1562.       d3ddev->SetTexture (0, NULL);
  1563.  
  1564.    // adjust the light reflection properties by setting the material
  1565.    if (sceneobject->material_index != -1)
  1566.       material = &materials[sceneobject->material_index];
  1567.    else
  1568.       material = &materials[material_count - 1];
  1569.    d3dmaterial.Ambient = D3DXCOLOR (material->ambient, material->ambient, material->ambient, material->transparency);
  1570.    d3dmaterial.Diffuse = D3DXCOLOR (material->diffuse, material->diffuse, material->diffuse, material->transparency);
  1571.    d3dmaterial.Emissive = D3DXCOLOR (material->emissive, material->emissive, material->emissive, material->transparency);
  1572.    d3dmaterial.Specular = D3DXCOLOR (material->specular, material->specular, material->specular, material->transparency);
  1573.    d3dmaterial.Power = material->shininess;
  1574.    d3ddev->SetMaterial (&d3dmaterial);
  1575.  
  1576.    // draw the mesh subset
  1577.    d3ddev->SetStreamSource (0, mesh->d3dvertices, 0, sizeof (vertex_t));
  1578.    d3ddev->SetFVF (mesh->vertex_format);
  1579.  
  1580.    // is there transparency on this mesh ?
  1581.    if (material->transparency < 1)
  1582.    {
  1583.       d3ddev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW); // draw the back faces
  1584.       if (mesh->is_indexed)
  1585.       {
  1586.          d3ddev->SetIndices (mesh->d3dindices);
  1587.          d3ddev->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, mesh->vertice_count, 0, mesh->indice_count / 3);
  1588.       }
  1589.       else
  1590.          d3ddev->DrawPrimitive (D3DPT_TRIANGLELIST, 0, mesh->vertice_count / 3);
  1591.    }
  1592.  
  1593.    // now draw the front faces
  1594.    d3ddev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CCW); // draw the front faces
  1595.    if (mesh->is_indexed)
  1596.    {
  1597.       d3ddev->SetIndices (mesh->d3dindices);
  1598.       d3ddev->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, mesh->vertice_count, 0, mesh->indice_count / 3);
  1599.    }
  1600.    else
  1601.       d3ddev->DrawPrimitive (D3DPT_TRIANGLELIST, 0, mesh->vertice_count / 3);
  1602.  
  1603.    return; // finished
  1604. }
  1605.  
  1606.  
  1607. static void Render_DrawSceneTile (sceneobject_t *sceneobject)
  1608. {
  1609.    // fast helper to draw a tile (i.e, an object that doesn't have a mesh) at a specified location
  1610.  
  1611.    D3DXMATRIX rotation_matrix;
  1612.    D3DXMATRIX translation_matrix;
  1613.    D3DXMATRIX scaling_matrix;
  1614.    material_t *material;
  1615.    D3DMATERIAL9 d3dmaterial;
  1616.    mesh_t *tile_mesh;
  1617.  
  1618.    tile_mesh = &meshes[theme->tile_meshindex]; // quick access to tile mesh
  1619.  
  1620.    // grab the tools we need in hand
  1621.    d3ddev->SetRenderState (D3DRS_AMBIENT, D3DCOLOR_RGBA (0xFF, 0xFF, 0xFF, 0xFF)); // raise light
  1622.  
  1623.    // set the world transform at location
  1624.    D3DXMatrixRotationYawPitchRoll (&rotation_matrix, sceneobject->pitch * TO_RADIANS, 0.0f, sceneobject->yaw * TO_RADIANS);
  1625.    D3DXMatrixTranslation (&translation_matrix, sceneobject->x, sceneobject->y, sceneobject->z);
  1626.    D3DXMatrixScaling (&scaling_matrix, sceneobject->scale, sceneobject->scale, 1.0f);
  1627.  
  1628.    // tell Direct3D about our matrix
  1629.    d3ddev->SetTransform (D3DTS_WORLD, &(scaling_matrix * rotation_matrix * translation_matrix));
  1630.  
  1631.    // adjust the light reflection properties by setting the material
  1632.    material = &materials[material_count - 1]; // use the default material for tiles
  1633.    d3dmaterial.Ambient = D3DXCOLOR (material->ambient, material->ambient, material->ambient, material->ambient);
  1634.    d3dmaterial.Diffuse = D3DXCOLOR (material->diffuse, material->diffuse, material->diffuse, material->diffuse);
  1635.    d3dmaterial.Emissive = D3DXCOLOR (material->emissive, material->emissive, material->emissive, material->emissive);
  1636.    d3dmaterial.Specular = D3DXCOLOR (material->specular, material->specular, material->specular, material->specular);
  1637.    d3dmaterial.Power = material->shininess;
  1638.    d3ddev->SetMaterial (&d3dmaterial);
  1639.  
  1640.    // select the texture we want
  1641.    d3ddev->SetTexture (0, textures[sceneobject->texture_index].d3dtexture);
  1642.  
  1643.    // and then draw it
  1644.    d3ddev->SetStreamSource (0, tile_mesh->d3dvertices, 0, sizeof (vertex_t)); // set stream source
  1645.    d3ddev->SetFVF (tile_mesh->vertex_format); // select which vertex format we are using
  1646.    d3ddev->SetRenderState (D3DRS_CULLMODE, D3DCULL_CW); // draw the back faces
  1647.    if (tile_mesh->is_indexed)
  1648.    {
  1649.       d3ddev->SetIndices (tile_mesh->d3dindices);
  1650.       d3ddev->DrawIndexedPrimitive (D3DPT_TRIANGLELIST, 0, 0, tile_mesh->vertice_count, 0, tile_mesh->indice_count / 3);
  1651.    }
  1652.    else
  1653.       d3ddev->DrawPrimitive (D3DPT_TRIANGLELIST, 0, tile_mesh->vertice_count / 3);
  1654.  
  1655.    // finished, reset ambient light to its previous value
  1656.    d3ddev->SetRenderState (D3DRS_AMBIENT, ambient_light);
  1657.  
  1658.    return; // finished
  1659. }
  1660.  
  1661.  
  1662. static void Render_DrawSprite (sprite_t *sprite, float x_percent, float y_percent, float width_percent, float height_percent, int alpha)
  1663. {
  1664.    // fast helper to draw a sprite at a specified location with certain parameters
  1665.  
  1666.    D3DXMATRIX scaling_matrix;
  1667.    texture_t *texture;
  1668.    float scale_x;
  1669.    float scale_y;
  1670.  
  1671.    texture = &textures[sprite->texture_index]; // quick access to sprite's texture
  1672.  
  1673.    scale_x = (width_percent * (float) initial_width) / (float) (100 * texture->width);
  1674.    scale_y = (height_percent * (float) initial_height) / (float) (100 * texture->height);
  1675.  
  1676.    // start rendering the sprite (an optimized version would draw all sprites in a row...)
  1677.    sprite->d3dsprite->Begin (D3DXSPRITE_ALPHABLEND);
  1678.  
  1679.    // scale and position the sprite
  1680.    D3DXMatrixTransformation2D (&scaling_matrix, // output matrix
  1681.                                NULL, // scaling center
  1682.                                0.0f, // scaling rotation
  1683.                                &D3DXVECTOR2 (scale_x, scale_y), // scaling ratio
  1684.                                &D3DXVECTOR2 (0, 0), // rotation center
  1685.                                0.0f, // rotation
  1686.                                &D3DXVECTOR2 (x_percent * (float) initial_width / 100.0f, y_percent * (float) initial_height / 100.0f)); // translation
  1687.    sprite->d3dsprite->SetTransform (&scaling_matrix); // tell the sprite about the scaling and position transform
  1688.  
  1689.    // now draw the sprite with the specified alpha and finish rendering
  1690.    sprite->d3dsprite->Draw (texture->d3dtexture, NULL, NULL, NULL, D3DCOLOR_ARGB (alpha, 255, 255, 255));
  1691.    sprite->d3dsprite->End ();
  1692.  
  1693.    return; // finished
  1694. }
  1695.  
  1696.  
  1697. static int Render_GetTextBoundaries (float maxwidth_percent, wchar_t *font_face, float font_sizepct, bool is_bold, bool is_italic, wchar_t *text, RECT *rect)
  1698. {
  1699.    // this function computes and returns the size of the rectangle the specified text will fit into. Note that text may be modified
  1700.    // to insert new lines if it doesn't fit in a single line.
  1701.  
  1702.    int char_index;
  1703.    int length;
  1704.    int optimal_length;
  1705.    bool have_split;
  1706.    int max_width;
  1707.    int font_id;
  1708.  
  1709.    // figure out the font to use and load it if necessary
  1710.    font_id = Render_LoadFont (font_face, font_sizepct, is_bold, is_italic);
  1711.  
  1712.    // blank out the output rectangle
  1713.    memset (rect, 0, sizeof (RECT));
  1714.  
  1715.    // ask direct3D to compute the text size a first time
  1716.    fonts[font_id].d3dfont->DrawText (NULL, text, -1, rect, DT_CALCRECT | DT_WORDBREAK, D3DCOLOR (0));
  1717.  
  1718.    // if max width is not set, set it to viewport width
  1719.    if (maxwidth_percent <= 0)
  1720.       max_width = initial_width; // then use it to compute the real max width
  1721.    else
  1722.       max_width = (int) ((maxwidth_percent * (float) initial_width) / 100.0f); // else if specified, convert this percentage to an absolute pixel width
  1723.  
  1724.    // do we need more than one line ?
  1725.    if (rect->right > max_width)
  1726.    {
  1727.       // see how many lines we need and compute the optimal length of one line
  1728.       length = wcslen (text);
  1729.       optimal_length = length / (1 + rect->right / (int) max_width);
  1730.       have_split = false;
  1731.       for (char_index = optimal_length; char_index < length; char_index++)
  1732.          if (iswspace (text[char_index]))
  1733.          {
  1734.             text[char_index] = L'\n'; // interpolate linefeeds into string
  1735.             have_split = true; // remember string has been split
  1736.             char_index += optimal_length;
  1737.          }
  1738.  
  1739.       // and ask direct3D to compute the text size again
  1740.       fonts[font_id].d3dfont->DrawText (NULL, text, -1, rect, DT_CALCRECT, D3DCOLOR (0));
  1741.    }
  1742.  
  1743.    return (font_id); // finished, return the index of the font in the fonts array
  1744. }
  1745.  
  1746.  
  1747. static void Render_wprintf (float x_percent, float y_percent, float maxwidth_percent, int horiz_align, int vert_align, int text_align, wchar_t *font_face, float font_sizepct, bool want_bold, bool want_italic, unsigned long color_rgba, int background_flags, RECT *out_rect, const wchar_t *fmt, ...)
  1748. {
  1749.    // this function displays text on the Direct3D interface according to the given parameters. X and Y are the base coordinates of
  1750.    // the text's bounding rectangle. Max_width is the maximum allowed width of this rectangle before wrapping words on a new line.
  1751.    // Horiz_align and vert_align are the alignment parameters of the RECTANGLE relatively to X and Y. Text_align is the alignment of
  1752.    // the TEXT inside this rectangle (meaning, you can have right-aligned text in a rectangle that is centered on a point). Font_id
  1753.    // and color.alphargb define the font and color of the text. Out_rect, if filled, will point to a RECT structure describing the text's
  1754.    // bounding rectangle, after any word wrapping corrections have been made. Fmt is a format string containing the text itself,
  1755.    // printf-style.
  1756.  
  1757.    DWORD text_format;
  1758.    va_list argptr;
  1759.    RECT rect;
  1760.    int font_id;
  1761.    int left;
  1762.    int top;
  1763.    int x;
  1764.    int y;
  1765.  
  1766.    // concatenate all the arguments in one string
  1767.    va_start (argptr, fmt);
  1768.    wvsprintf (printf_buffer, fmt, argptr);
  1769.    va_end (argptr);
  1770.  
  1771.    /*
  1772.    current_height = 1024   2048
  1773.    min               48     24
  1774.    */
  1775.  
  1776.    // bound font_size according to viewport height
  1777.  
  1778.    // translate percent coordinates into absolute coordinates
  1779.    x = (int) ((x_percent * (float) initial_width) / 100.0f);
  1780.    y = (int) ((y_percent * (float) initial_height) / 100.0f);
  1781.  
  1782.    // get the text boundaries
  1783.    font_id = Render_GetTextBoundaries (maxwidth_percent, font_face, font_sizepct, want_bold, want_italic, printf_buffer, &rect);
  1784.  
  1785.    // horizontal alignment
  1786.    if (horiz_align == ALIGN_LEFT)
  1787.       left = x;
  1788.    else if (horiz_align == ALIGN_RIGHT)
  1789.       left = x - rect.right;
  1790.    else
  1791.       left = x - rect.right / 2;
  1792.  
  1793.    // vertical alignment
  1794.    if (vert_align == ALIGN_TOP)
  1795.       top = y;
  1796.    else if (vert_align == ALIGN_BOTTOM)
  1797.       top = y - rect.bottom;
  1798.    else
  1799.       top = y - rect.bottom / 2;
  1800.  
  1801.    // now reposition our rectangle correctly acording to alignment
  1802.    OffsetRect (&rect, left, top);
  1803.  
  1804.    // compute the DirectX text format parameter value
  1805.    text_format = (text_align == ALIGN_LEFT ? DT_LEFT : (text_align == ALIGN_RIGHT ? DT_RIGHT : DT_CENTER));
  1806.  
  1807.    // draw a fading rectangle behind the text
  1808.    if (background_flags != TEXTBG_NONE)
  1809.    {
  1810.       static int textbgsprite_index = -1;
  1811.  
  1812.       D3DXMATRIX scaling_matrix;
  1813.       sprite_t *textbgsprite;
  1814.       texture_t *texture;
  1815.  
  1816.       if (textbgsprite_index == -1)
  1817.          textbgsprite_index = Render_LoadSprite (L"%s/data/sprites/textbg.png", app_path);
  1818.       textbgsprite = &sprites[textbgsprite_index];
  1819.       texture = &textures[textbgsprite->texture_index]; // quick access to sprite's texture
  1820.  
  1821.       // start rendering the sprite (an optimized version would draw all sprites in a row...)
  1822.       textbgsprite->d3dsprite->Begin (D3DXSPRITE_ALPHABLEND);
  1823.  
  1824.       // scale and position the sprite
  1825.       D3DXMatrixTransformation2D (&scaling_matrix, // output matrix
  1826.                                   NULL, // scaling center
  1827.                                   0.0f, // scaling rotation
  1828.                                   &D3DXVECTOR2 ((rect.right - rect.left + (((background_flags & TEXTBG_LEFT ? 0.5f : 0.0f) + (background_flags & TEXTBG_RIGHT  ? 0.5f : 0.0f)) * initial_width / 100.0f)) / (float) texture->width,
  1829.                                                 (rect.bottom - rect.top + (((background_flags & TEXTBG_TOP  ? 0.5f : 0.0f) + (background_flags & TEXTBG_BOTTOM ? 0.5f : 0.0f)) * initial_width / 100.0f)) / (float) texture->height), // scaling ratio
  1830.                                   &D3DXVECTOR2 (0, 0), // rotation center
  1831.                                   0.0f, // rotation
  1832.                                   &D3DXVECTOR2 ((float) rect.left - ((background_flags & TEXTBG_LEFT ? 0.5f : 0.0f) * initial_width / 100.0f),
  1833.                                                 (float) rect.top  - ((background_flags & TEXTBG_TOP  ? 0.5f : 0.0f) * initial_width / 100.0f))); // translation
  1834.       textbgsprite->d3dsprite->SetTransform (&scaling_matrix); // tell the sprite about the scaling and position transform
  1835.  
  1836.       // now draw the sprite with the specified alpha and finish rendering
  1837.       textbgsprite->d3dsprite->Draw (texture->d3dtexture, NULL, NULL, NULL, D3DCOLOR_ARGB (176, 255, 255, 255));
  1838.       textbgsprite->d3dsprite->End ();
  1839.    }
  1840.  
  1841.    // else if no fading rectangle, draw a letter border in inverted colors
  1842.    else if (current_height > 1000)
  1843.    {
  1844.       unsigned long inverted_argbcolor = 0x7F000000 | ((0xFFFFFFFF - color_rgba) >> 8);
  1845.  
  1846.       rect.left--; fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1847.       rect.top--;  fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1848.       rect.left++; fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1849.       rect.left++; fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1850.       rect.top++;  fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1851.       rect.top++;  fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1852.       rect.left--; fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1853.       rect.left--; fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, inverted_argbcolor);
  1854.  
  1855.       rect.left++; rect.top--; // restore original rectangle position
  1856.    }
  1857.  
  1858.    // and draw the text
  1859.    fonts[font_id].d3dfont->DrawText (NULL, printf_buffer, -1, &rect, text_format, 0xFF000000 | (RGBACOLOR_TO_ARGBCOLOR (color_rgba) & 0x00FFFFFF));
  1860.  
  1861.    // do we want the output rectangle ?
  1862.    if (out_rect != NULL)
  1863.       memcpy (out_rect, &rect, sizeof (rect)); // if so, copy it in the given variable
  1864.  
  1865.    return; // finished
  1866. }
  1867.  
  1868.  
  1869. static float DistanceToCamera (float x, float y, float z)
  1870. {
  1871.    // this function computes the distance of the point at coordinates x,y,z to the camera
  1872.  
  1873.    vector_t displacement;
  1874.  
  1875.    // compute displacement...
  1876.    displacement.x = x - camera_position.x;
  1877.    displacement.y = y - camera_position.y;
  1878.    displacement.z = z - camera_position.z;
  1879.  
  1880.    // ...and then Pythagores in 3D
  1881.    return (sqrtf (displacement.x * displacement.x + displacement.y * displacement.y + displacement.z * displacement.z));
  1882. }
  1883.  
  1884.  
  1885. static float FadeFloat (float from, float to, float start_time, float end_time)
  1886. {
  1887.    // helper function to return a progressive variation between from and to based on time
  1888.  
  1889.    if (end_time < current_time)
  1890.       return (to);
  1891.  
  1892.    //      base + (variation) * (               fraction of completion               )
  1893.    return (from + (to - from) * (current_time - start_time) / (end_time - start_time));
  1894. }
  1895.  
  1896.  
  1897. static unsigned long HashString (const wchar_t *string_buffer)
  1898. {
  1899.    // super fast string hash function, code courtesy of
  1900.    // http://www.azillionmonkeys.com/qed/hash.html
  1901.  
  1902.    unsigned long length;
  1903.    unsigned long hash;
  1904.    unsigned long tmp;
  1905.    int remaining;
  1906.  
  1907.    // first, get the string length and start with this as a hash value
  1908.    length = wcslen (string_buffer) * sizeof (wchar_t);
  1909.    hash = length;
  1910.  
  1911.    // figure out how many bytes there will remain after 32-bit processing
  1912.    remaining = length & 3;
  1913.    length >>= 2;
  1914.  
  1915.    // main loop, process 32-bit blocks
  1916.    for ( ; length > 0; length--)
  1917.    {
  1918.       hash += *((const unsigned short *) string_buffer);
  1919.       tmp = ((*((const unsigned short *) (string_buffer + 2))) << 11) ^ hash;
  1920.       hash = (hash << 16) ^ tmp;
  1921.       string_buffer += 2 * sizeof (unsigned short);
  1922.       hash += hash >> 11;
  1923.    }
  1924.  
  1925.    // handle the remaining bytes
  1926.    if (remaining == 3)
  1927.    {
  1928.       hash += *((const unsigned short *) string_buffer);
  1929.       hash ^= hash << 16;
  1930.       hash ^= string_buffer[sizeof (unsigned short)] << 18;
  1931.       hash += hash >> 11;
  1932.    }
  1933.    else if (remaining == 2)
  1934.    {
  1935.       hash += *((const unsigned short *) string_buffer);
  1936.       hash ^= hash << 11;
  1937.       hash += hash >> 17;
  1938.    }
  1939.    else if (remaining == 1)
  1940.    {
  1941.       hash += *string_buffer;
  1942.       hash ^= hash << 10;
  1943.       hash += hash >> 1;
  1944.    }
  1945.  
  1946.    // force "avalanching" of final 127 bits
  1947.    hash ^= hash << 3;
  1948.    hash += hash >> 5;
  1949.    hash ^= hash << 4;
  1950.    hash += hash >> 17;
  1951.    hash ^= hash << 25;
  1952.    hash += hash >> 6;
  1953.  
  1954.    return (hash); // finished, return the hash value
  1955. }
  1956.  
  1957.  
  1958. static unsigned long HashFile (const wchar_t *file_pathname)
  1959. {
  1960.    // super fast file content pseudo-hash function
  1961.  
  1962.    unsigned short value;
  1963.    unsigned long file_size;
  1964.    FILE *fp;
  1965.  
  1966.    // open the file
  1967.    _wfopen_s (&fp, file_pathname, L"rb");
  1968.    if (fp == NULL)
  1969.       return ((unsigned long) time (NULL)); // if file can't be open, return a random number
  1970.  
  1971.    // seek at end of file, read file size, and rewind
  1972.    fseek (fp, 0, SEEK_END);
  1973.    file_size = ftell (fp);
  1974.    fseek (fp, 0, SEEK_SET);
  1975.  
  1976.    // seek at 2/3 of file size (if file is small enough, return only its content)
  1977.    if (file_size >= 4)
  1978.    {
  1979.       fseek (fp, file_size * 2 / 3, SEEK_SET); // seek at 2/3 of file
  1980.       fread (&value, 2, 1, fp); // and read a word here
  1981.    }
  1982.    else if (file_size >= 2)
  1983.       fread (&value, 2, 1, fp);
  1984.    else if (file_size == 1)
  1985.       value = fgetc (fp);
  1986.    else
  1987.       value = 0;
  1988.  
  1989.    // finished, close the file
  1990.    fclose (fp);
  1991.  
  1992.    // and return a hash composed of the 16 lower bits of the file size and the value we read
  1993.    return ((unsigned long) (file_size << 16) | (unsigned long) value);
  1994. }
  1995.  
  1996.  
  1997. static void ResolveWildcard (wchar_t *file_pathname, wchar_t *extensions_separated_by_bars)
  1998. {
  1999.    // this function resolves a pathname ending with .* by testing with various possible
  2000.    // file extensions until one of the files formed that way is found to exist.
  2001.  
  2002.    static wchar_t extension_list[256]; // needs to be modifiable for strtok()
  2003.    wchar_t *current_extension;
  2004.    wchar_t *wcstok_context;
  2005.    int length;
  2006.  
  2007.    wcscpy_s (extension_list, WCHAR_SIZEOF (extension_list), extensions_separated_by_bars);
  2008.    length = wcslen (file_pathname); // get pathname length
  2009.  
  2010.    // does the pathname we want NOT end with a wildcard ?
  2011.    if ((length < 2) || (wcscmp (&file_pathname[length - 2], L".*") != 0))
  2012.       return; // no need to resolve anything
  2013.  
  2014.    // test each extension and see if a corresponding file exists
  2015.    current_extension = wcstok_s (extension_list, L"|", &wcstok_context);
  2016.    while (current_extension != NULL)
  2017.    {
  2018.       if (*current_extension == L'.')
  2019.          current_extension++; // if current extension starts with a dot, skip it
  2020.  
  2021.       wcscpy_s (&file_pathname[length - 1], wcslen (current_extension) + 1, current_extension);
  2022.       if (_waccess (file_pathname, 0) == 0)
  2023.          return; // found a file with this extension
  2024.       current_extension = wcstok_s (NULL, L"|", &wcstok_context);
  2025.    }
  2026.  
  2027.    wcscpy_s (&file_pathname[length - 1], 2, L"*");
  2028.    return; // if none of these extensions match, put the wildcard back and return
  2029. }
  2030.  
  2031.  
  2032. static int SortReflectedObjects (const void *object1, const void *object2)
  2033. {
  2034.    // callback function used by qsort() when sorting the reflected objects according to their distance to the viewer
  2035.  
  2036.    return ((int) (1000.0f * (((reflectedobject_t *) object2)->distance - ((reflectedobject_t *) object1)->distance)));
  2037. }
  2038.