Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,528 @@
|
||||
#include "l4particles.h"
|
||||
#include "../munga/time.h"
|
||||
|
||||
LPDIRECT3DDEVICE9 ParticleEngine::mDevice = NULL;
|
||||
PARTICLE_EFFECT ParticleEngine::mInstalledEffects[MAX_PARTICLE_EFFECTS];
|
||||
LPDIRECT3DTEXTURE9 ParticleEngine::mParticleTexture = NULL;
|
||||
|
||||
bool ParticleEngine::mActiveParticles = false;
|
||||
LPDIRECT3DVERTEXBUFFER9 ParticleEngine::mVertBuffer = NULL;
|
||||
|
||||
Particle *ParticleEngine::mParticlesHead = NULL;
|
||||
Particle *ParticleEngine::mParticlesTail = NULL;
|
||||
long ParticleEngine::mTotalParticleCount = 0;
|
||||
long ParticleEngine::mMaxParticleCount = 8192;
|
||||
|
||||
int compare(const Particle *a, const Particle *b, const D3DXMATRIX *view_matrix)
|
||||
{
|
||||
D3DXVECTOR3 aVec(a->mX, a->mY, a->mZ);
|
||||
D3DXVECTOR3 bVec(b->mX, b->mY, b->mZ);
|
||||
|
||||
D3DXVec3TransformCoord(&aVec, &aVec, view_matrix);
|
||||
D3DXVec3TransformCoord(&bVec, &bVec, view_matrix);
|
||||
|
||||
float aDist = aVec.x * aVec.x + aVec.y * aVec.y + aVec.z * aVec.z;
|
||||
float bDist = bVec.x * bVec.x + bVec.y * bVec.y + bVec.z * bVec.z;
|
||||
|
||||
// if both particles are within a certain distance from
|
||||
// each other then we'll sort them by effect instead of
|
||||
// distance so that they will be grouped together when
|
||||
// rendering
|
||||
//if (abs(bDist - aDist) < EFFECT_GROUPING_EPSILON)
|
||||
// return b->mEffect->id - a->mEffect->id;
|
||||
//else
|
||||
return (int)(bDist - aDist);
|
||||
}
|
||||
|
||||
void mergesort(Particle **headPtr, Particle **tailPtr, const D3DXMATRIX *view_matrix)
|
||||
{
|
||||
Particle *list = *headPtr;
|
||||
|
||||
if (!list)
|
||||
{
|
||||
(*tailPtr) = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
Particle *p, *q, *e, *tail, *oldhead;
|
||||
int insize, nmerges, psize, qsize, i;
|
||||
|
||||
insize = 1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
p = list;
|
||||
oldhead = list;
|
||||
list = NULL;
|
||||
tail = NULL;
|
||||
|
||||
nmerges = 0; // count number of merges we do in this pass
|
||||
|
||||
while (p)
|
||||
{
|
||||
nmerges++; // there exists a merge to be done
|
||||
// step 'insize' places along from p
|
||||
q = p;
|
||||
psize = 0;
|
||||
for (i=0; i<insize; i++)
|
||||
{
|
||||
psize++;
|
||||
q = q->Next();
|
||||
if (!q)
|
||||
break;
|
||||
}
|
||||
|
||||
// if q hasn't fallen off end, we have two lists to merge
|
||||
qsize = insize;
|
||||
|
||||
// now we have two lists; merge them
|
||||
while (psize > 0 || (qsize > 0 && q))
|
||||
{
|
||||
// decide whether next element of merge comes from p or q
|
||||
if (psize == 0)
|
||||
{
|
||||
// p is empty; e must come from q
|
||||
e = q;
|
||||
q = q->Next();
|
||||
qsize--;
|
||||
}
|
||||
else if (qsize == 0 || !q)
|
||||
{
|
||||
// q is empty; e must come from p
|
||||
e = p;
|
||||
p = p->Next();
|
||||
psize--;
|
||||
}
|
||||
else if (compare(p, q, view_matrix) <= 0)
|
||||
{
|
||||
// first elements of p is lower (or same);
|
||||
// e must come from p
|
||||
e = p;
|
||||
p = p->Next();
|
||||
psize--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// first element of q is lower; e must come from q
|
||||
e = q;
|
||||
q = q->Next();
|
||||
qsize--;
|
||||
}
|
||||
|
||||
// add the next element to the merged list
|
||||
if (tail)
|
||||
tail->mNextParticle = e;
|
||||
else
|
||||
list = e;
|
||||
e->mPrevParticle = tail;
|
||||
tail = e;
|
||||
}
|
||||
|
||||
// now p has stepped 'insize' places along, and q has too
|
||||
p = q;
|
||||
}
|
||||
|
||||
tail->mNextParticle = NULL;
|
||||
|
||||
// if we have done only one merge, we're finished
|
||||
if (nmerges <= 1) // allow for nmerges == 0, the empty list case
|
||||
{
|
||||
(*headPtr) = list;
|
||||
(*tailPtr) = tail;
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise repeat, merging lists twice the size
|
||||
insize *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
Particle::Particle(PARTICLE_EFFECT *effect)
|
||||
{
|
||||
mEffect = effect;
|
||||
mAge = 0.0f;
|
||||
mX = 0.0f;
|
||||
mY = 0.0f;
|
||||
mZ = 0.0f;
|
||||
mColor.argb = D3DCOLOR_ARGB(0xFF, 0xFF, 0x00, 0x00);
|
||||
memset(&mVelocity, 0, sizeof(mVelocity));
|
||||
memset(&mAcceleration, 0, sizeof(mAcceleration));
|
||||
}
|
||||
|
||||
void Particle::Execute(Scalar dT)
|
||||
{
|
||||
if (!mEffect)
|
||||
return;
|
||||
|
||||
if (IsAlive())
|
||||
{
|
||||
mX += mVelocity.x * dT;
|
||||
mY += mVelocity.y * dT;
|
||||
mZ += mVelocity.z * dT;
|
||||
|
||||
mVelocity.x += mAcceleration.x * dT;
|
||||
mVelocity.y += mAcceleration.y * dT;
|
||||
mVelocity.z += mAcceleration.z * dT;
|
||||
|
||||
mAge += dT;
|
||||
Scalar percentComplete = mAge / mEffect->fragLifetime;
|
||||
COLOR_POINT *cp0 = NULL;
|
||||
COLOR_POINT *cp1 = NULL;
|
||||
for (COLOR_POINT *cp = mEffect->colors + COLOR_POINT_COUNT - 2; cp >= mEffect->colors; cp--)
|
||||
{
|
||||
if (cp->active && percentComplete >= cp->time)
|
||||
{
|
||||
cp0 = cp;
|
||||
cp1 = cp + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cp0 || !cp1)
|
||||
{
|
||||
cp0 = mEffect->colors;
|
||||
|
||||
if (COLOR_POINT_COUNT > 1)
|
||||
{
|
||||
cp1 = mEffect->colors + 1;
|
||||
} else
|
||||
{
|
||||
cp1 = cp0;
|
||||
}
|
||||
}
|
||||
|
||||
percentComplete = (percentComplete - cp0->time) / (cp1->time - cp0->time);
|
||||
mColor.a = (unsigned char)(cp0->color.a + (cp1->color.a - cp0->color.a) * percentComplete);
|
||||
mColor.r = (unsigned char)(cp0->color.r + (cp1->color.r - cp0->color.r) * percentComplete);
|
||||
mColor.g = (unsigned char)(cp0->color.g + (cp1->color.g - cp0->color.g) * percentComplete);
|
||||
mColor.b = (unsigned char)(cp0->color.b + (cp1->color.b - cp0->color.b) * percentComplete);
|
||||
}
|
||||
}
|
||||
|
||||
ParticleEmitter::ParticleEmitter()
|
||||
: mEffect(NULL),
|
||||
mActive(false),
|
||||
mPosition(0.0f, 0.0f, 0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void ParticleEmitter::SetEffect(int effect)
|
||||
{
|
||||
mEffect = &ParticleEngine::mInstalledEffects[effect];
|
||||
}
|
||||
|
||||
void ParticleEmitter::Execute()
|
||||
{
|
||||
if (mEffect == NULL)
|
||||
return;
|
||||
|
||||
// calculate the number of particles we're going to need
|
||||
if (mActive)
|
||||
{
|
||||
bool fire = false;
|
||||
if (mEffect->id >= 1000)
|
||||
{
|
||||
// this is an independant effect
|
||||
static Scalar lastEmitted = (Scalar)Now();
|
||||
Scalar now = (Scalar)Now();
|
||||
|
||||
INDIE_EFFECT *indieEffect = (INDIE_EFFECT*)mEffect;
|
||||
if ((now - lastEmitted) >= indieEffect->releasePeriod)
|
||||
{
|
||||
lastEmitted = now;
|
||||
fire = true;
|
||||
}
|
||||
if ((now - mActivated) >= indieEffect->duration)
|
||||
mActive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fire = true;
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
if (fire)
|
||||
{
|
||||
for (int i = 0; i < mEffect->fragCount; ++i)
|
||||
ParticleEngine::CreateParticle(mPosition, mEffect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleEngine::Destroy()
|
||||
{
|
||||
mVertBuffer->Release();
|
||||
mParticleTexture->Release();
|
||||
}
|
||||
|
||||
void ParticleEngine::Initialize(LPDIRECT3DDEVICE9 device)
|
||||
{
|
||||
mDevice = device;
|
||||
memset(mInstalledEffects, 0, sizeof(mInstalledEffects));
|
||||
|
||||
ParticleEngine::mMaxParticleCount = atoi(getenv("MAXPARTICLES"));
|
||||
|
||||
// create the vertex buffer that will store the four vertices we need for the billboards
|
||||
mDevice->CreateVertexBuffer(ParticleEngine::mMaxParticleCount * 6 * sizeof(L4BASICVERTEX),
|
||||
D3DUSAGE_DYNAMIC,
|
||||
L4BASICVERTEX_FVF,
|
||||
D3DPOOL_DEFAULT,
|
||||
&mVertBuffer,
|
||||
NULL);
|
||||
|
||||
D3DXCreateTextureFromFile(mDevice, L"VIDEO\\particles.png", &mParticleTexture);
|
||||
}
|
||||
|
||||
void ParticleEngine::InstallEffect(int effectNumber, PARTICLE_EFFECT effect)
|
||||
{
|
||||
mInstalledEffects[effectNumber] = effect;
|
||||
mInstalledEffects[effectNumber].id = effectNumber;
|
||||
}
|
||||
|
||||
void ParticleEngine::CreateParticle(D3DXVECTOR3 position, PARTICLE_EFFECT *effect)
|
||||
{
|
||||
if (effect == NULL || effect->fragCount <= 0)
|
||||
return;
|
||||
|
||||
Particle *p = new Particle(effect);
|
||||
|
||||
// check to make sure we have room for this new particle
|
||||
if (mTotalParticleCount + 1 >= ParticleEngine::mMaxParticleCount)
|
||||
{
|
||||
mTotalParticleCount--;
|
||||
|
||||
// there isn't room for this particle so we'll delete the oldest particle
|
||||
if (mParticlesHead == mParticlesTail)
|
||||
{
|
||||
delete mParticlesHead;
|
||||
mParticlesHead = NULL;
|
||||
mParticlesTail = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Particle *temp = mParticlesTail;
|
||||
mParticlesTail = mParticlesTail->mPrevParticle;
|
||||
mParticlesTail->mNextParticle = NULL;
|
||||
delete temp;
|
||||
}
|
||||
}
|
||||
|
||||
// insert the new particle into the linked list
|
||||
if (mParticlesHead)
|
||||
mParticlesHead->mPrevParticle = p;
|
||||
p->mPrevParticle = NULL;
|
||||
p->mNextParticle = mParticlesHead;
|
||||
mParticlesHead = p;
|
||||
if (!mParticlesTail)
|
||||
mParticlesTail = p;
|
||||
|
||||
mTotalParticleCount++;
|
||||
|
||||
// now initialize all the particle data based on the effect
|
||||
p->mX = position.x + ((float)rand() * 2.0f / RAND_MAX - 1.0f) * effect->varianceX;
|
||||
p->mY = position.y + ((float)rand() * 2.0f / RAND_MAX - 1.0f) * effect->varianceY;
|
||||
p->mZ = position.z + ((float)rand() * 2.0f / RAND_MAX - 1.0f) * effect->varianceZ;
|
||||
|
||||
D3DXVECTOR3 vec(
|
||||
(float)rand() * 2.0f / RAND_MAX - 1.0f,
|
||||
(float)rand() * 2.0f / RAND_MAX - 1.0f,
|
||||
(float)rand() * 2.0f / RAND_MAX - 1.0f);
|
||||
|
||||
D3DXVec3Normalize(&vec, &vec);
|
||||
|
||||
p->mVelocity.x = vec.x * effect->velocity;
|
||||
p->mVelocity.y = vec.y * effect->velocity;
|
||||
p->mVelocity.z = vec.z * effect->velocity;
|
||||
|
||||
p->mAcceleration.x = 0.0f;
|
||||
p->mAcceleration.y = effect->gravity;
|
||||
p->mAcceleration.z = 0.0f;
|
||||
|
||||
// generate a random rotation amount in radians
|
||||
float centerX = effect->textureBounds.left + (effect->textureBounds.right - effect->textureBounds.left) / 2.0f;
|
||||
float centerY = effect->textureBounds.top + (effect->textureBounds.bottom - effect->textureBounds.top) / 2.0f;
|
||||
D3DXVECTOR2 rotCenter(centerX, centerY);
|
||||
D3DXMatrixTransformation2D(&p->mTextureTransform, NULL, 1, NULL, &rotCenter, ((float)rand() / RAND_MAX) * 2.0f * D3DX_PI, NULL);
|
||||
|
||||
p->mColor.argb = (effect->colors[0].active ? effect->colors[0].color.argb : 0);
|
||||
}
|
||||
|
||||
int ParticleEngine::BuildParticleVertices(const Particle *p, L4BASICVERTEX *verts, D3DXMATRIX *inverse_view_matrix)
|
||||
{
|
||||
D3DXMATRIX rotation;
|
||||
PARTICLE_EFFECT *effect = p->mEffect;
|
||||
|
||||
inverse_view_matrix->_41 = p->mX;
|
||||
inverse_view_matrix->_42 = p->mY;
|
||||
inverse_view_matrix->_43 = p->mZ;
|
||||
|
||||
float fragSize = effect->fragSize;
|
||||
D3DXVECTOR3 vector3s[] =
|
||||
{
|
||||
D3DXVECTOR3(-fragSize, fragSize, 0.0f), // upper left
|
||||
D3DXVECTOR3( fragSize, -fragSize, 0.0f), // lower right
|
||||
D3DXVECTOR3(-fragSize, -fragSize, 0.0f), // lower left
|
||||
|
||||
D3DXVECTOR3(-fragSize, fragSize, 0.0f), // upper left
|
||||
D3DXVECTOR3( fragSize, fragSize, 0.0f), // upper right
|
||||
D3DXVECTOR3( fragSize, -fragSize, 0.0f) // lower right
|
||||
};
|
||||
|
||||
D3DXVECTOR2 vector2s[] =
|
||||
{
|
||||
D3DXVECTOR2(effect->textureBounds.left, effect->textureBounds.top), // upper left
|
||||
D3DXVECTOR2(effect->textureBounds.right, effect->textureBounds.bottom), // lower right
|
||||
D3DXVECTOR2(effect->textureBounds.left, effect->textureBounds.bottom), // lower left
|
||||
|
||||
D3DXVECTOR2(effect->textureBounds.left, effect->textureBounds.top), // upper left
|
||||
D3DXVECTOR2(effect->textureBounds.right, effect->textureBounds.top), // upper right
|
||||
D3DXVECTOR2(effect->textureBounds.right, effect->textureBounds.bottom) // lower right
|
||||
};
|
||||
|
||||
D3DXVECTOR3 normal, inNormal(0.0f, 0.0f, 1.0f);
|
||||
D3DXVec3TransformCoord(&normal, &inNormal, inverse_view_matrix);
|
||||
|
||||
int count = (sizeof(vector3s) / sizeof(D3DXVECTOR3));
|
||||
for (int i=0; i<count; i++)
|
||||
{
|
||||
D3DXVECTOR3 outVec3;
|
||||
D3DXVECTOR2 outVec2;
|
||||
|
||||
D3DXVec3TransformCoord(&outVec3, &vector3s[i], inverse_view_matrix);
|
||||
|
||||
verts[i].x = outVec3.x;
|
||||
verts[i].y = outVec3.y;
|
||||
verts[i].z = outVec3.z;
|
||||
|
||||
verts[i].nx = normal.x;
|
||||
verts[i].ny = normal.y;
|
||||
verts[i].nz = normal.z;
|
||||
|
||||
if (effect->rotate)
|
||||
D3DXVec2TransformCoord(&outVec2, &vector2s[i], &p->mTextureTransform);
|
||||
else
|
||||
outVec2 = vector2s[i];
|
||||
|
||||
verts[i].u = outVec2.x;
|
||||
verts[i].v = outVec2.y;
|
||||
|
||||
verts[i].color = p->mColor.argb;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void ParticleEngine::ExecuteParticles(const D3DXMATRIX *view_matrix, Scalar timeSlice)
|
||||
{
|
||||
// if we don't have a device or we have no particles, early exit
|
||||
if (!mDevice || mTotalParticleCount <= 0)
|
||||
return; // we can't run without having been initialized with a device
|
||||
|
||||
// first we need to execute all the particles to update their positions, color, etc.
|
||||
for (Particle *p = mParticlesHead; p;)
|
||||
{
|
||||
if (p->IsAlive())
|
||||
{
|
||||
p->Execute(timeSlice);
|
||||
|
||||
p = p->mNextParticle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove from the list
|
||||
if (p->mPrevParticle)
|
||||
p->mPrevParticle->mNextParticle = p->mNextParticle;
|
||||
if (p->mNextParticle)
|
||||
p->mNextParticle->mPrevParticle = p->mPrevParticle;
|
||||
|
||||
// time to kill the particle
|
||||
if (p == mParticlesHead)
|
||||
{
|
||||
mParticlesHead = p->mNextParticle;
|
||||
}
|
||||
if (p == mParticlesTail)
|
||||
{
|
||||
mParticlesTail = p->mPrevParticle;
|
||||
}
|
||||
|
||||
Particle *temp = p;
|
||||
p = p->mNextParticle;
|
||||
|
||||
mTotalParticleCount--;
|
||||
delete temp;
|
||||
}
|
||||
}
|
||||
|
||||
// now we're going to sort the particle list based on distance to the camera
|
||||
mergesort(&mParticlesHead, &mParticlesTail, view_matrix);
|
||||
|
||||
//Scalar beforeBuild = (Scalar)Now();
|
||||
D3DXMATRIX inverse_view_matrix;
|
||||
D3DXMatrixInverse(&inverse_view_matrix, NULL, view_matrix);
|
||||
|
||||
// and finally, we'll build the vertex list to send to Direct3D
|
||||
L4BASICVERTEX *vertBuffer = NULL;
|
||||
mVertBuffer->Lock(0, mTotalParticleCount * sizeof(L4BASICVERTEX), (void**)&vertBuffer, D3DLOCK_DISCARD);
|
||||
|
||||
int actualParticles = 0;
|
||||
for (Particle *p = mParticlesHead; p; p = p->mNextParticle)
|
||||
{
|
||||
vertBuffer += BuildParticleVertices(p, vertBuffer, &inverse_view_matrix);
|
||||
actualParticles++;
|
||||
}
|
||||
|
||||
mVertBuffer->Unlock();
|
||||
|
||||
//Scalar afterBuild = (Scalar)Now();
|
||||
|
||||
//afterBuild = afterBuild;
|
||||
}
|
||||
|
||||
void ParticleEngine::RenderParticles(const D3DXMATRIX *view_matrix, Scalar timeSlice)
|
||||
{
|
||||
// we always have to execute otherwise our timing
|
||||
// variables get all screwed up when we don't run for a while
|
||||
ExecuteParticles(view_matrix, timeSlice);
|
||||
|
||||
if (!mDevice || mTotalParticleCount <= 0)
|
||||
return;
|
||||
|
||||
// setup stages for particle's texture
|
||||
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_DIFFUSE);
|
||||
|
||||
mDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
|
||||
mDevice->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
|
||||
|
||||
mDevice->SetTexture(0, mParticleTexture);
|
||||
|
||||
mDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
|
||||
|
||||
mDevice->SetFVF(L4BASICVERTEX_FVF);
|
||||
mDevice->SetStreamSource(0, mVertBuffer, 0, sizeof(L4BASICVERTEX));
|
||||
|
||||
//DWORD wrap_u, wrap_v;
|
||||
//mDevice->GetSamplerState(0, D3DSAMP_ADDRESSU, &wrap_u);
|
||||
//mDevice->GetSamplerState(0, D3DSAMP_ADDRESSV, &wrap_v);
|
||||
|
||||
//mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
|
||||
//mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
|
||||
|
||||
D3DXMATRIX world_matrix;
|
||||
D3DXMatrixIdentity(&world_matrix);
|
||||
mDevice->SetTransform(D3DTS_WORLD, &world_matrix);
|
||||
|
||||
mDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, mTotalParticleCount * 2);
|
||||
|
||||
mDevice->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
|
||||
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
|
||||
|
||||
//mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap_u);
|
||||
//mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap_v);
|
||||
}
|
||||
Reference in New Issue
Block a user