- dpl2d API fully recovered from the binary recorders (@487f34-488630): opcode model (points/lines/polyline/circle/color/width/matrix/push-pop), CallList = INLINE include (state persists to caller), centered coordinate frame (unit = half viewport height). game/reconstructed/dpl2d.cpp rework. - BTReticleRenderable ctor @004cc40c transcribed with the authentic calibration (originX .35, originY .25, scaleY .5, 0..1200m right range ladder, bottom heading tape, FUN_004cd938 tick ladders, lock rings, turn arrows); range caret slides from the live target range fed by the mech4 targeting step (BTSetHudTargetRange). - Weapon pips: the binary gate is IsDerivedFrom(0x511830 = MechWeapon::ClassDerivations) [T1: part_014.c:5386 hard-aborts on missing weapon attrs; part_012 counts + roster ORs capabilityFlags@+0x334] so ALL 7 BLH weapons register (3 lasers + 2 PPCs + 2 MissileLaunchers). Pip A (lit, authored PipColor) on TargetWithinRange, else dark ring B. - AddWeapon @004cdac0 store map corrected to the verified order (part_014.c:4827-4837); both state attrs are literally named "SimulationState" (strings @51d526/51d577) -> weapon simulationState. - Mech roster this[0x1ef] renamed poweredSubsystems -> weaponRoster (0x511830 is MechWeapon, not PoweredSubsystem=0x50f4bc); derivation-tag table added to context/decomp-reference.md. - Draw hook BTDrawReticle after the 3D scene, cockpit view only. Binary Execute @004cdcf0 is an un-exported gap -> Draw dynamics [T3], tracked in context/open-questions.md with the blx_cop canopy + PNAME pip meshes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9002 lines
312 KiB
C++
9002 lines
312 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;
|
|
}
|
|
}
|
|
|
|
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));
|
|
|
|
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, ¤tFog);
|
|
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*)(¤tFogNear)));
|
|
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(¤tFogFar)));
|
|
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*)(¤tFogNear)));
|
|
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(¤tFogFar)));
|
|
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*)(¤tFogNear)));
|
|
mDevice->SetRenderState(D3DRS_FOGEND, *((DWORD*)(¤tFogFar)));
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
//===========================================================================//
|