#include "mungal4.h" #pragma hdrstop #include "l4d3d.h" #include "L4VIDEO.h" #include 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 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::const_iterator iter = gReplacementData->find(string(replaceMatName)); if (iter != gReplacementData->end()) { 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()); } else { DEBUG_STREAM << "L4D3D.cpp no replacement material data for " << replaceMatName << "\n" << std::flush; } 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), ¢er, &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; } HRESULT hr = D3DXCreateTextureFromFileA(device, fileName, &texOp.texture); if (FAILED(hr)) { // texOp.texture stays NULL (memset above); draw ops render untextured, // matching the "no texture filename" path in LoadObject. DEBUG_STREAM << "L4D3D.cpp couldn't load texture " << fileName << " (hr=0x" << std::hex << hr << std::dec << ")\n" << std::flush; } mTextureCache.insert(std::pair(std::string(fileName), texOp)); } else texOp = mTextureCache[std::string(fileName)]; if (texOp.texture != NULL) texOp.texture->AddRef(); return texOp; } void d3d_OBJECT::FlushTextureCache() { stdext::hash_map::iterator iter; for (iter = mTextureCache.begin(); iter != mTextureCache.end(); ++iter) { if ((*iter).second.texture != NULL) (*iter).second.texture->Release(); } mTextureCache.clear(); } 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, ¢er, &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); } }