Files
BT412/engine/MUNGA_L4/L4VIDEO.cpp
T
arcattackandClaude Fable 5 cf9f56044e Aim coordinates: route all conversions through the true NDC (fixes the
sideways lock offset)

The windowed present stretches the fixed 800x600 backbuffer into the
client area while the projection's aspect follows the CLIENT (resize
rebuild) -- so the dpl2d/reticle frame, the client frame and NDC only
coincide for a backbuffer-shaped window. The mouse mapping and the
square-frame ray shortcut (cx = rx*tanHalfFov) were exact at screen
centre and drifted outward with a side-flipped sign: aiming left of the
enemy locked as if right of it (user report).

- BTSetAimProjection now publishes BOTH proj scales (P11 carries the
  aspect) + the backbuffer size (the dpl2d frame). NOTE: GetViewport at
  the loop top holds the PREVIOUS frame's last pass viewport (gauge
  passes shrink it) -> use the renderer's own GetWidth/GetHeight.
- BTGetAimRay / BTProjectToReticle convert reticle <-> NDC per axis
  (ndc_x = rx * vh/vw; camera x = ndc_x/P11) -- exact for any window
  shape and FOV.
- New BTClientToReticle undoes the present stretch for the mouse
  (client px -> viewport px -> reticle coords); mech4 uses it instead
  of the raw client-height mapping.
- 1Hz aim telemetry in the [target] log (hits/noRay/noPick): verified
  100% pick hit rate once the projection is live (the pre-visibility
  mission-load seconds report noRay -- that was the "flicker").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 22:13:45 -05:00

9160 lines
318 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "..\munga\door.h"
#include "..\munga\doorfram.h"
#include "..\munga\eyecandy.h"
#include "l4vidrnd.h"
#include "l4video.h"
#include "..\munga\matrix.h"
#include "..\munga\mover.h"
#include "..\munga\jmover.h"
#include "..\munga\player.h"
#include "..\munga\camship.h"
#include "..\munga\director.h"
#include "..\munga\mission.h"
#include "..\munga\cultural.h"
#include "..\munga\nttmgr.h"
#include "..\munga\explode.h" // Explosion::GetEntityHit (the effect-104 wreck swap)
#include "..\munga\app.h"
#include "l4particles.h"
#include "DXUtils.h"
#include "bgfload.h" // BT: SetVideoPathPriority (day/night path priority)
using namespace std;
#include <conio.h>
#include <vector>
#include <hash_map>
#include <map>
#include <algorithm> // std::sort (the .PFX particle depth sort)
LPDIRECT3D9 gD3D = NULL;
//STUBBED: DPL RB 1/14/07
// when this is resolved it can be removed
#include "..\DPLSTUB.h"
#include <d3dx9.h>
#define char8 unsigned char
#define uint32 unsigned __int32
// basic macro to help with releasing things
#define SAFE_RELEASE(handle) if (handle) { handle->Release(); handle = NULL; }
//===========================================================================//
// BT WEAPON BEAMS (port addition) -- the visible muzzle->hit beam the game's
// fire path pushes each shot. The 1995 IG board drew weapon beams through the
// dpl_* display-list layer that was never ported (so a firing Emitter drew
// NOTHING). This is a self-contained additive-quad beam renderer: BTPushBeam
// (called from the reconstructed fire path, mech4.cpp) queues a world-space
// segment; BTDrawBeams (called in the alpha pass below, world proj + view set,
// Z-test on so a beam into a hill is occluded) billboards each as a camera-
// facing additive quad and fades it over its short life. No content needed.
//===========================================================================//
struct BTBeamFx
{
D3DXVECTOR3 from, to;
DWORD color; // 0x00RRGGBB glow colour (additive; alpha ignored)
float ttl, maxTtl; // seconds
float width; // natural-model-scale multiplier (1.0 = authored radius)
int kind; // tube model: 0=ermlaser 1=ppc 2=slaser 3=mlaser 4=llaser
};
static std::vector<BTBeamFx> gBTBeams;
// the decoded beam grit sheet (built lazily in BTDrawBeams; shared with the
// .PFX particle layer below)
LPDIRECT3DTEXTURE9 gBTBeamGritTex = 0;
LPDIRECT3DTEXTURE9 BTGetBeamGritTexture() { return gBTBeamGritTex; }
// Called from the game (mech4.cpp) -- external linkage, matched by an extern decl there.
// LOD eyepoint feed (game -> renderer): the viewpoint mech's position, the
// authentic LOD reference (see the ExecuteImplementation banner). mech4 calls
// this every player frame; once fed, the view-matrix extraction fallback stops.
int gBTLodEyeValid = 0;
void BTSetLodEye(float x, float y, float z)
{
d3d_OBJECT::SetCameraPosition(x, y, z);
gBTLodEyeValid = 1;
// DIAG (BT_LOD_LOG): prove the feed runs + what it feeds (1-in-300 calls)
static int s_eyeLog = -1;
if (s_eyeLog < 0) { const char *lv = getenv("BT_LOD_LOG"); s_eyeLog = (lv != 0 && lv[0] == '1') ? 1 : 0; }
if (s_eyeLog)
{
static int s_n = 0;
if ((++s_n % 300) == 1)
DEBUG_STREAM << "[lodeye] fed (" << x << ", " << y << ", " << z << ") n=" << s_n << "\n" << std::flush;
}
}
// ---------------------------------------------------------------------------
// AIM CAMERA FEED (task #36 authentic targeting): the ACTIVE eye publishes its
// world pose here (DPLEyeRenderable, the mCamera==this write site) and the
// render loop publishes the live projection's FOV term; the game side then
// derives the reticle PICK RAY (crosshair -> world, the engine Reticle's
// "pick point intersection" input) and the inverse projection (world ->
// reticle coords, the target-designator box position). Reticle coordinate
// convention = the dpl2d frame: centered origin, +y down, unit = half the
// viewport height (see dpl2d.cpp MapX/MapY).
// ---------------------------------------------------------------------------
// COORDINATE MODEL: everything talks in the DPL2D/RETICLE frame -- centered
// origin, +y down, unit = half the D3D VIEWPORT height (dpl2d MapX/MapY).
// The windowed present STRETCHES the fixed backbuffer into the client area,
// and the projection's aspect follows the CLIENT (the resize rebuild) -- so
// the viewport frame, the client frame and the NDC frame all differ unless
// the window happens to be exactly backbuffer-shaped. Every conversion below
// goes through the true NDC (per-axis P11/P22), NOT a square-frame shortcut:
// a shortcut is exact at screen centre and drifts outward with a side-flipped
// sign (the "locks left of the mech when I aim right of it" report).
static float gBTAimCamPos[3] = {0, 0, 0};
static float gBTAimCamX[3] = {1, 0, 0}; // camera right (LookAtRH xaxis)
static float gBTAimCamY[3] = {0, 1, 0}; // camera up (LookAtRH yaxis)
static float gBTAimCamZ[3] = {0, 0, 1}; // camera BACK (LookAtRH zaxis; view dir = -Z)
static float gBTAimP11 = 0.0f; // proj._11 (x scale; carries the aspect)
static float gBTAimP22 = 0.0f; // proj._22 (y scale = 1/tan(fovY/2))
static float gBTAimVpW = 0.0f; // D3D viewport (backbuffer) size --
static float gBTAimVpH = 0.0f; // the dpl2d frame's pixel space
static int gBTAimCamValid = 0;
void BTSetAimCamera(const float pos[3], const float xax[3],
const float yax[3], const float zax[3])
{
for (int i = 0; i < 3; ++i)
{
gBTAimCamPos[i] = pos[i];
gBTAimCamX[i] = xax[i];
gBTAimCamY[i] = yax[i];
gBTAimCamZ[i] = zax[i];
}
gBTAimCamValid = 1;
}
void BTSetAimProjection(float p11, float p22, float vpW, float vpH)
{
if (p11 > 1e-6f && p22 > 1e-6f && vpW > 0.0f && vpH > 0.0f)
{
gBTAimP11 = p11;
gBTAimP22 = p22;
gBTAimVpW = vpW;
gBTAimVpH = vpH;
}
}
//
// Client-area mouse position -> reticle coords: undo the present stretch
// (client px -> viewport px), then centre/scale by the dpl2d unit. Clamped
// to the visible frame.
//
void BTClientToReticle(float mx, float my, float cw, float ch,
float *rx, float *ry)
{
if (cw <= 0.0f || ch <= 0.0f || gBTAimVpW <= 0.0f || gBTAimVpH <= 0.0f)
{
*rx = 0.0f; *ry = 0.0f;
return;
}
const float vx = mx * (gBTAimVpW / cw);
const float vy = my * (gBTAimVpH / ch);
float x = (vx - gBTAimVpW * 0.5f) / (gBTAimVpH * 0.5f);
float y = (vy - gBTAimVpH * 0.5f) / (gBTAimVpH * 0.5f);
const float xmax = gBTAimVpW / gBTAimVpH;
if (x < -xmax) x = -xmax;
if (x > xmax) x = xmax;
if (y < -1.0f) y = -1.0f;
if (y > 1.0f) y = 1.0f;
*rx = x;
*ry = y;
}
//
// Crosshair (reticle coords) -> world pick ray. reticle -> NDC:
// ndc_x = rx * vh/vw (the reticle x unit is half the viewport HEIGHT),
// ndc_y = -ry (reticle +y is down). NDC -> camera: divide by the true
// per-axis projection scales; RH camera looks down -Z.
//
int BTGetAimRay(float rx, float ry, float outStart[3], float outDir[3])
{
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimP22 <= 0.0f
|| gBTAimVpW <= 0.0f)
return 0;
const float ndcX = rx * (gBTAimVpH / gBTAimVpW);
const float ndcY = -ry;
const float cx = ndcX / gBTAimP11;
const float cy = ndcY / gBTAimP22;
float d[3];
float len = 0.0f;
for (int i = 0; i < 3; ++i)
{
d[i] = cx * gBTAimCamX[i] + cy * gBTAimCamY[i] - gBTAimCamZ[i];
len += d[i] * d[i];
}
len = sqrtf(len);
if (len < 1e-6f) return 0;
for (int i = 0; i < 3; ++i)
{
outStart[i] = gBTAimCamPos[i];
outDir[i] = d[i] / len;
}
return 1;
}
//
// World point -> reticle coords (the inverse; the designator box position).
// Returns 1 when the point is IN FRONT of the camera (rx/ry valid for
// on-screen placement); 0 when behind (rx still carries the correct SIDE
// sign so the caller can pick the left/right off-screen arrow).
//
int BTProjectToReticle(const float world[3], float *rx, float *ry)
{
if (!gBTAimCamValid || gBTAimP11 <= 0.0f || gBTAimP22 <= 0.0f
|| gBTAimVpH <= 0.0f)
{
*rx = 0.0f; *ry = 0.0f;
return 0;
}
float rel[3];
for (int i = 0; i < 3; ++i)
rel[i] = world[i] - gBTAimCamPos[i];
const float xc = rel[0]*gBTAimCamX[0] + rel[1]*gBTAimCamX[1] + rel[2]*gBTAimCamX[2];
const float yc = rel[0]*gBTAimCamY[0] + rel[1]*gBTAimCamY[1] + rel[2]*gBTAimCamY[2];
const float zc = rel[0]*gBTAimCamZ[0] + rel[1]*gBTAimCamZ[1] + rel[2]*gBTAimCamZ[2];
const float depth = -zc; // camera looks down -Z
if (depth < 0.1f)
{
*rx = (xc >= 0.0f) ? 2.0f : -2.0f; // side sign only
*ry = 0.0f;
return 0;
}
const float ndcX = (xc * gBTAimP11) / depth;
const float ndcY = (yc * gBTAimP22) / depth;
*rx = ndcX * (gBTAimVpW / gBTAimVpH);
*ry = -ndcY;
return 1;
}
void BTPushBeamKind(float fx, float fy, float fz, float tx, float ty, float tz,
unsigned color, float ttl, float width, int kind)
{
if (gBTBeams.size() > 256) return; // runaway guard
BTBeamFx b;
b.from = D3DXVECTOR3(fx, fy, fz);
b.to = D3DXVECTOR3(tx, ty, tz);
b.color = color;
b.ttl = ttl; b.maxTtl = (ttl > 1e-4f) ? ttl : 1e-4f;
b.width = width;
b.kind = (kind >= 0 && kind < 5) ? kind : 0;
gBTBeams.push_back(b);
}
void BTPushBeam(float fx, float fy, float fz, float tx, float ty, float tz,
unsigned color, float ttl, float width)
{
BTPushBeamKind(fx, fy, fz, tx, ty, tz, color, ttl, width, 0);
}
void BTDrawBeams(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
{
if (gBTBeams.empty()) return;
D3DXMATRIX iv; D3DXMatrixInverse(&iv, NULL, view);
const D3DXVECTOR3 cam(iv._41, iv._42, iv._43); // camera world position
DWORD sLight, sFog, sZW, sBlend, sSrc, sDst, sCull, sCop, sCa1, sCa2, sAop, sAa1, sAddrU, sAddrV;
dev->GetRenderState(D3DRS_LIGHTING, &sLight);
dev->GetRenderState(D3DRS_FOGENABLE, &sFog);
dev->GetRenderState(D3DRS_ZWRITEENABLE, &sZW);
dev->GetRenderState(D3DRS_ALPHABLENDENABLE, &sBlend);
dev->GetRenderState(D3DRS_SRCBLEND, &sSrc);
dev->GetRenderState(D3DRS_DESTBLEND, &sDst);
dev->GetRenderState(D3DRS_CULLMODE, &sCull);
dev->GetTextureStageState(0, D3DTSS_COLOROP, &sCop);
dev->GetTextureStageState(0, D3DTSS_COLORARG1, &sCa1);
dev->GetTextureStageState(0, D3DTSS_COLORARG2, &sCa2);
dev->GetTextureStageState(0, D3DTSS_ALPHAOP, &sAop);
dev->GetTextureStageState(0, D3DTSS_ALPHAARG1, &sAa1);
dev->GetSamplerState(0, D3DSAMP_ADDRESSU, &sAddrU);
dev->GetSamplerState(0, D3DSAMP_ADDRESSV, &sAddrV);
// AUTHENTIC BEAM TEXTURE (decoded): the original laser beam (ermlaser.bgf) is a
// tube textured with `beamwhite_scr_tex` -> the `bexp` image (VIDEO/TEX/BEXP.BSL,
// a chaotic red/yellow/green noise) SCROLLED fast (BTFX.VMF: SCROLL u=0.10 v=9.5)
// and ramp-colourised by `softer` (0.25->0.99) for the white core. Load BEXP.BSL
// once, ramp its luminance to a grayscale grit texture, and MODULATE the beam
// colour by it with a scrolling UV -> the streaming "gritty" look, not a clean
// gradient. BT_BEAM_TEX=0 falls back to the plain additive beam.
// (file-scope so the .PFX particle layer below can share the decoded sheet)
static int s_beamTexTried = 0;
extern LPDIRECT3DTEXTURE9 gBTBeamGritTex;
LPDIRECT3DTEXTURE9 &s_beamTex = gBTBeamGritTex;
if (!s_beamTexTried)
{
s_beamTexTried = 1;
const char *bv = getenv("BT_BEAM_TEX");
if (bv == 0 || bv[0] != '0')
{
FILE *fp = fopen("VIDEO\\TEX\\BEXP.BSL", "rb");
if (fp)
{
fseek(fp, 0, SEEK_END); long sz = ftell(fp); fseek(fp, 0, SEEK_SET);
std::vector<unsigned char> buf(sz > 0 ? sz : 1);
size_t got = fread(&buf[0], 1, sz, fp); fclose(fp);
if (got >= 16 && memcmp(&buf[0], "DIV-BSL2", 8) == 0)
{
int tw = *(int *)&buf[8], th = *(int *)&buf[12]; // 128 x 64
long need = (long)tw * th * 4;
if (tw > 0 && th > 0 && (long)got >= need &&
SUCCEEDED(dev->CreateTexture(tw, th, 1, 0, D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED, &s_beamTex, NULL)))
{
const unsigned char *bimg = &buf[got - need]; // trailing base image
D3DLOCKED_RECT lr;
if (SUCCEEDED(s_beamTex->LockRect(0, &lr, NULL, 0)))
{
// GRIT CONTRAST (BT_BEAM_GRIT, default 2.0): the authentic
// 'softer' ramp (0.25->0.99) only spans ~4:1, and additive
// blending over a bright scene flattens it to near-invisible.
// Expand the ramped luminance about its midpoint before baking
// so the interference pattern reads like the pod footage.
float grit = 2.0f;
{ const char *gv = getenv("BT_BEAM_GRIT"); if (gv) grit = (float)atof(gv); }
for (int y = 0; y < th; ++y)
{
DWORD *dst = (DWORD *)((char *)lr.pBits + y * lr.Pitch);
for (int x = 0; x < tw; ++x)
{
// BSL BIT-SLICE decode: beamwhite_scr_tex maps to 'bexp'
// with NO BITSLICE tag = slice 0 (bexp1, the grit sheet)
// = bits 12-15 (byte1 high nibble) of the texel word.
// (The old byte-luminance read mixed bexp1/bexp2 and the
// bexp99 RGBA-sprite nibbles.)
const unsigned char *p = &bimg[(y * tw + x) * 4];
float lum = (float)(p[1] & 0xF0) / 240.0f;
float v = 0.25f + 0.74f*lum; // 'softer' ramp
v = 0.62f + (v - 0.62f) * grit; // expand about ramp midpoint
if (v < 0.0f) v = 0.0f;
int gg = (int)(v * 255.0f);
if (gg > 255) gg = 255;
dst[x] = 0xFF000000u | (gg << 16) | (gg << 8) | gg;
}
}
s_beamTex->UnlockRect(0);
}
}
}
}
}
}
// AUTHENTIC GEOMETRY: render each weapon's REAL beam model (thin 2000-long
// tubes, -Z aligned, UVs tiled down the length), transformed per beam (scale
// the length to the shot distance, orient local -Z -> muzzle->target). The
// authored radii ARE the beam widths: ERMLASER 0.22, PPC 0.62 (a genuinely
// fatter bolt), SLASER 0.11 / MLASER 0.22 / LLASER 0.32 -- draw at natural
// scale (width multiplier 1.0), NOT inflated. BT_BEAM_TUBE=0 -> billboard.
static int s_tubeTried = 0;
struct BTTubeModel
{
std::vector<float> vb; // x,y,z,u,v per vertex
std::vector<uint32_t> ib;
int verts, tris;
};
static BTTubeModel s_tubes[5];
static const char *const kTubeNames[5] =
{ "ermlaser.bgf", "ppc.bgf", "slaser.bgf", "mlaser.bgf", "llaser.bgf" };
if (!s_tubeTried)
{
s_tubeTried = 1;
const char *tvv = getenv("BT_BEAM_TUBE");
if (tvv == 0 || tvv[0] != '0')
{
for (int t = 0; t < 5; ++t)
{
BgfData bd;
s_tubes[t].verts = s_tubes[t].tris = 0;
if (LoadBgfFile(kTubeNames[t], bd) && bd.ok && !bd.indices.empty())
{
s_tubes[t].verts = (int)bd.verts.size();
s_tubes[t].vb.reserve(bd.verts.size() * 5);
for (size_t k = 0; k < bd.verts.size(); ++k)
{
s_tubes[t].vb.push_back(bd.verts[k].x);
s_tubes[t].vb.push_back(bd.verts[k].y);
s_tubes[t].vb.push_back(bd.verts[k].z);
s_tubes[t].vb.push_back(bd.verts[k].u);
s_tubes[t].vb.push_back(bd.verts[k].v);
}
s_tubes[t].ib = bd.indices;
s_tubes[t].tris = (int)(bd.indices.size() / 3);
}
}
}
}
const bool useTube = (s_beamTex != 0 && s_tubes[0].tris > 0);
static float s_tubeWidth = -1.0f;
if (s_tubeWidth < 0.0f)
{ const char *wv = getenv("BT_BEAM_WIDTH"); s_tubeWidth = wv ? (float)atof(wv) : 1.0f; } // global width multiplier
static float s_beamTime = 0.0f; s_beamTime += dt;
const float uScroll = s_beamTime * 0.10f; // VMF SCROLL u-speed
const float vScroll = s_beamTime * 9.5f; // VMF SCROLL v-speed (fast)
DWORD sTFactor, sTexXf;
dev->GetRenderState(D3DRS_TEXTUREFACTOR, &sTFactor);
dev->GetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, &sTexXf);
D3DXMATRIX ident; D3DXMatrixIdentity(&ident);
dev->SetTransform(D3DTS_WORLD, &ident);
dev->SetFVF(useTube ? (D3DFVF_XYZ | D3DFVF_TEX1)
: (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1));
dev->SetRenderState(D3DRS_LIGHTING, FALSE);
dev->SetRenderState(D3DRS_FOGENABLE, FALSE);
dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); // additive glow
dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// beam colour: from the TFACTOR (tube -- verts carry no colour) or vertex DIFFUSE (billboard).
const DWORD kColArg = useTube ? D3DTA_TFACTOR : D3DTA_DIFFUSE;
if (s_beamTex)
{
dev->SetTexture(0, s_beamTex);
dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); // grit x beam colour
dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_COLORARG2, kColArg);
}
else
{
dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
dev->SetTextureStageState(0, D3DTSS_COLORARG1, kColArg);
}
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, kColArg);
// SCROLL + GRIT DENSITY: bake into the tube's UVs on the CPU, PER BEAM (89
// verts, trivial). Two lessons:
// (1) the previous D3DTS_TEXTURE0 texture-coordinate transform SILENTLY
// STOPPED APPLYING when the device moved to HARDWARE vertex processing
// (the perf fix) -- vertex UVs are pipeline-independent;
// (2) the tube's baked U (0..7.7 tiles over its NATIVE 2000u length) stays
// put while the GEOMETRY compresses to the shot distance, so at typical
// ranges all ~8 tiles squeeze into a short beam -> the grit minifies to
// ~3 texels/pixel and bilinear-averages into a SMOOTH GRADIENT (the
// "no interference pattern" report). Scale U by the compression ratio
// (beamLen/2000) so the grit density is WORLD-FIXED (~1 tile / 260u --
// the billboard's proven mapping).
static std::vector<float> s_tubeScrolled;
struct BV { float x, y, z; DWORD c; float u, v; };
for (size_t i = 0; i < gBTBeams.size(); ++i)
{
BTBeamFx &b = gBTBeams[i];
float f = b.ttl / b.maxTtl; if (f < 0) f = 0; if (f > 1) f = 1;
const int r = (int)(((b.color >> 16) & 0xFF) * f);
const int g = (int)(((b.color >> 8) & 0xFF) * f);
const int bl = (int)(( b.color & 0xFF) * f);
const DWORD col = 0xFF000000u | (r << 16) | (g << 8) | bl;
D3DXVECTOR3 d3 = b.to - b.from;
const float beamLen = D3DXVec3Length(&d3);
D3DXVECTOR3 dir = d3; D3DXVec3Normalize(&dir, &dir);
if (useTube)
{
// world = Scale(width,width,len/2000) * Rotate(local -Z -> dir) * Translate(muzzle).
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
if (fabsf(dir.y) > 0.99f) up = D3DXVECTOR3(1.0f, 0.0f, 0.0f);
D3DXVECTOR3 zc = -dir; // local +Z image
D3DXVECTOR3 xc; D3DXVec3Cross(&xc, &up, &zc); D3DXVec3Normalize(&xc, &xc);
D3DXVECTOR3 yc; D3DXVec3Cross(&yc, &zc, &xc);
D3DXMATRIX S, R, T, W;
// NATURAL width: the model's authored radius IS the beam width
// (ERMLASER 0.22u, PPC 0.62u...). b.width is a multiplier on that
// (1.0 for weapon beams); BT_BEAM_WIDTH is the global dev override.
const float ws = b.width * s_tubeWidth;
D3DXMatrixScaling(&S, ws, ws, beamLen / 2000.0f);
D3DXMatrixIdentity(&R);
R._11 = xc.x; R._12 = xc.y; R._13 = xc.z;
R._21 = yc.x; R._22 = yc.y; R._23 = yc.z;
R._31 = zc.x; R._32 = zc.y; R._33 = zc.z;
D3DXMatrixTranslation(&T, b.from.x, b.from.y, b.from.z);
D3DXMatrixMultiply(&W, &S, &R);
D3DXMatrixMultiply(&W, &W, &T);
dev->SetTransform(D3DTS_WORLD, &W);
dev->SetRenderState(D3DRS_TEXTUREFACTOR, col);
// the beam's own weapon-model tube (ermlaser/ppc/slaser/...); fall
// back to the ermlaser tube when that model didn't load
const BTTubeModel &tube =
(s_tubes[b.kind].tris > 0) ? s_tubes[b.kind] : s_tubes[0];
// per-beam UVs: world-fixed grit density + scroll (see banner above)
const float uScale = beamLen / 2000.0f;
s_tubeScrolled = tube.vb;
for (size_t k = 0; k + 4 < s_tubeScrolled.size(); k += 5)
{
s_tubeScrolled[k + 3] = s_tubeScrolled[k + 3] * uScale + uScroll;
s_tubeScrolled[k + 4] += vScroll;
}
dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, tube.verts, tube.tris,
&tube.ib[0], D3DFMT_INDEX32, &s_tubeScrolled[0], 5 * sizeof(float));
}
else
{
D3DXVECTOR3 mid = (b.from + b.to) * 0.5f;
D3DXVECTOR3 toCam = cam - mid; D3DXVec3Normalize(&toCam, &toCam);
D3DXVECTOR3 side; D3DXVec3Cross(&side, &dir, &toCam); D3DXVec3Normalize(&side, &side);
side *= b.width * 0.5f;
const float uLen = uScroll + beamLen / 260.0f;
const float v0 = vScroll, v1 = vScroll + 1.6f;
BV quad[4] =
{
{ b.from.x + side.x, b.from.y + side.y, b.from.z + side.z, col, uScroll, v0 },
{ b.from.x - side.x, b.from.y - side.y, b.from.z - side.z, col, uScroll, v1 },
{ b.to.x + side.x, b.to.y + side.y, b.to.z + side.z, col, uLen, v0 },
{ b.to.x - side.x, b.to.y - side.y, b.to.z - side.z, col, uLen, v1 },
};
dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(BV));
}
b.ttl -= dt;
}
dev->SetTexture(0, NULL);
dev->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, sTexXf);
dev->SetRenderState(D3DRS_TEXTUREFACTOR, sTFactor);
// drop expired beams
size_t w = 0;
for (size_t i = 0; i < gBTBeams.size(); ++i)
if (gBTBeams[i].ttl > 0.0f) gBTBeams[w++] = gBTBeams[i];
gBTBeams.resize(w);
dev->SetTransform(D3DTS_WORLD, &ident);
dev->SetRenderState(D3DRS_LIGHTING, sLight);
dev->SetRenderState(D3DRS_FOGENABLE, sFog);
dev->SetRenderState(D3DRS_ZWRITEENABLE, sZW);
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, sBlend);
dev->SetRenderState(D3DRS_SRCBLEND, sSrc);
dev->SetRenderState(D3DRS_DESTBLEND, sDst);
dev->SetRenderState(D3DRS_CULLMODE, sCull);
dev->SetTextureStageState(0, D3DTSS_COLOROP, sCop);
dev->SetTextureStageState(0, D3DTSS_COLORARG1, sCa1);
dev->SetTextureStageState(0, D3DTSS_COLORARG2, sCa2);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, sAop);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, sAa1);
dev->SetSamplerState(0, D3DSAMP_ADDRESSU, sAddrU);
dev->SetSamplerState(0, D3DSAMP_ADDRESSV, sAddrV);
}
//===========================================================================//
// BT PARTICLE EFFECTS (.PFX) -- the 1995 explosion/damage effect layer.
//
// The pod's damage/explosion visuals are DATA: VIDEO/*.PFX text files (format
// documented inside each file), mapped to dpl effect NUMBERS by BTDPL.INI's
// [pfx_day]/[pfx_night] pages ("psfxN=file.pfx" -- N is the number embedded in
// resources: 2..5 = the mech damage bands, 6 = projectile-gun hit, 7 = the
// mech-death explosion, 8 = zone destroyed...). The 1995 board consumed these
// through dpl particle calls that were never ported: DPLIndependantEffect's
// (<100) arm and ReadPSFX are both stubbed, so every weapon-hit / damage-band /
// death explosion rendered NOTHING. Like the weapon-beam layer above, this is
// the self-contained D3D9 port of that layer:
// BTLoadPfxFile -- parses a .PFX into BTPfxDef (the documented format)
// BTStartPfx -- starts an emitter instance at a world position
// (called by DPLIndependantEffect for effect_number < 100)
// BTDrawPfx -- per-frame sim + camera-facing additive-quad billboards
// (called beside BTDrawBeams in the render loop)
// Variance convention (decoded from DNBOOM.PFX: vel 150 + var -300 = a
// symmetric +-150 burst): sampled = value + variance * rand01().
// NOT yet honoured (noted, low-visibility): atten/attenv (distance
// attenuation), colorWarp/alphaWarp exponents are applied as t^(1/warp),
// the per-file texture name (all BT effects use the firesmoke sheet; we use
// the same decoded grit texture as the beams).
//===========================================================================//
struct BTPfxDef
{
int valid;
unsigned identifier;
int maxIssue; // total particles an instance may issue
float releasePeriod; // batch interval (s)
float rate; // particles per second while releasing
float px, py, pz, pv; // spawn offset + positional variance
float velx, vely, velz, velxv, velyv, velzv;
float rad, radv, exp, expv, dexp, dexpv; // radius, expansion rate, expansion decay
float accelx, accely, accelz, accelxv, accelyv, accelzv;
float atten, attenv; // (not yet honoured)
float sI[4], sIv[4]; // start colour inner RGBA + variance
float sO[4], sOv[4]; // start colour outer RGBA + variance
float eI[4], eIv[4]; // end colour inner RGBA + variance
float eO[4], eOv[4]; // end colour outer RGBA + variance
float colorWarp, alphaWarp;
float duration, durationv; // particle lifetime
};
#define BT_PFX_SLOTS 32
static BTPfxDef gBTPfxDefs[BT_PFX_SLOTS];
struct BTPfxEmitter
{
const BTPfxDef *def;
D3DXVECTOR3 pos;
// The effect's LOCAL FRAME (.PFX offsets/velocities are authored in the
// victim's mech-local space -- e.g. DAFC.PFX sprays -Z, out through the
// front armor toward the shooter). Identity when no frame is known.
D3DXVECTOR3 ax, ay, az;
float emitAccum; // fractional particles owed (rate * dt integration)
int issued;
int active;
};
struct BTPfxParticle
{
D3DXVECTOR3 pos, vel, accel;
float age, life;
float rad, exp, dexp;
float colorWarp, alphaWarp; // def-level warps, carried per particle
float sI[4], sO[4], eI[4], eO[4];
};
static std::vector<BTPfxEmitter> gBTPfxEmitters;
static std::vector<BTPfxParticle> gBTPfxParticles;
static float BTPfxRand01() // cheap deterministic LCG
{
static unsigned s = 0x2545F491u;
s = s * 1664525u + 1013904223u;
return (float)((s >> 8) & 0xFFFFFF) / 16777215.0f;
}
// Parse one .PFX text file (VIDEO\<name>) into a def slot. Format per the
// spec block carried inside every .PFX (and ReadPSFX's comment).
static int BTLoadPfxFile(const char *file_name, BTPfxDef &d)
{
char path[256];
strcpy(path, "VIDEO\\");
strcat(path, file_name);
FILE *fp = fopen(path, "rt");
if (!fp)
{
DEBUG_STREAM << "[pfx] could not open " << path << " -- its effects will not draw\n" << std::flush;
return 0;
}
memset(&d, 0, sizeof(d));
char line[256];
int ok = 1;
// line 1: texture name (recorded in the banner note; the shared sheet is used)
if (!fgets(line, sizeof(line), fp)) ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%x %d %f %f", &d.identifier, &d.maxIssue, &d.releasePeriod, &d.rate) == 4);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f %f %f", &d.px, &d.py, &d.pz, &d.pv) == 4);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f %f %f %f %f", &d.velx, &d.vely, &d.velz, &d.velxv, &d.velyv, &d.velzv) == 6);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f %f %f %f %f", &d.rad, &d.radv, &d.exp, &d.expv, &d.dexp, &d.dexpv) == 6);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f %f %f %f %f", &d.accelx, &d.accely, &d.accelz, &d.accelxv, &d.accelyv, &d.accelzv) == 6);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f", &d.atten, &d.attenv) == 2);
else ok = 0;
float *quads[8] = { d.sI, d.sIv, d.sO, d.sOv, d.eI, d.eIv, d.eO, d.eOv };
for (int q = 0; ok && q < 4; ++q) // 4 lines: sI+var, sO+var, eI+var, eO+var
{
if (fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f %f %f %f %f %f %f",
&quads[q*2][0], &quads[q*2][1], &quads[q*2][2], &quads[q*2][3],
&quads[q*2+1][0], &quads[q*2+1][1], &quads[q*2+1][2], &quads[q*2+1][3]) == 8);
else ok = 0;
}
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f", &d.colorWarp, &d.alphaWarp) == 2);
else ok = 0;
if (ok && fgets(line, sizeof(line), fp))
ok = (sscanf(line, "%f %f", &d.duration, &d.durationv) == 2);
else ok = 0;
fclose(fp);
if (!ok)
{
DEBUG_STREAM << "[pfx] " << path << " did not parse -- effect disabled\n" << std::flush;
return 0;
}
d.valid = 1;
return 1;
}
// Slot-checked load entry used by the psfx page loader (LoadMission walk).
int BTLoadPfxFile_slot(const char *file_name, int slot)
{
if (slot < 0 || slot >= BT_PFX_SLOTS)
return 0;
return BTLoadPfxFile(file_name, gBTPfxDefs[slot]);
}
// Start one effect instance at a world position (the DPLIndependantEffect
// contract: renderer-owned, runs to termination on its own). The optional
// frame rows orient the .PFX's mech-local offsets/velocities in the world
// (pass the victim entity's localToWorld X/Y/Z rows); identity when absent.
void BTStartPfxFrame(int effect_number, float x, float y, float z,
const float *xrow, const float *yrow, const float *zrow)
{
if (effect_number < 0 || effect_number >= BT_PFX_SLOTS)
return;
const BTPfxDef &d = gBTPfxDefs[effect_number];
if (!d.valid)
return;
if (gBTPfxEmitters.size() > 64) // runaway guard
return;
BTPfxEmitter e;
e.def = &d;
e.pos = D3DXVECTOR3(x, y, z);
e.ax = xrow ? D3DXVECTOR3(xrow[0], xrow[1], xrow[2]) : D3DXVECTOR3(1, 0, 0);
e.ay = yrow ? D3DXVECTOR3(yrow[0], yrow[1], yrow[2]) : D3DXVECTOR3(0, 1, 0);
e.az = zrow ? D3DXVECTOR3(zrow[0], zrow[1], zrow[2]) : D3DXVECTOR3(0, 0, 1);
e.emitAccum = 1.0f; // first particle immediately
e.issued = 0;
e.active = 1;
gBTPfxEmitters.push_back(e);
}
void BTStartPfx(int effect_number, float x, float y, float z)
{
BTStartPfxFrame(effect_number, x, y, z, 0, 0, 0);
}
// Spawn a few particles of a slot's effect DIRECTLY at a moving point (no
// emitter instance) -- the per-frame projectile SMOKE TRAIL (psfx 0 = dsrm,
// "the lrm smoke trail": its velocities stream +Z = BEHIND the round, so the
// frame is built with local +Z = the flight's backward direction).
static void BTPfxSpawn(const BTPfxEmitter &e); // defined below
void BTPfxTrailPuff(int effect_number, float x, float y, float z,
float backx, float backy, float backz, int count)
{
if (effect_number < 0 || effect_number >= BT_PFX_SLOTS)
return;
const BTPfxDef &d = gBTPfxDefs[effect_number];
if (!d.valid)
return;
BTPfxEmitter e; // transient -- used only as the spawn context
e.def = &d;
e.pos = D3DXVECTOR3(x, y, z);
D3DXVECTOR3 az(backx, backy, backz);
if (D3DXVec3LengthSq(&az) < 1e-6f)
az = D3DXVECTOR3(0, 0, 1);
D3DXVec3Normalize(&az, &az);
D3DXVECTOR3 up(0, 1, 0);
if (fabsf(az.y) > 0.99f) up = D3DXVECTOR3(1, 0, 0);
D3DXVECTOR3 ax; D3DXVec3Cross(&ax, &up, &az); D3DXVec3Normalize(&ax, &ax);
D3DXVECTOR3 ay; D3DXVec3Cross(&ay, &az, &ax);
e.ax = ax; e.ay = ay; e.az = az;
e.emitAccum = 0.0f; e.issued = 0; e.active = 0;
for (int i = 0; i < count; ++i)
BTPfxSpawn(e);
}
static void BTPfxSpawn(const BTPfxEmitter &e)
{
const BTPfxDef &d = *e.def;
if (gBTPfxParticles.size() > 2048) // global particle cap
return;
BTPfxParticle p;
// Sample in the effect's LOCAL frame, then orient into the world through
// the emitter's basis (the victim's localToWorld rows). The position
// jitter pv is an isotropic scatter about the base offset -> symmetric
// (rand +-pv); the paired variances stay value + var*rand01 (DNBOOM's
// vel 150 / var -300 decodes to the symmetric +-150 burst).
D3DXVECTOR3 lp(
d.px + d.pv * (BTPfxRand01() * 2.0f - 1.0f),
d.py + d.pv * (BTPfxRand01() * 2.0f - 1.0f),
d.pz + d.pv * (BTPfxRand01() * 2.0f - 1.0f));
D3DXVECTOR3 lv(
d.velx + d.velxv * BTPfxRand01(),
d.vely + d.velyv * BTPfxRand01(),
d.velz + d.velzv * BTPfxRand01());
D3DXVECTOR3 la(
d.accelx + d.accelxv * BTPfxRand01(),
d.accely + d.accelyv * BTPfxRand01(),
d.accelz + d.accelzv * BTPfxRand01());
p.pos = e.pos + e.ax * lp.x + e.ay * lp.y + e.az * lp.z;
p.vel = e.ax * lv.x + e.ay * lv.y + e.az * lv.z;
p.accel = e.ax * la.x + e.ay * la.y + e.az * la.z;
p.age = 0.0f;
p.life = d.duration + d.durationv * BTPfxRand01();
if (p.life < 0.05f) p.life = 0.05f;
p.rad = d.rad + d.radv * BTPfxRand01();
p.exp = d.exp + d.expv * BTPfxRand01();
p.dexp = d.dexp + d.dexpv * BTPfxRand01();
p.colorWarp = (d.colorWarp > 1e-3f) ? d.colorWarp : 1.0f;
p.alphaWarp = (d.alphaWarp > 1e-3f) ? d.alphaWarp : 1.0f;
for (int i = 0; i < 4; ++i)
{
p.sI[i] = d.sI[i] + d.sIv[i] * BTPfxRand01();
p.sO[i] = d.sO[i] + d.sOv[i] * BTPfxRand01();
p.eI[i] = d.eI[i] + d.eIv[i] * BTPfxRand01();
p.eO[i] = d.eO[i] + d.eOv[i] * BTPfxRand01();
}
gBTPfxParticles.push_back(p);
}
// The beams' decoded grit texture (built in BTDrawBeams) -- shared with the
// particles so the fire quads carry the authentic firesmoke-family noise.
extern LPDIRECT3DTEXTURE9 BTGetBeamGritTexture();
void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
{
// ---- sim ----
if (dt > 0.1f) dt = 0.1f; // stall guard
for (size_t ei = 0; ei < gBTPfxEmitters.size(); ++ei)
{
BTPfxEmitter &e = gBTPfxEmitters[ei];
if (!e.active) continue;
const BTPfxDef &d = *e.def;
// CONTINUOUS emission at `rate` particles/second until maximum_issue is
// exhausted. Data-verified semantics: in EVERY shipped .PFX,
// maxIssue/rate == release_period (DAFC 25/100~=0.2, DNBOOM 35/150~=0.2,
// DDAM2 35/16~=2, DDTHSMK 30/3=10) -- release_period IS the emission
// WINDOW, not a batch interval. (The old one-burst batching dumped a
// 10-second smoke plume in a single frame's puff.)
e.emitAccum += d.rate * dt;
int n = (int)e.emitAccum;
if (n > 0)
{
e.emitAccum -= (float)n;
if (e.issued + n > d.maxIssue) n = d.maxIssue - e.issued;
for (int b = 0; b < n; ++b)
BTPfxSpawn(e);
e.issued += n;
if (e.issued >= d.maxIssue)
e.active = 0; // done issuing -> instance ends
}
}
{ // compact finished emitters
size_t w = 0;
for (size_t i = 0; i < gBTPfxEmitters.size(); ++i)
if (gBTPfxEmitters[i].active) gBTPfxEmitters[w++] = gBTPfxEmitters[i];
gBTPfxEmitters.resize(w);
}
{ // advance + expire particles
size_t w = 0;
for (size_t i = 0; i < gBTPfxParticles.size(); ++i)
{
BTPfxParticle &p = gBTPfxParticles[i];
p.age += dt;
if (p.age >= p.life) continue;
p.vel += p.accel * dt;
p.pos += p.vel * dt;
p.rad += p.exp * dt;
p.exp += p.dexp * dt;
if (p.rad < 0.05f) p.rad = 0.05f;
gBTPfxParticles[w++] = p;
}
gBTPfxParticles.resize(w);
}
if (gBTPfxParticles.empty())
return;
// ---- draw: camera-facing additive quads (inner core + outer glow) ----
const D3DXVECTOR3 right(view->_11, view->_21, view->_31);
const D3DXVECTOR3 up (view->_12, view->_22, view->_32);
DWORD sLight, sFog, sZW, sBlend, sSrc, sDst, sCull, sCop, sCa1, sCa2, sAop, sAa1, sAa2;
dev->GetRenderState(D3DRS_LIGHTING, &sLight);
dev->GetRenderState(D3DRS_FOGENABLE, &sFog);
dev->GetRenderState(D3DRS_ZWRITEENABLE, &sZW);
dev->GetRenderState(D3DRS_ALPHABLENDENABLE, &sBlend);
dev->GetRenderState(D3DRS_SRCBLEND, &sSrc);
dev->GetRenderState(D3DRS_DESTBLEND, &sDst);
dev->GetRenderState(D3DRS_CULLMODE, &sCull);
dev->GetTextureStageState(0, D3DTSS_COLOROP, &sCop);
dev->GetTextureStageState(0, D3DTSS_COLORARG1, &sCa1);
dev->GetTextureStageState(0, D3DTSS_COLORARG2, &sCa2);
dev->GetTextureStageState(0, D3DTSS_ALPHAOP, &sAop);
dev->GetTextureStageState(0, D3DTSS_ALPHAARG1, &sAa1);
dev->GetTextureStageState(0, D3DTSS_ALPHAARG2, &sAa2);
// PREMULTIPLIED blending (ONE, INVSRCALPHA): the one model that renders BOTH
// authored particle families correctly. FIRE (bright colour, the additive
// glow) and SMOKE (dark/negative-ramp colour with high alpha -> OCCLUDES the
// scene behind it -- DDAM2 is 30% grey, DDTHSMK ramps to negative: both are
// invisible under pure additive, which is why damaged mechs never smoked).
// Vertex colour carries colour*alpha (premultiplied), vertex alpha carries
// the occlusion; the texture's falloff mask modulates both.
// BT_PFX_ADD=1 flips back to pure additive for A/B comparison.
static int s_pfxAdditive = -1;
if (s_pfxAdditive < 0)
{
const char *av = getenv("BT_PFX_ADD");
s_pfxAdditive = (av != 0 && av[0] == '1') ? 1 : 0;
}
dev->SetRenderState(D3DRS_LIGHTING, FALSE);
dev->SetRenderState(D3DRS_FOGENABLE, FALSE);
dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); // test Z, don't write it
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
dev->SetRenderState(D3DRS_DESTBLEND, s_pfxAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA);
dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// Particle sprite texture: the beams' authentic grit sheet MASKED by a
// radial falloff -> soft round fiery blobs (an unmasked square sheet on an
// additive quad reads as a hard BOX, which the 1995 sprites never did).
// Baked once from the decoded grit; pure radial gradient if grit is absent.
static LPDIRECT3DTEXTURE9 s_pfxTex = 0;
static int s_pfxTexTried = 0;
if (!s_pfxTexTried)
{
s_pfxTexTried = 1;
const int TW = 64, TH = 64;
LPDIRECT3DTEXTURE9 grit = BTGetBeamGritTexture();
unsigned char gritLum[128 * 64]; // grit is 128x64 when present
int gw = 0, gh = 0;
if (grit)
{
D3DSURFACE_DESC gd;
if (SUCCEEDED(grit->GetLevelDesc(0, &gd)) && gd.Width <= 128 && gd.Height <= 64)
{
D3DLOCKED_RECT glr;
if (SUCCEEDED(grit->LockRect(0, &glr, NULL, D3DLOCK_READONLY)))
{
gw = gd.Width; gh = gd.Height;
for (int y = 0; y < gh; ++y)
{
const DWORD *src = (const DWORD *)((const char *)glr.pBits + y * glr.Pitch);
for (int x = 0; x < gw; ++x)
gritLum[y * gw + x] = (unsigned char)(src[x] & 0xFF);
}
grit->UnlockRect(0);
}
}
}
if (SUCCEEDED(dev->CreateTexture(TW, TH, 1, 0, D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED, &s_pfxTex, NULL)))
{
D3DLOCKED_RECT lr;
if (SUCCEEDED(s_pfxTex->LockRect(0, &lr, NULL, 0)))
{
for (int y = 0; y < TH; ++y)
{
DWORD *dst = (DWORD *)((char *)lr.pBits + y * lr.Pitch);
for (int x = 0; x < TW; ++x)
{
float dx = (x + 0.5f) / TW * 2.0f - 1.0f;
float dy = (y + 0.5f) / TH * 2.0f - 1.0f;
float r = sqrtf(dx * dx + dy * dy);
float f = 1.0f - r; // radial falloff
if (f < 0.0f) f = 0.0f;
f = f * f * (3.0f - 2.0f * f); // smoothstep edge
float n = 1.0f;
if (gw > 0) // authentic grit detail
n = 0.35f + 0.65f * (gritLum[(y % gh) * gw + (x % gw)] / 255.0f);
int v = (int)(f * n * 255.0f);
if (v > 255) v = 255;
// the falloff mask lives in ALPHA too: the premultiplied
// draw (ONE, INVSRCALPHA) needs soft-edged OCCLUSION for
// smoke, not just soft-edged colour for fire
dst[x] = (v << 24) | (v << 16) | (v << 8) | v;
}
}
s_pfxTex->UnlockRect(0);
}
else { s_pfxTex->Release(); s_pfxTex = 0; }
}
}
dev->SetTexture(0, s_pfxTex);
dev->SetTextureStageState(0, D3DTSS_COLOROP, s_pfxTex ? D3DTOP_MODULATE : D3DTOP_SELECTARG1);
dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
dev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
// occlusion = vertex alpha x the texture's falloff mask (soft-edged smoke)
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, s_pfxTex ? D3DTOP_MODULATE : D3DTOP_SELECTARG1);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
D3DXMATRIX ident; D3DXMatrixIdentity(&ident);
dev->SetTransform(D3DTS_WORLD, &ident);
dev->SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
struct V { float x, y, z; DWORD c; float u, v; };
static std::vector<V> verts;
verts.clear();
// BACK-TO-FRONT order: premultiplied occlusion (smoke) composites in depth
// order; unsorted draws pop when a near puff renders before a far one.
static std::vector<size_t> order;
static std::vector<float> depth;
order.resize(gBTPfxParticles.size());
depth.resize(gBTPfxParticles.size());
for (size_t i = 0; i < gBTPfxParticles.size(); ++i)
{
const D3DXVECTOR3 &pp = gBTPfxParticles[i].pos;
order[i] = i;
depth[i] = pp.x * view->_13 + pp.y * view->_23 + pp.z * view->_33; // view-space z
}
std::sort(order.begin(), order.end(),
[](size_t a, size_t b) { return depth[a] > depth[b]; });
for (size_t oi = 0; oi < order.size(); ++oi)
{
const BTPfxParticle &p = gBTPfxParticles[order[oi]];
float t = p.age / p.life;
if (t < 0.0f) t = 0.0f; if (t > 1.0f) t = 1.0f;
// colour/alpha interpolation start->end, warped (t^(1/warp): a large
// warp shifts to the end colour early -- the fast orange->smoke shift)
float tc = powf(t, 1.0f / p.colorWarp);
float ta = powf(t, 1.0f / p.alphaWarp);
// two quads: outer glow (2.2x radius) then inner core
for (int layer = 0; layer < 2; ++layer)
{
const float *cs = layer ? p.sI : p.sO;
const float *ce = layer ? p.eI : p.eO;
float scale = layer ? 1.0f : 2.2f;
float r = cs[0] + (ce[0] - cs[0]) * tc;
float g = cs[1] + (ce[1] - cs[1]) * tc;
float b = cs[2] + (ce[2] - cs[2]) * tc;
float a = cs[3] + (ce[3] - cs[3]) * ta;
if (a <= 0.0f) continue;
if (a > 1.0f) a = 1.0f;
// PREMULTIPLIED: vertex rgb = colour x alpha (the framebuffer
// contribution), vertex a = alpha (the occlusion). A colour ramped
// NEGATIVE (DDTHSMK's smoke tail) clamps to 0 -> pure occluding
// black smoke; a bright fire colour with fading alpha dims out.
int ir = (int)(r * a * 255.0f); if (ir > 255) ir = 255; if (ir < 0) ir = 0;
int ig = (int)(g * a * 255.0f); if (ig > 255) ig = 255; if (ig < 0) ig = 0;
int ib = (int)(b * a * 255.0f); if (ib > 255) ib = 255; if (ib < 0) ib = 0;
int ia = (int)(a * 255.0f); if (ia > 255) ia = 255; if (ia < 0) ia = 0;
if (ia == 0 && ir == 0 && ig == 0 && ib == 0) continue;
DWORD c = ((DWORD)ia << 24) | (ir << 16) | (ig << 8) | ib;
float rad = p.rad * scale;
D3DXVECTOR3 rv = right * rad, uv = up * rad;
V q[6];
q[0].x = p.pos.x - rv.x - uv.x; q[0].y = p.pos.y - rv.y - uv.y; q[0].z = p.pos.z - rv.z - uv.z; q[0].u = 0; q[0].v = 1;
q[1].x = p.pos.x - rv.x + uv.x; q[1].y = p.pos.y - rv.y + uv.y; q[1].z = p.pos.z - rv.z + uv.z; q[1].u = 0; q[1].v = 0;
q[2].x = p.pos.x + rv.x + uv.x; q[2].y = p.pos.y + rv.y + uv.y; q[2].z = p.pos.z + rv.z + uv.z; q[2].u = 1; q[2].v = 0;
q[3] = q[0];
q[4] = q[2];
q[5].x = p.pos.x + rv.x - uv.x; q[5].y = p.pos.y + rv.y - uv.y; q[5].z = p.pos.z + rv.z - uv.z; q[5].u = 1; q[5].v = 1;
for (int k = 0; k < 6; ++k) { q[k].c = c; verts.push_back(q[k]); }
}
}
if (!verts.empty())
dev->DrawPrimitiveUP(D3DPT_TRIANGLELIST, (UINT)verts.size() / 3,
&verts[0], sizeof(V));
dev->SetTexture(0, NULL);
dev->SetRenderState(D3DRS_LIGHTING, sLight);
dev->SetRenderState(D3DRS_FOGENABLE, sFog);
dev->SetRenderState(D3DRS_ZWRITEENABLE, sZW);
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, sBlend);
dev->SetRenderState(D3DRS_SRCBLEND, sSrc);
dev->SetRenderState(D3DRS_DESTBLEND, sDst);
dev->SetRenderState(D3DRS_CULLMODE, sCull);
dev->SetTextureStageState(0, D3DTSS_COLOROP, sCop);
dev->SetTextureStageState(0, D3DTSS_COLORARG1, sCa1);
dev->SetTextureStageState(0, D3DTSS_COLORARG2, sCa2);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, sAop);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, sAa1);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, sAa2);
}
// BT (task #20): the live window client aspect (0 = never resized -> the
// projection builders fall back to the configured x_size/y_size). Set by
// L4NotifyWindowResized (WM_SIZE) so a user-resized window doesn't stretch
// the scene fat/skinny.
float gWindowAspect = 0.0f;
#define PILL_COUNT 20
#define PILL_SIZE (y_size*0.03125) // 32 @ 1280x1024
#define PILL_SPACING (PILL_SIZE*0.625) // 20 @ 1280x1024
#define LOAD_COLOR 0xFF6565FF
#define FADE_IN 0.05f
#define PILL_ON 0.05f
#define FADE_OUT 0.5f
#define LOAD_TEXT_HEIGHT PILL_SIZE // 32 @ 1280x1024
#define LOAD_TEXT_WIDTH (LOAD_TEXT_HEIGHT*12.125) // 388 @ 1280x1024
#define PS_OFF 0
#define PS_FADINGIN 1
#define PS_ON 2
#define PS_FADINGOUT 3
#define LOAD_TEXT_VERT (PILL_SIZE/2.0)
#ifdef LOGFRAMERATE
FILE *FRAMERATE_LOG;
#endif
//
// Includes for the DPL libraries
//
// RB 1/15/07
//#include <dpl\dpl.h>
//#include <dpl\dpl_2d.h>
//#include <dpl\dpl_vpx.h>
//#include <dpl\dplutils.h>
//#include <dpl\matrix.h>
//
// You can enable or disable these traces here for quicker rebuilding during
// profile testing, but for production they should be all turned off!
//
//#define TRACE_VIDEO_CULL_SETUP
//#define TRACE_VIDEO_VEHICLE_RENDERABLES
// #define TRACE_VIDEO_ALL_RENDERABLES
//
// These are other traces you can use to extract data
//
#if defined(TRACE_VIDEO_LOAD_OBJECT)
static BitTrace Video_Load_Object("Video Load Object");
#define SET_VIDEO_LOAD_OBJECT() Video_Load_Object.Set()
#define CLEAR_VIDEO_LOAD_OBJECT() Video_Load_Object.Clear()
#else
#define SET_VIDEO_LOAD_OBJECT()
#define CLEAR_VIDEO_LOAD_OBJECT()
#endif
#if defined(TRACE_VIDEO_CONSTRUCT_ROOT)
static BitTrace Video_Construct_Root("Video Construct Root");
#define SET_VIDEO_CONSTRUCT_ROOT() Video_Construct_Root.Set()
#define CLEAR_VIDEO_CONSTRUCT_ROOT() Video_Construct_Root.Clear()
#else
#define SET_VIDEO_CONSTRUCT_ROOT()
#define CLEAR_VIDEO_CONSTRUCT_ROOT()
#endif
//
// This is the time spent fetching and processing the pickpoint information
// from the DPL renderer.
//
#if defined(TRACE_VIDEO_PICKPOINT)
static BitTrace Video_Pickpoint("Video Pickpoint");
#define SET_VIDEO_PICKPOINT() Video_Pickpoint.Set()
#define CLEAR_VIDEO_PICKPOINT() Video_Pickpoint.Clear()
#else
#define SET_VIDEO_PICKPOINT()
#define CLEAR_VIDEO_PICKPOINT()
#endif
//
// Traces the time spent setting up the video culling parameters
//
#if defined(TRACE_VIDEO_CULL_SETUP)
static BitTrace Video_Cull_Setup("Video Cull Setup");
# define SET_VIDEO_CULL_SETUP() Video_Cull_Setup.Set()
# define CLEAR_VIDEO_CULL_SETUP() Video_Cull_Setup.Clear()
#else
# define SET_VIDEO_CULL_SETUP()
# define CLEAR_VIDEO_CULL_SETUP()
#endif
//
// This trace covers the entire time spent executing renderables as one
// transition. It goes up when the execute implimentation starts running
// over the all iterator and goes down when it's done.
//
#if defined(TRACE_VIDEO_RENDERABLES)
static BitTrace Video_Renderables("Video Renderables");
#define SET_VIDEO_RENDERABLES() Video_Renderables.Set()
#define CLEAR_VIDEO_RENDERABLES() Video_Renderables.Clear()
#else
#define SET_VIDEO_RENDERABLES()
#define CLEAR_VIDEO_RENDERABLES()
#endif
//
// Traces time spent executing renderables that belong to a vehicle entity
// (currently Mech, VTV or BTPlayer)
//
#if defined(TRACE_VIDEO_VEHICLE_RENDERABLES)
static BitTrace Video_Vehicle_Renderables("Video Vehicle Renderables");
# define SET_VIDEO_VEHICLE_RENDERABLES() Video_Vehicle_Renderables.Set()
# define CLEAR_VIDEO_VEHICLE_RENDERABLES() Video_Vehicle_Renderables.Clear()
#else
# define SET_VIDEO_VEHICLE_RENDERABLES()
# define CLEAR_VIDEO_VEHICLE_RENDERABLES()
#endif
//
// This trace will transition as each individual renderable is executed.
// It will buzz and cause serious impact to the accuracy of the trace but is
// good for identifying time-eating individual renderables
//
#if defined(TRACE_VIDEO_ALL_RENDERABLES)
static BitTrace Video_All_Renderables("Video All Renderables");
# define SET_VIDEO_ALL_RENDERABLES() Video_All_Renderables.Set()
# define CLEAR_VIDEO_ALL_RENDERABLES() Video_All_Renderables.Clear()
#else
# define SET_VIDEO_ALL_RENDERABLES()
# define CLEAR_VIDEO_ALL_RENDERABLES()
#endif
//
// This is the routine that does a DPL_FlushArticulations on the batch of
// DCS's that we've been holding during rendering, std::flushing them all to
// the card in a big batch
//
#if defined(TRACE_VIDEO_BATCH_FLUSH)
static BitTrace Video_Batch_Flush("Video Batch Flush");
# define SET_VIDEO_BATCH_FLUSH() Video_Batch_Flush.Set()
# define CLEAR_VIDEO_BATCH_FLUSH() Video_Batch_Flush.Clear()
#else
# define SET_VIDEO_BATCH_FLUSH()
# define CLEAR_VIDEO_BATCH_FLUSH()
#endif
//
// This trace shows the time spent doing the "frame done" processing where the
// renderer waits for the DPL card to become ready and then issues a command
// to start the scene drawing. An additional batch std::flush can happen here.
// If USE_ONE_VIDEO_TRACE is turned on, data from this trace will show up as
// part of the Video_Renderer trace.
//
#if defined(TRACE_VIDEO_RENDERER_FRAME_DONE)
# if defined(USE_ONE_VIDEO_TRACE)
# define SET_VIDEO_RENDERER_FRAME_DONE() Video_Renderer.Set()
# define CLEAR_VIDEO_RENDERER_FRAME_DONE() Video_Renderer.Clear()
# else
static BitTrace Video_Renderer_Frame_Done("Video Renderer Frame Done");
# define SET_VIDEO_RENDERER_FRAME_DONE() Video_Renderer_Frame_Done.Set()
# define CLEAR_VIDEO_RENDERER_FRAME_DONE() Video_Renderer_Frame_Done.Clear()
# endif
#else
# define SET_VIDEO_RENDERER_FRAME_DONE()
# define CLEAR_VIDEO_RENDERER_FRAME_DONE()
#endif
//
// The following traces are specifically designed for profileing the low level
// DPL routines in Division's libraries. You need to link to a special "profile"
// version of the division libraries for these to work. If you enable them
// with a production libdpl you will get NO traces. If you disable them and
// try to link to a profile version of libdpl you will get link errors. Sorry
// but this is the most efficient way of connecting the c++ trace routines to
// the C code of division's libraries.
//
// There are switches in the DPL libraries that handle turning these functions
// on and off in the profile version.
//
//#define TRACE_DPL_LOAD_OBJECT
//#define TRACE_DPL_TRANSPUTER_LINK // will buzz
//#define TRACE_DPL_DO_OUTSW // will buzz
//#define TRACE_DPL_OUTINT32 // will buzz
#if defined(TRACE_DPL_LOAD_OBJECT)
static BitTrace DPL_Load_Object("DPL Load Object");
extern "C"
{
void Set_DPL_Load_Object();
void Clear_DPL_Load_Object();
}
void
Set_DPL_Load_Object()
{
DPL_Load_Object.Set();
}
void
Clear_DPL_Load_Object()
{
DPL_Load_Object.Clear();
}
#endif
#if defined(TRACE_DPL_TRANSPUTER_LINK)
static BitTrace DPL_Transputer_Link("DPL Transputer Link");
extern "C"
{
void Set_DPL_Transputer_Link();
void Clear_DPL_Transputer_Link();
}
void
Set_DPL_Transputer_Link()
{
DPL_Transputer_Link.Set();
}
void
Clear_DPL_Transputer_Link()
{
DPL_Transputer_Link.Clear();
}
#endif
#if defined(TRACE_DPL_DO_OUTSW)
static BitTrace DPL_do_outsw("DPL do_outsw");
extern "C"
{
void Set_DPL_do_outsw();
void Clear_DPL_do_outsw();
}
void
Set_DPL_do_outsw()
{
DPL_do_outsw.Set();
}
void
Clear_DPL_do_outsw()
{
DPL_do_outsw.Clear();
}
#endif
#if defined(TRACE_DPL_OUTINT32)
static BitTrace DPL_outint32("DPL outint32");
extern "C"
{
void Set_DPL_outint32();
void Clear_DPL_outint32();
}
void
Set_DPL_outint32()
{
DPL_outint32.Set();
}
void
Clear_DPL_outint32()
{
DPL_outint32.Clear();
}
#endif
//
// The following trace will probably be obsolete soon
//
#if 0
#if defined(TRACE_VIDEO_FIRST_FRAME_DONE)
static BitTrace Video_First_Frame_Done("Video First Frame Done");
# define SET_VIDEO_FIRST_FRAME_DONE() Video_First_Frame_Done.Set()
# define CLEAR_VIDEO_FIRST_FRAME_DONE() Video_First_Frame_Done.Clear()
#else
# define SET_VIDEO_FIRST_FRAME_DONE()
# define CLEAR_VIDEO_FIRST_FRAME_DONE()
#endif
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Definitions of various debugging switches
//
#define DEBUG_SPECIAL_CALLBACK 0 // 0 = none, 1 = print special, 2 = real noisy
#define DEBUG_CREATE_CALLBACK 0 // 0 = none,
#define PRINT_THE_PFX False // Set to true and the pfx definition will print when it is read in.
#define RAPID_SECT_PIXEL True // True if you want to use the rapidsectpixel call for intersections
#define PARTICLE_TEST False // True to issue a stream of particles at the detected intersect point
#define PRINT_PICKPOINT_TEST False // True to print data whenever the intersect changes
#define NOISY_RENDERER False // True enables a lot of printouts as the renderer makes/kills object
#define USE_TRACKER_STRUCTURE False // True to enable the full tracker structure that holds damage zone names & such
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Definitions of system constants used only in this file
//
#define MAX_SPECIAL_ARGUMENTS 25 // Maximum number of arguments a "special" callback can handle
#define MAX_SPECIAL_SIZE 512 // Maximum size (characters) of a "special"
#define dpl_arg_sep '~' // seperator used when parsing dpl argument string
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity
*Entity_Being_Created = 0; // !!! temp, till callback handlers become a class
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// These extern "c" routines and constants are from startdpl or from dpl
// itself and need to be here.
//
extern "C" {
//STUBBED: DPL RB 1/15/07
//extern const int
// x_size, // x size of the screen (from startdpl)
// y_size; // y size of the screen (startdpl)
void
dpl_TexmapTexels2D ( // A routine from DPL we normally couldn't access
dpl_TEXMAP *tm,
uint32 *texels,
int32 u_size,
int32 v_size,
int32 bytes_per_texel);
int
screen_resolution( // Sets up the screen resolution (startdpl)
char *dpl_argv ),
explode_args( // Parses DPLARG system environmental (startdpl)
char **argv,
char *dpl_args,
char sep );
char
*dpl_TypeToString( // Converts a DPL type code into a string (libdpl)
dpl_TYPE t);
int32
dpl_DrawSceneComplete(void); // missing definition (libdpl)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// see DPLReportPerfStats()
//STUBBED: VIDEO RB 1/15/07
//extern "C" const int32 __sect_time;
//extern "C" const int32 __last_cull_time;
//extern "C" const int32 __last_draw_time;
//extern "C" const int32 __last_frame_time;
//extern "C" const int32 __last_pxpl_time;
//extern "C" const int32 __last_frame_prims;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dump_frame_buffer() std::declaration
void dump_frame_buffer(
dpl_VIEW *eye,
int32 x_size,
int32 y_size,
Logical antialias);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The following are definitions of DPL callback functions. These are called
// by the dpl file loader as it is loading graphics files so we can do material
// substitution and set special attributes on geometry & such.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The "special" callback is called whenever a "special" field is processed in
// incomming geometry.
// dPL special callback function type.
// typedef void (dpl_SPECIAL_CALLBACK)(dpl_TYPE type, void *handle,
// char8 *special, uint32 special_len);
static void
TestSpecialCallBack(
dpl_TYPE type,
void *handle,
char8 *special,
uint32 special_len)
{
//STUBBED: DPL RB 1/14/07
// char
// *args[MAX_SPECIAL_ARGUMENTS],
// **argptr,
// *argstring;
// int
// argcount;
// float
// u0,
// v0,
// du,
// dv;
// int32
// immunity;
// char
//// NameBuff[80],
// TempBuff[MAX_SPECIAL_SIZE + 1];
// #if USE_TRACKER_STRUCTURE
// dpl_tracker
// *this_tracker;
// #endif
//
// if(special_len > MAX_SPECIAL_SIZE)
// {
// DEBUG_STREAM<<"SPECIAL was bigger than "<<MAX_SPECIAL_SIZE<<"\n" << std::flush;
// Verify(special_len > MAX_SPECIAL_SIZE);
// }
// strncpy(TempBuff, (const char*)special, special_len);
// TempBuff[special_len] = 0;
// if(TempBuff[special_len-1] != 0)
// {
// DEBUG_STREAM<<"Caution, the following SPECIAL was not null terminated\n" << std::flush;
// printf("SPECIAL->%08x: %s, %s\n", handle, dpl_TypeToString(type), TempBuff);
// }
// #if DEBUG_SPECIAL_CALLBACK >= 1
// printf("SPECIAL->%08x: %s, %s\n", handle, dpl_TypeToString(type), TempBuff);
// #endif
////
//// Scan all the argument start points into an array for easier processing.
//// also avoids non-reentrand problem with strtok
////
// argstring = TempBuff;
// for(argcount = 0; argcount<MAX_SPECIAL_ARGUMENTS; argcount++)
// {
// args[argcount] = strtok(argstring," ");
// if(!args[argcount])
// break;
// argstring = NULL;
// }
// args[MAX_SPECIAL_ARGUMENTS-1] = 0;
////
//// Decode the dpl_type the special was found in, all I know of are listed here
//// though some we may never see.
////
// switch(type)
// {
// case dpl_type_error:
// break;
// case dpl_type_scene:
// break;
// case dpl_type_zones:
// break;
// case dpl_type_view:
// break;
// case dpl_type_instance:
// break;
// case dpl_type_dcs:
// break;
// case dpl_type_light:
// break;
// case dpl_type_object:
// for(argptr = args;
// *argptr;
// argptr++)
// {
// if (strcmp(*argptr, "ADDITIVE_LODS") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Calling SetObjectAdditiveLODs on an object\n");
// #endif
// // REMOVE dpl_SetObjectAdditiveLODs to run with older renderers
// dpl_SetObjectAdditiveLODs ( (dpl_OBJECT *)handle);
// }
// }
// break;
// case dpl_type_lod:
// break;
// case dpl_type_geogroup:
// #if USE_TRACKER_STRUCTURE
// this_tracker = (dpl_tracker *)dpl_GetAppSpecific(handle);
// #endif
// for(argptr = args;
// *argptr;
// argptr++)
// {
// if (strncmp(*argptr, "dz_", 3) == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP Damage zone ");
// #endif
//
// #if USE_TRACKER_STRUCTURE
// if (this_tracker)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("marked\n");
// #endif
// strncpy(this_tracker->dz_name, *argptr, MAX_DZ_NAME_LENGTH);
// this_tracker->dz_name[MAX_DZ_NAME_LENGTH - 1] = 0;
// #endif
// //---------------------------------------------
// // lookup damage zone index in global namelist
// //---------------------------------------------
// if (Entity_Being_Created->damageZones)
// {
// Check_Pointer(Entity_Being_Created->damageZones);
//
// int
// damage_zone_index;
//
// damage_zone_index =
// Entity_Being_Created->GetDamageZoneIndex(*argptr);
//
// if (damage_zone_index != -1)
// {
// #if USE_TRACKER_STRUCTURE
// this_tracker->Damage_Zone_Number = damage_zone_index;
// #else
// if(dpl_GetAppSpecific(handle))
// Fail("AppSpecific already set\n");
// dpl_PutAppSpecific(handle, (void *)(damage_zone_index+1));
// #endif
// }
// else
// {
// DEBUG_STREAM << std::endl << "Damage zone '" <<
// *argptr << "' not in table." << std::endl;
// }
// }
// #if USE_TRACKER_STRUCTURE
// }
// else
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("NOT marked\n");
// #endif
// }
// #endif
// }
// else if(strcmp(*argptr,"PUNCH") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP Punchize\n");
// #endif
// dpl_Punchize((dpl_GEOGROUP *)handle);
// }
// else if(strcmp(*argptr,"DAMAGE") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP damagize\n");
// #endif
// if(*(++argptr))
// {
// int
// status;
// dpl_MATERIAL *damagize_material = dpl_LookupMaterial ( *argptr, dpl_lookup_normal, &status );
// if(damagize_material)
// {
// dpl_Damagize((dpl_GEOGROUP *)handle, damagize_material);
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL GEOGROUP DAMAGE couldn't find material "<<*argptr<<"\n" << std::flush;
// Verify(damagize_material);
// }
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL GEOGROUP DAMAGE no material name \n" << std::flush;
// Verify(*argptr);
// }
// }
// else if(strcmp(*argptr,"WIREFRAME") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP WIREFRAME\n");
// #endif
// dpl_SetGeogroupWireframe((dpl_GEOGROUP *)handle, True);
// }
// else if(strcmp(*argptr,"GEOMETRIZE") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP geometrize\n");
// #endif
// if(*(++argptr))
// {
// dpl_GEOMETRY
// *geometry_ptr;
// long
// geometrize_code;
// int
// geometry_id = 0;
// sscanf(
// *argptr,
// "%lx",
// &geometrize_code);
// do
// {
// geometry_ptr = dpl_GetGeogroupGeometry((dpl_GEOGROUP *)handle,geometry_id);
// if(geometry_ptr)
// {
// dpl_Geometrize(geometry_ptr,geometrize_code);
// }
// geometry_id++;
// } while(geometry_ptr);
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL GEOGROUP GEOMETRIZE has no geometrize code\n" << std::flush;
// Verify(*argptr);
// }
// }
// else if(strcmp(*argptr,"DRAWLAST") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL GEOGROUP DRAWLAST\n");
// #endif
// dpl_SetGeogroupDrawLast((dpl_GEOGROUP *)handle, True);
// dpl_FlushGeogroup((dpl_GEOGROUP *)handle);
// }
// else if(strcmp(*argptr,"BLINK") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("SPECIAL GEOGROUP BLINK is not supported\n");
// #endif
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL GEOGROUP "<<*argptr<<" is not a geogroup modifier\n" << std::flush;
// }
// }
// break;
// case dpl_type_geometry:
// break;
// case dpl_type_material:
// for(argptr = args;
// *argptr;
// argptr++)
// {
// if(strcmp(*argptr,"IMMUNE") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL MATERIAL IMMUNE\n");
// #endif
// if(*(++argptr))
// {
// immunity = atol(*argptr);
// dpl_SetMaterialFogImmunity ( (dpl_MATERIAL *)handle, immunity );
// dpl_FlushMaterial((dpl_MATERIAL *)handle);
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL MATERIAL IMMUNE has no immune code\n" << std::flush;
// Verify(*argptr);
// }
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL MATERIAL "<<*argptr<<" is not a material modifier\n" << std::flush;
// }
// }
// break;
// case dpl_type_texture:
// for(argptr = args;
// *argptr;
// argptr++)
// {
// if(strcmp(*argptr,"SCROLL") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SCROLL\n");
// #endif
// u0 = v0 = du = dv = 0.0;
// if(*(++argptr))
// u0 = atof(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing u0\n" << std::flush;
// Verify(*argptr);
// }
// if(*(++argptr))
// v0 = atof(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing v0\n" << std::flush;
// Verify(*argptr);
// }
// if(*(++argptr))
// du = atof(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing du\n" << std::flush;
// Verify(*argptr);
// }
// if(*(++argptr))
// dv = atof(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TECTURE SCROLL missing dv\n" << std::flush;
// Verify(*argptr);
// }
// dpl_SetTextureScroll (
// (dpl_TEXTURE *)handle,
// u0,
// v0,
// du,
// dv);
// dpl_FlushTexture((dpl_TEXTURE *)handle);
// }
// else if(strcmp(*argptr,"FAKESIZE") == 0)
// {
// #if DEBUG_SPECIAL_CALLBACK >= 2
// Tell("Doing SPECIAL TEXURE FAKESIZE\n");
// #endif
// int
// size, offset;
// if(*(++argptr))
// size = atoi(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TEXTURE FAKESIZE missing size\n" << std::flush;
// Verify(*argptr);
// }
// if(*(++argptr))
// offset = atoi(*argptr);
// else
// {
// DEBUG_STREAM<<"SPECIAL TEXTURE FAKESIZE missing offset\n" << std::flush;
// Verify(*argptr);
// }
// dpl_FakeTextureSize((dpl_TEXTURE *)handle,size,offset);
// dpl_FlushTexture((dpl_TEXTURE *)handle);
// }
// else
// {
// DEBUG_STREAM<<"SPECIAL TEXTURE "<<*argptr<<" is not a texture modifier\n" << std::flush;
// }
// }
// break;
// case dpl_type_texmap:
// break;
// case dpl_type_ramp:
// break;
// }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The TestCreateCallBack callback is called whenever a dpl node of the indicated
// type is created while loading a DPL graphics file.
/* dPL create/delete callback function type. */
//typedef void (dpl_CREATE_DELETE_CALLBACK)(dpl_TYPE type, void *handle);
static void
TestCreateCallBack(
dpl_TYPE type,
#if DEBUG_CREATE_CALLBACK>=1
void *handle
#else
void *
#endif
)
{
//
// get a pointer to the node structure (first part of most dpl types)
//
#if DEBUG_CREATE_CALLBACK >=1
printf("CREATE->%08x: %s\n", handle, dpl_TypeToString(type));
#endif
switch(type)
{
case dpl_type_error:
break;
case dpl_type_scene:
break;
case dpl_type_zones:
break;
case dpl_type_view:
break;
case dpl_type_instance:
break;
case dpl_type_dcs:
break;
case dpl_type_light:
break;
case dpl_type_object:
break;
case dpl_type_lod:
break;
case dpl_type_geogroup:
#if USE_TRACKER_STRUCTURE
{
dpl_tracker *this_tracker = new dpl_tracker;
this_tracker->This_Entity = Entity_Being_Created;
this_tracker->dz_name[0] = 0;
this_tracker->Damage_Zone_Number = -1; // no damage zone (default)
if(dpl_GetAppSpecific(handle))
DEBUG_STREAM<<"app_specific hook already set!\n" << std::flush;
dpl_PutAppSpecific(handle, this_tracker);
break;
}
#endif
case dpl_type_geometry:
break;
case dpl_type_material:
break;
case dpl_type_texture:
break;
case dpl_type_texmap:
break;
case dpl_type_ramp:
break;
}
}
//#############################################################################
// Code to setup and handle material substitutions.
//#############################################################################
//
NameList
*materialSubstitutionList = NULL;
const char *opMaterialName(const char *fileName, int opId)
{
hash_map<string, hash_map<int, string>>::const_iterator fileIter = gOpNames->find(string(fileName));
if (fileIter != gOpNames->end())
{
hash_map<int, string>::const_iterator matIter = (*fileIter).second.find(opId);
if (matIter != (*fileIter).second.end())
{
return (*matIter).second.c_str();
}
}
return NULL;
}
void loadTables()
{
gOpNames = new hash_map<string, hash_map<int, string>>();
gReplacementData = new hash_map<string, ReplacementMaterialData>();
FILE * opNames = fopen("VIDEO\\REPLACEMATS.tbl", "rb");
size_t numMats;
fread(&numMats, sizeof(size_t), 1, opNames);
for (int i = 0; i < (int)numMats; i++)
{
int opNum, fileNameLen, matNameLen;
fread(&opNum, sizeof(int), 1, opNames);
fread(&fileNameLen, sizeof(int), 1, opNames);
char *fileName = new char[fileNameLen];
fread(fileName, sizeof(char), fileNameLen, opNames);
fread(&matNameLen, sizeof(int), 1, opNames);
char *matName = new char[matNameLen];
fread(matName, sizeof(char), matNameLen, opNames);
hash_map<string, hash_map<int, string>>::const_iterator fileIter = gOpNames->find(string(fileName));
if (fileIter != gOpNames->end())
{
hash_map<int, string>::const_iterator matIter = (*fileIter).second.find(opNum);
if (matIter != (*fileIter).second.end())
{
(*gOpNames)[string(fileName)][opNum] = string(matName);
} else
{
(*gOpNames)[string(fileName)].insert(pair<int, string>(opNum, string(matName)));
}
} else
{
hash_map<int, string> fileMap;
fileMap.insert(pair<int, string>(opNum, string(matName)));
gOpNames->insert(pair<string, hash_map<int, string>>(string(fileName), fileMap));
}
delete [] fileName;
delete [] matName;
}
fclose(opNames);
FILE *replacementData = fopen("VIDEO\\MATREPLACETABLE.tbl", "rb");
fread(&numMats, sizeof(size_t), 1, replacementData);
for (int i = 0; i < (int)numMats; i++)
{
int matNameLen, texNameLen;
fread(&matNameLen, sizeof(int), 1, replacementData);
char *matName = new char[matNameLen];
fread(matName, sizeof(char), matNameLen, replacementData);
fread(&texNameLen, sizeof(int), 1, replacementData);
char *texName = new char[texNameLen];
fread(texName, sizeof(char), texNameLen, replacementData);
ReplacementMaterialData data;
data.texName = string(texName);
fread(&data, sizeof(float), 3, replacementData);
gReplacementData->insert(pair<string, ReplacementMaterialData>(string(matName), data));
delete [] matName;
delete [] texName;
}
fclose(replacementData);
}
/* dPL material name callback function type. */
//typedef char8 *(dpl_MATERIAL_NAME_CALLBACK)(char8 *mat_name);
char*
substituteMaterial(
char *source
)
{
static char
buffer[MATERIAL_NAME_BUFFER_LENGTH];
NameList::Entry
*entry;
const char
*search,
*replace,
*pc;
int
len;
//----------------------------------------------
// perform text substitution
// first match in the sub list gets substituted
// materialSubstitutionList is a pre-prepared global namelist.
//----------------------------------------------
if (materialSubstitutionList == NULL)
{
return source;
}
entry = materialSubstitutionList->GetFirstEntry();
while (entry)
{
search = entry->GetName();
if (search && *search && (pc = strstr((char*)source, search)) != NULL)
{
replace = entry->GetChar();
*buffer = '\0';
len = (char*)pc - source;
while (*replace == '<')
{
++replace;
--len;
}
if (len > 0)
{
strncat(buffer, (const char*)source, len);
}
pc += strlen(search);
len = strlen(replace);
while (len && *(replace+len-1) == '>')
{
--len;
if (*pc) { ++pc; }
}
if (len > 0)
{
strncat(buffer, replace, len);
}
Str_Cat(buffer, pc, MATERIAL_NAME_BUFFER_LENGTH);
delete [] source;
source = new char[strlen(buffer) + 1];
Str_Copy((char*)source, buffer, MATERIAL_NAME_BUFFER_LENGTH);
break;
}
entry = entry->GetNextEntry();
}
return source;
}
vector<MONITORINFO> DPLRenderer::MonitorsCreateAll(int &monitorCount)
{
monitorCount = gD3D->GetAdapterCount();
vector<MONITORINFO> allMonitors(monitorCount);
for(UINT i = 0; i < monitorCount; i++)
{
HMONITOR monitorHandle = gD3D->GetAdapterMonitor(i);
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(monitorHandle, &info);
allMonitors[i] = info;
}
return allMonitors;
}
void DPLRenderer::SetCoreRenderStates()
{
mDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_USEW);
float size = 1.0f;
mDevice->SetRenderState(D3DRS_POINTSIZE, *(DWORD*)(&size));
mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
D3DXMATRIX view_matrix, proj_matrix;
D3DXMatrixIdentity(&view_matrix);
D3DXMatrixOrthoLH(&proj_matrix, x_size, y_size, 1.0f, 1000.0f);
mDevice->SetTransform(D3DTS_VIEW, &view_matrix);
mDevice->SetTransform(D3DTS_PROJECTION, &proj_matrix);
mDevice->SetRenderState(D3DRS_LIGHTING, false);
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
mDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
mDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
mDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
mDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
mDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
mDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
}
//-----------------------------------------------------------------------------
//--------------------------DPL video resource object--------------------------
//-----------------------------------------------------------------------------
//#############################################################################
//############################ L4VideoObject ############################
//#############################################################################
L4VideoObject::L4VideoObject(
const char *filename,
ResourceType resource_type,
Enumeration renderer_modes, // RendererModes
float blink_period,
float percent_time_on
)
{
Check_Pointer(filename);
// BT audit (task #20): count every loaded video object (map pieces + props +
// mech parts) so the runtime total can be compared against the map source's
// instance list. 1-per-10 logging keeps the volume sane.
{
static int s_vidObjCount = 0;
++s_vidObjCount;
if ((s_vidObjCount % 10) == 0 || s_vidObjCount < 5)
DEBUG_STREAM << "[vidobj] " << s_vidObjCount << " loaded (latest: "
<< filename << ")" << "\n" << std::flush;
}
Str_Copy(objectFilename, filename, sizeof(objectFilename));
//---------------------------------------------------------------
// pad objectFilename with nulls so all .res files are identical
//---------------------------------------------------------------
char
*p = objectFilename + strlen(filename) + 1,
*c = objectFilename + sizeof(objectFilename);
for (; p < c; ++p)
{ *p = '\0'; }
resourceType = resource_type;
rendererModes = renderer_modes;
blinkPeriod = blink_period;
percentTimeOn = percent_time_on;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4VideoObject::~L4VideoObject()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4VideoObject::TestInstance() const
{
Verify( strlen(objectFilename) < sizeof(objectFilename) );
return True;
}
//#############################################################################
//######################## L4VideoObjectWrapper #########################
//#############################################################################
L4VideoObjectWrapper::L4VideoObjectWrapper(
L4VideoObject *video_object,
Logical delete_object
)
{
Check_Pointer(video_object); // do not use Check()
videoObject = video_object;
deleteObject = delete_object;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
L4VideoObjectWrapper::~L4VideoObjectWrapper()
{
if (deleteObject)
{
Unregister_Pointer(videoObject);
delete videoObject;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
L4VideoObjectWrapper::TestInstance() const
{
if (videoObject)
{
Check_Pointer(videoObject); // do not use Check()
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// static
int L4VideoObjectWrapper::BuildVideoObjectChainFromResource(ChainOf<L4VideoObjectWrapper*> *video_chain, ResourceDescription *video_resource)
{
//do not Check(this) - static
Check(video_chain);
Check(video_resource);
const char *video_pointer;
int object_count, index;
long object_size;
L4VideoObject *video_object;
L4VideoObjectWrapper *video_wrapper;
//----------------------------------------------------
// convert video resource into chain of video objects
//----------------------------------------------------
video_pointer = (char *)video_resource->resourceAddress;
Check_Pointer(video_pointer);
object_count = *((int *)video_pointer);
video_pointer += sizeof(int);
object_size = sizeof(L4VideoObject);
Verify(video_resource->resourceSize == sizeof(int) + object_count * object_size);
//Tell("video chain: ("<<object_count<<")");
for (index = 0; index < object_count; ++index)
{
video_object = (L4VideoObject *)video_pointer;
//do not register (done through resource)
video_pointer += object_size;
//Tell(" '"<<video_object->GetObjectFilename()<<"' 0x"<<std::hex<<video_object->GetRendererModes());
video_wrapper = new L4VideoObjectWrapper(video_object, False);
Register_Object(video_wrapper);
video_chain->Add(video_wrapper);
}
//Tell(std::dec<<"\n");
return object_count;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// static
void
L4VideoObjectWrapper::DeleteVideoObjectChain(
ChainOf<L4VideoObjectWrapper*> *video_chain
)
{
//do not Check(this) - static
Check(video_chain);
ChainIteratorOf<L4VideoObjectWrapper*>
video_iterator(video_chain);
L4VideoObjectWrapper
*video_wrapper;
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
Check(video_wrapper);
Unregister_Object(video_wrapper);
delete video_wrapper;
}
return;
}
void DPLRenderer::FindBestAdapterIndices(bool isWindowed)
{
bool spanDisable = false;
bool *spanDisablePtr = NULL;
//Get all available monitor indices & stats
int monitorCount = 0;
std::vector<MONITORINFO> monitors = this->MonitorsCreateAll(monitorCount);
int monitorReserved = 0x10000000;
//Test for primary gauge override
char* gaugeAdapterString = getenv("PRIMGAUGE");
if (gaugeAdapterString != NULL)
{
mPrimaryIndex = new int;
*mPrimaryIndex = atoi(gaugeAdapterString);
DEBUG_STREAM << "Primary gauge override- adapter " << *mPrimaryIndex << std::endl;
}
//Test for secondary gauge override
gaugeAdapterString = getenv("SECGAUGE");
if (gaugeAdapterString != NULL)
{
mSecondaryIndex = new int;
*mSecondaryIndex = atoi(gaugeAdapterString);
DEBUG_STREAM << "Secondary gauge override- adapter " << *mSecondaryIndex << std::endl;
}
//Only do gauge overrides if we're using gauges during this execution
if (!Application::DoSuppressGauges())
{
//Test for gauge1 override
gaugeAdapterString = getenv("MFDGAUGE");
if (gaugeAdapterString != NULL)
{
mAux1Index = new int;
*mAux1Index = atoi(gaugeAdapterString);
DEBUG_STREAM << "MFD Gauge Override- adapter " << *mAux1Index << std::endl;
}
//Test for span disable override
spanDisablePtr = NULL;
gaugeAdapterString = getenv("SPANDISABLE");
if (gaugeAdapterString != NULL)
{
spanDisable = atoi(gaugeAdapterString);
spanDisablePtr = &spanDisable;
DEBUG_STREAM << "Spanning Override: Span ";
if (*spanDisablePtr)
{
DEBUG_STREAM << "Disabled";
} else
{
DEBUG_STREAM << "Enabled";
}
DEBUG_STREAM << std::endl;
}
if (spanDisablePtr == NULL || *spanDisablePtr)
{
//Test for gauge2 override
gaugeAdapterString = getenv("MFDGAUGE2");
if (gaugeAdapterString != NULL)
{
mAux2Index = new int;
*mAux2Index = atoi(gaugeAdapterString);
DEBUG_STREAM << "MFD Gauge #2 Override- adapter " << *mAux2Index << std::endl;
}
}
//Disable spanning if they overrode the 2nd gauge
if (spanDisablePtr == NULL && mAux2Index != NULL)
{
DEBUG_STREAM << "MFD Gauge #2 was overridden... forcing spanning disabled." << std::endl;
spanDisable = true;
spanDisablePtr = &spanDisable;
}
}
//Remove all monitors explicitly assigned somewhere from the list of available monitors
if (mPrimaryIndex != NULL && (*mPrimaryIndex) >= 0 && (*mPrimaryIndex) < monitorCount)
{
monitors[*mPrimaryIndex].dwFlags |= monitorReserved;
}
if (mSecondaryIndex != NULL && (*mSecondaryIndex) >= 0 && (*mSecondaryIndex) < monitorCount)
{
monitors[*mSecondaryIndex].dwFlags |= monitorReserved;
}
if (mAux1Index != NULL && (*mAux1Index) >= 0 && (*mAux1Index) < monitorCount)
{
monitors[*mAux1Index].dwFlags |= monitorReserved;
}
if (mAux2Index != NULL && (*mAux2Index) >= 0 && (*mAux2Index) < monitorCount)
{
monitors[*mAux2Index].dwFlags |= monitorReserved;
}
if (mPrimaryIndex == NULL)
{
DEBUG_STREAM << "Trying to find the monitor marked as active in Windows..." << std::endl;
//Set up the monitor so marked as the primary (unless none are marked)
for (int i = 0; i < monitorCount; i++)
{
int flags = monitors[i].dwFlags;
if ((flags & (MONITORINFOF_PRIMARY | monitorReserved)) == MONITORINFOF_PRIMARY)
{
DEBUG_STREAM << "Monitor " << i << " was set as active in Windows... setting it as the primary monitor." << endl;
mPrimaryIndex = new int;
*mPrimaryIndex = i;
monitors[i].dwFlags |= monitorReserved;
break;
}
}
}
//Do gauges next, since they're the easiest to pick out
if (!Application::DoSuppressGauges() && !isWindowed && monitorCount > 2)
{
DEBUG_STREAM << "Gauges are on, we are not in windowed mode, and we have " << monitorCount << " available monitors, so will try to find MFDs..." << std::endl;
if (mAux1Index == NULL && (spanDisablePtr == NULL || !(*spanDisablePtr)))
{
DEBUG_STREAM << "Don't have a MFD #1 monitor yet, and spanning is not explicitly disabled, so will try to find a really wide monitor for both MFDs..." << std::endl;
//try to find a really wide monitor to use as gauge1
for (int i = 0; i < monitorCount; i++)
{
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
RECT size = monitors[i].rcMonitor;
float aspect = ( (double)(size.right - size.left) / (double)(size.bottom - size.top));
if (aspect > 2.5f)
{
//Really wide! Probably a spanning monitor
DEBUG_STREAM << "Monitor " << i << " is really wide, so will try to use it as the MFDs." << std::endl;
mAux1Index = new int;
*mAux1Index = i;
monitors[i].dwFlags |= monitorReserved;
if (spanDisablePtr == NULL)
{
DEBUG_STREAM << "Explicitly setting spanning to enabled..." << std::endl;
spanDisable = FALSE;
spanDisablePtr = &(spanDisable);
}
break;
}
}
}
}
int possibleAux2Index = -1;
if (mAux1Index == NULL && (spanDisablePtr == NULL || *spanDisablePtr))
{
DEBUG_STREAM << "MFDs not found yet, and spanning isn't explicitly enabled. Will try to find two same-size monitors to use as MFDs..." << std::endl;
map<pair<int,int>,vector<int>> sameSizeMonitors;
//Group the unclaimed monitors by resolution
for (int i = 0; i < monitorCount; i++)
{
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
int width = monitors[i].rcMonitor.right - monitors[i].rcMonitor.left;
int height = monitors[i].rcMonitor.bottom - monitors[i].rcMonitor.top;
pair<int, int> resolution(width, height);
if (sameSizeMonitors.find(resolution) == sameSizeMonitors.end())
{
sameSizeMonitors[resolution] = vector<int>();
}
sameSizeMonitors[resolution].insert(sameSizeMonitors[resolution].begin(), i);
}
}
//Retrieve the bucket of monitors with at least two monitors and the smallest resolution (y res counts more than x res)
vector<int> bestMonitors;
int smallestYRes = -1;
int bestXRes = -1;
for (map<pair<int,int>,vector<int>>::iterator it = sameSizeMonitors.begin(); it != sameSizeMonitors.end(); it++)
{
if ((*it).second.size() >= 2)
{
int yRes = (*it).first.second;
int xRes = (*it).first.first;
if (smallestYRes >= 0)
{
DEBUG_STREAM << "Multiple sets of identically-sized monitors are available. Will use smallest set..." << std::endl;
}
if (smallestYRes < 0 || yRes < smallestYRes || (yRes == smallestYRes && xRes < bestXRes))
{
smallestYRes = yRes;
bestXRes = xRes;
bestMonitors = (*it).second;
}
}
}
int aux1Candidate = -1;
int aux2Candidate = -1;
int aux1Right = 0;
int aux2Right = 0;
if (bestMonitors.size() > 2)
{
DEBUG_STREAM << "More than 2 monitors in the set of smallest, identically-sized monitors... Will use the furthest right as right MFD, one just to the left of that as left MFD..." << std::endl;
}
//The second aux screen is the one furthest to the right
//the first aux screen is the one just to the left of that one
for (vector<int>::iterator it = bestMonitors.begin(); it != bestMonitors.end(); it++)
{
int i = (*it);
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
int right = monitors[i].rcMonitor.right;
if (aux2Candidate < 0 || aux2Right < right)
{
aux1Candidate = aux2Candidate;
aux1Right = aux2Right;
aux2Candidate = i;
aux2Right = right;
} else if (aux1Candidate < 0 || aux1Right < right)
{
aux1Candidate = i;
aux1Right = right;
}
}
}
if (aux1Candidate >= 0 && aux2Candidate >= 0)
{
DEBUG_STREAM << "Got two decent same-size monitors, will use as MFDs... Left is " << aux1Candidate << " and right is " << aux2Candidate << "." << std::endl;
mAux1Index = new int;
*mAux1Index = aux1Candidate;
monitors[aux1Candidate].dwFlags |= monitorReserved;
mAux2Index = new int;
*mAux2Index = aux2Candidate;
monitors[aux2Candidate].dwFlags |= monitorReserved;
spanDisable = true;
spanDisablePtr = &spanDisable;
} else
{
DEBUG_STREAM << "Could not find two identical monitors to use for MFDs." << std::endl;
}
}
if (mAux2Index == NULL && mAux1Index != NULL && (spanDisablePtr == NULL || *spanDisablePtr))
{
DEBUG_STREAM << "We have a left MFD and no right MFD, and spanning is not explicitly enabled. Will try to find identically-sized right MFD." << std::endl;
//find a monitor that is identical to the first gauge monitor
//if multiple, use the furthest to the right
int bestMonitorIndex = -1;
int bestMonitorRight = 0;
int aux1Width = (monitors[*mAux1Index].rcMonitor.right - monitors[*mAux1Index].rcMonitor.left);
int aux1Height = (monitors[*mAux1Index].rcMonitor.bottom - monitors[*mAux1Index].rcMonitor.top);
for (int i = 0; i < monitorCount; i++)
{
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
int monitorWidth = (monitors[i].rcMonitor.right - monitors[i].rcMonitor.left);
int monitorHeight = (monitors[i].rcMonitor.bottom - monitors[i].rcMonitor.top);
if (monitorWidth == aux1Width && monitorHeight == aux1Height)
{
if (bestMonitorIndex >= 0)
{
DEBUG_STREAM << "Found more than one monitor identically sized to the left MFD. Will use furthest-right monitor." << std::endl;
}
if (bestMonitorIndex < 0 || bestMonitorRight < monitors[i].rcMonitor.right)
{
bestMonitorIndex = i;
bestMonitorRight = monitors[i].rcMonitor.right;
}
}
}
}
if (bestMonitorIndex >= 0)
{
DEBUG_STREAM << "Using monitor " << bestMonitorIndex << "as right MFD monitor." << std::endl;
mAux2Index = new int;
*mAux2Index = bestMonitorIndex;
monitors[bestMonitorIndex].dwFlags |= monitorReserved;
} else
{
DEBUG_STREAM << "Could not find decent monitor for right MFD." << std::endl;
}
}
} else
{
DEBUG_STREAM << "Either MFDs are explicitly disabled, we're running in windowed mode, or we don't have enough monitors attached to this machine. MFD monitors will not be detected." << std::endl;
}
if (mPrimaryIndex == NULL)
{
DEBUG_STREAM << "Still no appropriate primary monitor- will find tallest monitor to use." << std::endl;
//Pick the monitor with the highest y resolution (leftmost if there's a tie)
int bestMonitorIndex = -1;
int bestMonitorHeight = 0;
int bestMonitorX = 0;
for (int i = 0; i < monitorCount; i++)
{
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
int height = (monitors[i].rcMonitor.bottom - monitors[i].rcMonitor.top);
if (bestMonitorIndex < 0 || height > bestMonitorHeight || (height == bestMonitorHeight && monitors[i].rcMonitor.left < bestMonitorX))
{
bestMonitorIndex = i;
bestMonitorHeight = height;
bestMonitorX = monitors[i].rcMonitor.left;
}
}
}
if (bestMonitorIndex >= 0)
{
DEBUG_STREAM << "Using monitor " << bestMonitorIndex << " as primary monitor." << std::endl;
mPrimaryIndex = new int;
*mPrimaryIndex = bestMonitorIndex;
monitors[bestMonitorIndex].dwFlags |= monitorReserved;
} else
{
DEBUG_STREAM << "Could not find decent primary monitor. Likely no unclaimed monitors." << std::endl;
}
}
if (mPrimaryIndex != NULL && isWindowed)
{
DEBUG_STREAM << "Game is currently running windowed mode- will set all remaining undefined monitors to be the same as the primary monitor." << std::endl;
if (mSecondaryIndex == NULL)
{
mSecondaryIndex = new int;
*mSecondaryIndex = *mPrimaryIndex;
}
if (mAux1Index == NULL && mAux2Index == NULL)
{
mAux1Index = new int;
*mAux1Index = *mPrimaryIndex;
mAux2Index = new int;
*mAux2Index = *mPrimaryIndex;
}
} else if (!isWindowed && mSecondaryIndex == NULL)
{
DEBUG_STREAM << "Detecting secondary monitor- will attempt to use furthest-left remaining monitor." << std::endl;
//Pick the leftmost remaining monitor
int leftmostIndex = -1;
int leftmostX = 0;
for (int i = 0; i < monitorCount; i++)
{
if ((monitors[i].dwFlags & monitorReserved) == 0)
{
if (leftmostIndex < 0 || monitors[i].rcMonitor.left < leftmostX)
{
leftmostIndex = i;
leftmostX = monitors[i].rcMonitor.left;
}
}
}
if (leftmostIndex >= 0)
{
DEBUG_STREAM << "Using monitor " << leftmostIndex << " as secondary monitor." << std::endl;
mSecondaryIndex = new int;
*mSecondaryIndex = leftmostIndex;
} else
{
DEBUG_STREAM << "Could not find appropriate secondary monitor. Likely no unclaimed monitors remain." << std::endl;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for DPLRenderer
//
DPLRenderer::DPLRenderer(
HWND hWnd,
unsigned int screenWidth,
unsigned int screenHeight,
bool fullscreen,
InterestType interest_type,
InterestDepth depth_calibration
):
VideoRenderer(
1.0f,
1.0f,
RendererPriority::DefaultRendererPriority,
interest_type,
depth_calibration
),
projectile_list(NULL),
dplObjectCacheSocket(NULL, False),
dplJointToDCSTranslatorSocket(NULL,False),
dplRenderableSocket(NULL),
mRenderables(NULL),
x_size(screenWidth),
y_size(screenHeight),
mReticle(NULL),
mCamShipHUD(NULL),
mStaticObjectsHead(NULL),
mStaticObjectsCount(0),
mPrimaryIndex(NULL),
mSecondaryIndex(NULL),
mAux1Index(NULL),
mAux2Index(NULL)
{
__int64 frequency = HiResCounterFreq();
#ifdef LOGFRAMERATE
FRAMERATE_LOG = fopen("framerate.log", "wb");
fwrite(&frequency, sizeof(__int64), 1, FRAMERATE_LOG);
#endif
mCamera = NULL;
loadTables();
// clear out our render lists
memset(mRenderLists, 0, sizeof(mRenderLists));
memset(mNameTextures, 0, sizeof(mNameTextures));
memset(mOrdinalTextures, 0, sizeof(mOrdinalTextures));
D3DXCreateMatrixStack(0, &m_MatrixStack);
m_MatrixStack->LoadIdentity();
gD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!gD3D)
{
DEBUG_STREAM<<"Couldn't create Direct3D interface!"<<std::endl<<std::flush << std::flush;
PostQuitMessage(1);
}
this->FindBestAdapterIndices(!fullscreen);
memset(&mPresentParams, 0, sizeof(D3DPRESENT_PARAMETERS));
mPresentParams.BackBufferCount = 1;
mPresentParams.MultiSampleType = (D3DMULTISAMPLE_TYPE)atol(getenv("MULTISAMPLE"));
if (mPresentParams.MultiSampleType > 0)
{
gD3D->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, !fullscreen, mPresentParams.MultiSampleType, &mPresentParams.MultiSampleQuality);
mPresentParams.MultiSampleQuality--;
}
mPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
mPresentParams.hDeviceWindow = hWnd;
mPresentParams.Flags = 0;
mPresentParams.FullScreen_RefreshRateInHz = (fullscreen)?60:D3DPRESENT_RATE_DEFAULT;
mPresentParams.PresentationInterval = D3DPRESENT_RATE_DEFAULT;
mPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8;
mPresentParams.EnableAutoDepthStencil = TRUE;
mPresentParams.AutoDepthStencilFormat = D3DFMT_D24X8;
mPresentParams.Windowed = !fullscreen;
if (fullscreen)
{
mPresentParams.BackBufferWidth = screenWidth;
mPresentParams.BackBufferHeight = screenHeight;
}
HRESULT hr;
//DEBUG_STREAM<<"**************************"<<std::endl<<"**************************"<<std::endl;
//for (UINT adapter = 0; adapter<=3; adapter++)
//{
// DEBUG_STREAM<<"\tAdapter "<<adapter<<": ";
// D3DFORMAT format = (adapter == 0 ? D3DFMT_X8R8G8B8 : D3DFMT_R5G6B5);
// UINT modes = gD3D->GetAdapterModeCount(adapter, format);
// DEBUG_STREAM<<modes<<" display modes"<<std::endl;
// for (UINT mode=0; mode<modes; mode++)
// {
// D3DDISPLAYMODE displayMode;
// hr = gD3D->EnumAdapterModes(adapter, format, mode, &displayMode);
// if (FAILED(hr))
// {
// DEBUG_STREAM<<"\t\tFailed to retrieve display mode "<<mode<<"."<<std::endl;
// continue;
// }
// DEBUG_STREAM<<"\t\t"<<displayMode.Format<<": "<<displayMode.Width<<" x "<<displayMode.Height<<" @ "<<displayMode.RefreshRate<<" Hz"<<std::endl;
// }
// DEBUG_STREAM<<std::endl;
//}
//DEBUG_STREAM<<"**************************"<<std::endl<<"**************************"<<std::endl<<std::flush;
if (mPrimaryIndex == NULL)
{
DEBUG_STREAM<<"Unable to locate a suitable primary device index."<<std::endl<<std::flush;
PostQuitMessage(1);
return;
}
// PORT (perf root cause): the device was created SOFTWARE_VERTEXPROCESSING --
// every vertex of every draw transformed + LIT on the CPU (the ~10fps baseline
// and the multi-hundred-ms "hitch" frames whenever the dense map side entered
// the view; the GPU sat idle, Present <1ms). Try HARDWARE first (any modern
// or pod GPU has fixed-function T&L); fall back to software if it fails, as
// the original error message always intended.
V(gD3D->CreateDevice(*mPrimaryIndex, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &mPresentParams, &mDevice));
if (FAILED(hr))
{
DEBUG_STREAM<<"Couldn't create HARDWARE_VERTEXPROCESSING device."<<std::endl<<std::flush;
V(gD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &mPresentParams, &mDevice));
if (FAILED(hr))
{
PostQuitMessage(1);
}
}
mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 0.0f, 0);
mDevice->Present(NULL, NULL, NULL, NULL);
ParticleEngine::Initialize(mDevice);
SetCoreRenderStates();
//STUBBED: DPL RB 1/14/07
char
Eye_Type[50],
*Eye_Arg, // Controls generation of the eye point
*DPL_Arg; // Points to DPLARG environment pointer once we get it
int
psfx_number;
// int
// i; // Temporary loop counter
float
eye_x,
eye_y,
eye_z,
eye_x_rot,
eye_y_rot,
eye_z_rot;
//
// Initialize a bunch of the variables to their starting values
//
eyeRelative = False;
completeCycleNeeded = False;
lastAppState = 0;
fogUpdating = True;
fogRed = 0.0f;
fogBlue = 0.0f;
fogGreen = 0.0f;
fogNear = 0.0f;
fogFar = 0.0f;
currentFogFar = 0.0f;
currentFogNear = 0.0f;
searchLightFogRed = 0.0f;
searchLightFogGreen = 0.0f;
searchLightFogBlue = 0.0f;
searchLightFogNear = 0.0f;
searchLightFogFar = 0.0f;
noSearchLightFogRed = 0.0f;
noSearchLightFogGreen = 0.0f;
noSearchLightFogBlue = 0.0f;
noSearchLightFogNear = 0.0f;
noSearchLightFogFar = 0.0f;
clipNear = 0.0f;
clipFar = 0.0f;
mEnvAmbient = 0x00404040; // bring-up floor until the env ambient loads
mCloudRed = mCloudGreen = mCloudBlue = 1.0f; // no cloud tint until env sets it
mCloudEmitRed = mCloudEmitGreen = mCloudEmitBlue = 0.0f;
backgroundRed = 0.0f;
backgroundGreen = 0.0f;
backgroundBlue = 0.0f;
viewAngle = 30.0f;
dplMainView = NULL;
dplDeathZone = NULL;
dplMainZone = NULL;
vehicleReticle = NULL;
dplHitInstance = NULL;
dplHitDCS = NULL;
dplHitGeoGroup = NULL;
dplHitGeometry = NULL;
delayedDCSCount = 0;
myUniqueID = 0;
sceneLightDCS = NULL;
sceneLight = NULL;
sceneLightCount = 0;
worldToEyeMatrix = LinearMatrix::Identity; // the current world to eye transform for our linked entity
currentFrameTime = Now(); // the time at the start of renderable execution
Verify(!DPLHeap);
// DPLHeap = new UserHeap("DPL Heap", 2000000);
Register_Object(DPLHeap);
//
// Clear the myPSFXDescriptons array to all zeros so we can detect attempts
// to use uninitialized items.
//
memset(myPSFXDescriptons, 0, sizeof(myPSFXDescriptons));
//
// These guys will come out of a world environment variable eventually
//
eye_x = 0.0f;
eye_y = 10.0f;
eye_z = 0.0f;
eye_x_rot = 0.0f;
eye_y_rot = 0.0f;
eye_z_rot = 0.0f;
//
// Get pointers to environmentals containing DPL startup arguments and
// eye positioning information. If L4EYES is present set the renderer
// variable that controls the hooking up of the eye position.
//
Eye_Type[0] = 0;
DPL_Arg = getenv("DPLARG");
Eye_Arg = getenv("L4EYES");
if(Eye_Arg)
{
sscanf(
Eye_Arg,
"%f %f %f %f %f %f %s",
&eye_x,
&eye_y,
&eye_z,
&eye_x_rot,
&eye_y_rot,
&eye_z_rot,
Eye_Type);
printf("%f, %f, %f %f, %f, %f\n",
eye_x,
eye_y,
eye_z,
eye_x_rot,
eye_y_rot,
eye_z_rot);
Disconnected_Eye = True; // tells vidrend:: to request outside view of linked entity
if(*Eye_Type == 'r')
{
DEBUG_STREAM<<"DPLRenderer::DPLRenderer Eye will be offset relative to vehicle\n" << std::flush;
eyeRelative = True;
}
}
//
// If the argument was empty, we can't render !!! should exit sensiblly
//
if(!DPL_Arg)
{
DEBUG_STREAM << "DPLARG must be set for the Division card to come up\n" << std::flush;
Verify(DPL_Arg);
}
//
// If we're still here, try to interpret and setup the screen resolution
// requested by DPLARG. The screen_resolution function is Phil's routine
// in startdpl.
//
// if (screen_resolution ( DPL_Arg ) == 0)
// {
// DEBUG_STREAM << "DPLARG is bad, I couldn't understand video format\n" << std::flush;
// Verify(DPL_Arg);
// }
//
// Breakdown the DPLARGS string using expload_args from startdpl, then feed
// the results to the dpl_Init routine to get DPL up and running.
//
char *argv[32];
// int argc = explode_args ( argv, DPL_Arg, dpl_arg_sep );
// dpl_Init ( argc, argv );
//
// Setup the file paths for geometry and texture loading
//
// dpl_SetObjectFilePath ( ".\\video", "\\video", "..\\video" );
// dpl_SetTexmapFilePath ( ".\\video", "\\video", "..\\video" );
// dpl_SetMaterialFilePath ( ".\\video", "\\video", "..\\video" );
// dpl_SetObjectFilePath ( ".\\video", "", "" );
// std::cout<<dpl_GetObjectFilePath()<<"\n";
// dpl_SetTexmapFilePath ( ".\\video", "", "" );
// std::cout<<dpl_GetTexmapFilePath()<<"\n";
// dpl_SetMaterialFilePath ( ".\\video", "", "" );
// std::cout<<dpl_GetMaterialFilePath()<<"\n";
//
// Set the extensions and the DPL routines that will load them
//
// dpl_SetExtnObjectLoadFunc (".bgf", dpl_bgfRead);
// dpl_SetExtnTexmapLoadFunc (".vtx", dpl_vtxRead);
// dpl_SetExtnTexmapLoadFunc (".tga", dpl_tgaRead);
// dpl_SetExtnTexmapLoadFunc (".int", dpl_sgiRead);
// dpl_SetExtnTexmapLoadFunc (".inta", dpl_sgiRead);
// dpl_SetExtnTexmapLoadFunc (".rgb", dpl_sgiRead);
// dpl_SetExtnTexmapLoadFunc (".rgba", dpl_sgiRead);
//
// Setup the dpl callback routines for handling specials, object creation and
// material name munging.
//
// dpl_SetSpecialCallback(TestSpecialCallBack);
// dpl_SetCreateNodeCallback(TestCreateCallBack);
// dpl_SetMaterialNameCallback(TestMaterialCallBack);
//
// Create the one zone everything will live in !!!! more zones later for smart culling
//
// dplMainZone = dpl_NewZone();
Check_Pointer(dplMainZone);
// dplDeathZone = dpl_NewZone();
Check_Pointer(dplDeathZone);
//
// Create the view we will use for our eyepoint
//
// dplMainView = dpl_NewView();
Check_Pointer(dplMainView);
//
// This creates a DCS and links it to the view, I probably should just let the
// eye renderable do this but it's convenient since it can be used as the
// eye point for testing.
//
// dplTestEyeDCS = dpl_NewDCS();
Check_Pointer ( dplTestEyeDCS);
// dpl_RotateDCS ( dplTestEyeDCS, eye_z_rot, dpl_Z );
// dpl_RotateDCS ( dplTestEyeDCS, eye_x_rot, dpl_X );
// dpl_RotateDCS ( dplTestEyeDCS, eye_y_rot, dpl_Y );
// dpl_TranslateDCS ( dplTestEyeDCS, eye_x, eye_y, eye_z);
// dpl_SetDCSZone ( dplTestEyeDCS, dplMainZone );
// dpl_SetViewDCS ( dplMainView, dplTestEyeDCS);
//
// Do some setup work on the zone
//
// dpl_SetZoneAllViewsOn ( dplMainZone );
// dpl_SetZoneAllViewsOn ( dplDeathZone );
//
// Setup some defaults so we can safely render a blank screen now
//
//
// dpl_SetViewClipPlanes ( dplMainView, 1.0f, 2.0f );
// dpl_SetViewBackGround ( dplMainView, 0.0f, 0.0f, 0.0f);
// dpl_SetPipeWindow (0, 0, 0, x_size, y_size);
// dpl_SetViewProjection ( dplMainView, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f/(tan( 0.5235987666667f)));
// dpl_SetViewPipe ( dplMainView, 0);
// dpl_AddViewToScene ( dplMainView );
// dpl_SetViewFog (dplMainView, dpl_fog_type_pixel_lin, 0.0, 0.0, 0.0, 0.01, 0.05 );
//
// DPLReadEnvironment();
//
// dpl_FlushDCS ( dplTestEyeDCS);
// dpl_FlushView ( dplMainView);
// dpl_FlushZone ( dplMainZone);
// dpl_FlushZone ( dplDeathZone);
//
// Call DrawScene to force the system do draw a blank screen
//
// dpl_DrawScene();
//
// Start a clock used to keep track of the frame rate
//
StartSample = Now();
FrameCount = 0;
ResetStatistics();
statistics_started = False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dpl_ZONE*
DPLRenderer::MakeNewZone()
{
//STUBBED: DPL RB 1/14/07
dpl_ZONE *temp_zone;
////
//// Make a new zone for use by someone constructing an object, switch it on
//// and std::flush it out to the card.
////
//temp_zone = dpl_NewZone();
//Check_Pointer(temp_zone);
//dpl_SetZoneAllViewsOn (temp_zone);
//dpl_FlushZone (temp_zone);
////
//// HACK !!! at this point we should put the zone into a chain so we can
//// find it to switch it on and off later.
////
return(temp_zone);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AddDynamicRenderable puts a renderable into this renderer's dynamic execution
// socket so it will be run every frame.
//
void DPLRenderer::AddDynamicRenderable(Component *my_renderable)
{
Check(my_renderable);
Check(&dplRenderableSocket);
dplRenderableSocket.Add(my_renderable);
}
void DPLRenderer::AddToPassList(d3d_OBJECT *object, int pass)
{
if (pass >= PASS_TOTAL_COUNT)
return;
for (d3d_OBJECT *iter = mRenderLists[pass]; iter; iter = iter->GetNext(pass))
if ((void*)iter == (void*)object)
return;
object->SetPrevious(NULL, pass);
object->SetNext(mRenderLists[pass], pass);
if (mRenderLists[pass] != NULL)
mRenderLists[pass]->SetPrevious(object, pass);
mRenderLists[pass] = object;
}
hyper DPLRenderer::HashAdd(hyper input, char data)
{
const hyper multiplier = 37;
return (input * multiplier) + data;
}
hyper DPLRenderer::HashDrawOp(L4DRAWOP *op)
{
char diffR = (char)(op->material.Diffuse.r * 255);
char diffG = (char)(op->material.Diffuse.g * 255);
char diffB = (char)(op->material.Diffuse.b * 255);
char diffA = (char)(op->material.Diffuse.a * 255);
char ambA = (char)(op->material.Ambient.a * 255);
char ambR = (char)(op->material.Ambient.r * 255);
char ambG = (char)(op->material.Ambient.g * 255);
char ambB = (char)(op->material.Ambient.b * 255);
char spcA = (char)(op->material.Specular.a * 255);
char spcR = (char)(op->material.Specular.r * 255);
char spcG = (char)(op->material.Specular.g * 255);
char spcB = (char)(op->material.Specular.b * 255);
char spcP = (char)(op->material.Power * 255);
char emiA = (char)(op->material.Emissive.a * 255);
char emiR = (char)(op->material.Emissive.r * 255);
char emiG = (char)(op->material.Emissive.g * 255);
char emiB = (char)(op->material.Emissive.b * 255);
const hyper startValue = 37;
hyper hash = startValue;
hash = HashAdd(hash, diffA);
hash = HashAdd(hash, diffR);
hash = HashAdd(hash, diffG);
hash = HashAdd(hash, diffB);
hash = HashAdd(hash, ambA);
hash = HashAdd(hash, ambR);
hash = HashAdd(hash, ambG);
hash = HashAdd(hash, ambB);
hash = HashAdd(hash, spcA);
hash = HashAdd(hash, spcR);
hash = HashAdd(hash, spcG);
hash = HashAdd(hash, spcB);
hash = HashAdd(hash, spcP);
hash = HashAdd(hash, emiA);
hash = HashAdd(hash, emiR);
hash = HashAdd(hash, emiG);
hash = HashAdd(hash, emiB);
hash = HashAdd(hash, (char)op->drawAsDecal);
hash = HashAdd(hash, (char)op->alphaTest);
hash = HashAdd(hash, (char)op->drawAsSky);
char *pointerPointer = (char*)(&(op->texture.texture));
for (int i = 0; i < 4; i++)
{
hash = HashAdd(hash, pointerPointer[i]);
}
return hash;
}
void DPLRenderer::AddStaticObject(d3d_OBJECT *object)
{
object->SetPrevious(NULL, -1);
object->SetNext(mStaticObjectsHead, -1);
if (mStaticObjectsHead != NULL)
mStaticObjectsHead->SetPrevious(object, -1);
mStaticObjectsHead = object;
mStaticObjectsCount++;
}
void DPLRenderer::RecurseStaticObject(HierarchicalDrawComponent *obj)
{
if (obj->IsStatic())
{
if (obj->GetDrawObj() != NULL && obj->GetDrawObj()->GetMesh() != NULL)
{
// ADDITIVE_LODS objects (decoded IG-board semantics, see bgfload.cpp
// TAG_OBJECT) carry per-op distance bands [0..OutDist) that DrawMesh
// gates each frame -- merging them into a consolidated static would
// draw every LOD of the composite simultaneously forever. Leave any
// object with a restricted band OUT of consolidation; its component
// keeps drawing it individually with the per-op band test.
bool banded = false;
d3d_OBJECT *dobj = obj->GetDrawObj();
for (int opn = 0; opn < dobj->GetDrawOpCount() && !banded; opn++)
{
const L4DRAWOP *op = dobj->GetDrawOp(opn);
if (op->lodFar > 0.0f && op->lodFar < 1.0e8f)
banded = true;
}
// baked ground-shadow models draw via the mIsShadow blend path
// (translucent + depth bias); merging them would draw them opaque.
if (dobj->GetIsShadow())
banded = true;
if (!banded)
{
//Only load it for a static object if it has a valid mesh-
//we'll handle valid sphere lists later
this->AddStaticObject(obj->GetDrawObj());
obj->ResetDrawObj();
}
}
std::vector<HierarchicalDrawComponent *>::const_iterator child_it = obj->Enumerate();
while (child_it != obj->End())
{
RecurseStaticObject(*child_it);
++child_it;
}
}
}
d3d_OBJECT * DPLRenderer::ConsolidateSingleObject(LPD3DXMESH *meshes, D3DXMATRIX *transforms, UINT startingMesh, UINT meshCount, hash_map<DWORD, hyper> subsetHash, hash_map<hyper, DWORD> hashToOp, vector<L4DRAWOP*> finalOps)
{
HRESULT hr;
LPD3DXMESH outMesh = NULL;
// BT bring-up: the pod content ships .bgf, loaded by d3d_OBJECT::LoadObjectBGF
// with D3DXMESH_32BIT (32-bit index buffer). D3DXConcatenateMeshes was called
// with only D3DXMESH_MANAGED (16-bit output) -> concatenating 32-bit-index
// source meshes into a 16-bit output fails, leaving outMesh NULL -> the
// GetNumFaces() below dereferenced NULL and crashed (RP never hit this: its
// world meshes load from .x as 16-bit). Match the source meshes' index width.
V( D3DXConcatenateMeshes(meshes + startingMesh, meshCount, D3DXMESH_MANAGED | D3DXMESH_32BIT, transforms + startingMesh, NULL, NULL, mDevice, &outMesh) );
if (outMesh == NULL)
{
DEBUG_STREAM << "ConsolidateSingleObject: D3DXConcatenateMeshes failed (hr=0x"
<< std::hex << (unsigned)hr << std::dec << "), skipping " << meshCount
<< " static meshes\n" << std::flush;
return NULL;
}
DWORD *attributes;
int numFaces = outMesh->GetNumFaces();
V( outMesh->LockAttributeBuffer(0, &attributes) );
for (int i = 0; i < numFaces; i++)
{
stdext::hash_map<DWORD, hyper>::const_iterator face_it = subsetHash.find(attributes[i]);
if (face_it == subsetHash.end())
{
//Freak out
}
stdext::hash_map<hyper, DWORD>::const_iterator subset_it = hashToOp.find((*face_it).second);
if (subset_it == hashToOp.end())
{
//Freak out
}
attributes[i] = (*subset_it).second;
}
//
// PORT (draw-cost fix): sort the faces by draw-op OURSELVES and record each
// op's contiguous index range for the direct-draw path. The old path relied
// on GenerateAdjacency + OptimizeInplace(ATTRSORT) to build D3DX's attribute
// table -- but the concatenated source meshes are double-sided BGF geometry,
// whose degenerate adjacency makes the optimize FAIL silently. DrawSubset then
// SCANS the whole attribute buffer of this huge merged mesh per call (~350us
// x ~1300 ops when a merged map chunk is in view = the ~500ms hitch frames).
//
const int numOps = (int)finalOps.size();
std::vector<int> opStart(numOps, 0), opCount(numOps, 0);
{
DWORD *ib = NULL;
V( outMesh->LockIndexBuffer(0, (void **)&ib) ); // 32-bit (created 32BIT above)
if (ib != NULL)
{
int badAttr = 0; // remap failures (see audit below)
for (int i = 0; i < numFaces; i++)
{
DWORD op = attributes[i];
if ((int)op >= numOps) { op = 0; ++badAttr; } // SAME clamp as the write pass
++opCount[op];
}
for (int o = 1; o < numOps; ++o)
opStart[o] = opStart[o - 1] + opCount[o - 1];
std::vector<DWORD> sortedIB((size_t)numFaces * 3);
std::vector<DWORD> sortedAttr((size_t)numFaces);
std::vector<int> cursor(opStart.begin(), opStart.end());
for (int i = 0; i < numFaces; i++)
{
DWORD op = attributes[i];
if ((int)op >= numOps) op = 0;
const int dst = cursor[op]++;
sortedIB[(size_t)dst * 3 + 0] = ib[(size_t)i * 3 + 0];
sortedIB[(size_t)dst * 3 + 1] = ib[(size_t)i * 3 + 1];
sortedIB[(size_t)dst * 3 + 2] = ib[(size_t)i * 3 + 2];
sortedAttr[dst] = op;
}
memcpy(ib, sortedIB.data(), sortedIB.size() * sizeof(DWORD));
memcpy(attributes, sortedAttr.data(), sortedAttr.size() * sizeof(DWORD));
V( outMesh->UnlockIndexBuffer() );
// AUDIT (turret-panels hunt): faces whose remapped attribute is out of
// range mean the subsetHash/hashToOp lookup FAILED for their source op
// ("Freak out" above is a no-op) -- those faces draw with op 0's material
// or, before this counting fix, corrupted neighbouring ranges.
DEBUG_STREAM << "[consol] group: srcMeshes=" << meshCount
<< " faces=" << numFaces << " ops=" << numOps
<< " badAttr=" << badAttr << "\n" << std::flush;
}
}
V( outMesh->UnlockAttributeBuffer() );
// explicit attribute table over the now-sorted faces (keeps DrawSubset and any
// D3DX consumer valid; the direct-draw ranges below are the primary path)
{
std::vector<D3DXATTRIBUTERANGE> atable((size_t)numOps);
for (int o = 0; o < numOps; ++o)
{
atable[o].AttribId = (DWORD)o;
atable[o].FaceStart = (DWORD)opStart[o];
atable[o].FaceCount = (DWORD)opCount[o];
atable[o].VertexStart = 0;
atable[o].VertexCount = outMesh->GetNumVertices();
}
V( outMesh->SetAttributeTable(atable.data(), (DWORD)numOps) );
}
d3d_OBJECT *consolObj = new d3d_OBJECT(mDevice, outMesh, NULL, finalOps.size());
// direct-draw ranges + cached buffers (same fast path as LoadObjectBGF objects)
outMesh->GetVertexBuffer(&consolObj->mBgfVB);
outMesh->GetIndexBuffer(&consolObj->mBgfIB);
consolObj->mBgfStride = outMesh->GetNumBytesPerVertex();
consolObj->mBgfNumVerts = outMesh->GetNumVertices();
for (int o = 0; o < numOps; ++o)
{
consolObj->GetDrawOp(o)->bgfStartIndex = opStart[o] * 3;
consolObj->GetDrawOp(o)->bgfPrimCount = opCount[o];
}
for (int drawOpInd = 0; drawOpInd < consolObj->GetDrawOpCount(); drawOpInd++)
{
L4DRAWOP *op = consolObj->GetDrawOp(drawOpInd);
L4DRAWOP *source = finalOps[drawOpInd];
op->material = source->material;
op->texture = source->texture;
op->drawAsDecal = source->drawAsDecal;
op->alphaTest = source->alphaTest;
op->drawAsSky = source->drawAsSky;
// CONSOLIDATED-WORLD DEPTH RECESSION (coplanar cross-object resolution):
// entity props lay flat polys EXACTLY in the floor plane (MECHMOVR's
// dead-mech ground plates: 71 verts at y=0.000 vs afloor's y=0 top --
// the "flickering floor tile under the wreckage"). The board's global
// submission-order rule drew scene traversal LAST-over-FIRST on exact
// plane ties; in D3D terms, recess the merged static world by 3 depth-
// buffer steps so anything drawn individually (entity props, banded
// structures, the mech) deterministically wins floor-plane ties.
// 7.5e-7 NDC ~ 3 LSB of D24: invisible as parallax, decisive vs
// interpolation rounding.
op->lodDepthBias = 7.5e-7f;
if (op->texture.texture != NULL)
{
op->texture.texture->AddRef();
}
}
return consolObj;
}
void DPLRenderer::ConsolidateStaticObjects()
{
HRESULT hr;
// Consolidation runs by DEFAULT (the shipping-engine configuration). It is
// skipped when (a) BT_CONSOL=0 (diagnostic kill-switch), or (b) the
// EXPERIMENTAL runtime-LOD selection is on (BT_LODSEL=1): merged ops combine
// many instances + LOD bands of one material, so per-op band selection cannot
// survive the merge (a consolidated run draws every LOD simultaneously).
{
const char *cv = getenv("BT_CONSOL");
const char *lv = getenv("BT_LODSEL");
const bool lodSel = (lv != 0 && *lv == '1');
if ((cv != 0 && *cv == '0') || lodSel)
{
DEBUG_STREAM << "[consol] OFF (" << (lodSel ? "BT_LODSEL experimental" : "BT_CONSOL=0")
<< ") -- statics render individually\n" << std::flush;
return;
}
}
HierarchicalDrawComponent *drawComp;
SChainIteratorOf<HierarchicalDrawComponent*> iterator(&mRenderables);
while ((drawComp = iterator.ReadAndNext()) != NULL)
{
if (drawComp->IsStatic())
{
drawComp->Execute();
this->RecurseStaticObject(drawComp);
}
}
LPD3DXMESH *meshes = new LPD3DXMESH[mStaticObjectsCount];
D3DXMATRIX *transforms = new D3DXMATRIX[mStaticObjectsCount];
int i = 0;
int opCount = 0;
std::vector<L4DRAWOP*> finalOps;
stdext::hash_map<hyper, DWORD> hashToOp;
stdext::hash_map<DWORD, hyper> subsetHash;
int vertCount = 0;
int startMesh = 0;
int meshCount = 0;
for (d3d_OBJECT *obj = mStaticObjectsHead; obj; obj = obj->GetNext(-1), i++)
{
if (vertCount + obj->GetMesh()->GetNumVertices() > 65535)
{
d3d_OBJECT *consol = this->ConsolidateSingleObject(meshes, transforms, startMesh, meshCount, subsetHash, hashToOp, finalOps);
if (consol != NULL)
this->mConsolidatedStaticObjects.push_back(consol);
vertCount = 0;
startMesh = meshCount;
meshCount = 0;
opCount = 0;
//Clear the draw ops lists
finalOps.clear();
hashToOp.clear();
subsetHash.clear();
}
meshes[i] = obj->GetMesh();
transforms[i] = obj->GetLocalToWorld();
vertCount += meshes[i]->GetNumVertices();
meshCount++;
for (int opNum = 0; opNum < obj->GetDrawOpCount(); opNum++)
{
hyper hash = this->HashDrawOp(obj->GetDrawOp(opNum));
stdext::hash_map<hyper, DWORD>::const_iterator hash_it = hashToOp.find(hash);
if (hash_it == hashToOp.end())
{
finalOps.insert(finalOps.end(), obj->GetDrawOp(opNum));
hashToOp.insert(std::pair<hyper, DWORD>(hash, (DWORD)(finalOps.size() - 1)));
hash_it = hashToOp.find(hash);
}
subsetHash.insert(std::pair<DWORD, hyper>((DWORD)opCount, hash));
opCount++;
}
}
if (meshCount > 0)
{
d3d_OBJECT *consol = this->ConsolidateSingleObject(meshes, transforms, startMesh, meshCount, subsetHash, hashToOp, finalOps);
if (consol != NULL)
this->mConsolidatedStaticObjects.push_back(consol);
}
d3d_OBJECT *obj = mStaticObjectsHead;
while (obj != NULL)
{
d3d_OBJECT *next = obj->GetNext(-1);
delete obj;
obj = next;
}
mStaticObjectsHead = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RemoveDynamicRenderable removes a renderable from the dynamic execution
// socket.
//
void
DPLRenderer::RemoveDynamicRenderable(Component *my_renderable)
{
Check(my_renderable);
Check(&dplRenderableSocket);
PlugIterator remover(my_renderable);
remover.RemoveSocket(&dplRenderableSocket);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
DPLRenderer::SetFogStyle(FogStyle my_fog)
{
//STUBBED: DPL RB 1/14/07
switch(my_fog)
{
case updateFogSetting:
fogUpdating = True;
break;
case noUpdateFogSetting:
fogUpdating = False;
break;
case searchLightOnFogStyle:
fogRed = searchLightFogRed;
fogGreen = searchLightFogGreen;
fogBlue = searchLightFogBlue;
fogNear = searchLightFogNear;
fogFar = searchLightFogFar;
if(fogUpdating)
{
mDevice->SetRenderState(D3DRS_FOGCOLOR, D3DCOLOR_XRGB((int)(255 * fogRed), (int)(255 * fogGreen), (int)(255 * fogBlue)));
// dpl_SetViewFog(
// dplMainView,
// dpl_fog_type_pixel_lin,
// fogRed,
// fogGreen,
// fogBlue,
// fogNear,
// fogFar );
// dpl_FlushView(dplMainView);
}
break;
case searchLightOffFogStyle:
fogRed = noSearchLightFogRed;
fogGreen = noSearchLightFogGreen;
fogBlue = noSearchLightFogBlue;
fogNear = noSearchLightFogNear;
fogFar = noSearchLightFogFar;
if(fogUpdating)
{
mDevice->SetRenderState(D3DRS_FOGCOLOR, D3DCOLOR_XRGB((int)(255 * fogRed), (int)(255 * fogGreen), (int)(255 * fogBlue)));
// dpl_SetViewFog(
// dplMainView,
// dpl_fog_type_pixel_lin,
// fogRed,
// fogGreen,
// fogBlue,
// fogNear,
// fogFar );
// dpl_FlushView(dplMainView);
}
break;
case winnersCircleFogStyle:
//
// HACK!! This really shouldn't reset the clip planes, but since
// it only happens at the end of the review, it should be safe for now.
//
// dpl_SetViewClipPlanes ( dplMainView, 0.25f, 1100.0f );
// dpl_SetViewFog(
// dplMainView,
// dpl_fog_type_pixel_lin,
// 0.32,
// 0.3,
// 0.65,
// 100.0f,
// 1050.0f );
// dpl_FlushView(dplMainView);
break;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
DPLRenderer::GetCurrentFogSettings(
float *fog_Red,
float *fog_Green,
float *fog_Blue,
float *fog_Near,
float *fog_Far)
{
*fog_Red = fogRed;
*fog_Green = fogGreen;
*fog_Blue = fogBlue;
*fog_Near = fogNear;
*fog_Far = fogFar;
}
void DPLRenderer::SetCurrentFogLimits(
float fog_Near,
float fog_Far)
{
currentFogNear = fog_Near;
currentFogFar = fog_Far;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// MarkDCSHiearchy This will std::decend a DCS tree and set the appSpecific hook
// in every DCS equal to the entity pointer "entity", it calls itself recursively
// to do this.
//
void
DPLRenderer::MarkDCSHiearchy(
dpl_DCS *root_DCS, // Root of the hiearchy to mark
Entity *entity) // Entity pointer to mark it with
{
//STUBBED: DPL RB 1/14/07
//int
// dcs_counter;
//dpl_DCS
// *child_DCS;
////
//// First, mark this DCS
////
//if(dpl_GetAppSpecific(root_DCS))
// Fail("DPLRenderer::MarkDCSHiearchy tried to mark a DCS that was already marked!\n");
//dpl_PutAppSpecific(root_DCS,entity);
////
//// Now call this routine on all this dcs's children
////
//dcs_counter = 0;
//while((child_DCS=dpl_GetDCSChildDCS(root_DCS,dcs_counter)) != NULL)
//{
// MarkDCSHiearchy(child_DCS, entity);
// dcs_counter++;
//}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ReadPSFX
// reads a pfx file into a supplied array
// Most of this routine was a direct copy of phil's code and as such is a bit
// on the messy side.
//
dpl_PARTICLESTART_EFFECT_INFO*
DPLRenderer::ReadPSFX(
const char *file_name) // Name of the file containing the PSFX description
{
//STUBBED: DPL RB 1/14/07
// FILE
// *fp;
// int
// i,
// status;
// char
// line[256],
// *cp;
// //
// // Open the file containing the psfx, fail if it doesn't exist
// //
// strcpy(line,"video\\");
// strcat(line,file_name);
// fp = fopen ( line, "rt" );
// if(!fp)
// {
// DEBUG_STREAM<<"Could not open pfx file "<<line<<" effects using it will fail\n" << std::flush;
// return(NULL);
//// Fail("Required PFX file not found\n");
// }
// //
// // Allocate the memory for the pfx to be stored in.
// //
dpl_PARTICLESTART_EFFECT_INFO *psfx_info = new dpl_PARTICLESTART_EFFECT_INFO;
// if(!psfx_info)
// {
// Fail("ran out of RAM trying to read a pfx file\n");
// }
// //
// // This is the format of the psfx file we are going to read
// //
// #if 0
// format of psfx file is (NO BLANK LINES UNTIL END!)
// %s texture
// %d identifier %d maximum_issue %f release_period %f rate
// %f px %f py %f pz %f pv
// %f velx %f vely %f velz %f velxv %f velyv %f velzv
// %f rad %f radv %f exp %f expv %f dexp %f dexpv
// %f accelx %f accely %f accelz %f accelxv %f accelyv %f accelzv
// %f atten %f attenv
// %f sRi %f sGi %f sBi %f sAi %f sRiv %f sGiv %f sBiv %f sAiv
// %f sRo %f sGo %f sBo %f sAo %f sRov %f sGov %f sBov %f sAov
// %f eRi %f eGi %f eBi %f eAi %f eRiv %f eGiv %f eBiv %f eAiv
// %f eRo %f eGo %f eBo %f eAo %f eRov %f eGov %f eBov %f eAov
// %f color_warp %f alpha_warp
// %f duration %f durationv
// #endif
// //
// // Read in the texture name, null terminate it and look it up,
// // we don't complain if it fails since it is possible to do one of these
// // effects with no texture on it.
// //
// cp = fgets ( line, 255, fp );
// for (i=0; i<255; i++ )
// {
// if (cp[i] == '\n')
// cp[i]=0x0;
// }
// psfx_info->tex = dpl_LookupTexture ( cp, dpl_lookup_normal, &status );
// #if PRINT_THE_PFX
// printf ( "texture => %s\n", cp );
// #endif
// //
// // The remander of these statements get the rest of the PFX data
// //
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp,
// "%x %d %f %f\n",
// &psfx_info->identifier, &psfx_info->maximum_issue, &psfx_info->release_period, &psfx_info->rate );
// if(status < 4)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f\n", &psfx_info->px, &psfx_info->py, &psfx_info->pz, &psfx_info->pv );
// if(status < 4)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f\n",
// &psfx_info->velx, &psfx_info->vely, &psfx_info->velz,
// &psfx_info->velxv, &psfx_info->velyv, &psfx_info->velzv );
// if(status < 6)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f\n",
// &psfx_info->rad, &psfx_info->radv, &psfx_info->exp, &psfx_info->expv, &psfx_info->dexp, &psfx_info->dexpv );
// if(status < 6)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f\n",
// &psfx_info->accelx, &psfx_info->accely, &psfx_info->accelz,
// &psfx_info->accelxv, &psfx_info->accelyv, &psfx_info->accelzv );
// if(status < 6)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f\n", &psfx_info->atten, &psfx_info->attenv );
// if(status < 2)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f %f %f\n",
// &psfx_info->sRi,&psfx_info->sGi,&psfx_info->sBi,&psfx_info->sAi,&psfx_info->sRiv,&psfx_info->sGiv,&psfx_info->sBiv,&psfx_info->sAiv );
// if(status < 8)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f %f %f\n",
// &psfx_info->sRo,&psfx_info->sGo,&psfx_info->sBo,&psfx_info->sAo,&psfx_info->sRov,&psfx_info->sGov,&psfx_info->sBov,&psfx_info->sAov );
// if(status < 8)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f %f %f\n",
// &psfx_info->eRi,&psfx_info->eGi,&psfx_info->eBi,&psfx_info->eAi,&psfx_info->eRiv,&psfx_info->eGiv,&psfx_info->eBiv,&psfx_info->eAiv );
// if(status < 8)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f %f %f %f %f %f %f\n",
// &psfx_info->eRo,&psfx_info->eGo,&psfx_info->eBo,&psfx_info->eAo,&psfx_info->eRov,&psfx_info->eGov,&psfx_info->eBov,&psfx_info->eAov );
// if(status < 8)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f\n", &psfx_info->colour_warp, &psfx_info->alpha_warp );
// if(status < 2)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// cp = fgets ( line, 255, fp );
// status = sscanf ( cp, "%f %f\n", &psfx_info->dur, &psfx_info->durv );
// if(status < 2)
// {
// std::cout<<"pfx file "<<file_name<<" did not read correctly\n";
// return(NULL);
// }
//
// fclose (fp);
// #if PRINT_THE_PFX
// printf ( "Read psfx ->\n" );
// /*{{{ trace the psfx*/
// printf ( "psfx_info->tex = 0x%x\n",
// psfx_info->tex );
// printf ( "identifier = 0x%x ",
// psfx_info->identifier );
// printf ( "maximum_issue = %d ",
// psfx_info->maximum_issue );
// printf ( "release_period = %f\n",
// psfx_info->release_period );
// printf ( "rate = %f ",
// psfx_info->rate );
// printf ( "px = %f ",
// psfx_info->px );
// printf ( "py = %f ",
// psfx_info->py );
// printf ( "pz = %f ",
// psfx_info->pz );
// printf ( "pv = %f\n",
// psfx_info->pv );
// printf ( "velx = %f ",
// psfx_info->velx );
// printf ( "vely = %f ",
// psfx_info->vely );
// printf ( "velz = %f\n",
// psfx_info->velz );
// printf ( "velxv = %f ",
// psfx_info->velxv );
// printf ( "velyv = %f ",
// psfx_info->velyv );
// printf ( "velzv = %f\n",
// psfx_info->velzv );
// printf ( "rad = %f ",
// psfx_info->rad );
// printf ( "radv = %f\n",
// psfx_info->radv );
// printf ( "exp = %f ",
// psfx_info->exp );
// printf ( "expv = %f ",
// psfx_info->expv );
// printf ( "dexp = %f ",
// psfx_info->dexp );
// printf ( "dexpv = %f\n",
// psfx_info->dexpv );
// printf ( "accelx = %f ",
// psfx_info->accelx );
// printf ( "accely = %f ",
// psfx_info->accely );
// printf ( "accelz = %f ",
// psfx_info->accelz );
// printf ( "accelxv = %f ",
// psfx_info->accelxv );
// printf ( "accelyv = %f ",
// psfx_info->accelyv );
// printf ( "accelzv = %f\n",
// psfx_info->accelzv );
// printf ( "atten = %f ",
// psfx_info->atten );
// printf ( "attenv = %f\n",
// psfx_info->attenv );
// printf ( "sRi = %f ",
// psfx_info->sRi );
// printf ( "sGi = %f ",
// psfx_info->sGi );
// printf ( "sBi = %f ",
// psfx_info->sBi );
// printf ( "sAi = %f\n",
// psfx_info->sAi );
// printf ( "sRiv = %f ",
// psfx_info->sRiv );
// printf ( "sGiv = %f ",
// psfx_info->sGiv );
// printf ( "sBiv = %f ",
// psfx_info->sBiv );
// printf ( "sAiv = %f\n",
// psfx_info->sAiv );
// printf ( "sRo = %f ",
// psfx_info->sRo );
// printf ( "sGo = %f ",
// psfx_info->sGo );
// printf ( "sBo = %f ",
// psfx_info->sBo );
// printf ( "sAo = %f\n",
// psfx_info->sAo );
// printf ( "sRov = %f ",
// psfx_info->sRov );
// printf ( "sGov = %f ",
// psfx_info->sGov );
// printf ( "sBov = %f ",
// psfx_info->sBov );
// printf ( "sAov = %f\n",
// psfx_info->sAov );
// printf ( "eRi = %f ",
// psfx_info->eRi );
// printf ( "eGi = %f ",
// psfx_info->eGi );
// printf ( "eBi = %f ",
// psfx_info->eBi );
// printf ( "eAi = %f\n",
// psfx_info->eAi );
// printf ( "eRiv = %f ",
// psfx_info->eRiv );
// printf ( "eGiv = %f ",
// psfx_info->eGiv );
// printf ( "eBiv = %f ",
// psfx_info->eBiv );
// printf ( "eAiv = %f\n",
// psfx_info->eAiv );
// printf ( "eRo = %f ",
// psfx_info->eRo );
// printf ( "eGo = %f ",
// psfx_info->eGo );
// printf ( "eBo = %f ",
// psfx_info->eBo );
// printf ( "eAo = %f\n",
// psfx_info->eAo );
// printf ( "eRov = %f ",
// psfx_info->eRov );
// printf ( "eGov = %f ",
// psfx_info->eGov );
// printf ( "eBov = %f ",
// psfx_info->eBov );
// printf ( "eAov = %f\n",
// psfx_info->eAov );
// printf ( "colour_warp = %f ",
// psfx_info->colour_warp );
// printf ( "alpha_warp = %f ",
// psfx_info->alpha_warp );
// printf ( "dur = %f ",
// psfx_info->dur );
// printf ( "durv = %f\n",
// psfx_info->durv );
// #endif
return(psfx_info);
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// New INI file handler
// routine to read in an environment from a notation file and setup lights,
// spfx and such...
//
void
DPLRenderer::DPLReadINIPage(
NotationFile *master_notation_file,
const char *starting_page_name,
Mission *mission,
Logical debug_printing)
{
//STUBBED: DPL RB 1/14/07
float
red,
green,
blue,
x_rotate,
y_rotate,
z_rotate;
NameList
*cache_namelist,
*light_namelist,
*psfx_namelist,
*specialfx_namelist,
*include_pages,
*path_pages;
NameList::Entry
*entry;
const char
*next_include_page_name,
*TempStringPtr,
*compare_source;
if (debug_printing)
std::cout<<"DPLReadINIPage processing "<<starting_page_name<<"\n";
//
//---------------------------------------------------
// Process all the DPL gunk found on this page first
//---------------------------------------------------
//
// BT (task #20): accumulate the day/night search paths (was DPL-stubbed).
// Each entry is PREPENDED so the last-encountered (more-specific: the
// des_day/arena branch pages are visited after dpl_defaults) ends up FIRST
// = highest priority. Consumed by SetVideoPathPriority in DPLReadEnvironment.
if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "objectpath")) != NULL)
{
Register_Object(path_pages);
for(entry = path_pages->GetFirstEntry(); entry; entry = entry->GetNextEntry())
{
mObjectPaths.insert(mObjectPaths.begin(), std::string((char *)entry->dataReference));
if (debug_printing)
std::cout << "objectpath '" << (char *)entry->dataReference << "'\n";
}
Unregister_Object(path_pages);
delete path_pages;
}
if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "texmappath")) != NULL)
{
Register_Object(path_pages);
for(entry = path_pages->GetFirstEntry(); entry; entry = entry->GetNextEntry())
{
mTexmapPaths.insert(mTexmapPaths.begin(), std::string((char *)entry->dataReference));
if (debug_printing)
std::cout << "texmappath '" << (char *)entry->dataReference << "'\n";
}
Unregister_Object(path_pages);
delete path_pages;
}
if ((path_pages = master_notation_file->MakeEntryList(starting_page_name, "materialpath")) != NULL)
{
Register_Object(path_pages);
for(entry = path_pages->GetFirstEntry(); entry; entry = entry->GetNextEntry())
{
mMaterialPaths.insert(mMaterialPaths.begin(), std::string((char *)entry->dataReference));
if (debug_printing)
std::cout << "materialpath '" << (char *)entry->dataReference << "'\n";
}
Unregister_Object(path_pages);
delete path_pages;
}
if (master_notation_file->GetEntry(starting_page_name, "priorityobjectpath", &TempStringPtr))
{
// dpl_AddToObjectFilePath((char *)TempStringPtr, dpl_path_system);
if (debug_printing)
{ DEBUG_STREAM << "priorityobjectpath '" << TempStringPtr << "'" << std::endl << std::flush; }
}
if (master_notation_file->GetEntry(starting_page_name, "prioritytexmappath", &TempStringPtr))
{
// dpl_AddToTexmapFilePath((char *)TempStringPtr, dpl_path_system);
if (debug_printing)
{ DEBUG_STREAM << "prioritytexmappath '" << TempStringPtr << "'" << std::endl << std::flush; }
}
if (master_notation_file->GetEntry(starting_page_name, "prioritymaterialpath", &TempStringPtr))
{
// dpl_AddToMaterialFilePath((char *)TempStringPtr, dpl_path_system);
if (debug_printing)
{ DEBUG_STREAM << "prioritymaterialpath '" << TempStringPtr << "'" << std::endl << std::flush; }
}
//
// Get a the list of dpl objects that should be loaded into cache and load them
//
if ((cache_namelist = master_notation_file->MakeEntryList(starting_page_name, "cache")) != NULL)
{
d3d_OBJECT *d3d_a_object;
Register_Object(cache_namelist);
for(entry = cache_namelist->GetFirstEntry(); entry; entry = entry->GetNextEntry())
{
d3d_a_object = d3d_OBJECT::LoadObject(mDevice, (char *)entry->dataReference);
if(!d3d_a_object)
DEBUG_STREAM<<"Unable to cache "<<(char *)entry->dataReference<<"\n" << std::flush;
else
{
if(debug_printing)
std::cout<<"Caching "<<(char *)entry->dataReference<<"\n";
}
}
Unregister_Object(cache_namelist);
delete cache_namelist;
}
//
// Get the clip range
//
if(master_notation_file->GetEntry(starting_page_name, "clip" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f %f", &clipNear, &clipFar);
if(debug_printing)
std::cout<<"Clip Range "<<TempStringPtr<<"\n";
}
//
// Background color
//
if(master_notation_file->GetEntry(starting_page_name, "backgnd" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f", &backgroundRed, &backgroundGreen, &backgroundBlue);
if(debug_printing)
std::cout<<"Background Color "<<TempStringPtr<<"\n";
}
//
// Get the projection, pipe and other view related junk
//
if(master_notation_file->GetEntry(starting_page_name, "viewangle" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f", &viewAngle);
if(debug_printing)
std::cout<<"View Angle "<<TempStringPtr<<"\n";
}
//
// now that we have the viewing angle, screen size, and clip ranges, setup the projection matrix
//
D3DXMatrixIdentity(&mProjectionMatrix);
// DEBUG(bring-up): the camera view matrix is built with D3DXMatrixLookAtRH
// (DPLEyeRenderable, L4VIDRND.cpp) -> visible geometry has NEGATIVE view-space
// Z. A LH projection expects +Z and clips everything (w<0) -> black frame.
// Use the matching RH projection so the mech becomes visible.
// BT (task #20): honor the live window aspect if the window has been resized
// (gWindowAspect, top of file; set by L4NotifyWindowResized on WM_SIZE).
D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI/180.0f),
gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size, clipNear, clipFar);
//mProjectionMatrix(0, 0) *= -1;
mDecalEpsilon = 0.0000005f;
mDecalProjectionMatrix = mProjectionMatrix;
mDecalProjectionMatrix._33 -= mDecalEpsilon;
//
// setup the fog
//
if(master_notation_file->GetEntry(starting_page_name, "fog" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f %f %f", &fogNear, &fogFar, &fogRed, &fogGreen, &fogBlue);
searchLightFogRed = fogRed;
searchLightFogGreen = fogGreen;
searchLightFogBlue = fogBlue;
searchLightFogNear = fogNear;
searchLightFogFar = fogFar;
noSearchLightFogRed = fogRed;
noSearchLightFogGreen = fogGreen;
noSearchLightFogBlue = fogBlue;
noSearchLightFogNear = fogNear;
noSearchLightFogFar = fogFar;
currentFogNear = fogNear;
currentFogFar = fogFar;
// Force a 0-0 black fog on startup
// dpl_SetViewFog(dplMainView, dpl_fog_type_pixel_lin, 0.0, 0.0, 0.0, 0.01, 0.05);
//TODO: for fog testing, just set the values here
mDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
mDevice->SetRenderState(D3DRS_FOGCOLOR, D3DCOLOR_XRGB((int)(255 * fogRed), (int)(255 * fogGreen), (int)(255 * fogBlue)));
mDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
if(debug_printing)
std::cout<<"Fog "<<TempStringPtr<<"\n";
}
else
{
//
// BT port (task #20): the SHIPPED maps define no fog entry at all (verified:
// no 'fog' key in any .MAP source nor in BTL4.RES strings) -- so the world
// edge and the void beyond are fully visible, which no pod player ever saw
// (cockpit FOV + mission design kept views inward). Provide a period-
// plausible default curtain: color matched to the horizon so geometry fades
// into the void seamlessly. env BT_FOG="near far r g b" overrides;
// BT_FOG=0 disables.
//
float fN = 150.0f, fF = 520.0f, fR = 0.44f, fG = 0.44f, fB = 0.62f;
int fogOn = 1;
if (const char *bf = getenv("BT_FOG"))
{
if (bf[0] == '0' && bf[1] == 0)
fogOn = 0;
else
sscanf(bf, "%f %f %f %f %f", &fN, &fF, &fR, &fG, &fB);
}
if (fogOn)
{
fogNear = fN; fogFar = fF; fogRed = fR; fogGreen = fG; fogBlue = fB;
searchLightFogRed = noSearchLightFogRed = fogRed;
searchLightFogGreen = noSearchLightFogGreen = fogGreen;
searchLightFogBlue = noSearchLightFogBlue = fogBlue;
searchLightFogNear = noSearchLightFogNear = fogNear;
searchLightFogFar = noSearchLightFogFar = fogFar;
currentFogNear = fogNear;
currentFogFar = fogFar;
mDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
mDevice->SetRenderState(D3DRS_FOGCOLOR, D3DCOLOR_XRGB((int)(255 * fogRed), (int)(255 * fogGreen), (int)(255 * fogBlue)));
mDevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_LINEAR);
}
}
//
// setup the no searchlight fog if any
//
if(master_notation_file->GetEntry(starting_page_name, "nosearchlightfog" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f %f %f", &noSearchLightFogNear, &noSearchLightFogFar, &noSearchLightFogRed, &noSearchLightFogGreen, &noSearchLightFogBlue);
if(debug_printing)
std::cout<<"nosearchlightfog "<<TempStringPtr<<"\n";
}
//
// setup the ambient light
//
if(master_notation_file->GetEntry(starting_page_name, "ambient" ,&TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f ", &red, &green, &blue);
mEnvAmbient = D3DCOLOR_XRGB((int)(255 * red), (int)(255 * green), (int)(255 * blue));
mDevice->SetRenderState(D3DRS_AMBIENT, mEnvAmbient); // BT: capture for the per-frame re-assert
if(debug_printing)
std::cout<<"Ambient Light "<<TempStringPtr<<"\n";
}
//
// Set up the day/night cloud color
//
if (master_notation_file->GetEntry(starting_page_name, "clouds", &TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f ", &this->mCloudRed, &this->mCloudGreen, &this->mCloudBlue);
}
if (master_notation_file->GetEntry(starting_page_name, "cloudemit", &TempStringPtr))
{
sscanf(TempStringPtr, "%f %f %f ", &this->mCloudEmitRed, &this->mCloudEmitGreen, &this->mCloudEmitBlue);
}
//
// Get a the list of lights from this page
//
if ((light_namelist = master_notation_file->MakeEntryList(starting_page_name, "light")) != NULL)
{
Register_Object(light_namelist);
//
// HACK !!! All the lights must (temporarily) be defined on a single page.
// The lights and their DCS's should really be stored in a light object on
// a chain, or as an actual entity in the simulation. This line will throw
// an exception if this rule is violated
//
if(light_namelist->EntryCount() != 0)
{
if(sceneLightCount != 0)
{
Fail("All lights must be defined on a single INI file page!\n");
}
//
// Create storage space for the dpl_LIGHT and dpl_DCS structures
//
sceneLightCount = light_namelist->EntryCount();
if (sceneLightCount)
{
sceneLight = new D3DLIGHT9[sceneLightCount];
memset(sceneLight, 0, sizeof(D3DLIGHT9) * sceneLightCount);
}
int current_entry = 0;
for(entry = light_namelist->GetFirstEntry(); entry; entry = entry->GetNextEntry(), ++current_entry)
{
//
// Read the parameters for the light
//
sscanf(
(char *)entry->dataReference,
"%f %f %f %f %f %f",
&red,
&green,
&blue,
&x_rotate,
&y_rotate,
&z_rotate);
if(debug_printing)
std::cout<<"Light "<<(char *)entry->dataReference<<"\n";
//
// Create and rotate a dcs to hold the light, and the light itself
//
sceneLight[current_entry].Type = D3DLIGHT_DIRECTIONAL;
sceneLight[current_entry].Diffuse.r = red;
sceneLight[current_entry].Diffuse.g = green;
sceneLight[current_entry].Diffuse.b = blue;
sceneLight[current_entry].Diffuse.a = 1.0f;
D3DXMATRIX rot, rotX, rotY, rotZ;
D3DXMatrixRotationX(&rotX, x_rotate * (PI/180.0f));
D3DXMatrixRotationY(&rotY, y_rotate * (PI/180.0f));
D3DXMatrixRotationZ(&rotZ, z_rotate * (PI/180.0f));
rot = rotZ * rotX * rotY;
D3DXVECTOR3 dir(0, 0, -1);
D3DXVECTOR4 vec;
D3DXVec3Transform(&vec, &dir, &rot);
dir = D3DXVECTOR3(vec.x, vec.y, vec.z);
D3DXVec3Normalize(&dir, &dir);
sceneLight[current_entry].Direction.x = dir.x;
sceneLight[current_entry].Direction.y = dir.y;
sceneLight[current_entry].Direction.z = dir.z;
mDevice->SetLight(current_entry, &sceneLight[current_entry]);
mDevice->LightEnable(current_entry, TRUE);
// sceneLight[current_entry] = dpl_NewLight();
// sceneLightDCS[current_entry] = dpl_NewDCS();
// dpl_AddDCSToScene ( sceneLightDCS[current_entry] );
// dpl_SetLightType ( sceneLight[current_entry], dpl_light_type_directional );
// dpl_SetLightColor ( sceneLight[current_entry], red, green, blue);
// dpl_SetLightDCS ( sceneLight[current_entry], sceneLightDCS[current_entry] );
// dpl_RotateDCS ( sceneLightDCS[current_entry], z_rotate, dpl_Z );
// dpl_RotateDCS ( sceneLightDCS[current_entry], x_rotate, dpl_X );
// dpl_RotateDCS ( sceneLightDCS[current_entry], y_rotate, dpl_Y );
// dpl_FlushLight ( sceneLight[current_entry] );
// dpl_FlushDCS ( sceneLightDCS[current_entry] );
}
}
//
// Get rid of the light entry list
//
Unregister_Object(light_namelist);
delete light_namelist;
}
//
// Get the list of PSFX effects we should load into RAM. RECONSTRUCTED
// (was stubbed with ReadPSFX): the "psfxN=file.pfx" entries on the visited
// pages ([pfx_day]/[pfx_night], include-reached from the mission's arena
// page) bind each dpl effect NUMBER to its authentic .PFX definition,
// loaded into the BT particle layer (BTLoadPfxFile / BTStartPfx / BTDrawPfx
// at the top of this file) that DPLIndependantEffect's <100 arm consumes.
//
if ((psfx_namelist = master_notation_file->MakeEntryList(starting_page_name, "psfx")) != NULL)
{
extern int BTLoadPfxFile_slot(const char *file_name, int slot);
int pfx_loaded = 0;
for (entry = psfx_namelist->GetFirstEntry();
entry;
entry = entry->GetNextEntry())
{
int psfx_number = atoi(entry->GetName() + 4); // "psfxN"
const char *psfx_file_name = entry->GetChar();
pfx_loaded += BTLoadPfxFile_slot(psfx_file_name, psfx_number);
}
DEBUG_STREAM << "[pfx] page '" << starting_page_name << "': "
<< pfx_loaded << " effect definitions loaded\n" << std::flush;
delete psfx_namelist;
}
////
//// (original stubbed loader retained for provenance)
////
//if ((psfx_namelist = master_notation_file->MakeEntryList(starting_page_name, "psfx")) != NULL)
//{
// const char *psfx_file_name;
// int psfx_number;
// //
// // create all the PSFX on the current page
// //
// for( entry = psfx_namelist->GetFirstEntry();
// entry;
// entry = entry->GetNextEntry())
// {
// psfx_number = atoi(entry->GetName()+4);
// psfx_file_name = entry->GetChar();
// if(debug_printing)
// std::cout<<"psfx"<<psfx_number<<" "<<psfx_file_name<<"\n";
// if(psfx_number < 0 || psfx_number > MAX_PSFX_COUNT-1)
// {
// Fail("PSFX id number was not in the allowed range");
// }
// //
// // See if we are overwriting an existing psfx
// //
// if(myPSFXDescriptons[psfx_number])
// {
// std::cout<<"psfx#"<<psfx_number<<" already existed from an earlier page, redefining it\n";
// delete myPSFXDescriptons[psfx_number];
// Unregister_Pointer(myPSFXDescriptons[psfx_number]);
// }
//// DEBUG_STREAM<<"Loading PSFX# "<<psfx_number<<" from file "<<psfx_file_name<<"\n" << std::flush;
// myPSFXDescriptons[psfx_number] = ReadPSFX(psfx_file_name);
// Register_Pointer(myPSFXDescriptons[psfx_number]);
// }
// //
// // Get rid of the PSFX entry list
// //
// delete psfx_namelist;
//}
//
// Get a the list of special effects
//
if ((specialfx_namelist = master_notation_file->MakeEntryList(starting_page_name, "effect")) != NULL)
{
const char *effect_page_name;
int status, spfx_number, version;
INDIE_EFFECT spfx;
memset(&spfx, 0, sizeof(PARTICLE_EFFECT));
Register_Object(specialfx_namelist);
//
// create all the effects on the current page
//
for( entry = specialfx_namelist->GetFirstEntry();
entry;
entry = entry->GetNextEntry())
{
//
// Get the page with this effect on it
//
effect_page_name = entry->GetChar();
if(debug_printing)
std::cout<<"specialfx"<<spfx_number<<" "<<effect_page_name<<"\n";
master_notation_file->GetEntry(effect_page_name, "id", &spfx_number);
version = 1;
master_notation_file->GetEntry(effect_page_name, "version", &version);
if (version < 2)
continue;
master_notation_file->GetEntry(effect_page_name, "texbounds", &TempStringPtr);
sscanf(TempStringPtr, "%f %f %f %f", &spfx.textureBounds.left, &spfx.textureBounds.top, &spfx.textureBounds.right, &spfx.textureBounds.bottom);
master_notation_file->GetEntry(effect_page_name, "rotate", (int*)&spfx.rotate);
master_notation_file->GetEntry(effect_page_name, "size", &spfx.fragSize);
master_notation_file->GetEntry(effect_page_name, "velocity", &spfx.velocity);
master_notation_file->GetEntry(effect_page_name, "varianceX", &spfx.varianceX);
master_notation_file->GetEntry(effect_page_name, "varianceY", &spfx.varianceY);
master_notation_file->GetEntry(effect_page_name, "varianceZ", &spfx.varianceZ);
master_notation_file->GetEntry(effect_page_name, "gravity", &spfx.gravity);
master_notation_file->GetEntry(effect_page_name, "count", &spfx.fragCount);
master_notation_file->GetEntry(effect_page_name, "life", &spfx.fragLifetime);
if (!master_notation_file->GetEntry(effect_page_name, "max_repeat", &spfx.maxRepeat))
spfx.maxRepeat = 30.0;
NameList *color_list = master_notation_file->MakeEntryList(effect_page_name, "color");
int i = 0;
for (NameList::Entry *color_entry = color_list->GetFirstEntry(); color_entry; color_entry = color_entry->GetNextEntry())
{
memset(&spfx.colors[i], 0, sizeof(COLOR_POINT));
spfx.colors[i].active = true;
int a, r, g, b;
sscanf(color_entry->GetChar(), "%f %d %d %d %d", &spfx.colors[i].time, &a, &r, &g, &b);
spfx.colors[i].color.argb = D3DCOLOR_ARGB(a, r, g, b);
i++;
}
for (;i < COLOR_POINT_COUNT; i++)
{
memset(&spfx.colors[i], 0, sizeof(COLOR_POINT));
}
//
// install the effect
//
int isIndie = 0;
master_notation_file->GetEntry(effect_page_name, "independent", &isIndie);
if (isIndie)
{
master_notation_file->GetEntry(effect_page_name, "maxIssue", &spfx.maxIssue);
master_notation_file->GetEntry(effect_page_name, "releasePeriod", &spfx.releasePeriod);
master_notation_file->GetEntry(effect_page_name, "duration", &spfx.duration);
spfx.id = 1000 + spfx_number;
myPSFXDescriptons[spfx_number] = spfx;
}
else
ParticleEngine::InstallEffect(spfx_number, *((PARTICLE_EFFECT*)&spfx));
}
//
// Get rid of the effect entry list
//
Unregister_Object(specialfx_namelist);
delete specialfx_namelist;
}
//--------------------------------------------------
// Make a list of all the include pages on this page
//--------------------------------------------------
if ((include_pages = master_notation_file->MakeEntryList(starting_page_name, "include")) != NULL)
{
Register_Object(include_pages);
//
// Recursively process all the include pages
//
for( entry = include_pages->GetFirstEntry();
entry;
entry = entry->GetNextEntry())
{
// Get the name of the next include page
next_include_page_name = (char *)entry->dataReference;
// Process the next page recursively through this routine
DPLReadINIPage(
master_notation_file,
next_include_page_name,
mission,
debug_printing);
if(debug_printing)
std::cout<<"DPLReadINIPage returned to "<<starting_page_name<<"\n";
}
Unregister_Object(include_pages);
delete include_pages;
}
//
// Process a compare and branch if there is one on this page
//
if(master_notation_file->GetEntry(starting_page_name, "compare" ,&compare_source))
{
Logical
match;
NameList
*branch_pages;
CString
compare_strings[10],
wild_card("*"),
token_string,
target_string,
master_compare_string(compare_source);
int
token_count;
ResourceFile
*this_resource_file;
//
// Get a pointer to our resource file so we can find the name of the map
// resource later in this process.
//
Check(application);
this_resource_file = application->GetResourceFile();
Check(this_resource_file);
//
// Print the compare string
//
if(debug_printing)
std::cout<<"compare = "<<master_compare_string<<"\n";
//
// Break the compare string apart, fetch the mission variables for each
// item in it and store them in the compare_strings array for later.
//
token_count = 0;
while(target_string = master_compare_string.GetNthToken(token_count,","))
{
// std::cout<<"Mission variable "<<token_count<<": ";
if(strcmp(target_string,"location") == 0)
{
compare_strings[token_count] = (this_resource_file->FindResourceDescription(mission->GetMapID()))->resourceName;
// std::cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
}
else if(strcmp(target_string,"time") == 0)
{
compare_strings[token_count] = mission->GetMissionTime();
// std::cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
}
else if(strcmp(target_string,"weather") == 0)
{
compare_strings[token_count] = mission->GetMissionWeather();
// std::cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
}
else if(strcmp(target_string,"scenario") == 0)
{
compare_strings[token_count] = mission->GetScenarioName();
// std::cout<<target_string<<" = "<<compare_strings[token_count]<<"\n";
}
else
{
std::cout<<"Mission variable "<<token_count<<": "<<target_string<<" IS AN UNKNOWN MISSION VARIABLE\n";
}
token_count++;
}
//
// Print the version of the compare string with the actual mission variables
// in it
//
int i;
if(debug_printing)
{
std::cout<<"comparing to: ";
for(i = 0; i<token_count; i++)
{
std::cout<<compare_strings[i]<<", ";
}
std::cout<<"\n";
}
//
// Now compare all the branch pages till we have done them all or found a match
//
branch_pages = master_notation_file->MakeEntryList(starting_page_name,"branch");
Register_Object(branch_pages);
for( entry = branch_pages->GetFirstEntry();
entry;
entry = entry->GetNextEntry())
{
CString
branch_command,
match_string;
int
compare_token;
//
// Get the text of the branch command and print it
//
branch_command = (char *)entry->dataReference;
if(debug_printing)
std::cout<<"branch = "<<branch_command<<"\n";
//
// Grab each token out of the branch string and compare it to the
// corrisponding item in compare_strings. This also checks for the
// wildcard cararcter. Any failure to match skips this branch string.
//
match = True;
for(compare_token = 0; compare_token<token_count; compare_token++)
{
match_string = branch_command.GetNthToken(compare_token,",");
if(match_string != compare_strings[compare_token] && match_string != wild_card)
{
match = False;
// std::cout<<match_string<<"!="<<compare_strings[compare_token]<<"\n";
break;
}
else
{
// std::cout<<match_string<<"=="<<compare_strings[compare_token]<<"\n";
}
}
//
// See if we had a successful match
//
if(match)
{
// Match! Get the name of the page to branch to and do it.
match_string = branch_command.GetNthToken(compare_token,",");
DPLReadINIPage(
master_notation_file,
match_string,
mission,
debug_printing);
if(debug_printing)
std::cout<<"DPLReadINIPage returned to "<<starting_page_name<<"\n";
// now break out of the loop because we only allow one page to match
break;
}
}
Unregister_Object(branch_pages);
delete branch_pages;
//
// If we found no match, yell about it
//
if(!match)
{
DEBUG_STREAM << "BRANCH ERROR IN '" <<
master_notation_file->GetFileName() <<
"' NO MATCH FOUND!!" << std::endl;
}
}
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Read environment
// routine to read in an environment from a notation file and setup lights,
// spfx and such...
//
void
DPLRenderer::DPLReadEnvironment(Mission *mission)
{
//STUBBED: DPL RB 1/14/07
char *L4DPLcfg;
// const char
// *cc;
NotationFile *renderer_environment;
Logical debug_printing;
//
// Get the name of the notation file from an environmental
//
L4DPLcfg = getenv("L4DPLCFG");
if (!L4DPLcfg)
{
DEBUG_STREAM<<"L4DPLCFG not defined, using dpldflt.ini!\n" << std::flush;
L4DPLcfg = "dpldflt.ini";
}
//
// Read the environment out of a notation file
//
renderer_environment = new NotationFile(L4DPLcfg);
Register_Object(renderer_environment);
if (renderer_environment->PageCount() == 0)
{
DEBUG_STREAM<<"Renderer config notation file "<<L4DPLcfg<<"was empty, can't continue\n" << std::flush;
Verify(renderer_environment->PageCount() == 0);
}
//
// Register the notation file and check it
//
Check(renderer_environment);
// BT (task #20): reset the day/night search-path accumulators before reading
// this mission's INI; DPLReadINIPage prepends each objectpath/materialpath/
// texmappath entry as it visits pages (most-specific ends up first).
mObjectPaths.clear();
mMaterialPaths.clear();
mTexmapPaths.clear();
if (renderer_environment->PageExists("main"))
{
if (!(renderer_environment->GetLogicalEntry("main", "debug", &debug_printing)))
{
debug_printing=False;
}
DPLReadINIPage(renderer_environment, "main", mission, debug_printing);
#if 0
if ((cc = dpl_GetObjectFilePath()) == NULL || *cc == NULL)
{
dpl_SetObjectFilePath ( ".\\video", NULL, NULL );
}
if ((cc = dpl_GetTexmapFilePath()) == NULL || *cc == NULL)
{
dpl_SetTexmapFilePath ( ".\\video", NULL, NULL );
}
if ((cc = dpl_GetMaterialFilePath()) == NULL || *cc == NULL)
{
dpl_SetMaterialFilePath ( ".\\video", NULL, NULL );
}
#endif
}
else
{
if (!(renderer_environment->GetLogicalEntry("dpl_config", "debug", &debug_printing)))
{
debug_printing=False;
}
std::cout<<"READING OLD FORMAT INI FILE '"<<renderer_environment->GetFileName()<<"'---hope this works\n";
// dpl_SetObjectFilePath ( ".\\video", NULL, NULL );
// dpl_SetTexmapFilePath ( ".\\video", NULL, NULL );
// dpl_SetMaterialFilePath ( ".\\video", NULL, NULL );
DPLReadINIPage(renderer_environment, "dpl_config", mission, debug_printing);
}
if (debug_printing)
{
// DEBUG_STREAM << "ObjectFilePath '" << dpl_GetObjectFilePath() << "'" << std::endl << std::flush;
// DEBUG_STREAM << "TexmapFilePath '" << dpl_GetTexmapFilePath() << "'" << std::endl << std::flush;
// DEBUG_STREAM << "MaterialFilePath '" << dpl_GetMaterialFilePath() << "'" << std::endl << std::flush;
}
//
// BT (task #20): push the accumulated day/night search-path priority to the
// BGF/BMF/texture loader. objectpath->.bgf, materialpath->.bmf, texmappath->
// textures. This invalidates the loader's cached indices so the next object
// load (the mission entities, incl. the sky dome) picks the correct
// time-of-day variant of every colliding stem.
//
// Apply the day/night SEARCH-PATH priority for ALL indices (default ON now).
// The mat\day material libs carry the AUTHENTIC per-time-of-day colour RAMPs
// (rock 0.25,0.21,0.16->0.8,0.5,0.4 warm tan) which the loader now bakes into
// the terrain textures -- so preferring mat\day gives the correct warm desert.
// (Earlier BT_MATPRI grayed the terrain ONLY because the ramp wasn't applied:
// mat\day materials have no diffuse and fell back to gray. With the ramp bake
// live they are correct.) BT_MATPRI=0 falls back to first-match (generic
// GEO/*.BMF gray ramps + the vertex tint) for A/B.
SetVideoPathPriority("bgf", mObjectPaths);
{
const char *mp = getenv("BT_MATPRI");
if (mp == 0 || mp[0] != '0')
{
SetVideoPathPriority("bmf", mMaterialPaths);
SetVideoPathPriority("img", mTexmapPaths);
}
}
if (debug_printing)
{
DEBUG_STREAM << "[env] object paths (" << mObjectPaths.size() << "), material paths ("
<< mMaterialPaths.size() << "), texmap paths (" << mTexmapPaths.size()
<< ") -- most-specific first\n" << std::flush;
}
//
// Close the notation file
//
Verify( !renderer_environment->IsDirty() );
Unregister_Object(renderer_environment);
delete renderer_environment;
//
// Flush all the things we've set/changed here
//
// dpl_FlushView(dplMainView);
return;
}
//
//#############################################################################
// Destructor for the video renderer
//#############################################################################
//
DPLRenderer::~DPLRenderer()
{
delete gOpNames;
delete gReplacementData;
if (mPrimaryIndex != NULL)
{
delete mPrimaryIndex;
}
if (mSecondaryIndex != NULL)
{
delete mSecondaryIndex;
}
if (mAux1Index != NULL)
{
delete mAux1Index;
}
if (mAux2Index != NULL)
{
delete mAux2Index;
}
SAFE_RELEASE(mDevice);
SAFE_RELEASE(gD3D);
//STUBBED: DPL RB 1/14/07
//int
// psfx_number;
////------------------------------------------------------
//// release allocated memory used by dump_frame_buffer()
////------------------------------------------------------
//dump_frame_buffer(NULL, NULL, NULL, NULL);
////
//// Delete any memory allocated to hold psfx effect descriptions
////
//for( psfx_number = 0; psfx_number < MAX_PSFX_COUNT; psfx_number++)
//{
// if(myPSFXDescriptons[psfx_number])
// {
// delete myPSFXDescriptons[psfx_number];
// Unregister_Pointer(myPSFXDescriptons[psfx_number]);
// myPSFXDescriptons[psfx_number] = NULL;
// }
//}
////
////~~~~~~~~~~~~~~~~~~
//// Delete all Lights
////~~~~~~~~~~~~~~~~~~
////
//if (ambientLight)
//{
// Check_Pointer(ambientLight);
// Unregister_Pointer(ambientLight);
// dpl_DeleteLight(ambientLight);
//}
//if (sceneLightDCS && sceneLight)
//{
// Check_Pointer(sceneLightDCS);
// for(int ii=0;ii<sceneLightCount;++ii)
// {
// Check_Pointer(sceneLight[ii]);
// Check_Pointer(sceneLightDCS[ii]);
// dpl_RemoveDCSFromScene(sceneLightDCS[ii]);
// dpl_DeleteDCS(sceneLightDCS[ii]);
// Unregister_Pointer(sceneLight[ii]);
// Unregister_Pointer(sceneLightDCS[ii]);
// }
// Unregister_Pointer(sceneLight);
// delete[] sceneLight;
// Unregister_Pointer(sceneLightDCS);
// delete[] sceneLightDCS;
//}
//Unregister_Object(DPLHeap);
//delete DPLHeap;
// DPLHeap = NULL;
}
//
//#############################################################################
// TestInstance
//#############################################################################
//
Logical
DPLRenderer::TestInstance() const
{
VideoRenderer::TestInstance();
Verify(dplMainView != NULL);
Verify(dplMainZone != NULL);
return True;
}
//
//#############################################################################
// FlushBitSliceTexture
// Sends the bitslice texture to the division card.
// LoadBitSliceTexture
// Handles building of a bitmap into a texture map for the Division card, this
// specifically deals with merging a 1 bit bitmap into a 4 bit bitslice texmap.
//#############################################################################
//
void
DPLRenderer::FlushBitSliceTexture(
unsigned int *local_storage)
{
//STUBBED: DPL RB 1/14/07
//int
// status;
//dpl_TEXTURE *temp_texture =
// dpl_LookupTexture("bmap:bmap1_tex", dpl_lookup_normal, &status);
//if(temp_texture == NULL)
//{
// DEBUG_STREAM<<"WARNING, textures for player names not defined\n" << std::flush;
//}
//else
//{
// dpl_TEXMAP *temp_texmap =
// dpl_GetTextureTexmap(temp_texture);
// if(temp_texmap != NULL)
// {
// dpl_TexmapTexels2D ( temp_texmap, local_storage,
// 128,
// 64,
// 4 );
// dpl_FlushTexmap(temp_texmap);
// }
// else
// {
// DEBUG_STREAM<<"WARNING, texmap for player names not defined\n" << std::flush;
// }
//}
}
void DPLRenderer::LoadBitSliceTexture(BitMap *bitmap_to_load, LPDIRECT3DTEXTURE9 local_storage)
{
// lock the texture and grab a pointer to the data
D3DLOCKED_RECT lockedRect;
local_storage->LockRect(0, &lockedRect, NULL, D3DLOCK_DISCARD);
//
// Figure out the position to start copying the data at
//
Word *word_pointer = bitmap_to_load->Data.MapPointer;
__int16 *pixel_pointer = (__int16*)lockedRect.pBits;
//
// Actually process the texels
//
int bit_mask = 0x8000;
for(int y = 0; y < 32; y++)
{
for(int x = 0; x < 128; x++)
{
if (*word_pointer & bit_mask)
*(pixel_pointer++) = 0xFFFF;
else
*(pixel_pointer++) = 0x0000;
bit_mask >>= 1;
if (bit_mask == 0)
{
bit_mask = 0x8000;
word_pointer++;
}
}
}
// unlock the texture now that we're done updating it
local_storage->UnlockRect(0);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CacheExplosionScripts
// Handles code to preload the morph and other caches with things needed by
// explosion scripts. Should be called at startup, once for each.
//
void
DPLRenderer::CacheExplosionScripts(
int script_select) // The script to be cached
{
//STUBBED: DPL RB 1/14/07
// dpl_OBJECT
// *object;
// //
// // Load the morph targets into the MUNGA object cache
// //
// switch(script_select)
// {
// case 104: // Thor death explosion
// case 106: // Thor death explosion
// {
// dpl_LoadObject("flamebig.bgf", dpl_load_normal);
// dpl_LoadObject("thrdbr.bgf" , dpl_load_normal);
// dpl_LoadObject("ldbr.bgf" , dpl_load_normal);
// dpl_LoadObject("flamesml.bgf", dpl_load_normal);
//// DEBUG_STREAM << "explosion "<< script_select<<" cached\n" << std::flush;
// break;
// }
// }
}
//
//#############################################################################
// ExplosionScripts
// Contains several scripts for running various types of explosion effects.
//#############################################################################
//
void
DPLRenderer::ExplosionScripts(
Entity *entity, // The entity we are dealing with
ResourceDescription* ,//model_resource, // Pointer to the video resource
ViewFrom ,//view_type, // Type of reference (inside/outside...etc.)
int script_select)
{
//STUBBED: DPL RB 1/14/07
// dpl_OBJECT
// *object_1;
// //
// // Construct a root renderable to hang the explosion effects on
// //
// RootRenderable *this_root =
// new RootRenderable(
// entity, // Entity to attach the renderable to
// RootRenderable::Dynamic, // How/when to execute the renderable
// NULL, // object to hang on the DCS, may be a list later <NULL>
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL); // intersection mask for the object
// Register_Object(this_root);
// //
// // Select which script to construct
// //
// switch(script_select)
// {
// case 0: // Shock wave
// {
// //
// // Load object(s) we will be using for the explosions
// //
// object_1 = dpl_LoadObject("shock.bgf", dpl_load_normal);
// //
// // Setup control variables and transforms we need
// //
// Vector3D scaling_velocity_1(3.5, 3.5, 3.5);
// Vector3D velocity_accel_1(0.0, 0.0, 0.0);
// LinearMatrix explosion_1_offset(True);
// //
// // Create the scaling explosion renderables
// //
// ScalingExplosionRenderable *explosion_1 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_1_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_1, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_1,
// 0.0f,
// NULL); // -.25 gravity is normal
// Register_Object(explosion_1);
// //
// // Create a sweep renderable to drive the material morph
// //
// SweepRenderable *sweep_1 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 0.5f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// NULL);
// Register_Object(sweep_1);
// //
// // Lookup a texture that we can use for the morphing materials
// //
// int
// status;
// dpl_TEXTURE *effect_texture =
// dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
// if (effect_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n" << std::flush;
// //
// // Setup a morph material renderable for each explosion shape
// //
// MorphMaterialRenderable *morph_material_1 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 1.0f,1.0f,1.0f, // Material's ambient component
// 1.0f,1.0f,1.0f, // Material's emissive component
// 1.0f,1.0f,1.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 4.0f,0.2f,-0.05f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 0, // Material's Fog Imunity value
// 0.5f,0.5f,0.5f, // Material's ambient component
// 0.5f,0.5f,0.5f, // Material's emissive component
// 0.5f,0.5f,0.5f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
//// 0.25f,0.25f,-0.05f, // Material's opacity
// 0.1f,0.1f,-0.05f, // Material's opacity
// 0.0f, // Material's Z dither value
// sweep_1->GetSweepAttribute());
// Register_Object(morph_material_1);
// dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
// if(!explosion_1_instance)
// Fail("explosion_1_instance came back null\n");
// dpl_SetInstanceFrontMaterial(explosion_1_instance, morph_material_1->GetMaterial());
// dpl_FlushInstance(explosion_1_instance);
// break;
// }
// case 1: // Big explosion
// {
// //
// // Load object(s) we will be using for the explosions
// //
// object_1 = dpl_LoadObject("exp.bgf", dpl_load_normal);
// //
// // Setup control variables and transforms we need
// //
// Vector3D scaling_velocity_1(0.15, 0.15, 0.15);
// Vector3D scaling_velocity_2(0.18, 0.22, 0.18);
// Vector3D velocity_accel_1(0.0, 0.0, 0.0);
// Vector3D velocity_accel_2(0.0, 0.0, 0.0);
// LinearMatrix explosion_1_offset(True);
// LinearMatrix explosion_2_offset(True);
// Point3D explosion_2_translate(0.0f, 0.0f, 0.0f);
// EulerAngles explosion_2_rotate(0.0f, 1.0f, 0.0f);
// explosion_2_offset = explosion_2_rotate;
// explosion_2_offset = explosion_2_translate;
// //
// // Create the scaling explosion renderables
// //
// ScalingExplosionRenderable *explosion_1 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_1_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_1, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_1,
// 0.0f,
// NULL); // -.25 gravity is normal
// Register_Object(explosion_1);
// ScalingExplosionRenderable *explosion_2 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_2_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_2, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_2,
// 0.0f,
// NULL);
// Register_Object(explosion_2);
// //
// // Create a sweep renderable to drive the material morph
// //
// SweepRenderable *sweep_1 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 3.0f,
// 1, // number of times to cycle before stopping
// NULL); // How long to take to sweep from 0 to 1
// Register_Object(sweep_1);
// SweepRenderable *sweep_2 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 2.0f,
// 1, // number of times to cycle before stopping
// NULL); // How long to take to sweep from 0 to 1
// Register_Object(sweep_2);
// //
// // Lookup a texture that we can use for the morphing materials
// //
// int
// status;
// dpl_TEXTURE *effect_texture =
// dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
// if (effect_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n" << std::flush;
// //
// // Setup a morph material renderable for each explosion shape
// //
// #if 0
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,1.0f,0.0f, // Material's emissive component
// 1.0f,0.7f,0.2f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 8.0f,0.2f,-0.05f, // Material's opacity
// #endif
// MorphMaterialRenderable *morph_material_1 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,0.2f,0.0f, // Material's emissive component
// 1.0f,0.2f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 10.0f,0.6f,-0.05f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 0, // Material's Fog Imunity value
// 0.3f,0.0f,0.0f, // Material's ambient component
// 0.5f,0.0f,0.0f, // Material's emissive component
// 0.2f,0.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 0.25f,0.25f,-0.05f, // Material's opacity
// 15.0f, // Material's Z dither value
// sweep_1->GetSweepAttribute());
// Register_Object(morph_material_1);
// MorphMaterialRenderable *morph_material_2 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,0.6f,0.3f, // Material's emissive component
// 1.0f,0.4f,0.2f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 8.0f,0.2f,-0.05f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 0, // Material's Fog Imunity value
// 0.1f,0.0f,0.0f, // Material's ambient component
// 0.0f,0.0f,0.0f, // Material's emissive component
// 0.1f,0.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 0.25f,0.25f,-0.05f, // Material's opacity
// 15.0f, // Material's Z dither value
// sweep_2->GetSweepAttribute());
// Register_Object(morph_material_2);
// //
// // Override the materials in the two explosion shapes using these morph materials
// //
// dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
// dpl_INSTANCE *explosion_2_instance = dpl_GetDCSInstance(explosion_2->GetDCS(), 1);
//
// if(!explosion_1_instance)
// Fail("explosion_1_instance came back null\n");
// if(!explosion_2_instance)
// Fail("explosion_2_instance came back null\n");
//
// dpl_SetInstanceFrontMaterial(explosion_1_instance, morph_material_1->GetMaterial());
// dpl_SetInstanceFrontMaterial(explosion_2_instance, morph_material_2->GetMaterial());
//
// dpl_FlushInstance(explosion_1_instance);
// dpl_FlushInstance(explosion_2_instance);
// break;
// }
// case 2: // Big explosion
// {
// //
// // Load object(s) we will be using for the explosions
// //
// object_1 = dpl_LoadObject("exp.bgf", dpl_load_normal);
// //
// // Setup control variables and transforms we need
// //
// Vector3D scaling_velocity_1(1.0, 0.2, 1.0);
// Vector3D scaling_velocity_2(0.75, 0.75, 0.75);
// Vector3D velocity_accel_1(-0.00725, -0.004, -0.00725);
//// Vector3D velocity_accel_1(-0.00925, -0.00925, -0.00925);
// Vector3D velocity_accel_2(-0.01388, -0.01388, -0.01388);
// LinearMatrix explosion_1_offset(True);
// LinearMatrix explosion_2_offset(True);
// Point3D explosion_2_translate(0.0f, 0.0f, 0.0f);
// EulerAngles explosion_2_rotate(0.0f, 1.0f, 0.0f);
// explosion_2_offset = explosion_2_rotate;
// explosion_2_offset = explosion_2_translate;
// //
// // Create the scaling explosion renderables
// //
// ScalingExplosionRenderable *explosion_1 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_1_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_1, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_1,
// 0.0f,
// NULL); // -.25 gravity is normal
// Register_Object(explosion_1);
// ScalingExplosionRenderable *explosion_2 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_2_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_2, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_2,
// 0.0f,
// NULL);
// Register_Object(explosion_2);
// //
// // Create a sweep renderable to drive the material morph
// //
// SweepRenderable *sweep_1 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 3.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// NULL);
// Register_Object(sweep_1);
// SweepRenderable *sweep_2 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 2.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// NULL);
// Register_Object(sweep_2);
// //
// // Lookup a texture that we can use for the morphing materials
// //
// int
// status;
// dpl_TEXTURE *effect_texture =
// dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
// if (effect_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n" << std::flush;
// //
// // Setup a morph material renderable for each explosion shape
// //
// #if 0
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,1.0f,0.0f, // Material's emissive component
// 1.0f,0.7f,0.2f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 8.0f,0.2f,-0.05f, // Material's opacity
// #endif
// MorphMaterialRenderable *morph_material_1 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,0.2f,0.0f, // Material's emissive component
// 1.0f,0.2f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 8.0f,0.2f,-0.05f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 0, // Material's Fog Imunity value
// 0.3f,0.0f,0.0f, // Material's ambient component
// 0.5f,0.0f,0.0f, // Material's emissive component
// 0.2f,0.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 0.25f,0.25f,-0.05f, // Material's opacity
// 15.0f, // Material's Z dither value
// sweep_1->GetSweepAttribute());
// Register_Object(morph_material_1);
// MorphMaterialRenderable *morph_material_2 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 0.9f,0.0f,0.0f, // Material's ambient component
// 1.0f,0.6f,0.4f, // Material's emissive component
// 1.0f,0.3f,0.2f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 8.0f,0.2f,-0.05f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 0, // Material's Fog Imunity value
// 0.1f,0.0f,0.0f, // Material's ambient component
// 0.0f,0.0f,0.0f, // Material's emissive component
// 0.0f,0.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 0.25f,0.25f,-0.05f, // Material's opacity
// 15.0f, // Material's Z dither value
// sweep_2->GetSweepAttribute());
// Register_Object(morph_material_2);
// //
// // Override the materials in the two explosion shapes using these morph materials
// //
// dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
// dpl_INSTANCE *explosion_2_instance = dpl_GetDCSInstance(explosion_2->GetDCS(), 1);
//
// if(!explosion_1_instance)
// Fail("explosion_1_instance came back null\n");
// if(!explosion_2_instance)
// Fail("explosion_2_instance came back null\n");
//
// dpl_SetInstanceFrontMaterial(explosion_1_instance, morph_material_1->GetMaterial());
// dpl_SetInstanceFrontMaterial(explosion_2_instance, morph_material_2->GetMaterial());
//
// dpl_FlushInstance(explosion_1_instance);
// dpl_FlushInstance(explosion_2_instance);
// break;
// }
// case 3: // Sparks
// {
// //
// // Load object(s) we will be using for the explosions
// //
// object_1 = dpl_LoadObject("spk1.bgf", dpl_load_normal);
// //
// // Setup control variables and transforms we need
// //
// Vector3D scaling_velocity_1(0.1, 0.1, 0.1);
// Vector3D velocity_accel_1(0.0, 0.0, 0.0);
// LinearMatrix explosion_1_offset(True);
// //
// // Create the scaling explosion renderables
// //
// ScalingExplosionRenderable *explosion_1 =
// new ScalingExplosionRenderable(
// entity, // Entity to attach the renderable to
// ScalingExplosionRenderable::Dynamic, // How/when to execute the renderable
// object_1, // This will be the scaling explosion object
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS(), // the parent DCS we will be offsetting from
// &explosion_1_offset, // offset matrix to be applied prior to joint DCS
// &scaling_velocity_1, // Effect control vector, Y is acceleration, X, Z are velocity
// &velocity_accel_1,
// -0.15f,
// NULL); // -.25 gravity is normal
// Register_Object(explosion_1);
// //
// // Create a sweep renderable to drive the material morph
// //
// SweepRenderable *sweep_1 =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 0.5f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// NULL);
// Register_Object(sweep_1);
// //
// // Lookup a texture that we can use for the morphing materials
// //
// int
// status;
// dpl_TEXTURE *effect_texture =
// dpl_LookupTexture ( "btfx:firesmoke1_scr_tex", dpl_lookup_normal, &status );
// if (effect_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:firesmoke1_scr_tex for an effect\n" << std::flush;
// //
// // Setup a morph material renderable for each explosion shape
// //
// MorphMaterialRenderable *morph_material_1 =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 1.0f,1.0f,0.0f, // Material's ambient component
// 1.0f,1.0f,0.0f, // Material's emissive component
// 1.0f,1.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 1.0f,1.0f,1.0f, // Material's opacity
// effect_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 2, // Material's Fog Imunity value
// 0.9f,0.0f,0.0f, // Material's ambient component
// 0.9f,0.0f,0.0f, // Material's emissive component
// 0.9f,0.0f,0.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0, // Material's specular component
// 1.0f,1.0f,1.0f, // Material's opacity
// 0.0f, // Material's Z dither value
// sweep_1->GetSweepAttribute());
// Register_Object(morph_material_1);
// dpl_INSTANCE *explosion_1_instance = dpl_GetDCSInstance(explosion_1->GetDCS(), 1);
// if(!explosion_1_instance)
// Fail("explosion_1_instance came back null\n");
// dpl_SetInstanceFrontMaterial(explosion_1_instance, morph_material_1->GetMaterial());
// dpl_FlushInstance(explosion_1_instance);
// break;
// }
// case 4: // A huge mech explodes (yawn)
// {
// Point3D null_offset(0.0f, 0.0f, 0.0f);
// LinearMatrix null_offset_matrix(True);
// Point3D local_height(0.0f, 6.2f, 0.0f); // height of Thor
// LinearMatrix local_offset(True);
// local_offset = local_height;
// DPLStaticChildRenderable *local_root =
// new DPLStaticChildRenderable(
// entity,
// false,
// NULL,
// dpl_isect_mode_obj,
// NULL,
// local_offset,
// this_root->GetDCS());
// Register_Object(local_root);
// //
// // Load morph sources we need for the effect
// //
// dpl_OBJECT *thor_debris_object = dpl_LoadObject("thrdbr.bgf", dpl_load_normal);
// dpl_OBJECT *large_debris_object = dpl_LoadObject("ldbr.bgf", dpl_load_normal);
// dpl_OBJECT *small_flames_object = dpl_LoadObject("flamesml.bgf", dpl_load_normal);
// //
// // Load morph targets we need for the effect
// //
// //------------------------------------------------
// // Renderables to handle the chunks and fireballs
// //------------------------------------------------
//#if 0
// OneShotDelayRenderable *fireball_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f); // How long to wait before raising the trigger
// Register_Object(fireball_delay);
// Point3D my_offset(0.0, 4.0, 0.0);
// #if DEBUG_LEVEL > 0
// DPLPSFXRenderable* initial_boom =
// #endif
// new DPLPSFXRenderable(
// entity, // Entity to attach the renderable to
// DPLPSFXRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay->GetTriggerAttribute(), // address containing the trigger
// myPSFXDescriptons[7], // pointer to the PFX description
// this_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &my_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(initial_boom);
// #if DEBUG_LEVEL > 0
// DPLEffectRenderable *chunks_effect =
// #endif
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay->GetTriggerAttribute(), // address containing the trigger
// 3, // Chunks // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &null_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(chunks_effect);
// Point3D fireball_offset(1.0f, 5.0f, 1.0f);
// #if DEBUG_LEVEL > 0
// DPLEffectRenderable *fireball_effect =
// #endif
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay->GetTriggerAttribute(), // address containing the trigger
// 15, // Fireball // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &fireball_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(fireball_effect);
//#endif
// OneShotDelayRenderable *fireball_delay_2 =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.55f); // How long to wait before raising the trigger
// Register_Object(fireball_delay_2);
// Point3D fireball_offset_2(-1.0f, 5.0f, -1.0f);
// #if DEBUG_LEVEL > 0
// DPLEffectRenderable *fireball_effect_2 =
// #endif
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay_2->GetTriggerAttribute(), // address containing the trigger
// 15, // Fireball // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &fireball_offset_2); // Offset (or world coordinants if DCS is NULL)
// Register_Object(fireball_effect_2);
// //-----------------------------------------------
// // Renderables to handle static debris
// //-----------------------------------------------
// OneShotDelayRenderable *static_debris_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f); // How long to wait before raising the trigger
// Register_Object(static_debris_delay);
// DPLStaticChildRenderable *mech_debris =
// new DPLStaticChildRenderable(
// entity,
// false,
// thor_debris_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// this_root->GetDCS());
// Register_Object(mech_debris);
// #if DEBUG_LEVEL > 0
// MakeDCSFall *mech_debris_fall =
// #endif
// new MakeDCSFall(
// entity, // Entity to attach the renderable to
// MakeDCSFall::Dynamic, // How/when to execute the renderable
// mech_debris->GetDCS(), // the DCS to control
// -0.025f, // Gravity in meters/sec squared
// static_debris_delay->GetTriggerAttribute()); // true if the instance is on, false if off
// Register_Object(mech_debris_fall);
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *mech_debris_instance = dpl_GetDCSInstance(mech_debris->GetDCS(), 1);
// #if DEBUG_LEVEL > 0
// InstanceSwitchRenderable *mech_debris_instance_switch =
// #endif
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// mech_debris_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
// Register_Object(mech_debris_instance_switch);
//
// DPLStaticChildRenderable *large_debris =
// new DPLStaticChildRenderable(
// entity,
// false,
// large_debris_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// mech_debris->GetDCS());
// Register_Object(large_debris);
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *large_debris_instance = dpl_GetDCSInstance(large_debris->GetDCS(), 1);
// #if DEBUG_LEVEL > 0
// InstanceSwitchRenderable *large_debris_instance_switch =
// #endif
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// large_debris_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
// Register_Object(large_debris_instance_switch);
// //-----------------------------------------------
// // Renderables to handle fires
// //-----------------------------------------------
// OneShotDelayRenderable *fires_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f); // How long to wait before raising the trigger
// Register_Object(fires_delay);
// DPLStaticChildRenderable *fires =
// new DPLStaticChildRenderable(
// entity,
// false,
// NULL,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// this_root->GetDCS());
// Register_Object(fires);
// #if DEBUG_LEVEL > 0
// MakeDCSFall *fires_fall =
// #endif
// new MakeDCSFall(
// entity, // Entity to attach the renderable to
// MakeDCSFall::Dynamic, // How/when to execute the renderable
// fires->GetDCS(), // the DCS to control
// -0.01f, // Gravity in meters/sec squared
// fires_delay->GetTriggerAttribute()); // true if the instance is on, false if off
// Register_Object(fires_fall);
// DPLStaticChildRenderable *small_flames =
// new DPLStaticChildRenderable(
// entity,
// false,
// small_flames_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// fires->GetDCS());
// Register_Object(small_flames);
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *small_flames_instance = dpl_GetDCSInstance(small_flames->GetDCS(), 1);
// #if DEBUG_LEVEL > 0
// InstanceSwitchRenderable *small_flames_instance_switch =
// #endif
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// small_flames_instance, // the instance to control
// True, // Instance is on when trigger is...
// fires_delay->GetTriggerAttribute());
// Register_Object(small_flames_instance_switch);
//
// dpl_OBJECT *big_flames_object = dpl_LoadObject("flamebig.bgf", dpl_load_normal);
// DPLStaticChildRenderable *big_flames =
// new DPLStaticChildRenderable(
// entity,
// false,
// big_flames_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// fires->GetDCS());
// Register_Object(big_flames);
// // Make big flames object billboard along y-axis
// dpl_SetDCSReorientAxes(big_flames->GetDCS(), dpl_reorient_axes_y);
// dpl_FlushDCS(big_flames->GetDCS());
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *big_flames_instance = dpl_GetDCSInstance(big_flames->GetDCS(), 1);
// #if DEBUG_LEVEL > 0
// InstanceSwitchRenderable *big_flames_instance_switch =
// #endif
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// big_flames_instance, // the instance to control
// True, // Instance is on when trigger is...
// fires_delay->GetTriggerAttribute());
// Register_Object(big_flames_instance_switch);
// //
// // These renderables create the rising smoke column using a pfx effect.
// //
//#if 0
// Point3D my_offset2(0.0, 5.0, 0.0);
// #if DEBUG_LEVEL > 0
// OnePSFXRenderable *this_effect=
// #endif
// new OnePSFXRenderable(
// entity, // Entity to attach the renderable to
// OnePSFXRenderable::Static, // How/when to execute the renderable
// myPSFXDescriptons[1], // name of file with the PFX description in it
// this_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &my_offset2); // Offset (or world coordinants if DCS is NULL)
// Register_Object(this_effect);
//#endif
// break;
// }
// //----------------------------------------
// case 5: // Sfx #105 - Delayed ground hit
// //----------------------------------------
// {
// //
// // Create the delayed ground hit renderables
// //
// Point3D null_offset(0.0f, 0.0f, 0.0f);
// OneShotDelayRenderable *ground_hit_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.15f); // How long to wait before raising the trigger
// Register_Object(ground_hit_delay);
// #if DEBUG_LEVEL > 0
// DPLEffectRenderable *ground_hit_effect =
// #endif
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// ground_hit_delay->GetTriggerAttribute(), // address containing the trigger
// 12, // GroundHit // DPL effect number to trigger
// this_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &null_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(ground_hit_effect);
// break;
// }
// //------------------------------------------
// case 6: // Sfx #106 - Thor death explosion
// //------------------------------------------
// {
// #if 0
// int status; // used with dpl calls
// Point3D null_offset(0.0f, 0.0f, 0.0f);
// LinearMatrix null_offset_matrix(True);
// Point3D local_height(0.0f, 6.2f, 0.0f); // height of Thor
// LinearMatrix local_offset(True);
// local_offset = local_height;
// DPLStaticChildRenderable *local_root =
// new DPLStaticChildRenderable(
// entity,
// false,
// NULL,
// dpl_isect_mode_obj,
// NULL,
// local_offset,
// this_root->GetDCS());
// Register_Object(local_root);
//#if 0
// //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// //
// // Show static thor (TEMPORARY for testing)
// //
// dpl_OBJECT *static_thr_object = dpl_LoadObject("thr.bgf", dpl_load_normal);
// DPLStaticChildRenderable *static_thr =
// new DPLStaticChildRenderable(
// entity,
// false,
// static_thr_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// local_root->GetDCS());
// Register_Object(static_thr);
// // This defines how long the static_thr stays visible
// OneShotDelayRenderable *static_thr_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.5f); // How long to wait before raising the trigger
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *static_thr_instance = dpl_GetDCSInstance(static_thr->GetDCS(), 1);
// InstanceSwitchRenderable *static_thr_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// static_thr_instance, // the instance to control
// False, // Instance is on when trigger is...
// static_thr_delay->GetTriggerAttribute());
// //
// //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//#endif
// //
// // Load objects we will be using for the explosion effect
// //
// DPLObjectWrapper *flash_object_wrapper =
// new DPLObjectWrapper(
// entity, // Entity to attach the renderable to
// "exdisk_A.bgf", // Name of the DPL object to load into the wrapper
// dpl_load_nocache); // Type of loading to perform on this object
// Register_Object(flash_object_wrapper);
// dpl_OBJECT *flash_object_a = flash_object_wrapper->GetDPLObject();
// dpl_OBJECT *flash_object_b = dpl_LoadObject("exdisk_B.bgf", dpl_load_normal);
// dpl_OBJECT *flash_object_c = dpl_LoadObject("exdisk_C.bgf", dpl_load_normal);
// DPLObjectWrapper *debris_object_wrapper =
// new DPLObjectWrapper(
// entity, // Entity to attach the renderable to
// "thrtor_A.bgf", // Name of the DPL object to load into the wrapper
// dpl_load_nocache); // Type of loading to perform on this object
// Register_Object(debris_object_wrapper);
// dpl_OBJECT *morph_debris_a = debris_object_wrapper->GetDPLObject();
// dpl_OBJECT *morph_debris_b = dpl_LoadObject("thrtor_B.bgf", dpl_load_normal);
// dpl_OBJECT *morph_debris_c = dpl_LoadObject("thrtor_C.bgf", dpl_load_normal);
// DPLObjectWrapper *hips_object_wrapper =
// new DPLObjectWrapper(
// entity, // Entity to attach the renderable to
// "thrhip_A.bgf", // Name of the DPL object to load into the wrapper
// dpl_load_nocache); // Type of loading to perform on this object
// Register_Object(hips_object_wrapper);
// dpl_OBJECT *morph_hips_a = hips_object_wrapper->GetDPLObject();
// dpl_OBJECT *morph_hips_b = dpl_LoadObject("thrhip_B.bgf", dpl_load_normal);
// dpl_OBJECT *morph_hips_c = dpl_LoadObject("thrhip_C.bgf", dpl_load_normal);
// dpl_OBJECT *thor_debris_object = dpl_LoadObject("thrdbr.bgf", dpl_load_normal);
// dpl_OBJECT *large_debris_object = dpl_LoadObject("ldbr.bgf", dpl_load_normal);
// dpl_OBJECT *small_flames_object = dpl_LoadObject("flamesml.bgf", dpl_load_normal);
// dpl_OBJECT *big_flames_object = dpl_LoadObject("flamebig.bgf", dpl_load_normal);
// //----------------------------------------------
// // Renderables to perform morphing flash object
// //----------------------------------------------
// SweepRenderable *flash_morph_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 1.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// NULL, // trigger
// -0.5f, // Initial value
// 2.0f); // Final value
// ChildMorphRenderable *flash_morph =
// new ChildMorphRenderable(
// entity, // Entity to attach the renderable to
// ChildMorphRenderable::Dynamic, // How/when to execute the renderable
// flash_object_a, // destination
// flash_object_b, // start object
// flash_object_c, // end object
// flash_morph_sweep->GetSweepAttribute(), // pointer to control variable
// dpl_morph_vertices | dpl_morph_colors, // Defines type of morph to do
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS()); // the parent DCS we will be offsetting from
// // Make flash object billboard along y-axis
// dpl_SetDCSReorientAxes(flash_morph->GetDCS(), dpl_reorient_axes_y);
// dpl_FlushDCS(flash_morph->GetDCS());
// // This defines how long the flash stays up
// OneShotDelayRenderable *flash_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 1.0f); // How long to wait before raising the trigger
// Register_Object(flash_delay);
// // Find the instance with the flash in it and hook up the instance switch
// dpl_INSTANCE *flash_morph_instance = dpl_GetDCSInstance(flash_morph->GetDCS(), 1);
// InstanceSwitchRenderable *flash_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// flash_morph_instance, // the instance to control
// False, // Instance is on when trigger is...
// flash_delay->GetTriggerAttribute());
// //---------------------------------------------------
// // Create a material morph to fade flash object away
// //---------------------------------------------------
// OneShotDelayRenderable *flash_fade_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.5f); // How long to wait before raising the trigger
// SweepRenderable *flash_fade_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 0.4f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// flash_fade_delay->GetTriggerAttribute()); // trigger variable
// Register_Object(flash_fade_sweep);
// // Lookup texture for smoke column
// dpl_TEXTURE *flash_texture =
// dpl_LookupTexture("btfx:bexp9_tex", dpl_lookup_normal, &status);
// if (flash_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:bexp9_tex for an effect\n" << std::flush;
// //
// MorphMaterialRenderable *flash_fade_material =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 1.0f,1.0f,1.0f, // Material's ambient component
// 1.0f,1.0f,1.0f, // Material's emissive component
// 1.0f,1.0f,1.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0f, // Material's specular component
// 1.0f,1.0f,1.0f, // Material's opacity
// flash_texture, // Material's texture pointer
// 0.0f, // Material's Z dither value
// 1, // Material's Fog Imunity value
// 1.0f,1.0f,1.0f, // Material's ambient component
// 1.0f,1.0f,1.0f, // Material's emissive component
// 1.0f,1.0f,1.0f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0f, // Material's specular component
// 0.0f,0.0f,0.0f, // Material's opacity
// 0.0f, // Material's Z dither value
// flash_fade_sweep->GetSweepAttribute());
// Register_Object(flash_fade_material);
//// dpl_INSTANCE *flash_morph_instance = dpl_GetDCSInstance(flash_morph->GetDCS(), 1);
// if (!flash_morph_instance)
// Fail("flash_morph_instance came back null\n");
// dpl_SetInstanceFrontMaterial(flash_morph_instance, flash_fade_material->GetMaterial());
// dpl_FlushInstance(flash_morph_instance);
// //------------------------------------------------
// // Renderables to handle the chunks and fireballs
// //------------------------------------------------
// OneShotDelayRenderable *fireball_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f); // How long to wait before raising the trigger
// Register_Object(fireball_delay);
// DPLEffectRenderable *chunks_effect =
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay->GetTriggerAttribute(), // address containing the trigger
// 3, // Chunks // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &null_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(chunks_effect);
// Point3D fireball_offset(1.0f, 5.0f, 1.0f);
// DPLEffectRenderable *fireball_effect =
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay->GetTriggerAttribute(), // address containing the trigger
// 15, // Fireball // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &fireball_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(fireball_effect);
// OneShotDelayRenderable *fireball_delay_2 =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.55f); // How long to wait before raising the trigger
// Register_Object(fireball_delay_2);
// Point3D fireball_offset_2(-1.0f, 5.0f, -1.0f);
// DPLEffectRenderable *fireball_effect_2 =
// new DPLEffectRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// fireball_delay_2->GetTriggerAttribute(), // address containing the trigger
// 15, // Fireball // DPL effect number to trigger
// local_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &fireball_offset_2); // Offset (or world coordinants if DCS is NULL)
// Register_Object(fireball_effect_2);
// //------------------------------------------------------
// // Renderables to handle the torso debris explode morph
// //------------------------------------------------------
// OneShotDelayRenderable *morph_debris_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f, // How long to wait before raising the trigger
// 4.25f); // Duration of trigger
// SweepRenderable *debris_morph_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 4.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// morph_debris_delay->GetTriggerAttribute(), // trigger variable
// 0.0f, // initial value of sweep
// 9.0f, // final value of sweep
// SweepRenderable::Y_SQR_X); // function to apply
// ChildMorphRenderable *debris_morph =
// new ChildMorphRenderable(
// entity, // Entity to attach the renderable to
// ChildMorphRenderable::Dynamic, // How/when to execute the renderable
// morph_debris_a, // destination
// morph_debris_b, // start object
// morph_debris_c, // end object
// debris_morph_sweep->GetSweepAttribute(), // pointer to control variable
// dpl_morph_vertices, // Defines type of morph to do
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// local_root->GetDCS()); // the parent DCS we will be offsetting from
// MakeDCSFall *debris_make_fall =
// new MakeDCSFall(
// entity, // Entity to attach the renderable to
// MakeDCSFall::Dynamic, // How/when to execute the renderable
// debris_morph->GetDCS(), // the DCS to control
// -9.81f, // Gravity in meters/sec squared
// morph_debris_delay->GetTriggerAttribute()); // true if the instance is on, false if off
// // Find the instance with the debris in it and hook up the instance switch
// dpl_INSTANCE *debris_morph_instance = dpl_GetDCSInstance(debris_morph->GetDCS(), 1);
// InstanceSwitchRenderable *debris_morph_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// debris_morph_instance, // the instance to control
// True, // Instance is on when trigger is...
// morph_debris_delay->GetTriggerAttribute());
// //-----------------------------------------------------
// // Renderables to handle the hips debris explode morph
// //-----------------------------------------------------
// ChildMorphRenderable *hips_morph =
// new ChildMorphRenderable(
// entity, // Entity to attach the renderable to
// ChildMorphRenderable::Dynamic, // How/when to execute the renderable
// morph_hips_a, // destination
// morph_hips_b, // start object
// morph_hips_c, // end object
// debris_morph_sweep->GetSweepAttribute(), // pointer to control variable
// dpl_morph_vertices, // Defines type of morph to do
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// local_root->GetDCS()); // the parent DCS we will be offsetting from
// MakeDCSFall *hips_make_fall =
// new MakeDCSFall(
// entity, // Entity to attach the renderable to
// MakeDCSFall::Dynamic, // How/when to execute the renderable
// hips_morph->GetDCS(), // the DCS to control
// -9.81f, // Gravity in meters/sec squared
// morph_debris_delay->GetTriggerAttribute()); // true if the instance is on, false if off
// // Find the instance with the debris in it and hook up the instance switch
// dpl_INSTANCE *hips_morph_instance = dpl_GetDCSInstance(hips_morph->GetDCS(), 1);
// InstanceSwitchRenderable *hips_morph_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// hips_morph_instance, // the instance to control
// True, // Instance is on when trigger is...
// morph_debris_delay->GetTriggerAttribute());
// //-----------------------------------------------
// // Renderables to handle static debris and fires
// //-----------------------------------------------
// OneShotDelayRenderable *static_debris_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.25f); // How long to wait before raising the trigger
// DPLStaticChildRenderable *mech_debris =
// new DPLStaticChildRenderable(
// entity,
// false,
// thor_debris_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// this_root->GetDCS());
// Register_Object(mech_debris);
// MakeDCSFall *mech_debris_fall =
// new MakeDCSFall(
// entity, // Entity to attach the renderable to
// MakeDCSFall::Dynamic, // How/when to execute the renderable
// mech_debris->GetDCS(), // the DCS to control
// -0.01f, // Gravity in meters/sec squared
// static_debris_delay->GetTriggerAttribute()); // true if the instance is on, false if off
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *mech_debris_instance = dpl_GetDCSInstance(mech_debris->GetDCS(), 1);
// InstanceSwitchRenderable *mech_debris_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// mech_debris_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
//
// DPLStaticChildRenderable *large_debris =
// new DPLStaticChildRenderable(
// entity,
// false,
// large_debris_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// mech_debris->GetDCS());
// Register_Object(large_debris);
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *large_debris_instance = dpl_GetDCSInstance(large_debris->GetDCS(), 1);
// InstanceSwitchRenderable *large_debris_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// large_debris_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
//
// DPLStaticChildRenderable *small_flames =
// new DPLStaticChildRenderable(
// entity,
// false,
// small_flames_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// mech_debris->GetDCS());
// Register_Object(small_flames);
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *small_flames_instance = dpl_GetDCSInstance(small_flames->GetDCS(), 1);
// InstanceSwitchRenderable *small_flames_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// small_flames_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
//
// DPLStaticChildRenderable *big_flames =
// new DPLStaticChildRenderable(
// entity,
// false,
// big_flames_object,
// dpl_isect_mode_obj,
// NULL,
// null_offset_matrix,
// mech_debris->GetDCS());
// Register_Object(big_flames);
// // Make big flames object billboard along y-axis
// dpl_SetDCSReorientAxes(big_flames->GetDCS(), dpl_reorient_axes_y);
// dpl_FlushDCS(big_flames->GetDCS());
// // Find the instance with the static_thr in it and hook up the instance switch
// dpl_INSTANCE *big_flames_instance = dpl_GetDCSInstance(big_flames->GetDCS(), 1);
// InstanceSwitchRenderable *big_flames_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// big_flames_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
// //-------------------------------------------------------------
// // Renderables to handle the rising smoke column (first shape)
// //-------------------------------------------------------------
//#if 1
// OneShotDelayRenderable *first_one_shot =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 0.0f); // How long to wait before raising the trigger
// Register_Object(first_one_shot);
// Point3D my_offset(0.0, 6.0, 0.0);
// #if DEBUG_LEVEL > 0
// DPLPSFXRenderable* this_effect =
// #endif
// new DPLPSFXRenderable(
// entity, // Entity to attach the renderable to
// DPLPSFXRenderable::Dynamic, // How/when to execute the renderable
// first_one_shot->GetTriggerAttribute(), // address containing the trigger
// myPSFXDescriptons[1], // pointer to the PFX description
// this_root->GetDCS(), // DCS the effect is relative to (may be NULL)
// &my_offset); // Offset (or world coordinants if DCS is NULL)
// Register_Object(this_effect);
//
//// the below code will be removed in favor of PFX effects once they are tested
//#else
//// OneShotDelayRenderable *smoke_1_morph_delay =
//// new OneShotDelayRenderable(
//// entity, // Entity to attach the renderable to
//// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
//// 0.25f); // How long to wait before raising the trigger
// SweepRenderable *smoke_1_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 40.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// static_debris_delay->GetTriggerAttribute(),
// 0.2f,
// 2.5f);
// ChildMorphRenderable *smoke_1_morph =
// new ChildMorphRenderable(
// entity, // Entity to attach the renderable to
// ChildMorphRenderable::Dynamic, // How/when to execute the renderable
// smoke_1_a, // destination
// smoke_b, // start object
// smoke_c, // end object
// smoke_1_sweep->GetSweepAttribute(), // pointer to control variable
// dpl_morph_vertices, // Defines type of morph to do
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS()); // the parent DCS we will be offsetting from
// // Find the instance with the flash in it and hook up the instance switch
// dpl_INSTANCE *smoke_1_instance = dpl_GetDCSInstance(smoke_1_morph->GetDCS(), 1);
// InstanceSwitchRenderable *smoke_1_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// smoke_1_instance, // the instance to control
// True, // Instance is on when trigger is...
// static_debris_delay->GetTriggerAttribute());
// //--------------------------------------------------------------
// // Renderables to handle the rising smoke column (second shape)
// //--------------------------------------------------------------
//#if 0
// OneShotDelayRenderable *smoke_2_morph_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 6.0f); // How long to wait before raising the trigger
// SweepRenderable *smoke_2_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 30.0f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// smoke_2_morph_delay->GetTriggerAttribute(),
// 0.1f,
// 1.0f);
// ChildMorphRenderable *smoke_2_morph =
// new ChildMorphRenderable(
// entity, // Entity to attach the renderable to
// ChildMorphRenderable::Dynamic, // How/when to execute the renderable
// smoke_2_a, // destination
// smoke_b, // start object
// smoke_c, // end object
// smoke_2_sweep->GetSweepAttribute(), // pointer to control variable
// dpl_morph_vertices, // Defines type of morph to do
// false, // DPL Zone this stuff will live in (for culling)
// dpl_isect_mode_obj, // type of intersections to do on this object
// NULL, // intersection mask for the object
// this_root->GetDCS()); // the parent DCS we will be offsetting from
// // Find the instance with the flash in it and hook up the instance switch
// dpl_INSTANCE *smoke_2_instance = dpl_GetDCSInstance(smoke_2_morph->GetDCS(), 1);
// InstanceSwitchRenderable *smoke_2_morph_instance_switch =
// new InstanceSwitchRenderable(
// entity, // Entity to attach the renderable to
// InstanceSwitchRenderable::Dynamic, // How/when to execute the renderable
// smoke_2_instance, // the instance to control
// True, // Instance is on when trigger is...
// smoke_2_morph_delay->GetTriggerAttribute());
//#endif
// //-------------------------------------------
// // Set dither Z on material for smoke column
// //-------------------------------------------
// dpl_MATERIAL *damage_material =
// dpl_LookupMaterial ("btfx:smoke1_mtl",
// dpl_lookup_normal,
// &status);
// if (damage_material == 0)
// {
// std::cout << "couldn't find material\n";
// }
// else
// {
// dpl_SetMaterialDitherZ(damage_material, 10.0f);
// dpl_FlushMaterial(damage_material);
// }
// //---------------------------------------------------
// // Create a material morph to fade smoke column away
// //---------------------------------------------------
// OneShotDelayRenderable *smoke_fade_delay =
// new OneShotDelayRenderable(
// entity, // Entity to attach the renderable to
// OneShotDelayRenderable::Dynamic, // How/when to execute the renderable
// 25.0f); // How long to wait before raising the trigger
// SweepRenderable *smoke_fade_sweep =
// new SweepRenderable(
// entity, // Entity to attach the renderable to
// SweepRenderable::Dynamic, // How/when to execute the renderable
// 18.5f, // How long to take to sweep from 0 to 1
// 1, // number of times to cycle before stopping
// smoke_fade_delay->GetTriggerAttribute()); // trigger variable
// Register_Object(smoke_fade_sweep);
// // Lookup texture for smoke column
// dpl_TEXTURE *smoke_texture =
// dpl_LookupTexture("btfx:smoke1_scr_tex", dpl_lookup_normal, &status);
// if (smoke_texture == NULL)
// DEBUG_STREAM<<"couldn't find texture btfx:smoke1_scr_tex for an effect\n" << std::flush;
// //
// MorphMaterialRenderable *smoke_fade_material =
// new MorphMaterialRenderable(
// entity, // Entity to attach the renderable to
// MorphMaterialRenderable::Dynamic, // How/when to execute the renderable
// 1.0f,1.0f,1.0f, // Material's ambient component
// 0.7f,0.4f,0.4f, // Material's emissive component
// 0.3714f,0.2899f,0.3714f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0f, // Material's specular component
// 0.7f,0.99f,0.0f, // Material's opacity
// smoke_texture, // Material's texture pointer
// 10.0f, // Material's Z dither value
// 3, // Material's Fog Imunity value
// 1.0f,1.0f,1.0f, // Material's ambient component
// 0.7f,0.4f,0.4f, // Material's emissive component
// 0.3714f,0.2899f,0.3714f, // Material's diffuse component
// 0.0f,0.0f,0.0f,0.0f, // Material's specular component
// 0.0f,0.99f,0.0f, // Material's opacity
// 10.0f, // Material's Z dither value
// smoke_fade_sweep->GetSweepAttribute());
// Register_Object(smoke_fade_material);
//// dpl_INSTANCE *smoke_1_instance = dpl_GetDCSInstance(smoke_1_morph->GetDCS(), 1);
// if (!smoke_1_instance)
// Fail("smoke_1_instance came back null\n");
// dpl_SetInstanceFrontMaterial(smoke_1_instance, smoke_fade_material->GetMaterial());
// dpl_FlushInstance(smoke_1_instance);
//#endif
// #else
// DEBUG_STREAM <<"Explosion effect 104 called, this is disabled and shouldn't be used!\n" << std::flush;
// #endif
// break;
// }
// default:
// {
// break;
// }
// }
}
//
//#############################################################################
// MakeEntityRenderables handles creating all the renderables necessary to
// display an object. This routine contains default behaviors for creating
// some types of simple objects. The idea is that if a higher level routine
// can't figure out how to make renderables for something this routine will be
// called and the default behavior will be used.
//#############################################################################
//
void
DPLRenderer::MakeEntityRenderables(
Entity *entity, // The entity we are dealing with
ResourceDescription *model_resource, // Pointer to the video resource
ViewFrom view_type) // Type of reference (inside/outside...etc.)
{
//STUBBED: DPL RB 1/14/07
char *object_filename;
#if DEBUG_LEVEL > 0
int object_count;
#endif
L4VideoObject *video_object;
L4VideoObjectWrapper *video_wrapper;
ChainOf<L4VideoObjectWrapper*> video_chain(NULL);
L4VideoObject::ResourceType resource_type;
// dpl_DCS *my_root_dcs;
Enumeration renderer_modes;
//
// Set the Entity_Being_Created global so the C language callback will
// be able to mark the geometry with damage zone values if necessary.
//
Entity_Being_Created = entity;
// my_root_dcs = NULL;
//
// convert video resource into chain of video objects and make an iterator
// for that chain
//
if (model_resource)
{
#if DEBUG_LEVEL > 0
object_count =
#endif
L4VideoObjectWrapper::BuildVideoObjectChainFromResource(&video_chain, model_resource);
}
ChainIteratorOf<L4VideoObjectWrapper*> video_iterator(video_chain);
//
// Switch to allow us to have scripts at this level for constructing certain
// types of video objects (whether or not they have a video resource)
//
switch (entity->GetClassID())
{
//
// Dropzones have no graphical appearance so we do nothing
//
case DropZoneClassID:
break;
//
// Player objects have no graphical appearance unless one is created
// at a higher (game specific) level, so we do nothing here.
//
case PlayerClassID:
break;
// this entity has no visual appearance
case AudioEntityClassID:
break;
//
// Cultural and landmark are the only ones with "destroyed" processing
// This is similar to the default case but does not allow us to be
// inside the entity, does not allow us to be a mover,
//
case CulturalIconClassID:
case LandmarkClassID:
{
dpl_INSTANCE *this_instance = NULL; // BT bring-up: the per-object instance
// assignments below are DPL-stubbed
// (commented out) -> initialise so the
// /RTC1 uninitialised-variable check does
// not fault when this_instance is passed
// to StateInstanceSwitchRenderable.
d3d_OBJECT *this_object;
dpl_ISECT_MODE intersect_mode;
HierarchicalDrawComponent *component = NULL;
uint32 intersect_mask;
LinearMatrix offset_matrix = LinearMatrix::Identity;
//dpl_DCS *root_DCS, *this_DCS;
//
// Make sure the object has a video resource
//
if (!model_resource)
{
VideoRenderer::MakeEntityRenderables(entity, model_resource, view_type);
Entity_Being_Created = NULL;
return;
}
// intersect_mode = dpl_isect_mode_geometry;
intersect_mask = INTERSECT_ALL;
Logical first_object = True;
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
object_filename = video_object->GetObjectFilename();
resource_type = video_object->GetResourceType();
renderer_modes = video_object->GetRendererModes();
#if NOISY_RENDERER
Tell("L4VIDEO.cpp loading object " << object_filename);
Tell(" type " << resource_type);
Tell(" mode 0x" <<std::hex<<renderer_modes<<std::dec<< "." << std::endl);
#endif
if ((resource_type != L4VideoObject::Object) &&
(resource_type != L4VideoObject::Rubble))
{
DEBUG_STREAM << "L4VIDEO.cpp wrong video resource type for object "<<object_filename<<"\n" << std::flush;
continue; // next object
}
SET_VIDEO_LOAD_OBJECT();
// HACK !!! load the object as uncached so entity identification will work right
// this_object = dpl_LoadObject(object_filename, dpl_load_normal);
this_object = d3d_OBJECT::LoadObject(mDevice, object_filename);
CLEAR_VIDEO_LOAD_OBJECT();
if (this_object == NULL)
{
DEBUG_STREAM << "L4VIDEO.cpp couldn't load object "<<object_filename<<"\n" << std::flush;
if (!first_object)
{
continue; // next object
}
}
if (first_object)
{
first_object = False;
component = new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Static, // How/when to execute the renderable
this_object, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
intersect_mode, // type of intersections to do on this object
intersect_mask); // intersection mask for the object
//root_DCS = this_static_root->GetDCS();
//------------------------------------------------------
// if root object billboards (all others billboard too)
//------------------------------------------------------
//this_DCS = root_DCS;
// this_instance = this_static_root->GetInstance();
//
// Set my_root_dcs because we want this guy's hiearchy to be marked
// with his entity pointer
//
// my_root_dcs = root_DCS;
}
else
{
if (renderer_modes | L4VideoObject::BillboardObject)
{
//------------------------------------------
// attach additional object to separate DCS
// because it is billboarded
//------------------------------------------
component = new DPLStaticChildRenderable(
entity,
false,
this_object,
intersect_mode,
intersect_mask,
offset_matrix,
NULL);
// this_DCS = this_child->GetDCS();
// this_instance = this_child->GetInstance();
}
else
{
//----------------------------------------
// attach additional objects to root_DCS
// (HACK) temporary implementation (HACK)
//----------------------------------------
component = new DCSInstanceRenderable(
entity, // Entity to attach the renderable to
DCSInstanceRenderable::Static, // How/when to execute the renderable
this_object, // object to connect to the instance
NULL, // the DCS to add the instance to
intersect_mode, // type of intersections to do on this object
intersect_mask, // intersection mask for the object
True); // initial visibility setting
//this_DCS = NULL;
// this_instance = another_instance->GetInstance();
}
}
// add the new object to our renderables list
if (component)
mRenderables.Add(component);
component = NULL;
//
// Hook up the object to an instance switch renderable responsive to state
//
StateIndicator* simulation_state = (StateIndicator *)entity->GetAttributePointer("SimulationState");
switch(resource_type)
{
case L4VideoObject::Object:
{
component = new StateInstanceSwitchRenderable(
entity, // Entity to attach the renderable to
StateInstanceSwitchRenderable::Watcher, // How/when to execute the renderable
this_instance, // the instance to control
False, // true to turn on in this state, false for off
simulation_state, // State dial we use to control the on/off
CulturalIcon::BurningState); // State that we look for
break;
}
case L4VideoObject::Rubble:
{
component = new StateInstanceSwitchRenderable(
entity, // Entity to attach the renderable to
StateInstanceSwitchRenderable::Watcher, // How/when to execute the renderable
this_instance, // the instance to control
True, // true to turn on in this state, false for off
simulation_state, // State dial we use to control the on/off
CulturalIcon::BurningState); // State that we look for
break;
}
}
if (component)
mRenderables.Add(component);
//----------------------------------
// billboard object if so indicated
//----------------------------------
// if (this_DCS && (renderer_modes & L4VideoObject::BillboardObject))
// {
// int axes = dpl_reorient_axes_none;
// if (renderer_modes & L4VideoObject::BillboardXAxis)
// {
// axes |= dpl_reorient_axes_x;
// }
// if (renderer_modes & L4VideoObject::BillboardYAxis)
// {
// axes |= dpl_reorient_axes_y;
// }
// if (renderer_modes & L4VideoObject::BillboardZAxis)
// {
// axes |= dpl_reorient_axes_z;
// }
// dpl_SetDCSReorientAxes(this_DCS, (dpl_REORIENT_AXES)axes);
// dpl_FlushDCS(this_DCS);
// }
}
break;
}
//
// Case to handle rivets using the fast projectile code
//
case RivetClassID:
{
d3d_OBJECT
*this_object;
video_iterator.First();
video_wrapper = video_iterator.ReadAndNext();
Check(video_wrapper);
video_object = video_wrapper->GetVideoObject();
Check_Pointer(video_object);
object_filename = video_object->GetObjectFilename();
this_object = d3d_OBJECT::LoadObject(GetDevice(), object_filename);
#if DEBUG_LEVEL > 0
ProjectileRootRenderable *projectile =
#endif
new ProjectileRootRenderable(
entity, // Entity to attach the renderable to
ProjectileRootRenderable::Dynamic, // How/when to execute the renderable
this_object, // object to hang on the DCS, may be a list later <NULL>
false); // DPL Zone this stuff will live in (for culling)
Register_Object(projectile);
break;
}
//
// Script for generating renderables for eyecandy
//
case EyeCandyClassID:
{
int effect_number;
dpl_ISECT_MODE dpl_isect_mode_obj;
RootRenderable *this_root =
new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Dynamic, // How/when to execute the renderable
NULL, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
dpl_isect_mode_obj, // type of intersections to do on this object
NULL); // intersection mask for the object
Register_Object(this_root);
StateIndicator* simulation_state = (StateIndicator *)entity->GetAttributePointer("SimulationState");
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
effect_number = atoi(video_object->GetObjectFilename());
#if DEBUG_LEVEL > 0
DPLSFXRenderable *this_effect =
#endif
new DPLSFXRenderable(
entity, // Entity to attach the effect to
false, // DPL zone everything will be in
Point3D::Identity, // Point offset from the parent DCS
this_root, // Parent DCS (can be NULL for world)
simulation_state, // Trigger effect when this state changes
EyeCandy::effectOn, // Trigger effect when in this state
effect_number, // Type of effect to trigger
.01); // Effect repeat speed.
Register_Object(this_effect);
}
break;
}
//
// Script for generating an explosion of type specified in the resource file
//
case ExplosionClassID:
{
DEBUG_STREAM << "Explosion Created in MakeEntityRenderables:" << std::endl << std::flush;
int effect_number;
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
effect_number = atoi(video_object->GetObjectFilename());
DEBUG_STREAM << " ** effect_number = " << effect_number << std::endl << std::flush;
if(effect_number < 100 || effect_number >= 1000)
{
DEBUG_STREAM << " ** DPLIndependantEffect(["
<< entity->localOrigin.linearPosition.x
<< ", " << entity->localOrigin.linearPosition.y
<< ", " << entity->localOrigin.linearPosition.z << "], " << effect_number << ");" << std::endl << std::flush;
// Both effect-number ENCODINGS resolve to the same psfx slot:
// <100 = the raw dpl board number; >=1000 = the WinTesla-era
// "1000+slot" INDIE id carried by the damage-band resources
// (1002-1005 = ddam1-4 light..critical damage smoke, 1008 =
// ddam5 zone-destroyed -- the SAME [pfx_day] mapping). BT
// ships no INDIE descriptors (the version-2 specialfx pages
// don't exist), so all slots route to the BT .PFX layer, with
// the Explosion entity's ORIENTATION (the victim's frame) --
// the .PFX offsets/velocities are authored mech-local.
{
int pfx_slot = (effect_number >= 1000)
? effect_number - 1000 : effect_number;
extern void BTStartPfxFrame(int, float, float, float,
const float *, const float *, const float *);
float xr[3] = { (float)entity->localToWorld(0,0), (float)entity->localToWorld(0,1), (float)entity->localToWorld(0,2) };
float yr[3] = { (float)entity->localToWorld(1,0), (float)entity->localToWorld(1,1), (float)entity->localToWorld(1,2) };
float zr[3] = { (float)entity->localToWorld(2,0), (float)entity->localToWorld(2,1), (float)entity->localToWorld(2,2) };
BTStartPfxFrame(pfx_slot,
(float)entity->localOrigin.linearPosition.x,
(float)entity->localOrigin.linearPosition.y,
(float)entity->localOrigin.linearPosition.z,
xr, yr, zr);
}
}
else
{
DEBUG_STREAM << " ** ExplosionScripts(*entity*, RES["
<< model_resource->resourceID << " - " << model_resource->resourceName << "], "
<< "ViewFrom::" << (view_type == ViewFrom::insideEntity ? "insideEntity" : (view_type == ViewFrom::outsideEntity ? "outsideEntity" : "collisionEntity"))
<< ", " << (effect_number - 100) << ");" << std::endl << std::flush;
//
// Effect 104 = the death-WRECK script (1996 ExplosionScripts
// case 4: swap the victim to its <mech>dbr burning hulk),
// dispatched by the per-mech death ModelLists ('blhdead'...).
// RECONSTRUCTED in the BT renderer (SwapToWreck); the other
// script numbers remain stubbed with the rest of the layer.
//
if (effect_number == 104)
{
extern void BTSwapMechToWreck(Entity *victim);
BTSwapMechToWreck(((Explosion *)entity)->GetEntityHit());
}
else
ExplosionScripts(
entity, // The entity we are dealing with
model_resource, // Pointer to the video resource
view_type, // Type of reference (inside/outside...etc.)
effect_number - 100);
}
}
break;
}
case CameraDirectorClassID:
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create the CameraDirector HUD Renderable if not a Replicant
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
if (entity->GetInstance() == CameraDirector::MasterInstance)
{
CameraDirector *camera_director = (CameraDirector*) entity;
Check_Pointer(camera_director);
int *player_index = &camera_director->goalPlayerIndex;
Check_Pointer(player_index);
Logical *display_rank_window = &camera_director->displayRankingWindow;
Check_Pointer(display_rank_window);
mCamShipHUD = new CameraShipHUDRenderable(entity, CameraShipHUDRenderable::Dynamic, player_index, display_rank_window);
}
break;
}
//
// Script for a drivable camera, the camera is invisible to other players
//
case CameraShipClassID:
{
if(view_type == insideEntity)
{
//
// Build an empty root renderable and an eye renderable
//
dpl_ISECT_MODE dpl_isect_mode_obj;
RootRenderable *this_root =
new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Dynamic, // How/when to execute the renderable
NULL, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
dpl_isect_mode_obj, // type of intersections to do on this object
NULL); // intersection mask for the object
Register_Object(this_root);
#if 0 //DEBUG_LEVEL > 0
DPLEyeRenderable* this_eye =
#endif
mCamera =
new DPLEyeRenderable(
entity,
LinearMatrix::Identity,
this_root,
NULL
);
Register_Object(this_eye);
}
break;
}
//
// Script for doorframe, so it can be used in any game
//
case DoorFrameClassID:
{
Verify( object_count == 6 );
//------------------------------------------------
// First video object is root shape for the door
// followed by left door shape, right door shape,
// door lights, left door lights, and right door
// lights.
//------------------------------------------------
d3d_OBJECT
*object,
*left,
*right,
*object_lights,
*left_lights,
*right_lights;
int object_number = 0;
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
object_filename = video_object->GetObjectFilename();
#if NOISY_RENDERER
Tell("L4VIDEO.cpp loading door object "<<object_filename<<"\n");
#endif
switch (object_number)
{
case 0:
// object = dpl_LoadObject(object_filename, dpl_load_normal);
object = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
case 1:
// left = dpl_LoadObject(object_filename, dpl_load_normal);
left = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
case 2:
// right = dpl_LoadObject(object_filename, dpl_load_normal);
right = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
case 3:
// object_lights = dpl_LoadObject(object_filename, dpl_load_normal);
object_lights = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
case 4:
// left_lights = dpl_LoadObject(object_filename, dpl_load_normal);
left_lights = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
case 5:
// right_lights = dpl_LoadObject(object_filename, dpl_load_normal);
right_lights = d3d_OBJECT::LoadObject(mDevice, object_filename);
break;
default:
// ignore any additional objects
break;
}
++object_number;
}
//
// HACK Hard Coded Doorframes SubsystemArray access
//
Door *left_door_sub = (Door*) entity->GetSubsystem(0);
Point3D* left_door_position = &left_door_sub->currentPosition;
Door *right_door_sub = (Door*) entity->GetSubsystem(1);
Point3D* right_door_position = &right_door_sub->currentPosition;
dpl_ISECT_MODE dpl_isect_mode_obj;
RootRenderable *door_sill =
new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Static, // How/when to execute the renderable
object, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
dpl_isect_mode_obj, // type of intersections to do on this object
NULL); // intersection mask for the object
Register_Object(door_sill);
//
// Set my_root_dcs because we want this guy's hiearchy to be marked
// with his entity pointer
//
// my_root_dcs = door_sill->GetDCS();
LinearMatrix my_ident(True);
DPLChildPointRenderable* left_door =
new DPLChildPointRenderable(
entity,
false,
left,
dpl_isect_mode_obj,
NULL,
my_ident,
door_sill,
left_door_position);
Register_Object(left_door);
DPLChildPointRenderable* right_door =
new DPLChildPointRenderable(
entity,
false,
right,
dpl_isect_mode_obj,
NULL,
my_ident,
door_sill,
right_door_position);
Register_Object(right_door);
#if DEBUG_LEVEL > 0
DCSInstanceRenderable *door_sill_lights_instance =
#endif
new DCSInstanceRenderable(
entity, // Entity to attach the renderable to
DCSInstanceRenderable::Static, // How/when to execute the renderable
object_lights, // object to connect to the instance
door_sill, // the DCS to add the instance to
dpl_isect_mode_obj, // type of intersections to do on this object
NULL, // intersection mask for the object
True); // initial visibility setting
Register_Object(door_sill_lights_instance);
#if DEBUG_LEVEL > 0
DCSInstanceRenderable *left_door_lights_instance =
#endif
new DCSInstanceRenderable(
entity, // Entity to attach the renderable to
DCSInstanceRenderable::Static, // How/when to execute the renderable
left_lights, // object to connect to the instance
left_door, // the DCS to add the instance to
dpl_isect_mode_obj, // type of intersections to do on this object
NULL, // intersection mask for the object
True); // initial visibility setting
Register_Object(left_door_lights_instance);
#if DEBUG_LEVEL > 0
DCSInstanceRenderable *right_door_lights_instance =
#endif
new DCSInstanceRenderable(
entity, // Entity to attach the renderable to
DCSInstanceRenderable::Static, // How/when to execute the renderable
right_lights, // object to connect to the instance
right_door, // the DCS to add the instance to
dpl_isect_mode_obj, // type of intersections to do on this object
NULL, // intersection mask for the object
True); // initial visibility setting
Register_Object(right_door_lights_instance);
break;
}
//
// This is the script run on anything that isn't already listed above
//
default:
{
d3d_OBJECT *this_object;
dpl_ISECT_MODE intersect_mode;
uint32 intersect_mask;
LinearMatrix offset_matrix = LinearMatrix::Identity;
Component *component = NULL;
HierarchicalDrawComponent *rootCom = NULL;
HierarchicalDrawComponent *thisCom = NULL;
//
// First, establish that this level doesn't know what to do
// if the object has no video resource.
//
if (!model_resource)
{
VideoRenderer::MakeEntityRenderables(entity, model_resource, view_type);
Entity_Being_Created = NULL;
return;
}
//
// If we're inside the entity, set up the intersect mode and mask so we
// won't intersect ourselves with the pickpoint. Other items get full
// geometry intersection.
//
if (view_type == insideEntity)
{
// intersect_mode = dpl_isect_mode_obj;
intersect_mask = NULL;
}
else
{
// intersect_mode = dpl_isect_mode_geometry;
intersect_mask = INTERSECT_ALL;
}
Logical first_object = True;
video_iterator.First();
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
object_filename = video_object->GetObjectFilename();
resource_type = video_object->GetResourceType();
renderer_modes = video_object->GetRendererModes();
#if NOISY_RENDERER
Tell("L4VIDEO.cpp loading object " << object_filename);
Tell(" type " << resource_type);
Tell(" mode 0x" <<std::hex<<renderer_modes<<std::dec<< "." << std::endl);
#endif
if ((resource_type != L4VideoObject::Object) && (resource_type != L4VideoObject::Rubble))
{
DEBUG_STREAM << "L4VIDEO.cpp wrong video resource type for object "<<object_filename<<"\n" << std::flush;
continue; // next object
}
SET_VIDEO_LOAD_OBJECT();
this_object = d3d_OBJECT::LoadObject(mDevice, object_filename);
CLEAR_VIDEO_LOAD_OBJECT();
if (this_object == NULL)
{
DEBUG_STREAM << "L4VIDEO.cpp couldn't load object "<<object_filename<<"\n" << std::flush;
if (!first_object)
{
continue; // next object
}
}
if (first_object)
{
first_object = False;
//
// Determine if the entity is a mover or not, which tells us what type of
// renderable to use to construct it.
//
if (entity->IsDerivedFrom(*Mover::GetClassDerivations()))
{
//
// It's a mover, construct it with a dynamic root renderable so it can move
// also remember it's DCS so we can hook other shapes to it later.
//
SET_VIDEO_CONSTRUCT_ROOT();
rootCom = new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Dynamic, // How/when to execute the renderable
this_object, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
intersect_mode, // type of intersections to do on this object
intersect_mask); // intersection mask for the object
Register_Object(this_root);
CLEAR_VIDEO_CONSTRUCT_ROOT();
}
else
{
//
// It's a static, construct it with a static RootRenderable and
// remember it's DCS so we can hook other shapes to it later.
//
rootCom = new RootRenderable(
entity, // Entity to attach the renderable to
RootRenderable::Static, // How/when to execute the renderable
this_object, // object to hang on the DCS, may be a list later <NULL>
false, // DPL Zone this stuff will live in (for culling)
intersect_mode, // type of intersections to do on this object
intersect_mask); // intersection mask for the object
}
//
// Set my_root_dcs because we want this guy's hiearchy to be marked
// with his entity pointer
//
// my_root_dcs = root_DCS;
//
// If we are inside the entity, then we must build an eyepoint for it too.
// since there is no special construction, we build the eyepoint with a
// zero offset.
//
if (view_type == insideEntity)
{
EulerAngles *eyepoint_rotation = (EulerAngles *)entity->GetAttributePointer("EyepointRotation");
#if 0 //DEBUG_LEVEL > 0
DPLEyeRenderable *this_eye =
#endif
mCamera =
new DPLEyeRenderable(
entity,
LinearMatrix::Identity,
rootCom,
eyepoint_rotation);
Register_Object(this_eye);
}
//------------------------------------------------------
// if root object billboards (all others billboard too)
//------------------------------------------------------
// this_DCS = root_DCS;
}
else
{
if (renderer_modes & L4VideoObject::BillboardObject)
{
//------------------------------------------
// attach additional object to separate DCS
// because it is billboarded
//------------------------------------------
thisCom = new DPLStaticChildRenderable(
entity,
false,
this_object,
intersect_mode,
intersect_mask,
offset_matrix,
rootCom);
Register_Object(this_child);
// this_DCS = this_child->GetDCS();
}
else
{
//----------------------------------------
// attach additional objects to root_DCS
// (HACK) temporary implementation (HACK)
//----------------------------------------
thisCom = new DCSInstanceRenderable(
entity, // Entity to attach the renderable to
DCSInstanceRenderable::Static, // How/when to execute the renderable
this_object, // object to connect to the instance
rootCom, // the DCS to add the instance to
intersect_mode, // type of intersections to do on this object
intersect_mask, // intersection mask for the object
True); // initial visibility setting
Register_Object(another_instance);
// this_DCS = NULL;
}
}
//----------------------------------
// billboard object if so indicated
//----------------------------------
if (thisCom && (renderer_modes & L4VideoObject::BillboardObject))
{
// int axes = dpl_reorient_axes_none;
if (renderer_modes & L4VideoObject::BillboardXAxis)
{
// axes |= dpl_reorient_axes_x;
}
if (renderer_modes & L4VideoObject::BillboardYAxis)
{
// axes |= dpl_reorient_axes_y;
}
if (renderer_modes & L4VideoObject::BillboardZAxis)
{
// axes |= dpl_reorient_axes_z;
}
// dpl_SetDCSReorientAxes(this_DCS, (dpl_REORIENT_AXES)axes);
// dpl_FlushDCS(this_DCS);
}
//MOVED THIS TO ROOTRENDERABLE - Was this a hack?
//mRenderables.Add(component);
}
break;
}
}
//
// release the video chain memory
//
L4VideoObjectWrapper::DeleteVideoObjectChain(&video_chain);
//
// Clear the entity pointer passed to the callback system
//
Entity_Being_Created = NULL;
//
// If my_root_dcs is non-null, mark the entity's DCS hiearchy with it's
// entity pointer.
//
// if(my_root_dcs)
// {
// MarkDCSHiearchy(my_root_dcs,entity);
//#if 0
// if(entity->GetClassID() == DemolitionPackClassID)
// {
// Point3D temp_point(0.0,0.0,0.0);
// #if DEBUG_LEVEL > 0
// OnePSFXRenderable *this_effect=
// #endif
// new OnePSFXRenderable(
// entity, // Entity to attach the renderable to
// OnePSFXRenderable::Static, // How/when to execute the renderable
// myPSFXDescriptons[3], // name of file with the PFX description in it
// my_root_dcs, // DCS the effect is relative to (may be NULL)
// &temp_point); // Offset (or world coordinants if DCS is NULL)
// Register_Object(this_effect);
// }
//#endif
// }
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This should be called prior to the rendering of a frame to setup the culling
// data the renderer keeps (mostly the world to eye transform)
//
void
DPLRenderer::SetupCull()
{
Entity
*linked_entity;
LinearMatrix
site_to_world,
world_to_site,
eye_rotation,
eye_inverted;
EntitySegment
*eyepoint_segment;
EulerAngles
*eyepoint_rotation;
//
// To test the functionality, this is not as efficient as it could be.
// when it gets fully patched into the renderer it will be improved.
//
// Figure out what type of entity this is.
//
linked_entity = GetLinkedEntity();
eyepoint_rotation = (EulerAngles*)linked_entity->GetAttributePointer("EyepointRotation");
if(linked_entity->IsDerivedFrom(*JointedMover::GetClassDerivations()))
{
JointedMover
*linked_jointed_mover;
linked_jointed_mover = Cast_Object(JointedMover*, linked_entity);
eyepoint_segment = linked_jointed_mover->GetSegment("siteeyepoint");
if(!eyepoint_segment)
{
Fail("DPLRenderer::SetupCull jointed mover had no siteeyepoint\n");
}
if(!eyepoint_rotation)
{
Fail("DPLRenderer::SetupCull jointed mover had no EyepointRotation attribute\n");
}
linked_jointed_mover->GetSegmentToWorld(*eyepoint_segment, &site_to_world);
if(eyepoint_rotation)
{
world_to_site.Invert(site_to_world);
eye_rotation = *eyepoint_rotation;
eye_inverted.Invert(eye_rotation);
worldToEyeMatrix.Multiply(world_to_site, eye_inverted);
}
else
{
worldToEyeMatrix.Invert(site_to_world);
}
}
else
{
site_to_world = linked_entity->localOrigin;
if(eyepoint_rotation)
{
world_to_site.Invert(site_to_world);
eye_rotation = *eyepoint_rotation;
eye_inverted.Invert(eye_rotation);
worldToEyeMatrix.Multiply(world_to_site, eye_inverted);
}
else
{
worldToEyeMatrix.Invert(site_to_world);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
DPLRenderer::ResetStatistics()
{
total_cull = 0;
total_draw = 0;
total_pixelplanes = 0;
total_frame_time = 0;
frame_count = 0;
target_frame_time = 56000;
target_frame_count= 0;
report_time = currentFrameTime + 60.0f;
}
void
DPLRenderer::ReportStatistics()
{
if(frame_count != 0)
{
std::cout<<"Frames "<<frame_count<<"\n";
std::cout<<((float)target_frame_count/(float)frame_count)*100.0f<<"% of the frames were under the target frame time\n";
std::cout<<"Average Cull "<<(unsigned long)((float)total_cull/(float)frame_count)<<"\n";
std::cout<<"Average Draw "<<(unsigned long)((float)total_draw/(float)frame_count)<<"\n";
std::cout<<"Average Pixel Planes "<<(unsigned long)((float)total_pixelplanes/(float)frame_count)<<"\n";
std::cout<<"Average Frame Time "<<(unsigned long)((float)total_frame_time/(float)frame_count)<<"\n";
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute Method, performs the rendering of one frame
//
void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::InterestingEntityIterator* all_iterator)
{
Component *component;
HRESULT hr;
// timing variables
__int64 ticks = HiResNowTicks();
#ifdef LOGFRAMERATE
fputc(0, FRAMERATE_LOG);
fwrite(&ticks, sizeof(__int64), 1, FRAMERATE_LOG);
#endif
int currentAppState = application->GetApplicationState();
switch (currentAppState)
{
case Application::CreatingMission:
case Application::LoadingMission:
case Application::WaitingForLaunch:
//if (mLoadingScreenThread == NULL)
//{
// mLoadingScreenRunning = true;
// mLoadingScreenThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ExecuteLoadScreenThread, this, 0, NULL);
//}
//hr = mDevice->TestCooperativeLevel();
//if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET)
//{
// mPrimaryDeviceReseting = true;
// DEBUG_STREAM << this << " RESETTING DEVICE - WAITING FOR FRAME TO FINISH" << std::endl << std::flush;
// while (mRenderingLoadingFrame)
// {
// Sleep(100);
// }
// DEBUG_STREAM << this << " RESETTING DEVICE - DONE WAITING FOR FRAME TO FINISH" << std::endl << std::flush;
// int bbCount = mPresentParams.BackBufferCount;
// int bbWidth = mPresentParams.BackBufferWidth;
// int bbHeight = mPresentParams.BackBufferHeight;
// ParticleEngine::Destroy();
// V(mDevice->Reset(&mPresentParams));
// ParticleEngine::Initialize(mDevice);
// this->SetCoreRenderStates();
// mPresentParams.BackBufferCount = bbCount;
// mPresentParams.BackBufferWidth = bbWidth;
// mPresentParams.BackBufferHeight = bbHeight;
// mPrimaryDeviceReseting = false;
//}
ticks = HiResNowTicks();
#ifdef LOGFRAMERATE
fputc(1, FRAMERATE_LOG);
fwrite(&ticks, sizeof(__int64), 1, FRAMERATE_LOG);
#endif
return;
case Application::LaunchingMission:
case Application::RunningMission:
break;
}
if (lastAppState != currentAppState && currentAppState == Application::LaunchingMission)
{
this->mDevice->SetRenderState(D3DRS_LIGHTING, true);
this->mDevice->SetTransform(D3DTS_PROJECTION, &this->mProjectionMatrix);
this->mDevice->SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_ARGB(128, 255, 255, 255));
this->mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
}
lastAppState = currentAppState;
// BT (task #20): window-resize aspect fix, applied PER-FRAME on the render
// thread. (The WM_SIZE-time path can't reach the renderer: APPMGR.cpp:73
// reassigns the global `application` while iterating and leaves it NULL
// between frames, so l4_application is NULL at message-pump time.) The
// WndProc just records gWindowAspect; here the projection is rebuilt when
// it changes so the fixed-size backbuffer's stretch into the resized client
// area is cancelled.
{
static float appliedAspect = 0.0f;
if (gWindowAspect > 0.0f && gWindowAspect != appliedAspect
&& viewAngle > 0.0f && clipFar > clipNear)
{
appliedAspect = gWindowAspect;
D3DXMatrixPerspectiveFovRH(&mProjectionMatrix,
viewAngle * (PI / 180.0f), appliedAspect, clipNear, clipFar);
mDecalProjectionMatrix = mProjectionMatrix;
mDecalProjectionMatrix._33 -= mDecalEpsilon;
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
DEBUG_STREAM << "[resize] projection rebuilt (frame): aspect="
<< appliedAspect << "\n" << std::flush;
}
}
// before we execute everything we need to clear
// the render lists
memset(mRenderLists, 0, sizeof(mRenderLists));
// aim-ray feed (task #36): publish the live projection's per-axis scales +
// the BACKBUFFER size (= the dpl2d reticle frame -- the full-screen
// viewport the 2D layer draws in) so the game side can build the reticle
// pick ray / designator projection through the TRUE NDC. NOTE: do NOT
// poll GetViewport here -- at this point it still holds whatever viewport
// the PREVIOUS frame's last pass set (gauge/MFD passes shrink it), which
// made the pick ray flicker frame to frame.
{
extern void BTSetAimProjection(float p11, float p22, float vpW, float vpH);
BTSetAimProjection(mProjectionMatrix._11, mProjectionMatrix._22,
(float)GetWidth(), (float)GetHeight());
}
HierarchicalDrawComponent *drawComp;
SChainIteratorOf<HierarchicalDrawComponent*> iterator(&mRenderables);
while ((drawComp = iterator.ReadAndNext()) != NULL)
drawComp->Execute();
if (mReticle)
mReticle->Execute();
if (mCamShipHUD)
mCamShipHUD->Execute();
gNumBatches = 0;
{ extern int gBTNumCulled; gBTNumCulled = 0; }
static Time lastFrameTime = mTargetRenderTime;
Scalar dT = mTargetRenderTime - lastFrameTime;
lastFrameTime = mTargetRenderTime;
currentFrameTime = Now();
// DIAG (turn-hitch hunt): time the render phases -- draw CPU vs Present
// (GPU-queue block). Logged on slow frames + 1 Hz stats.
LARGE_INTEGER _rt0; QueryPerformanceCounter(&_rt0);
DWORD currentFog;
mDevice->GetRenderState(D3DRS_FOGCOLOR, &currentFog);
hr = mDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, currentFog, 1.0f, 0);
hr = mDevice->BeginScene();
mDevice->SetFVF(L4VERTEX_FVF);
D3DXMATRIX viewTransform;
mDevice->GetTransform(D3DTS_VIEW, &viewTransform);
{
static int dbgVP = 0;
if (dbgVP < 3)
{
D3DXMATRIX projNow;
mDevice->GetTransform(D3DTS_PROJECTION, &projNow);
DEBUG_STREAM << "[VP] VIEW row3=(" << viewTransform._41 << "," << viewTransform._42 << "," << viewTransform._43
<< ") VIEW._33=" << viewTransform._33
<< " | PROJ diag=(" << projNow._11 << "," << projNow._22 << "," << projNow._33 << "," << projNow._34
<< ") PROJ._43=" << projNow._43 << "\n" << std::flush;
++dbgVP;
}
}
// PORT (turn-hitch fix): publish this frame's view frustum so d3d_OBJECT::Draw
// can skip objects fully outside it. The 1995 code drew everything (the IG
// board clipped in hardware) -- ~2700 draw calls/frame = ~107ms CPU on D3D9;
// culling the off-screen set is the port's equivalent of that hardware stage.
// Gate: BT_CULL_FRUSTUM (default ON; =0 restores draw-everything).
{
static int s_fcull = -1;
if (s_fcull < 0)
{
const char *cv = getenv("BT_CULL_FRUSTUM");
s_fcull = (cv == 0 || *cv != '0') ? 1 : 0;
}
// GPU WARM-UP: managed-pool buffers/textures upload to the GPU on FIRST
// USE, so the first frame that reveals a previously-unculled object pays
// its upload right then (one big hitch on the first look around). Draw
// EVERYTHING for the first few frames so all uploads land during mission
// start, then cull normally.
static int s_warmup = 3;
if (s_warmup > 0)
{
--s_warmup;
d3d_OBJECT::SetCullFrustum(NULL);
}
else if (s_fcull)
{
// use the renderer's OWN main projection (mProjectionMatrix), not a
// GetTransform read-back -- the device state between frames can hold a
// pass-specific matrix (sky/decal/HUD), which made the frustum silently
// degenerate for whole stretches (nothing culled).
D3DXMATRIX viewProj;
D3DXMatrixMultiply(&viewProj, &viewTransform, &mProjectionMatrix);
d3d_OBJECT::SetCullFrustum(&viewProj);
}
else
d3d_OBJECT::SetCullFrustum(NULL);
// camera world position for LOD distance selection. Row-vector D3D view
// matrix (LookAt layout): the camera BASIS VECTORS ARE THE COLUMNS
// (xaxis = (_11,_21,_31) etc.) and row 4 = (-eye.x_axis, -eye.y_axis,
// -eye.z_axis), so eye = -(t.x*col1 + t.y*col2 + t.z*col3). (The first
// version used the ROWS as the basis -- a transposed rotation whose
// "position" varied with the camera ANGLE, making LOD bands pop in/out
// as the view rotated: structures AND the mech blinked with turns.)
// LOD EYEPOINT: prefer the GAME-FED viewpoint-entity position (BTSetLodEye
// from mech4 -- the authentic reference: the pod's eyepoint sat ON the
// mech, so turning never changed any LOD distance). Our chase camera
// ORBITS the mech +-40u as it turns, which swept objects near their band
// edges in and out ("scenery blinks with viewing angle", floor flicker at
// the arena fringe). Fall back to the camera extracted from the view
// matrix (eye = -t*colBasis) when the game never fed a position.
extern int gBTLodEyeValid;
if (!gBTLodEyeValid)
{
const float ex = -(viewTransform._41 * viewTransform._11 + viewTransform._42 * viewTransform._12 + viewTransform._43 * viewTransform._13);
const float ey = -(viewTransform._41 * viewTransform._21 + viewTransform._42 * viewTransform._22 + viewTransform._43 * viewTransform._23);
const float ez = -(viewTransform._41 * viewTransform._31 + viewTransform._42 * viewTransform._32 + viewTransform._43 * viewTransform._33);
d3d_OBJECT::SetCameraPosition(ex, ey, ez);
}
}
//
// Start with the opaque pass
//
//
// BACKFACE CULLING (task #20): the bring-up D3DCULL_NONE drew interior/back
// faces the original never rendered -- distant "dark wedge" shapes were the
// INSIDES of dune/butte meshes, and standing inside a crescent-ridge mound
// looked like being sealed in rock (the authentic renderer culls those, so
// you can see out). BT_CULL selects the mode: cw (default) / ccw / off.
//
{
static DWORD s_cull = 0;
if (s_cull == 0)
{
const char *cv = getenv("BT_CULL");
s_cull = (cv == NULL) ? D3DCULL_CW
: (cv[0]=='0' || cv[0]=='n' || cv[0]=='N') ? D3DCULL_NONE
: (cv[0]=='c' && cv[1]=='c') ? D3DCULL_CCW : D3DCULL_CW;
}
mDevice->SetRenderState(D3DRS_CULLMODE, s_cull);
}
//
// LIGHTING (task #20): the maps define their own directional lights (an INI
// light page parsed at map load into sceneLight[] -- SetLight/LightEnable at
// :3065) and the BGF loader computes smooth per-vertex normals (bgfload.cpp:
// 319-456), so the whole authentic pipeline already exists; the old bring-up
// line here force-disabled it. Lit whenever the map shipped lights (env
// BT_LIGHTING=0 falls back to flat); ambient floor keeps the shadow sides
// readable (BT_AMBIENT=<hex> to tune, default 0x404040).
//
{
static int s_lit = -1;
static int s_ambientOverride = 0;
static DWORD s_ambient = 0x00404040;
if (s_lit < 0)
{
const char *lv = getenv("BT_LIGHTING");
s_lit = (lv == 0 || *lv != '0') ? 1 : 0;
const char *av = getenv("BT_AMBIENT");
if (av != 0)
{
s_ambient = (DWORD)strtoul(av, NULL, 16);
s_ambientOverride = 1;
}
}
if (s_lit && sceneLightCount > 0)
{
mDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
// BT (task #20): re-assert the AUTHENTIC env ambient (mEnvAmbient, set
// by the map's INI 'ambient=' page -- e.g. des_day 0.45) each frame,
// NOT the bring-up gray floor. BT_AMBIENT=<hex> still overrides for tuning.
mDevice->SetRenderState(D3DRS_AMBIENT, s_ambientOverride ? s_ambient : mEnvAmbient);
mDevice->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE); // animated joints scale
}
else
{
mDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
}
}
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(&currentFogNear)));
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(&currentFogFar)));
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//
// (task #20 lighting) With lighting ON, take the lit material colors from the
// per-vertex diffuse (the BGF loader bakes the BMF material tint there) --
// no SetMaterial exists in this path and D3D's default material has a BLACK
// ambient (shadow sides would render pitch black).
//
{
const char *lv2 = getenv("BT_LIGHTING");
if ((lv2 == 0 || *lv2 != '0') && sceneLightCount > 0)
{
mDevice->SetRenderState(D3DRS_COLORVERTEX, TRUE);
mDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
mDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);
}
else
{
mDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
}
}
mDevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
d3d_OBJECT::ResetState(mDevice);
if (!l4_application->IsDead())
{
std::list<d3d_OBJECT*>::const_iterator iter;
for (iter = this->mConsolidatedStaticObjects.begin(); iter != this->mConsolidatedStaticObjects.end(); ++iter)
{
(*iter)->Draw(PASS_OPAQUE, &viewTransform, mTargetRenderTime);
}
}
for (d3d_OBJECT *obj = mRenderLists[PASS_OPAQUE]; obj != NULL; obj = obj->GetNext(PASS_OPAQUE))
obj->Draw(PASS_OPAQUE, &viewTransform, mTargetRenderTime);
//
// Next up is the decal pass
//
mDevice->SetTransform(D3DTS_PROJECTION, &mDecalProjectionMatrix);
if (!l4_application->IsDead())
{
std::list<d3d_OBJECT*>::const_iterator iter;
for (iter = this->mConsolidatedStaticObjects.begin(); iter != this->mConsolidatedStaticObjects.end(); ++iter)
{
(*iter)->Draw(PASS_DECAL, &viewTransform, mTargetRenderTime);
}
}
for (d3d_OBJECT *obj = mRenderLists[PASS_DECAL]; obj != NULL; obj = obj->GetNext(PASS_DECAL))
obj->Draw(PASS_DECAL, &viewTransform, mTargetRenderTime);
//
// The sphere pass
//
//Set it up so the fog won't affect spheres' colors so much- extend the near fog plane out
float fogModNear = currentFogFar;
float fogModFar = fogModNear + (currentFogFar - currentFogNear);
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(&fogModNear)));
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(&fogModFar)));
for (d3d_OBJECT *obj = mRenderLists[PASS_SPHERE]; obj != NULL; obj = obj->GetNext(PASS_SPHERE))
obj->Draw(PASS_SPHERE, &viewTransform, mTargetRenderTime);
//
// The sky pass
//
// BT (task #20): HORIZON SEAM fix. The sky is a FLAT plane at Y~110 spanning
// +/-6000 (not a dome). Reusing the world far=2100 projection TRUNCATES that
// plane a few degrees above the true horizon, and the gap below its rim shows
// the frame Clear = fog colour -> a hard lavender band. Also the old code
// extended sky fog to near*3/far*6, leaving the plane's rim near-unfogged
// (saturated blue edge above the band). FIX: (a) a sky-only projection with
// a far plane large enough for the plane to reach the horizon; (b) the SAME
// fog as the world so the descended rim hazes to the fog colour continuously.
// Both restored after the sky loop (the alpha-blend/particle passes MUST run
// under the world projection + world fog). BT_SKY_FAR=0 restores the old
// truncated behaviour for A/B.
static const int s_skyFar =
(getenv("BT_SKY_FAR") == 0 || getenv("BT_SKY_FAR")[0] != '0');
if (s_skyFar)
{
// world fog (no *3/*6): the sky plane's far edge fogs to the horizon colour.
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(&currentFogNear)));
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(&currentFogFar)));
D3DXMATRIX skyProj;
D3DXMatrixPerspectiveFovRH(&skyProj, viewAngle * (PI / 180.0f),
gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size,
clipNear, 9000.0f);
mDevice->SetTransform(D3DTS_PROJECTION, &skyProj);
}
else
{
//Further extend the fog distance
fogModNear = currentFogNear * 3;
fogModFar = currentFogFar * 6;
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(&fogModNear)));
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(&fogModFar)));
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
}
// BT fix (task #20): the SKY is pre-shaded art and must draw FULLBRIGHT --
// with scene lighting enabled (the task-#20 lighting revival) the dome was
// being lit like world geometry: daylight on the sun side, black on the far
// side ("the sky is half day and half black"). Exempt the sky pass.
DWORD skySavedLighting = FALSE;
mDevice->GetRenderState(D3DRS_LIGHTING, &skySavedLighting);
mDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
// BT (task #20): SCREEN-toward-white combine for the sky (was MODULATE).
// The cloud art (bintA) is a GRAYSCALE intensity map; the sky's colour is the
// material tint (0.3,0.5,1.0) baked into the vertex diffuse. MODULATE (texel
// x tint) gave saturated NAVY with dark banding ("two-tone, nothing like
// clouds"). The original combined them as a SCREEN / lerp-to-white:
// screen(D,T) = T + D*(1-T) = lerp(D, white, T) -- bright cloud texels -> near
// white, dark -> the pale blue tint. One texture stage does it exactly:
// D3DTOP_LERP(Arg0=TEXTURE, Arg1=TFACTOR=white, Arg2=DIFFUSE) = T*1 + D*(1-T).
// Measured screen(0.3,0.5,1.0, 0.85)=(228,236,255) ~ original top (229,230,255).
// NOW DEFAULT OFF: the sky material (dsky_mtl) has its own 'sky' RAMP
// (0,0,0.6 -> 0.99,0.99,0.99) which the loader bakes into the sky texture and
// draws with a white vertex/material -- the authentic path with proper WHITES.
// The screen combine was the pre-ramp heuristic; BT_SKY_SCREEN=1 re-enables it.
static const int s_skyScreen =
(getenv("BT_SKY_SCREEN") != 0 && getenv("BT_SKY_SCREEN")[0] != '0');
DWORD skyOp = 0, skyA0 = 0, skyA1 = 0, skyA2 = 0, skyTF = 0;
if (s_skyScreen)
{
mDevice->GetTextureStageState(0, D3DTSS_COLOROP, &skyOp);
mDevice->GetTextureStageState(0, D3DTSS_COLORARG0, &skyA0);
mDevice->GetTextureStageState(0, D3DTSS_COLORARG1, &skyA1);
mDevice->GetTextureStageState(0, D3DTSS_COLORARG2, &skyA2);
mDevice->GetRenderState(D3DRS_TEXTUREFACTOR, &skyTF);
mDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0xFFFFFFFF);
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_LERP);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG0, D3DTA_TEXTURE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
}
if (!l4_application->IsDead())
{
std::list<d3d_OBJECT*>::const_iterator iter;
for (iter = this->mConsolidatedStaticObjects.begin(); iter != this->mConsolidatedStaticObjects.end(); ++iter)
{
(*iter)->Draw(PASS_SKY, &viewTransform, mTargetRenderTime);
}
}
for (d3d_OBJECT *obj = mRenderLists[PASS_SKY]; obj != NULL; obj = obj->GetNext(PASS_SKY))
obj->Draw(PASS_SKY, &viewTransform, mTargetRenderTime);
if (s_skyScreen)
{
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, skyOp);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG0, skyA0);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, skyA1);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, skyA2);
mDevice->SetRenderState(D3DRS_TEXTUREFACTOR, skyTF);
}
mDevice->SetRenderState(D3DRS_LIGHTING, skySavedLighting); // restore for the world
// BT (task #20): RESTORE the world projection after the sky pass -- the
// alpha-blend (explosions/effects), particle, and reticle/HUD passes below
// MUST render under the world far=2100 projection, not the sky's far=9000.
if (s_skyFar)
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
//Reactivate fog
mDevice->SetRenderState(D3DRS_FOGSTART, *((DWORD*)(&currentFogNear)));
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(&currentFogFar)));
//
// Finally we do the alpha blend pass
//
mDevice->SetRenderState(D3DRS_ZWRITEENABLE,false);
mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);
if (!l4_application->IsDead())
{
std::list<d3d_OBJECT*>::const_iterator iter;
for (iter = this->mConsolidatedStaticObjects.begin(); iter != this->mConsolidatedStaticObjects.end(); ++iter)
{
(*iter)->Draw(PASS_ALPHABLEND, &viewTransform, mTargetRenderTime);
}
}
for (d3d_OBJECT *obj = mRenderLists[PASS_ALPHABLEND]; obj != NULL; obj = obj->GetNext(PASS_ALPHABLEND))
obj->Draw(PASS_ALPHABLEND, &viewTransform, mTargetRenderTime);
// BT weapon beams (port addition): draw + age the queued muzzle->hit beams
// here in the alpha pass (world projection + view already set; Z-test on so a
// beam is occluded where it enters terrain). See BTDrawBeams above.
BTDrawBeams(mDevice, &viewTransform, (float)dT);
// BT .PFX particle effects (explosions / damage bands / mech death) -- the
// same pass as the beams: world proj+view set, Z-test on, additive quads.
{
extern void BTDrawPfx(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt);
BTDrawPfx(mDevice, &viewTransform, (float)dT);
}
// The BT targeting reticle / weapon pips (2D screen space, cockpit view
// only -- the dpl2d layer; see game/reconstructed/dpl2d.cpp).
{
extern void BTDrawReticle(struct IDirect3DDevice9 *device);
BTDrawReticle(mDevice);
}
//
// And don't forget particles too
//
D3DXMATRIX ident;
mDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&ident));
if (!l4_application->IsDead())
{
ParticleEngine::RenderParticles(&viewTransform, dT);
for (int i=0; i<MAX_INDIE_EMITTERS; ++i)
myPSFXEmitters[i].Execute();
}
//
// Wrap it up by doing the 2D pass
//
mDevice->SetFVF(L4VERTEX_2D_FVF);
mDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
mDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
mDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
if (mReticle && !l4_application->IsDead())
mReticle->Render(0, &viewTransform);
if (mCamShipHUD)
mCamShipHUD->Render(0, &viewTransform);
// DEV-COMPOSITE: in DOCKED mode (BT_DEV_GAUGES_DOCK) blit the 6-surface gauge panel
// into this window as the LAST draw before EndScene (no-op otherwise / off pod).
extern void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device);
BTDrawGaugeInset(mDevice);
hr = mDevice->EndScene();
// DEV-COMPOSITE: default mode -- render the 6 cockpit surfaces into a SEPARATE window
// (its own additional swap chain on this device) + present it, between the main
// EndScene and the main Present. No-op unless BT_DEV_GAUGES (and not docked mode).
extern void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device);
BTGaugeWindowRenderAndPresent(mDevice);
// DIAG (turn-hitch hunt): draw CPU is _rt0..here; Present blocks on the GPU.
LARGE_INTEGER _rt1; QueryPerformanceCounter(&_rt1);
hr = mDevice->Present(NULL, NULL, NULL, NULL);
{
LARGE_INTEGER _rt2, _rf; QueryPerformanceCounter(&_rt2); QueryPerformanceFrequency(&_rf);
const double drawMs = (double)(_rt1.QuadPart - _rt0.QuadPart) * 1000.0 / (double)_rf.QuadPart;
const double presentMs = (double)(_rt2.QuadPart - _rt1.QuadPart) * 1000.0 / (double)_rf.QuadPart;
static double sAcc = 0.0, sMaxD = 0.0, sMaxP = 0.0; static int sFrames = 0;
sAcc += drawMs + presentMs; ++sFrames;
if (drawMs > sMaxD) sMaxD = drawMs;
if (presentMs > sMaxP) sMaxP = presentMs;
if (drawMs + presentMs > 150.0)
DEBUG_STREAM << "[rslow] draw=" << drawMs << "ms present=" << presentMs
<< "ms batches=" << gNumBatches << "\n" << std::flush;
if (sAcc >= 1000.0)
{
extern int gBTNumCulled;
DEBUG_STREAM << "[rstat] frames=" << sFrames << " avg=" << (sAcc / sFrames)
<< "ms maxDraw=" << sMaxD << " maxPresent=" << sMaxP
<< " batches=" << gNumBatches << " culled=" << gBTNumCulled << "\n" << std::flush;
sAcc = 0.0; sFrames = 0; sMaxD = 0.0; sMaxP = 0.0;
}
}
if (hr == D3DERR_DEVICELOST)
{
int bbCount = mPresentParams.BackBufferCount;
int bbWidth = mPresentParams.BackBufferWidth;
int bbHeight = mPresentParams.BackBufferHeight;
ParticleEngine::Destroy();
V(mDevice->Reset(&mPresentParams));
ParticleEngine::Initialize(mDevice);
this->SetCoreRenderStates();
mPresentParams.BackBufferCount = bbCount;
mPresentParams.BackBufferWidth = bbWidth;
mPresentParams.BackBufferHeight = bbHeight;
}
ticks = HiResNowTicks();
#ifdef LOGFRAMERATE
fputc(1, FRAMERATE_LOG);
fwrite(&ticks, sizeof(__int64), 1, FRAMERATE_LOG);
#endif
}
void DPLRenderer::ExecuteIdle()
{
HRESULT hr;
hr = mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0);
hr = mDevice->BeginScene();
hr = mDevice->EndScene();
if (mDevice->Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST)
{
int bbCount = mPresentParams.BackBufferCount;
int bbWidth = mPresentParams.BackBufferWidth;
int bbHeight = mPresentParams.BackBufferHeight;
ParticleEngine::Destroy();
V(mDevice->Reset(&mPresentParams));
ParticleEngine::Initialize(mDevice);
this->SetCoreRenderStates();
mPresentParams.BackBufferCount = bbCount;
mPresentParams.BackBufferWidth = bbWidth;
mPresentParams.BackBufferHeight = bbHeight;
}
}
//
//#############################################################################
// DPLDelayDCSFlush and DPLDoDCSBatchFlush queue up a list of DCS pointers
// for later std::flushing in one big batch.
//#############################################################################
//
void
DPLRenderer::DPLDelayDCSFlush(
dpl_DCS *my_dcs) // The DCS we want to remember for later
{
//
// Make sure the array hasn't become overfilled somehow and make
// sure the DCS is valid.
//
Verify(delayedDCSCount <= DELAY_DCS_FLUSH_ARRAY_SIZE);
Check_Pointer(my_dcs);
//
// If the array is full, std::flush it out to make space for this DCS
//
if(delayedDCSCount == DELAY_DCS_FLUSH_ARRAY_SIZE)
{
DPLDoDCSBatchFlush();
}
//
// Add the New DCS to the list
//
delayDCSFlushArray[delayedDCSCount++] = my_dcs;
}
void
DPLRenderer::DPLDoDCSBatchFlush() // Flush the dcs's remembered by DPLDelayDCSFlush
{
//STUBBED: DPL RB 1/14/07
//SET_VIDEO_BATCH_FLUSH();
////
//// Make sure the array hasn't become overfilled somehow
////
//Verify(delayedDCSCount <= DELAY_DCS_FLUSH_ARRAY_SIZE);
////
//// Flush the array and reset the counters that go with it
////
//if(delayedDCSCount != 0)
//{
// delayDCSFlushArray[delayedDCSCount] = NULL;
// dpl_FlushDCSArticulations(delayDCSFlushArray);
// delayedDCSCount = 0;
//}
//CLEAR_VIDEO_BATCH_FLUSH();
}
//
//#############################################################################
// DPLReportFreeMemory writes current free memory in graphics card to any
// output stream.
//#############################################################################
//
void
DPLRenderer::DPLReportFreeMemory(std::ostream &output)
{
//STUBBED: DPL RB 1/14/07
// output << "Free memory in card: " << dpl_FreeMemory() << " bytes." << std::endl;
return;
}
//
//#############################################################################
// DPLReportPerfStats writes performance statistics to std::cout (stdout).
//#############################################################################
//
void
DPLRenderer::DPLReportPerfStats(std::ostream &output)
{
//STUBBED: DPL RB 1/15/07
////-----------------------------------------------------
//// HACK - copied from camera.c must re-copy if changed
////
//// case '?':
//// printf ("sect time %d dcs 0x%x inst 0x%x\n",
//// __sect_time, sect_dcs, sect_inst );
////-----------------------------------------------------
//output << "sect time " << __sect_time <<
// " dcs and inst not available" << std::endl;
////------------------------------------------------------------
//// HACK - copied from dpl_vpx.c must re-copy if changed
////
//// void dpl_PerfStats(void)
//// printf ( "cull %d draw %d frame %d pxpl %d prims %d\n",
//// __last_cull_time,
//// __last_draw_time,
//// __last_frame_time,
//// __last_pxpl_time,
//// __last_frame_prims );
////------------------------------------------------------------
//output <<
// "cull " << __last_cull_time <<
// " draw " << __last_draw_time <<
// " frame " << __last_frame_time <<
// " pxpl " << __last_pxpl_time <<
// " prims " << __last_frame_prims <<
// std::endl;
//if(statistics_started)
//{
// ReportStatistics();
//}
//else
//{
// ResetStatistics();
// statistics_started = True;
//}
//return;
}
//
//#############################################################################
// DPLToggleWireframe toggles the state of dpl global wireframe.
//#############################################################################
//
void
DPLRenderer::DPLToggleWireframe()
{
//STUBBBED: DPL RB 1/14/07
//static Logical wireframe_on = 0;
//if ((wireframe_on ^= 1) != 0)
//{
// DEBUG_STREAM << "wireframe ON" << std::endl << std::flush;
// dpl_SetRenderProperty(dpl_render_prop_wireframe, dpl_render_value_on, NULL );
//}
//else
//{
// DEBUG_STREAM << "wireframe OFF" << std::endl << std::flush;
// dpl_SetRenderProperty(dpl_render_prop_wireframe, dpl_render_value_off, NULL );
//}
}
//
//#############################################################################
// DPLTogglePVision toggles the state of dpl "predator" vision.
//#############################################################################
//
void
DPLRenderer::DPLTogglePVision()
{
//STUBBED: DPL RB 1/14/07
//static Logical pvision_on = 0;
//dpl_EXPLOSION_EFFECT_INFO sfx_info;
//sfx_info.x = sfx_info.y = sfx_info.z = 0;
//if ((pvision_on ^= 1) != 0)
//{
// DEBUG_STREAM << "pvision ON" << std::endl << std::flush;
// sfx_info.type = -1;
//}
//else
//{
// DEBUG_STREAM << "pvision OFF" << std::endl << std::flush;
// sfx_info.type = -2;
//}
//dpl_Effect(dpl_effect_type_explosion, NULL, &sfx_info);
//return;
}
//
//#############################################################################
// DPLFrameDump writes screen image to targa file.
//#############################################################################
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// oneD_filter() antialiased framegrab support
void
oneD_filter(
uint32 *linebuffer,
unsigned short *rframe,
unsigned short *gframe,
unsigned short *bframe,
int32 y,
int32 x_size
)
{
int32
pos,
i,
r, g, b;
unsigned char
*clinebuf = (unsigned char *)linebuffer;
pos = y * x_size;
for (i=0; i<x_size; ++i)
{
r = rframe[pos];
g = gframe[pos];
b = bframe[pos];
++pos;
*clinebuf++ = (unsigned char)((r >> 8) & 0xff);
*clinebuf++ = (unsigned char)((g >> 8) & 0xff);
*clinebuf++ = (unsigned char)((b >> 8) & 0xff);
}
return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// dump_frame_buffer()
void
dump_frame_buffer(
dpl_VIEW *eye,
int32 x_size,
int32 y_size,
Logical antialias
)
{
//STUBBED: DPL RB 1/14/07
////do not check eye here
////----------------------------------------
//// control variable and operating buffers
////----------------------------------------
//static uint32
// dumped_frames = 0,
// *line_buffer = NULL;
//static unsigned short
// *rframebuffer = NULL,
// *gframebuffer = NULL,
// *bframebuffer = NULL;
//if (eye == NULL)
//{
// //--------------------------
// // release allocated memory
// //--------------------------
// if (line_buffer)
// {
// Unregister_Pointer(line_buffer);
// delete line_buffer;
// }
// if (rframebuffer)
// {
// Unregister_Pointer(rframebuffer);
// delete rframebuffer;
// }
// if (gframebuffer)
// {
// Unregister_Pointer(gframebuffer);
// delete gframebuffer;
// }
// if (bframebuffer)
// {
// Unregister_Pointer(bframebuffer);
// delete bframebuffer;
// }
// return;
//}
//Check_Pointer(eye);
////-----------------------------------------------------------------
//// AA kernel definition:
////
//// normalized screen coordinates are -1.0 .. +1.0
//// these map (in NTSC) to 705 and 512.
//// So a pixel in x is
//// 2.0 / 704 = 2.841e-3
//// in y = 3.906e-3
//// for the re-use 5-sample kernel we displace by 0.5 pixels ...
////-----------------------------------------------------------------
//static const int32 kernel_size = 4;
//static const float xk = 2.0f / x_size;
//static const float yk = 2.0f / y_size;
//static const float ox = xk / 4.0f;
//static const float oy = yk / 4.0f;
//static const float jx = 0.0f; // ox / 4.0f
//static const float jy = 0.0f; // oy / 4.0f
//static const float x_kernel[4] = { -(ox+jx), ox-jx, jx-ox, jx+ox };
//static const float y_kernel[4] = { oy-jy, oy+jy, -(jy+oy), jy-oy };
//static const int kernel_weights[4] = { 64, 64, 64, 64 };
////---------------------
//// operating variables
////---------------------
//int32 passes = (antialias)?kernel_size:1;
//int32 size = x_size * y_size;
//int32 frameptr;
//int32 i, x, y;
//DEBUG_STREAM << "Dump frame buffer (antialias=" <<
// ((antialias)?"True":"False") << ") - press Esc to cancel." << std::endl;
////----------------------------
//// allocate operating buffers
////----------------------------
//if (line_buffer == NULL)
//{
// //-----------------------------------------------------------------------
// // NOTE - must call dump_frame_buffer(NULL, NULL, NULL, NULL) to release
// //-----------------------------------------------------------------------
// line_buffer = new uint32[1024];
// Register_Pointer(line_buffer);
// rframebuffer = new unsigned short[size];
// Register_Pointer(rframebuffer);
// gframebuffer = new unsigned short[size];
// Register_Pointer(gframebuffer);
// bframebuffer = new unsigned short[size];
// Register_Pointer(bframebuffer);
//}
////------------------------------
//// clear out r g b framebuffers
////------------------------------
//frameptr = 0;
//for (i=0; i<size; ++i)
//{
// rframebuffer[frameptr] = 0x0;
// gframebuffer[frameptr] = 0x0;
// bframebuffer[frameptr] = 0x0;
// ++frameptr;
//}
//dpl_RedrawScene();
//dpl_RedrawScene();
////--------------------------------
//// perform AA framecapture passes
////--------------------------------
//int this_weight;
//float32 x0, y0, x1, y1, zeye;
//for (i=0; i<passes; ++i)
//{
// DEBUG_STREAM << " pass " << (i+1) << " of " << passes << " " << std::flush;
// //------------
// // jitter eye
// //------------
// dpl_GetViewProjection(eye, &x0, &y0, &x1, &y1, &zeye);
// dpl_SetViewProjection(eye,
// x0 + x_kernel[i],
// y0 + y_kernel[i],
// x1 + x_kernel[i],
// y1 + y_kernel[i], zeye);
// dpl_FlushView(eye);
// //--------------
// // redraw frame
// //--------------
// dpl_RedrawScene();
// dpl_RedrawScene();
// //--------------------
// // capture accumulate
// //--------------------
// this_weight = kernel_weights[i];
// frameptr = 0;
// for (y=0; y<y_size; ++y )
// {
// unsigned char *linep = (unsigned char *)&line_buffer[0];
// //------------------------------
// // Esc key cancelles frame dump
// //------------------------------
// if (kbhit())
// {
// if (getch() == 27)
// {
// DEBUG_STREAM << std::endl << "cancelled." << std::endl << std::flush;
// return;
// }
// }
// if ((y & 15) == 0)
// { DEBUG_STREAM << "." << std::flush; }
// dpl_ReadFrameStore(eye, (uint32 *)linep, 0, y, x_size, 1);
// for (x=0; x<x_size; ++x)
// {
// rframebuffer[frameptr] += (unsigned short)(linep[2] * this_weight);
// gframebuffer[frameptr] += (unsigned short)(linep[1] * this_weight);
// bframebuffer[frameptr] += (unsigned short)(linep[0] * this_weight);
// ++frameptr;
// linep += 4;
// }
// }
// DEBUG_STREAM << std::endl << std::flush;
//}
////----------------------------------
//// write frame buffer to targa file
////----------------------------------
//FILE *fp;
//char fname[64];
//unsigned char tga_hdr[18];
//sprintf(fname, "dump%d.tga", dumped_frames);
//tga_hdr[0] = 0x00;
//tga_hdr[1] = 0x00;
//tga_hdr[2] = 0x02;
//tga_hdr[3] = 0x00;
//tga_hdr[4] = 0x00;
//tga_hdr[5] = 0x00;
//tga_hdr[6] = 0x00;
//tga_hdr[7] = 0x00;
//tga_hdr[8] = 0x00;
//tga_hdr[9] = 0x00;
//tga_hdr[10] = 0x00;
//tga_hdr[11] = 0x00;
//tga_hdr[12] = (unsigned char)(x_size & 0xff);
//tga_hdr[13] = (unsigned char)((x_size >> 8) & 0xff);
//tga_hdr[14] = (unsigned char)(y_size & 0xff);
//tga_hdr[15] = (unsigned char)((y_size >> 8) & 0xff);
//tga_hdr[16] = 0x18;
//tga_hdr[17] = 0x00;
//DEBUG_STREAM << "Writing image to file '" << fname << "' . . . " << std::flush;
//fp = fopen(fname, "wb");
//fwrite(tga_hdr, 18, 1, fp);
//for (y=0; y<y_size; ++y )
//{
// oneD_filter(line_buffer,
// rframebuffer,
// gframebuffer,
// bframebuffer,
// (y_size-1) - y,
// x_size);
// fwrite(line_buffer, x_size*3, 1, fp);
//}
//fclose(fp);
//DEBUG_STREAM << "done." << std::endl << std::flush;
//++dumped_frames; // increment for next frame dump
//return;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DPLRenderer::DPLFrameDump()
void
DPLRenderer::DPLFrameDump(Logical antialias)
{
//STUBBED: DPL RB 1/15/07
//dump_frame_buffer(dplMainView, x_size, y_size, antialias);
//return;
}
//
//#############################################################################
// DPLIndependantPFX lets you start up an effect in absolute space that is
// run to termination automatically by the renderer.
//#############################################################################
//
void
DPLRenderer::DPLIndependantPFX(
Point3D location, // Location in space to trigger the effect
dpl_PARTICLESTART_EFFECT_INFO *psfx_definition, // Description of the pfx
dpl_DCS *my_DCS, // Optional DCS to link to
int subid) // inserted into third byte of effect id.
{
//STUBBED: DPL RB 1/14/07
// we put our id into the lower 16 bits because (at least) the upper 8 bits are flags
// HACK...for testing pfx bug
//dpl_PARTICLESTART_EFFECT_INFO tempParticle;
//tempParticle = *psfx_definition;
//tempParticle.identifier = (psfx_definition->identifier & 0xffff0000) | GetUniqueID() | ((subid << 16) & 0x00ff0000);
//tempParticle.px = location.x;
//tempParticle.py = location.y;
//tempParticle.pz = location.z;
//dpl_Effect(dpl_effect_type_particlestart, my_DCS, &tempParticle);
// std::cout<<"psfx identifier used was "<<tempParticle.identifier<<"\n";
}
//
//#############################################################################
// DPLIndependantEffect lets you start up an effect in absolute space that is
// run to termination automatically by the renderer.
//#############################################################################
//
void
DPLRenderer::DPLIndependantEffect(
Point3D location,
int effect_number,
dpl_DCS *my_DCS, // Optional DCS to link to
int subid)
{
//STUBBED: DPL RB 1/14/07
if(effect_number >= 1000)
{
effect_number -= 1000;
if(effect_number < 0 || effect_number > MAX_PSFX_COUNT-1)
{
Fail("PSFX id number was not in the allowed range");
}
// DPLIndependantPFX(location,myPSFXDescriptons[effect_number],my_DCS,subid);
// find a free emitter
ParticleEmitter *emitter = NULL;
for (int i=0; i<MAX_INDIE_EMITTERS; ++i)
{
if (!myPSFXEmitters[i].IsActive())
{
emitter = &myPSFXEmitters[i];
break;
}
}
if (emitter != NULL)
{
emitter->SetEffect(&myPSFXDescriptons[effect_number]);
emitter->SetPosition(location.x, location.y, location.z);
emitter->Start();
}
return;
}
//
// Board effect numbers (<100) -- the 1995 dpl explosion/damage effects.
// RECONSTRUCTED: routed to the BT .PFX particle layer (the [pfx_day]/
// [pfx_night] psfxN mapping loads each number's authentic .PFX definition;
// see the layer banner at the top of this file). This is what makes every
// weapon-hit / damage-band / mech-death explosion actually VISIBLE -- the
// original dpl_Effect(dpl_effect_type_explosion, ...) below was IG-board
// hardware and was never ported.
//
{
extern void BTStartPfx(int effect_number, float x, float y, float z);
BTStartPfx(effect_number, location.x, location.y, location.z);
}
//dpl_EXPLOSION_EFFECT_INFO my_explosion;
//my_explosion.type = effect_number;
//my_explosion.x = location.x;
//my_explosion.y = location.y;
//my_explosion.z = location.z;
//dpl_Effect ( dpl_effect_type_explosion, my_DCS, &my_explosion );
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DPLRenderer::SetViewAngle(Degree new_angle)
{
//STUBBED: DPL RB 1/14/07
//Check(this);
////
////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//// Convert From Degree To Radian
////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
////
//Radian view_angle;
//view_angle = new_angle;
//viewAngle = view_angle;
//viewRatio = tan(viewAngle/2.0f);
////
////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//// Calc Aspect Ratio and Set View Projection
////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
////
//aspectRatio = (float) y_size / (float) x_size;
//dpl_SetViewProjection ( dplMainView, -1.0f, -aspectRatio, 1.0f, aspectRatio, 1.0f/viewRatio);
//dpl_FlushView(dplMainView);
}
//
//#############################################################################
// Startup the implementation of the Division video renderer
//#############################################################################
//
void
DPLRenderer::LoadMissionImplementation(Mission *mission)
{
Check(this);
Tell("DPLVideoRenderer::StartImplementation has been called\n");
DPLReadEnvironment(mission);
LoadNameBitmaps();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// BT (task #20): window-resize aspect fix. gWindowAspect (defined at the top
// of this file) holds the live client aspect; D3D9 stretches the fixed-size
// backbuffer into the client area, so rendering with the CLIENT aspect cancels
// the stretch.
//
void L4NotifyWindowResized(int client_w, int client_h)
{
if (client_w <= 0 || client_h <= 0)
return;
gWindowAspect = (float)client_w / (float)client_h;
DEBUG_STREAM << "[resize] client " << client_w << "x" << client_h
<< " aspect=" << gWindowAspect
<< " app=" << (void *)l4_application << "\n" << std::flush;
if (l4_application != NULL)
{
DPLRenderer *renderer = l4_application->GetVideoRenderer();
if (renderer != NULL)
renderer->UpdateWindowAspect(client_w, client_h);
}
}
void
DPLRenderer::UpdateWindowAspect(int client_w, int client_h)
{
if (client_w <= 0 || client_h <= 0)
return;
gWindowAspect = (float)client_w / (float)client_h;
if (viewAngle <= 0.0f || clipFar <= clipNear)
{
DEBUG_STREAM << "[resize] projection not built yet (viewAngle="
<< viewAngle << ")\n" << std::flush;
return; // projection not built yet; the builder below picks it up
}
D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI / 180.0f),
gWindowAspect, clipNear, clipFar);
mDecalProjectionMatrix = mProjectionMatrix;
mDecalProjectionMatrix._33 -= mDecalEpsilon;
if (mDevice != NULL)
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
DEBUG_STREAM << "[resize] projection rebuilt: fov=" << viewAngle
<< " aspect=" << gWindowAspect << " _11=" << mProjectionMatrix._11
<< " _22=" << mProjectionMatrix._22 << "\n" << std::flush;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DEBUG(bring-up): install a valid projection + lighting so loaded geometry is
// visible even when the full DPLReadEnvironment path has not been wired (BT).
//
void
DPLRenderer::EnsureValidProjection()
{
//
// BT (task #20): this was a bring-up SAFETY NET for when the real DPL
// environment (BTDPL.INI via DPLReadEnvironment) had not yet been wired --
// it forced near/far=0.25/1300, KILLED fog, and set WHITE ambient, which
// clobbered the authentic clip range (2100), the haze fog (600/2050), and
// the map ambient (0.45). DPLReadEnvironment now runs and sets all of those
// from the map's INI page, so honour them: only fall back to fixed values
// when the env genuinely failed to produce a valid projection.
//
if (viewAngle > 0.0f && clipFar > clipNear)
{
// Env is valid -- just (re)assert the env-built projection on the device.
// Do NOT touch fog or ambient (the env set the authentic values).
if (mDevice != NULL)
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
DEBUG_STREAM << "[VP] env projection honoured: fov=" << viewAngle
<< " near=" << clipNear << " far=" << clipFar
<< " fog=" << currentFogNear << ".." << currentFogFar << "\n" << std::flush;
return;
}
// ---- FALLBACK: env did not set a projection (should not happen with the
// catch-all branch in BTDPL.INI, but keeps the mech visible if it does).
viewAngle = 60.0f;
clipNear = 0.25f;
clipFar = 1300.0f;
viewRatio = (float)tan(viewAngle * 0.5f * (PI / 180.0f));
D3DXMatrixIdentity(&mProjectionMatrix);
D3DXMatrixPerspectiveFovRH(&mProjectionMatrix, viewAngle * (PI / 180.0f),
gWindowAspect > 0.0f ? gWindowAspect : (float)x_size / (float)y_size,
clipNear, clipFar);
mDecalEpsilon = 0.0000005f;
mDecalProjectionMatrix = mProjectionMatrix;
mDecalProjectionMatrix._33 -= mDecalEpsilon;
// push fog far away so the mech is not fogged to the background colour
fogNear = currentFogNear = 1.0e9f;
fogFar = currentFogFar = 1.0e9f;
if (mDevice != NULL)
{
mDevice->SetTransform(D3DTS_PROJECTION, &mProjectionMatrix);
mDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
mDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255));
}
DEBUG_STREAM << "[VP] EnsureValidProjection FALLBACK (env failed): RH fov="
<< viewAngle << " near=" << clipNear << " far=" << clipFar << "\n" << std::flush;
}
//##############################################################################
// Name Bitmap Support
//
void DPLRenderer::SortAndReloadNameBitmaps()
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get the Entity Group of Players
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup *player_group = application->GetEntityManager()->FindGroup("Players");
ChainIteratorOf<Node*> player_iterator(player_group->groupMembers);
Player *current_player;
while ((current_player = (Player*)player_iterator.ReadAndNext()) != NULL)
{
BitMap *name_bitmap = application->GetCurrentMission()->GetLargeNameBitmap(current_player->playerBitmapIndex);
if (name_bitmap && current_player->IsScoringPlayer())
{
int index = current_player->playerRanking + 1;
if (mNameTextures[index])
mNameTextures[index]->Release();
mDevice->CreateTexture(128, 32, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &mNameTextures[index], NULL);
LoadBitSliceTexture(name_bitmap, mNameTextures[index]);
}
}
LoadOrdinalBitmaps();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DPLRenderer::LoadOrdinalBitmaps()
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Check if we have a Director on this machine,
// if so, create load the ordinal Bitmaps only
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//
if (application->GetMissionPlayer()->GetInstance() == CameraDirector::MasterInstance &&
application->GetMissionPlayer()->IsDerivedFrom(*CameraDirector::GetClassDerivations()))
{
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Make Sure index is in the right place
// in case < 8 players!!!!
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int index = 0;
for(int ii=1; ii<5; ++ii)
{
//
//~~~~~~~~~~~~~~~~~~
// Index Starts at 1
//~~~~~~~~~~~~~~~~~~
//
BitMap *ordinal_bitmap = application->GetCurrentMission()->GetOrdinalBitmap(ii);
if (mOrdinalTextures[index])
mOrdinalTextures[index]->Release();
mDevice->CreateTexture(128, 32, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &mOrdinalTextures[index], NULL);
LoadBitSliceTexture(ordinal_bitmap, mOrdinalTextures[index]);
++index;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned int*
DPLRenderer::MakeBitSliceStorage()
{
Check(this);
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Allocate some temporary memory
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
uint32 *worst_case_texels = new uint32[128*64];
if(!worst_case_texels)
{
Fail("Could not allocate RAM for worst case texels\n");
}
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Zero Out Memory so Empty Texture space is Black
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
for(int ii=0;ii<8192;++ii)
{
worst_case_texels[ii] = 0;
}
return worst_case_texels;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void DPLRenderer::LoadNameBitmaps()
{
int player_count = application->GetCurrentMission()->GetPlayerCount();
for (int ii=0; ii<player_count; ++ii)
{
BitMap *name_bitmap = application->GetCurrentMission()->GetLargeNameBitmap(ii + 1);
if (name_bitmap)
{
if (mNameTextures[ii])
mNameTextures[ii]->Release();
HRESULT hr;
V(mDevice->CreateTexture(128, 32, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &mNameTextures[ii], NULL));
LoadBitSliceTexture(name_bitmap, mNameTextures[ii]);
}
}
LoadOrdinalBitmaps();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
DPLRenderer::ShutdownImplementation()
{
//STUBBED: DPL RB 1/14/07
// Tell("DPLVideoRenderer::StopImplementation has been called\n");
// dpl_EnableSyncOnCreate();
// SChainIteratorOf<InnerProjectileRenderable*> projectile_iterator(&projectile_list);
// TreeIteratorOf<DPLObjectCacheLine*, CString> cache_iterator(&dplObjectCacheSocket);
//#if defined(LAB_ONLY)
// std::cout<<"max projectiles "<<projectile_iterator.GetSize()<<"\n";
// std::cout<<"max cached objects "<<cache_iterator.GetSize()<<"\n";
//#endif
//// std::cout<<"DPL renderer has "<<dpl_FreeMemory()<<" free\n";
}
void
DPLRenderer::SuspendImplementation()
{
Tell("DPLVideoRenderer::SuspendImplementation has been called\n");
}
void
DPLRenderer::ResumeImplementation()
{
Tell("DPLVideoRenderer::ResumeImplementation has been called\n");
}
//-----------------------------------------------------------------------------
//--------------------------Id's for psfx effects---------------------------------
//-----------------------------------------------------------------------------
int
DPLRenderer::GetUniqueID()
{
myUniqueID = (myUniqueID + 1) & 0x0000ffff;
return(myUniqueID);
}
//-----------------------------------------------------------------------------
//--------------------------MUNGA caching of objects---------------------------
//-----------------------------------------------------------------------------
dpl_OBJECT*
DPLRenderer::GetCachedObject(
const CString &object_name) // Name of the object we want to get
{
dpl_OBJECT
*object_pointer;
//
// Get this object from the cache by name
//
DPLObjectCacheLine *my_cache_line;
if ((my_cache_line = dplObjectCacheSocket.Find(object_name)) == NULL)
return NULL;
//
// Remove the cache line from the cache
//
object_pointer = my_cache_line->objectPointer;
delete my_cache_line;
//
// Return the object pointer
//
return(object_pointer);
}
void
DPLRenderer::PutCachedObject(
const CString &object_name, // Name of the object we will cache
dpl_OBJECT *object_pointer) // pointer to the object being cached
{
//
// Create the cache line data structure
//
#if 0
typedef PlugOf<dpl_OBJECT*> DPLObjectCacheLine;
DPLObjectCacheLine *my_cache_line = new DPLObjectCacheLine(object_pointer);
#endif
DPLObjectCacheLine *my_cache_line =
new DPLObjectCacheLine(
object_name,
object_pointer);
Register_Object(my_cache_line);
//
// Store this cache line in the cache
//
dplObjectCacheSocket.AddValue(my_cache_line, object_name);
}
//-----------------------------------------------------------------------------
//--------------------------Projectile Speedup---------------------------------
//-----------------------------------------------------------------------------
InnerProjectileRenderable*
DPLRenderer::GetProjectile(
d3d_OBJECT *graphical_object, // object to hang on the DCS, may be a list later <NULL>
bool isDeathZone) // DPL Zone this stuff will live in (for culling)
{
//STUBBED: DPL RB 1/14/07
//InnerProjectileRenderable
// *return_projectile;
//dpl_INSTANCE
// *temp_instance;
////
//// Are there projectiles in the list?
////
//SChainIteratorOf<InnerProjectileRenderable*> iterator(&projectile_list);
//if ((return_projectile = iterator.GetCurrent()) != NULL)
//{
// // Yes, remove it from the list set it's instance and return it
// iterator.Remove();
// temp_instance = return_projectile->GetInstance();
// if(graphical_object != dpl_GetInstanceObject(temp_instance))
// {
// dpl_SetInstanceObject (temp_instance, graphical_object);
// dpl_FlushInstance (temp_instance);
// }
// return(return_projectile);
//}
//else
//{
// // no, make a new one and return that
InnerProjectileRenderable *projectile =
new InnerProjectileRenderable(
graphical_object, // object to hang on the DCS, may be a list later <NULL>
isDeathZone); // DPL Zone this stuff will live in (for culling)
return(projectile);
//}
}
void
DPLRenderer::ReleaseProjectile(
InnerProjectileRenderable* inner_projectile)
{
// add the projectile back to the list
//projectile_list.Add(inner_projectile);
}
//-----------------------------------------------------------------------------
//--------------------------Joint to DCS translator----------------------------
//-----------------------------------------------------------------------------
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DPLJointToDCSTranslator is a class that contains a joint number to dcs
// pointer translation table. This lets a renderer find the dcs that goes
// with a particular segment.
//
DPLJointToDCSTranslator::DPLJointToDCSTranslator(
Entity *entity, // The entity to translate
dpl_DCS *dcs_array[]) // Array of DCS's to translate
{
JointedMover
*my_jointed_mover;
//
// Make sure this is a jointed mover, then cast the entity pointer over
//
if (entity->IsDerivedFrom(*JointedMover::GetClassDerivations()))
{
my_jointed_mover = (JointedMover*)entity;
}
else
{
Fail("DPLJointToDCSTranslator was called on an entity NOT a JointedMover\n");
}
//
// Find out how many joints the entity has, then allocate enough RAM for a
// DCS pointer to every joint.
//
JointSubsystem* joint_subsystem = my_jointed_mover->GetJointSubsystem();
Check(joint_subsystem);
translation_array = new (dpl_DCS(*[joint_subsystem->GetJointCount()]));
Register_Pointer(translation_array);
//
// Setup to iterate the entity segment table
//
EntitySegment::SegmentTableIterator segment_iterator(my_jointed_mover->segmentTable);
EntitySegment *current_segment;
while ((current_segment = segment_iterator.ReadAndNext()) != NULL)
{
//
// For each segment, see if it has a joint index, if it does, put the
// DCS for that joint into the translation array
//
Check(current_segment);
int joint_index = current_segment->GetJointIndex();
if(joint_index != -1)
{
translation_array[joint_index] = dcs_array[current_segment->GetIndex()];
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DPLJointToDCSTranslator::~DPLJointToDCSTranslator()
{
Unregister_Pointer(translation_array);
delete[] translation_array;
translation_array = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Logical
DPLJointToDCSTranslator::TestInstance() const
{
return True;
}
//-----------------------------------------------------------------------------
//--------------------------Resource creation support--------------------------
//-----------------------------------------------------------------------------
//#############################################################################
//############# DPLRenderer::CreateModelVideoStreamResource #############
//#############################################################################
ResourceDescription::ResourceID
DPLRenderer::CreateModelVideoStreamResource(
ResourceFile *resource_file,
const char *model_name,
NotationFile *model_file,
const ResourceDirectories * /*directories*/
)
{
ResourceDescription *res_description =
resource_file->FindResourceDescription(
model_name,
ResourceDescription::VideoModelResourceType
);
if (res_description == NULL)
{
NameList *video_entries = model_file->MakeEntryList("video");
if (video_entries != NULL)
{
Register_Object(video_entries);
//----------------------------------------------
// parse video model data and store in resource
//----------------------------------------------
Tell("Building video resource for model '" << model_name << "'.\n");
NameList::Entry
*entry,
*next_entry;
const char
*entry_name,
*entry_pointer,
*argument_start;
int
length;
char
argument[40],
*argument_pointer;
L4VideoObject::ResourceType
resource_type;
Enumeration //(see L4VideoObject::RendererModes)
renderer_modes;
enum
{
parsing_filename,
seeking_switch,
parsing_switch,
parsing_billboard
} state;
L4VideoObject
*video_object;
L4VideoObjectWrapper
*video_wrapper;
ChainOf<L4VideoObjectWrapper*>
video_chain(NULL);
char
*video_stream,
*video_pointer;
int
object_count;
long
object_size,
stream_length;
//------------------------------------------------
// parse each entry in [video] page of model file
//------------------------------------------------
next_entry = video_entries->GetFirstEntry();
while (next_entry)
{
entry = next_entry;
Check(entry);
next_entry = entry->GetNextEntry(); // so 'continue' works
entry_name = entry->GetName();
if (entry_name && *entry_name)
{
//--------------------------------------------
// could be "skeleton" or "object" or comment
//--------------------------------------------
if (strcmp(entry_name, "object") == 0)
{
resource_type = L4VideoObject::Object;
}
else if (strcmp(entry_name, "rubble") == 0)
{
resource_type = L4VideoObject::Rubble;
}
else if (strcmp(entry_name, "skeleton") == 0)
{
resource_type = L4VideoObject::Skeleton;
}
else if (Comment_Line(entry_name))
{
// do nothing - skip comments
continue;
}
else if (strncmp(entry_name, "skeleton", 8) == 0)
{
// do nothing - skip temporary skeleton entries
continue;
}
else if (strncmp(entry_name, "destroyed", 9) == 0)
{
// do nothing - skip destroyed skeleton entries
continue;
}
else if (strncmp(entry_name, "dzm", 3) == 0)
{
// do nothing - skip damage zone material entries
continue;
}
else
{
// resource_type = L4VideoObject::Unknown;
DEBUG_STREAM << "Unknown entry '" << entry_name <<
"' in model '" << model_name << "' ignored." << std::endl;
continue;
}
//---------------------------------
// parse and process each argument
//---------------------------------
entry_pointer = entry->GetChar();
while (entry_pointer && *entry_pointer)
{
//-------------------------
// skip leading whitespace
//-------------------------
while (*entry_pointer != '\0' && isspace(*entry_pointer))
{
++entry_pointer;
}
//---------------------------
// exit loop if nothing left
//---------------------------
if (*entry_pointer == '\0')
{
break;
}
//-------------------------------------------
// parse the next argument into local buffer
//-------------------------------------------
argument_start = entry_pointer;
while (*entry_pointer != '\0' && !isspace(*entry_pointer))
{
++entry_pointer;
}
length = entry_pointer - argument_start;
Verify( length < sizeof(argument) );
strncpy(argument, argument_start, length);
argument[length] = '\0';
//----------------------
// parse local argument
//----------------------
argument_pointer = argument;
renderer_modes = L4VideoObject::Normal;
state = parsing_filename;
while (*argument_pointer != '\0')
{
switch (state)
{
case parsing_filename:
switch (*argument_pointer)
{
#if 0
case '.':
if ((resource_type == L4VideoObject::Object) ||
(resource_type == L4VideoObject::Rubble))
{
*argument_pointer = '\0'; // terminate filename
state = seeking_switch;
}
break;
#endif
case '/':
*argument_pointer = '\0'; // terminate filename
state = parsing_switch;
break;
default:
// later check for valid filename character...
break;
}
break;
case seeking_switch:
if (*argument_pointer == '/')
{ state = parsing_switch; }
break;
case parsing_switch:
switch (*argument_pointer)
{
case 'b':
case 'B':
state = parsing_billboard;
break;
case 'i':
case 'I':
renderer_modes |= L4VideoObject::IntersectImmune;
state = seeking_switch;
break;
default:
state = seeking_switch;
break;
}
break;
case parsing_billboard:
switch (*argument_pointer)
{
case 'x':
case 'X':
renderer_modes |= L4VideoObject::BillboardXAxis;
break;
case 'y':
case 'Y':
renderer_modes |= L4VideoObject::BillboardYAxis;
break;
case 'z':
case 'Z':
renderer_modes |= L4VideoObject::BillboardZAxis;
break;
case '/':
state = parsing_switch;
break;
default:
state = seeking_switch;
break;
}
break;
// no default:
}
++argument_pointer;
}
//-----------------------------------------------
// create video resource object and add to chain
//-----------------------------------------------
video_object =
new L4VideoObject(
argument,
resource_type,
renderer_modes
);
Register_Pointer(video_object); // not _Object!
video_wrapper =
new L4VideoObjectWrapper(
video_object,
True // delete object when done
);
Register_Object(video_wrapper);
//Tell(" adding video object '"<<argument<<"' type ");
//Tell(resource_type<<" mode 0x"<<std::hex<<renderer_modes<<"\n");
video_chain.Add(video_wrapper);
} // arguments processed for this entry
}
} // all arguments processed
//----------------------------------------------
// convert chain of video resources into stream
//----------------------------------------------
ChainIteratorOf<L4VideoObjectWrapper*>
video_iterator(video_chain);
object_count = video_iterator.GetSize();
object_size = sizeof(L4VideoObject);
stream_length = sizeof(int) + object_count * object_size;
video_stream = new char[stream_length];
Register_Pointer(video_stream);
video_pointer = video_stream;
*((int *)video_pointer) = object_count;
video_pointer += sizeof(int);
Tell(" count " <<std::dec<< object_count << ", resource size " << stream_length << "\n");
while ((video_wrapper = video_iterator.ReadAndNext()) != NULL)
{
video_object = video_wrapper->GetVideoObject();
if (!stricmp(video_object->GetObjectFilename(), "bp1.bgf"))
video_pointer += 1 - 1;
*((L4VideoObject *)video_pointer) = *video_object;
video_pointer += object_size;
}
//--------------------------------------------------
// store stream of video resources in resource file
//--------------------------------------------------
res_description =
resource_file->AddResource(
model_name,
ResourceDescription::VideoModelResourceType,
1,
ResourceDescription::Preload,
video_stream,
stream_length
);
//--------------------------
// release allocated memory
//--------------------------
// video_chain
L4VideoObjectWrapper::DeleteVideoObjectChain(&video_chain);
// video_stream
Unregister_Pointer(video_stream);
delete video_stream;
// video_entries
Unregister_Object(video_entries);
delete video_entries;
}
else
{
//-------------------------------
// no [video] page in model file
//-------------------------------
return (ResourceDescription::NullResourceID);
}
}
return (res_description->resourceID);
}
//===========================================================================//