Files
RP412/MUNGA_L4/L4D3D.cpp
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

464 lines
13 KiB
C++

#include "mungal4.h"
#pragma hdrstop
#include "l4d3d.h"
#include "L4VIDEO.h"
#include <algorithm>
int gNumBatches = 0;
bool d3d_OBJECT::mLastTextureScrollState = false;
L4TEXOP::WrapType d3d_OBJECT::mLastWrapU = L4TEXOP::WrapType::REPEAT;
L4TEXOP::WrapType d3d_OBJECT::mLastWrapV = L4TEXOP::WrapType::REPEAT;
bool d3d_OBJECT::mLastTexturingState = true;
long d3d_OBJECT::mNextID = 1;
stdext::hash_map<std::string, L4TEXOP> d3d_OBJECT::mTextureCache;
void chgext(char *filePath, const char *newExtension)
{
int len = strlen(filePath);
for (int i=len-1; i>=0; i--)
{
if (filePath[i] == '.')
{
strcpy(&filePath[i + 1], newExtension);
return;
}
}
}
void d3d_OBJECT::ResetState(LPDIRECT3DDEVICE9 device)
{
d3d_OBJECT::mLastTextureScrollState = false;
device->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
d3d_OBJECT::mLastWrapU = L4TEXOP::WrapType::REPEAT;
device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
d3d_OBJECT::mLastWrapV = L4TEXOP::WrapType::REPEAT;
device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
d3d_OBJECT::mLastTexturingState = true;
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
}
d3d_OBJECT *d3d_OBJECT::LoadObject(LPDIRECT3DDEVICE9 device, char *fileName)
{
char fullPath[512];
sprintf(fullPath, "VIDEO\\%s", fileName);
chgext(fullPath, "x");
//Get a ref to the renderer
DPLRenderer *renderer = l4_application->GetVideoRenderer();
LPD3DXMESH mesh;
LPD3DXBUFFER materialBuffer, adjacencyBuffer;
DWORD materialCount;
HRESULT hr = D3DXLoadMeshFromXA(fullPath, D3DXMESH_MANAGED, device, &adjacencyBuffer, &materialBuffer, NULL, &materialCount, &mesh);
if (FAILED(hr))
{
chgext(fullPath, "sph");
return d3d_OBJECT::LoadSpheres(device, fullPath);
}
chgext(fullPath, "det");
FILE *detFile = fopen(fullPath, "rb");
int numDetailOps = 0;
int *detailOps = NULL;
bool isSky = false;
if (detFile != NULL)
{
fread(&numDetailOps, sizeof(int), 1, detFile);
detailOps = new int[numDetailOps];
fread(detailOps, sizeof(int), numDetailOps, detFile);
fclose(detFile);
}
chgext(fullPath, "sky");
FILE *skyFile = fopen(fullPath, "rb");
if (skyFile != NULL)
{
isSky = true;
fclose(skyFile);
}
d3d_OBJECT *object = new d3d_OBJECT(device, mesh, (DWORD*)adjacencyBuffer->GetBufferPointer(), materialCount);
D3DXMATERIAL *materials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
int nextDetailOp = 0;
for (DWORD i = 0; i < materialCount; i++)
{
chgext(fullPath, "BGF");
string path(fullPath);
string actualPath = path.substr(path.find("\\") + 1);
std::transform(actualPath.begin(), actualPath.end(), actualPath.begin(), toupper);
const char *matName = opMaterialName(actualPath.c_str(), i);
char *replaceMatName = NULL;
char *replaceTexName = NULL;
if (matName != NULL)
{
char *tmpMatName = new char[strlen(matName) + 1];
strcpy_s(tmpMatName, strlen(matName) + 1, matName);
replaceMatName = substituteMaterial(tmpMatName);
}
object->mDrawOps[i].material = materials[i].MatD3D;
if (replaceMatName != NULL)
{
hash_map<string, ReplacementMaterialData>::const_iterator iter = gReplacementData->find(string(replaceMatName));
ReplacementMaterialData data = (*iter).second;
object->mDrawOps[i].material.Diffuse.r = data.r;
object->mDrawOps[i].material.Diffuse.g = data.g;
object->mDrawOps[i].material.Diffuse.b = data.b;
replaceTexName = new char[data.texName.size() + 1];
strcpy_s(replaceTexName, data.texName.size() + 1, data.texName.c_str());
delete [] replaceMatName;
}
//TODO: set ambient of material; doesn't come back from .X file
object->mDrawOps[i].material.Ambient = object->mDrawOps[i].material.Diffuse;
object->mDrawOps[i].texture.texture = NULL;
if (materials[i].pTextureFilename || replaceTexName)
{
char textureFilename[512];
if (replaceTexName)
{
sprintf(textureFilename, "VIDEO\\%s.png", replaceTexName);
delete [] replaceTexName;
} else
{
sprintf(textureFilename, "VIDEO\\%s", materials[i].pTextureFilename);
}
object->mDrawOps[i].texture = LoadTexture(device, textureFilename);
}
if (nextDetailOp < numDetailOps && detailOps[nextDetailOp] == i)
{
object->mDrawOps[i].drawAsDecal = true;
nextDetailOp++;
}
if (abs(object->mDrawOps[i].material.Diffuse.a - 1.0f) > 0.0001f)
{
object->mDrawOps[i].alphaTest = true;
}
object->mDrawOps[i].drawAsSky = isSky;
if (isSky)
{
object->mDrawOps[i].material.Ambient.r *= renderer->GetCloudRed();
object->mDrawOps[i].material.Ambient.g *= renderer->GetCloudGreen();
object->mDrawOps[i].material.Ambient.b *= renderer->GetCloudBlue();
object->mDrawOps[i].material.Diffuse.r *= renderer->GetCloudRed();
object->mDrawOps[i].material.Diffuse.g *= renderer->GetCloudGreen();
object->mDrawOps[i].material.Diffuse.b *= renderer->GetCloudBlue();
object->mDrawOps[i].material.Emissive.r = renderer->GetCloudEmitRed();
object->mDrawOps[i].material.Emissive.g = renderer->GetCloudEmitGreen();
object->mDrawOps[i].material.Emissive.b = renderer->GetCloudEmitBlue();
}
}
materialBuffer->Release();
return object;
}
d3d_OBJECT* d3d_OBJECT::LoadSpheres(LPDIRECT3DDEVICE9 device, char *fileName)
{
FILE *file;
float emissionColor[3];
int vertexCount;
if ((file = fopen(fileName, "rb")) == NULL)
return NULL;
fread(emissionColor, sizeof(float), 3, file);
fread(&vertexCount, sizeof(int), 1, file);
d3d_OBJECT *object = new d3d_OBJECT(device, vertexCount);
void *buffer = NULL;
object->mVB->Lock(0, 0, &buffer, 0);
fread(buffer, sizeof(L4POINTVERTEX), vertexCount, file);
D3DXVECTOR3 center(0, 0, 0);
D3DXComputeBoundingSphere((D3DXVECTOR3*)buffer, vertexCount, sizeof(L4POINTVERTEX), &center, &object->mRadius);
object->mVB->Unlock();
object->mDrawOps[0].material.Emissive.r = emissionColor[0];
object->mDrawOps[0].material.Emissive.g = emissionColor[1];
object->mDrawOps[0].material.Emissive.b = emissionColor[2];
object->mDrawOps[0].material.Emissive.a = 1.0f;
fclose(file);
return object;
}
L4TEXOP d3d_OBJECT::LoadTexture(LPDIRECT3DDEVICE9 device, const char *fileName)
{
L4TEXOP texOp;
FILE *file;
if (mTextureCache.find(fileName) == mTextureCache.end())
{
struct
{ unsigned char minify;
unsigned char magnify;
unsigned char alpha;
unsigned char wrap_u;
unsigned char wrap_v;
bool doScroll;
float scrollUDelta;
float scrollVDelta;
} fileTexOp;
char metFileName[512];
strcpy(metFileName, fileName);
chgext(metFileName, "met");
memset(&texOp, 0, sizeof(L4TEXOP));
if ((file = fopen(metFileName, "rb")) != NULL)
{
fread(&fileTexOp, sizeof(fileTexOp), 1, file);
fclose(file);
texOp.wrap_u = (L4TEXOP::WrapType)fileTexOp.wrap_u;
texOp.wrap_v = (L4TEXOP::WrapType)fileTexOp.wrap_v;
texOp.doScroll = fileTexOp.doScroll;
texOp.scrollUDelta = fileTexOp.scrollUDelta;
texOp.scrollVDelta = fileTexOp.scrollVDelta;
}
D3DXCreateTextureFromFileA(device, fileName, &texOp.texture);
mTextureCache.insert(std::pair<std::string,L4TEXOP>(std::string(fileName), texOp));
}
else
texOp = mTextureCache[std::string(fileName)];
texOp.texture->AddRef();
return texOp;
}
d3d_OBJECT::d3d_OBJECT(LPDIRECT3DDEVICE9 device, int vertCount)
: mDevice(device),
mVertCount(vertCount),
mDrawOpCount(1),
mImmune(false),
mVB(NULL),
mMesh(NULL),
mDrawOps(NULL),
mLocalToWorld(),
mOrigin(),
mRadius(0.0f),
mID(mNextID++)
{
HRESULT hr;
hr = device->CreateVertexBuffer(vertCount * sizeof(L4POINTVERTEX), D3DUSAGE_WRITEONLY, L4POINTVERTEX_FVF, D3DPOOL_MANAGED, &mVB, NULL);
mDrawOps = new L4DRAWOP();
memset(mDrawOps, 0, sizeof(L4DRAWOP));
memset(mNext, 0, sizeof(mNext));
memset(mPrev, 0, sizeof(mPrev));
D3DXMatrixIdentity(&mLocalToWorld);
}
d3d_OBJECT::d3d_OBJECT(LPDIRECT3DDEVICE9 device, LPD3DXMESH mesh, DWORD *adjacencyBuffer, int drawOpCount)
: mDevice(device),
mVertCount(-1),
mDrawOpCount(drawOpCount),
mImmune(false),
mVB(NULL),
mMesh(mesh),
mLocalToWorld(),
mOrigin(),
mRadius(0.0f),
mID(mNextID++)
{
mDrawOps = new L4DRAWOP[drawOpCount];
memset(mDrawOps, 0, sizeof(L4DRAWOP) * drawOpCount);
if (adjacencyBuffer != NULL)
{
//Null adjacency buffer means the mesh should have already been optimized
mesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, adjacencyBuffer, NULL, NULL, NULL);
}
DWORD vertCount = mesh->GetNumVertices();
DWORD vertStride = mesh->GetNumBytesPerVertex();
D3DXVECTOR3 *data;
D3DXVECTOR3 center(0, 0, 0);
mesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&data);
D3DXComputeBoundingSphere(data, vertCount, vertStride, &center, &mRadius);
mesh->UnlockVertexBuffer();
memset(mNext, 0, sizeof(mNext));
memset(mPrev, 0, sizeof(mPrev));
D3DXMatrixIdentity(&mLocalToWorld);
}
d3d_OBJECT::~d3d_OBJECT()
{
if (mVB)
{
mVB->Release();
mVB = NULL;
}
if (mDrawOps)
{
delete[] mDrawOps;
mDrawOps = NULL;
}
}
void d3d_OBJECT::Draw(int pass, const D3DXMATRIX *viewTransform, Time targetRenderFrame)
{
// set our world transform
mDevice->SetTransform(D3DTS_WORLD, &mLocalToWorld);
if (mMesh != NULL && pass != PASS_SPHERE)
return DrawMesh(pass, viewTransform, targetRenderFrame);
else if (mMesh == NULL && mVB != NULL && pass == PASS_SPHERE)
return DrawSpheres(pass, viewTransform);
}
void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time targetRenderFrame)
{
for (int iOp = 0; iOp < mDrawOpCount; iOp++)
{
L4DRAWOP *drawOp = &mDrawOps[iOp];
if (drawOp->alphaTest != (pass == PASS_ALPHABLEND))
continue;
if (drawOp->drawAsDecal != (pass == PASS_DECAL))
continue;
if (drawOp->drawAsSky != (pass == PASS_SKY))
continue;
mDevice->SetMaterial(&drawOp->material);
#ifndef RP3_EMULATE
SetTextureScrolling(&(drawOp->texture), targetRenderFrame);
SetTexture(drawOp->texture.texture);
#endif
gNumBatches++;
mMesh->DrawSubset(iOp);
}
}
void d3d_OBJECT::DrawSpheres(int pass, const D3DXMATRIX *viewTransform)
{
mDevice->SetFVF(L4POINTVERTEX_FVF);
mDevice->SetStreamSource(0, mVB, 0, sizeof(L4POINTVERTEX));
mDevice->SetMaterial(&mDrawOps[0].material);
SetTexture(NULL);
mDevice->DrawPrimitive(D3DPT_POINTLIST, 0, mVertCount);
}
void d3d_OBJECT::SetTextureScrolling(const L4TEXOP *texture, Time targetRenderFrame)
{
if (d3d_OBJECT::mLastTextureScrollState != texture->doScroll)
{
// the texture scrolling state has changed
if (texture->doScroll)
{
mDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
SetTextureAddressing(texture->wrap_u, texture->wrap_v);
}
else
{
mDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
SetTextureAddressing(L4TEXOP::WrapType::REPEAT, L4TEXOP::WrapType::REPEAT);
}
d3d_OBJECT::mLastTextureScrollState = texture->doScroll;
}
// if the texture is supposed to be scrolled then
// we have to set the transforms regardless of state
if (texture->doScroll)
{
Scalar time = (Scalar)targetRenderFrame;
D3DXMATRIX translate;
D3DXMatrixIdentity(&translate);
translate._31 = -texture->scrollUDelta * time;
translate._32 = texture->scrollVDelta * time;
mDevice->SetTransform(D3DTS_TEXTURE0, &translate);
}
}
void d3d_OBJECT::SetTextureAddressing(L4TEXOP::WrapType wrap_u, L4TEXOP::WrapType wrap_v)
{
if (wrap_u != d3d_OBJECT::mLastWrapU)
{
d3d_OBJECT::mLastWrapU = wrap_u;
if (wrap_u == L4TEXOP::REPEAT)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
else if (wrap_u == L4TEXOP::CLAMP)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
else if (wrap_u == L4TEXOP::SELECT)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
}
if (wrap_v != d3d_OBJECT::mLastWrapV)
{
d3d_OBJECT::mLastWrapV = wrap_v;
if (wrap_v == L4TEXOP::REPEAT)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
else if (wrap_v == L4TEXOP::CLAMP)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
else if (wrap_v == L4TEXOP::SELECT)
mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
}
}
void d3d_OBJECT::SetTexture(LPDIRECT3DTEXTURE9 texture)
{
bool texturingOn = (texture != NULL);
if (texturingOn != d3d_OBJECT::mLastTexturingState)
{
d3d_OBJECT::mLastTexturingState = texturingOn;
if (texturingOn)
{
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
}
else
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
}
if (texture != NULL)
{
mDevice->SetTexture(0, texture);
}
}