#include "CKAll.h" #include "CKRasterizer.h" #include "ParticleEmitter.h" #include "ParticleSystemRenderCallbacks.h" int (*renderParticles)(CKRenderContext *dev,CKRenderObject *obj,void *arg); // ACC - July 10,2002 // Need to add code into each RenderCB to test if shutdown // Then we need to wait on PE to be finished // extern VxMutex logguard; extern FILE* ACCLOG; #ifdef USE_THR void ThreadWaitForCompletion(ParticleEmitter* em,const char* RenderingMethod) { #ifdef MT_VERB // ACC, July 10, 2002 { VxMutexLock lock (logguard); fprintf(ACCLOG, "About to render a %s\n",RenderingMethod); fflush(ACCLOG); } #endif // Wait if (!em->hasBeenRendered && em->hasBeenEnqueud) { WaitForSingleObject(em->hasBeenComputedEvent, INFINITE); em->hasBeenRendered = TRUE; } } #endif bool IsRenderingNeeded(ParticleEmitter *em) { // This section of code can go into the render call back, and return premature // if particle is shutdown // if there is no more particles and the behavior is inactive // We get the activity CKBehavior* beh = em->m_Behavior; int activity = 0; beh->GetLocalParameterValue(1,&activity); if(!activity && !(em->getParticles())) { ShowParticles(beh,FALSE); beh->ActivateOutput(1); return false; } if(!em->particleCount) return false; return true; } int RenderParticles_P(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"Point"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VC,em->particleCount); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); dev->SetWorldTransformationMatrix(oldmatrix*mov->GetInverseWorldMatrix()); // we don't let write to the ZBuffer dev->SetTexture(NULL); dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE); dev->SetState(VXRENDERSTATE_SRCBLEND, em->m_SrcBlend); dev->SetState(VXRENDERSTATE_DESTBLEND, em->m_DestBlend); if(em->m_DestBlend!=VXBLEND_ZERO) dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, TRUE); else dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, FALSE); dev->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1); Particle* p = em->getParticles(); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); while(p) { *positions = p->pos; *colors = RGBAFTOCOLOR(&(p->m_Color)); // next point p = p->next; ++colors; ++positions; } // The drawing dev->DrawPrimitive(VX_POINTLIST,(WORD*)NULL,em->particleCount,data); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 0; } int RenderParticles_L(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"Line"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VC,2*em->particleCount); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); dev->SetWorldTransformationMatrix(oldmatrix*mov->GetInverseWorldMatrix()); // we don't let write to the ZBuffer dev->SetTexture(NULL); dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE); dev->SetState(VXRENDERSTATE_SRCBLEND, em->m_SrcBlend); dev->SetState(VXRENDERSTATE_DESTBLEND, em->m_DestBlend); if(em->m_DestBlend!=VXBLEND_ZERO) dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, TRUE); else dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, FALSE); dev->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1); Particle* p = em->getParticles(); int i=0; VxColor oldColor; XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); while(p) { // Colors oldColor.r = p->m_Color.r - p->deltaColor.r*p->m_DeltaTime; oldColor.g = p->m_Color.g - p->deltaColor.g*p->m_DeltaTime; oldColor.b = p->m_Color.b - p->deltaColor.b*p->m_DeltaTime; oldColor.a = p->m_Color.a - p->deltaColor.a*p->m_DeltaTime; // Start of line *positions = p->pos - p->dir*p->m_DeltaTime; ++positions; *colors = RGBAFTOCOLOR(&oldColor); ++colors; // End of line *positions = p->pos + p->dir*p->m_Angle; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; // Next particle p = p->next; } // the Drawing itself dev->DrawPrimitive(VX_LINELIST,(WORD*)NULL,em->particleCount,data); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 0; } int RenderParticles_LL(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"LongLine"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VC, (em->m_TrailCount) * 2 * em->particleCount); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); dev->SetWorldTransformationMatrix(oldmatrix*mov->GetInverseWorldMatrix()); // we don't let write to the ZBuffer dev->SetTexture(NULL); dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE); dev->SetState(VXRENDERSTATE_SRCBLEND, em->m_SrcBlend); dev->SetState(VXRENDERSTATE_DESTBLEND, em->m_DestBlend); if(em->m_DestBlend!=VXBLEND_ZERO) dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, TRUE); else dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, FALSE); dev->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1); Particle* p = em->getParticles(); int i=0; VxColor oldColor; XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); DWORD num_prims = 0; while(p) { #define for if (false) {} else for ParticleEmitter::ParticleHistoric &ph = em->GetParticleHistoric(p); // L. Puts the particle in the VB // Assumes, that when count isn't to the max, start is always 0; unsigned int prev = 0; unsigned int px = 1; unsigned int l1end = 0; unsigned int l2end = 0; if (ph.start == 0) { l1end = ph.count; l2end = 0; } else { l1end = ph.start; l2end = ph.particles.Size(); *positions = ph.particles[l2end - 1].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; *positions = ph.particles[0].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; } for (; px < l1end; prev = px++) { *positions = ph.particles[prev].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; *positions = ph.particles[px].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; } for (prev = px++; px < l2end; prev = px++) { *positions = ph.particles[prev].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; *positions = ph.particles[px].pos; ++positions; *colors = RGBAFTOCOLOR(&(p->m_Color)); ++colors; } num_prims += ph.count - 1; // Next particle p = p->next; } // the Drawing itself dev->DrawPrimitive(VX_LINELIST,(WORD*)NULL,num_prims,data); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 0; } int RenderParticles_S(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"Sprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; const int VBUFFERSIZE = 4000; int pc = em->particleCount; #ifndef PSX2 VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(4*pc>VBUFFERSIZE)?VBUFFERSIZE:4*pc); #else VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(4*pc>VBUFFERSIZE)?VBUFFERSIZE:4*pc); #endif XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); /// // Indices if (pc*6 > ParticleEmitter::m_GlobalIndicesCount) { delete [] ParticleEmitter::m_GlobalIndices; ParticleEmitter::m_GlobalIndicesCount = pc*6; ParticleEmitter::m_GlobalIndices = new WORD[ParticleEmitter::m_GlobalIndicesCount]; int ni = 0; int fi = 0; for (int i=0; iSetState(dev); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camright; Vx3DRotateVector(&camright,realmatrix,&mat[0]); VxVector camup; Vx3DRotateVector(&camup,realmatrix,&mat[1]); camright.Normalize(); camup.Normalize(); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)(sqrtf((float)(em->m_TextureFrameCount-1))); step = 1.0f/framehc; } Particle* p = em->getParticles(); VxVector cr,cu; VxVector rcr,rcu; VxVector pos; float u = 0.0f; float v = 0.0f; int np=0; int remaining = 4*pc; unsigned int (*ColorConvertor) (const VxColor*); if (dev->GetRasterizerContext()->m_Driver->m_3DCaps.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_VERTEXCOLORABGR) ColorConvertor = &BGRAFTOCOLOR; else ColorConvertor = &RGBAFTOCOLOR; while(p) { pos = p->pos; // Precalcul for positions float halfsize = p->m_Size*0.5f; cr = camright*halfsize; cu = camup*halfsize; // Angle rotation, if necessary if(p->m_Angle) { float cosa = cosf(p->m_Angle); float sina = sinf(p->m_Angle); rcr = cr*cosa - cu*sina; rcu = cr*sina + cu*cosa; cr = rcr; cu = rcu; } // Precalcul for color // We must now swap the color according to the rasterizer DWORD col = (*ColorConvertor)(&(p->m_Color)); // Precalcul for uvs if (changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Filling vertices *positions = pos-cr+cu; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos+cr+cu; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v; ++uvs; *positions = pos+cr-cu; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = pos-cr-cu; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; ++np; p = p->next; if (np*4 == VBUFFERSIZE) { // need to flush the vertex buffer // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6*np, data ); remaining -= VBUFFERSIZE; np = 0; // restart if (remaining > 0) { #ifndef PSX2 data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #else data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #endif uvs = data->TexCoord; positions = data->Positions; colors = data->Colors; } } } // The Primitive Drawing if (remaining) em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6*np, data ); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } //----------------------------------------------------------------------------- // Long sprite (with trail) int RenderParticles_LS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"LongSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; const int VBUFFERSIZE = 4000; assert(em->m_TrailCount > 0); int pc = em->particleCount * em->m_TrailCount; #ifndef PSX2 VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(4*pc>VBUFFERSIZE)?VBUFFERSIZE:4*pc); #else VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(4*pc>VBUFFERSIZE)?VBUFFERSIZE:4*pc); #endif XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); /// // Indices if (pc*6 > ParticleEmitter::m_GlobalIndicesCount) { delete [] ParticleEmitter::m_GlobalIndices; ParticleEmitter::m_GlobalIndicesCount = pc*6; ParticleEmitter::m_GlobalIndices = new WORD[ParticleEmitter::m_GlobalIndicesCount]; int ni = 0; int fi = 0; for (int i=0; iSetState(dev); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camright; Vx3DRotateVector(&camright,realmatrix,&mat[0]); VxVector camup; Vx3DRotateVector(&camup,realmatrix,&mat[1]); camright.Normalize(); camup.Normalize(); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)(sqrtf((float)(em->m_TextureFrameCount-1))); step = 1.0f/framehc; } Particle* p = em->getParticles(); int np=0; int remaining = 4*pc; while(p) { struct local { static inline void FillParticle(const Particle *p, VxDrawPrimitiveData* data, XPtrStrided& positions, XPtrStrided& colors, XPtrStrided& uvs, const VxVector &camup, const VxVector &camright, BOOL changeuv, int framehc, float step) { VxVector pos = p->pos; // Precalcul for positions float halfsize = p->m_Size * 0.5f; VxVector cr = camright*halfsize; VxVector cu = camup*halfsize; VxVector rcr , rcu; // Angle rotation, if necessary if(p->m_Angle) { float cosa = cosf(p->m_Angle); float sina = sinf(p->m_Angle); rcr = cr*cosa - cu*sina; rcu = cr*sina + cu*cosa; cr = rcr; cu = rcu; } // Precalcul for color DWORD col = RGBAFTOCOLOR(&(p->m_Color)); float u = 0.0f; float v = 0.0f; // Precalcul for uvs if (changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Filling vertices *positions = pos-cr+cu; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos+cr+cu; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v; ++uvs; *positions = pos+cr-cu; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = pos-cr-cu; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; } }; // need to flush the vertex buffer if ((np + em->m_TrailCount) * 4 > VBUFFERSIZE) { // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6 * np, data ); remaining -= np * 4; np = 0; // restart if (remaining > 0) { #ifndef PSX2 data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #else data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #endif uvs = data->TexCoord; positions = data->Positions; colors = data->Colors; } } if (em->m_TrailCount == 0) { local::FillParticle(p, data, positions, colors, uvs, camup, camright, changeuv, framehc, step); ++np; } else { // Assumes that when start != 0, count == m_TrailCount // So the buffer is full, and order does not matter. ParticleEmitter::ParticleHistoric &ph = em->GetParticleHistoric(p); for (int px = 0; px < ph.count; ++px) { local::FillParticle(&ph.particles[px], data, positions, colors, uvs, camup, camright, changeuv, framehc, step); ++np; } } p = p->next; } // The Primitive Drawing if (remaining && np > 0) em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6 * np, data ); dev->ReleaseCurrentVB(); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } int RenderParticles_FS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"FastSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; int pc = em->particleCount; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),3*pc); XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); // Render States // TMP TMP em->SetState(dev); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camright; Vx3DRotateVector(&camright,realmatrix,&mat[0]); VxVector camup; Vx3DRotateVector(&camup,realmatrix,&mat[1]); camright.Normalize(); camup.Normalize(); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)(sqrtf((float)(em->m_TextureFrameCount-1))); step = 1.0f/framehc; } Particle* p = em->getParticles(); VxVector cr,cu; VxVector rcr,rcu; VxVector pos; float u = 0.0f; float v = 0.0f; int count = 0; while(p) { cr = camright*p->m_Size*0.5f; cu = camup*p->m_Size*0.5f; // Positions if(p->m_Angle) { float cosa = cosf(p->m_Angle); float sina = sinf(p->m_Angle); rcr = cr*cosa - cu*sina; rcu = cr*sina + cu*cosa; cr = rcr; cu = rcu; } pos = p->pos; // Colors DWORD col = RGBAFTOCOLOR(&(p->m_Color)); // The texture coordinates if(changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } *positions = pos+cu; ++positions; *colors = col; ++colors; uvs->u = u+step*0.5f; uvs->v = v; ++uvs; *positions = pos+cr-cu; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = pos-cr-cu; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; p = p->next; ++count; } // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, (WORD*)NULL, 3*pc, data ); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } // Oriented Sprite int RenderParticles_OS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"OrientableSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; int pc = em->particleCount; #ifndef PSX2 VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT/*|CKRST_DP_VBUFFER*/),4*pc); #else VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),4*pc); #endif XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); /// // Indices if (pc*6 > ParticleEmitter::m_GlobalIndicesCount) { delete [] ParticleEmitter::m_GlobalIndices; ParticleEmitter::m_GlobalIndicesCount = pc*6; ParticleEmitter::m_GlobalIndices = new WORD[ParticleEmitter::m_GlobalIndicesCount]; int ni = 0; int fi = 0; for (int i=0; iSetState(dev,TRUE); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camright; Vx3DRotateVector(&camright,realmatrix,&mat[0]); VxVector camup; Vx3DRotateVector(&camup,realmatrix,&mat[1]); VxVector camdir; Vx3DRotateVector(&camdir,realmatrix,&mat[2]); camright.Normalize(); camup.Normalize(); camdir.Normalize(); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)((float)(sqrtf((float)(em->m_TextureFrameCount-1)))); step = 1.0f/framehc; } Particle* p = em->getParticles(); VxVector pos,dir,old,vv(0,0,0),ww(0,0,0); float oldsize; VxColor oldColor; float u = 0.0f; float v = 0.0f; int ni = 0; int fi = 0; while(p) { pos = p->pos; dir = p->dir; // the old pos old = pos - dir*p->m_DeltaTime; // the old size oldsize = p->m_Size - p->m_DeltaSize * p->m_DeltaTime; // we exagerate the current position according to the latenty pos += dir*p->m_Angle; vv = pos - old; vv.Normalize(); ww = Normalize(CrossProduct(camdir,vv)); // the colors oldColor.r = p->m_Color.r - p->deltaColor.r*p->m_DeltaTime; oldColor.g = p->m_Color.g - p->deltaColor.g*p->m_DeltaTime; oldColor.b = p->m_Color.b - p->deltaColor.b*p->m_DeltaTime; oldColor.a = p->m_Color.a - p->deltaColor.a*p->m_DeltaTime; DWORD col = RGBAFTOCOLOR(&(p->m_Color)); DWORD oldcol = RGBAFTOCOLOR(&oldColor); // The texture coordinates if(changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Filling vertices *positions = pos+vv*p->m_Size+ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos+vv*p->m_Size-ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v; ++uvs; *positions = old-vv*oldsize-ww*oldsize; ++positions; *colors = oldcol; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = old-vv*oldsize+ww*oldsize; ++positions; *colors = oldcol; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; p = p->next; ni += 4; } // The Primitive Drawing em->DrawPrimitive(dev, TRUE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6*pc, data ); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } //----------------------------------------------------------------------------- // Long Oriented Sprite (with trail) int RenderParticles_LOS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"LongOrientableSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; const int VBUFFERSIZE = 4000; assert(em->m_TrailCount > 1); int pc = em->particleCount; // Maximum number of vertices per particle. const int num_vtx_max = (em->m_TrailCount) * 2; // Maximum number of indices per particle. const int num_idx_max = (em->m_TrailCount - 1) * 2 * 3; #ifndef PSX2 VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(num_vtx_max*pc>VBUFFERSIZE)?VBUFFERSIZE:num_vtx_max*pc); #else VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(num_vtx_max*pc>VBUFFERSIZE)?VBUFFERSIZE:num_vtx_max*pc); #endif XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); // Index buffer CKWORD *index_buffer = dev->GetDrawPrimitiveIndices(pc * num_idx_max); // Render States // TMP TMP em->SetState(dev); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camdir; Vx3DRotateVector(&camdir,realmatrix,&mat[2]); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)(sqrtf((float)(em->m_TextureFrameCount-1))); step = 1.0f/framehc; } Particle* p = em->getParticles(); int remaining = num_vtx_max * pc; int fi=0; // face index int ni=0; // number of indices while(p) { // Precalcul for positions float halfsize = p->m_Size * 0.5f; VxVector cd = CrossProduct(camdir, p->dir); cd.Normalize(); cd *= halfsize; // Precalcul for color DWORD col = RGBAFTOCOLOR(&(p->m_Color)); float u = 0.0f; float v = 0.0f; // Precalcul for uvs if (changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Assumes that when start != 0, count == m_TrailCount // So the buffer is full. // Along u texture coordinate. ParticleEmitter::ParticleHistoric &ph = em->GetParticleHistoric(p); // Number of vertices per particles. const int num_vtx = (ph.count) * 2; // need to flush the vertex buffer if (ni + num_vtx > VBUFFERSIZE) { // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, index_buffer, fi, data ); remaining -= ni; fi = 0; ni = 0; if (remaining > 0) { #ifndef PSX2 data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #else data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); #endif uvs = data->TexCoord; positions = data->Positions; colors = data->Colors; index_buffer = dev->GetDrawPrimitiveIndices(pc * num_idx_max); } } float local_step = step * (1.0f / (ph.count + 1)); int start = ph.start; int size = ph.count; while (start || size) { for (int px = start; px < size; ++px) { VxVector &pos = ph.particles[px].pos; DWORD col = RGBAFTOCOLOR(&ph.particles[px].m_Color); // Filling vertices *positions = pos + cd; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos - cd; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v + step; ++uvs; u += local_step; if (px != ph.start) { index_buffer[fi] = ni-2; index_buffer[fi+1] = ni; index_buffer[fi+2] = ni+1; index_buffer[fi+3] = ni-2; index_buffer[fi+4] = ni+1; index_buffer[fi+5] = ni-1; fi += 6; } ni += 2; } if (size == ph.count) { start = 0; size = ph.start; } else { start = size = 0; } } p = p->next; } // The Primitive Drawing if (remaining && fi > 0) { data->VertexCount = ni; em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, index_buffer, fi, data); } dev->ReleaseCurrentVB(); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } // Comet Sprite int RenderParticles_CS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"Comet"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; int pc = em->particleCount; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VCT,4*pc); XPtrStrided uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); /// // Indices if (pc*6 > ParticleEmitter::m_GlobalIndicesCount) { delete [] ParticleEmitter::m_GlobalIndices; ParticleEmitter::m_GlobalIndicesCount = pc*6; ParticleEmitter::m_GlobalIndices = new WORD[ParticleEmitter::m_GlobalIndicesCount]; int ni = 0; int fi = 0; for (int i=0; iSetState(dev,TRUE); CK3dEntity *cam = dev->GetViewpoint(); const VxMatrix& mat = cam->GetWorldMatrix(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); VxMatrix realmatrix = oldmatrix*mov->GetInverseWorldMatrix(); dev->SetWorldTransformationMatrix(realmatrix); VxVector camright; Vx3DRotateVector(&camright,realmatrix,&mat[0]); VxVector camup; Vx3DRotateVector(&camup,realmatrix,&mat[1]); VxVector camdir; Vx3DRotateVector(&camdir,realmatrix,&mat[2]); camright.Normalize(); camup.Normalize(); camdir.Normalize(); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)((float)(sqrtf((float)(em->m_TextureFrameCount-1)))); step = 1.0f/framehc; } Particle* p = em->getParticles(); VxVector pos,dir,center,old,vv(0,0,0),ww(0,0,0); VxColor oldColor; float u = 0.0f; float v = 0.0f; int ni = 0; int fi = 0; while(p) { pos = p->pos; dir = p->dir; float halfsize = p->m_Size; float speed = Magnitude(dir); dir /= speed; // the old pos old = pos - dir*(2.0f*halfsize+speed*p->m_Angle*p->m_DeltaTime); vv = pos - old; float fu = -halfsize*DotProduct(dir,camright); float fv = -halfsize*DotProduct(dir,camup); ww = -fv*camright+fu*camup; center = pos - halfsize*dir; // w = Normalize(CrossProduct(camdir,v)); // Colors oldColor.r = p->m_Color.r - p->deltaColor.r*p->m_DeltaTime; oldColor.g = p->m_Color.g - p->deltaColor.g*p->m_DeltaTime; oldColor.b = p->m_Color.b - p->deltaColor.b*p->m_DeltaTime; oldColor.a = p->m_Color.a - p->deltaColor.a*p->m_DeltaTime; DWORD col = RGBAFTOCOLOR(&(p->m_Color)); DWORD oldcol = RGBAFTOCOLOR(&oldColor); // The texture coordinates if(changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Filling vertices *positions = center+ww; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v; ++uvs; *positions = center-ww; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = old; ++positions; *colors = oldcol; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; p = p->next; ni += 4; } // The Primitive Drawing em->DrawPrimitive(dev, TRUE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, 6*pc, data ); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } int RenderParticles_RS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; // ACC, July 10, 2002 #ifdef USE_THR ThreadWaitForCompletion(em,"Radial"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; int pc = em->particleCount; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VCT,4*pc); /// // Indices if (pc*6 > ParticleEmitter::m_GlobalIndicesCount) { delete [] ParticleEmitter::m_GlobalIndices; ParticleEmitter::m_GlobalIndicesCount = pc*6; ParticleEmitter::m_GlobalIndices = new WORD[ParticleEmitter::m_GlobalIndicesCount]; int ni = 0; int fi = 0; for (int i=0; i uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); // Render States // TMP TMP em->SetState(dev); CK3dEntity *cam = dev->GetViewpoint(); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); dev->SetWorldTransformationMatrix( cam->GetWorldMatrix() ); BOOL changeuv = FALSE; float step = 1.0f; int framehc = 0; if(em->m_TextureFrameCount>1) { changeuv = TRUE; framehc = 1+(int)(sqrtf((float)(em->m_TextureFrameCount-1))); step = 1.0f/framehc; } Particle* p = em->getParticles(); VxVector pos,old(0,0,0),vv(0,0,0),ww(0,0,0); float u = 0.0f; float v = 0.0f; CK3dEntity* ent = (CK3dEntity*)dev->GetCKContext()->GetObject(em->m_Entity); cam->InverseTransform(&old,&vv,ent); const VxMatrix& worldCam = cam->GetWorldMatrix(); // move the reference point backward in camera space (don't work with z-buffer test, so added the choice for this offset in the settings) old += Normalize(old)* em->m_RadialParticleOffset; CK_ID oldtex = -1; const VxMatrix& invCam = cam->GetInverseWorldMatrix(); VxMatrix finalMat = /*ent->GetWorldMatrix()*/invCam; int fi = 0; int ni = 0; while(p) { Vx3DMultiplyMatrixVector(&pos,finalMat,&p->pos); vv.x = pos.x - old.x; vv.y = pos.y - old.y; vv.Normalize(); ww.x = -vv.y; ww.y = vv.x; // the colors DWORD col = RGBAFTOCOLOR(&(p->m_Color)); // The texture coordinates if(changeuv) { v = 0.0f; int c = p->m_CurrentFrame; while(c >= framehc) { v += step; c -= framehc; } u=c*step; } // Filling vertices *positions = pos+vv*p->m_Size+ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v; ++uvs; *positions = pos+vv*p->m_Size-ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v; ++uvs; *positions = old-vv*p->m_Size-ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u+step; uvs->v = v+step; ++uvs; *positions = old-vv*p->m_Size+ww*p->m_Size; ++positions; *colors = col; ++colors; uvs->u = u; uvs->v = v+step; ++uvs; p = p->next; ni += 4; } em->DrawPrimitive(dev, FALSE, VX_TRIANGLELIST, ParticleEmitter::m_GlobalIndices, pc*6, data ); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetWorldTransformationMatrix(oldmatrix); return 1; } // TODO : change this callback : not very nice... int RenderParticles_O(CKRenderContext *dev,CKRenderObject *obj,void *arg) { // int todo_manage_reflection_in_objects; CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"Object"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; VxMatrix backup = mov->GetWorldMatrix(); CKGroup* group = (CKGroup*)dev->GetCKContext()->GetObject(em->m_Group); if(!group) return 1; CK3dEntity* ent; CKMaterial* mat; CKMesh* mesh; Particle* p = em->getParticles(); VxVector dir,up,right; int oc = group->GetObjectCount(); while(p) { if(p->m_GroupIndex < oc) ent = (CK3dEntity*)group->GetObject(p->m_GroupIndex); else ent = (CK3dEntity*)group->GetObject(0); if(CKIsChildClassOf(ent,CKCID_3DENTITY)) { mesh = ent->GetCurrentMesh(); if(mesh) { mat = mesh->GetFaceMaterial(0); if(mat) mat->SetDiffuse((p->m_Color)); ent->SetPosition(&(p->pos), NULL); dir = Normalize(p->dir); up.Set(0,1,0); right = CrossProduct(up,dir); up = CrossProduct(dir,right); ent->SetOrientation(&dir,&up,&right); if(p->m_Angle) { ent->Rotate(&dir,p->m_Angle); } VxVector v(p->m_Size,p->m_Size,p->m_Size); ent->SetScale(&v); ent->Render( dev, FALSE ); } } p = p->next; } dev->SetWorldTransformationMatrix(backup); return 1; } ////////////////////////////////////////////////////////////////////////// // PointSprite (Hardware) Render int RenderParticles_PS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"PointSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; const int VBUFFERSIZE = 4000; int pc = em->particleCount; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DPFLAGS(CKRST_DP_TR_CL_VC|CKRST_DP_VBUFFER), (pc>VBUFFERSIZE)?VBUFFERSIZE:pc); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); // To manage reflection dev->SetWorldTransformationMatrix(oldmatrix*mov->GetInverseWorldMatrix()); // we don't let write to the ZBuffer // TMP TMP em->SetState(dev); float averageSize = em->m_StartSize * 2.0f; float minSize = 0.0f; float maxSize = 4096.0f; dev->SetState(VXRENDERSTATE_POINTSPRITEENABLE, TRUE); dev->SetState(VXRENDERSTATE_POINTSIZE, *(DWORD*)&averageSize); dev->SetState(VXRENDERSTATE_POINTSIZE_MIN,*(DWORD*)&minSize); dev->SetState(VXRENDERSTATE_POINTSIZE_MAX,*(DWORD*)&maxSize); dev->SetState(VXRENDERSTATE_POINTSCALEENABLE, TRUE); float pointScaleA = 1.0f; float pointScaleB = 0.0f; float pointScaleC = 1.0f; dev->SetState(VXRENDERSTATE_POINTSCALE_A,*(DWORD*)&pointScaleA); dev->SetState(VXRENDERSTATE_POINTSCALE_B,*(DWORD*)&pointScaleB); dev->SetState(VXRENDERSTATE_POINTSCALE_C,*(DWORD*)&pointScaleC); Particle* p = em->getParticles(); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); int np = 0; int remaining = pc; unsigned int (*ColorConvertor) (const VxColor*); if (dev->GetRasterizerContext()->m_Driver->m_3DCaps.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_VERTEXCOLORABGR) ColorConvertor = &BGRAFTOCOLOR; else ColorConvertor = &RGBAFTOCOLOR; while (p) { *positions = p->pos; *colors = (*ColorConvertor)(&(p->m_Color)); // next point p = p->next; ++colors; ++positions; ++np; if (np == VBUFFERSIZE) { // need to flush the vertex buffer // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_POINTLIST, (WORD*)0, np, data); remaining -= VBUFFERSIZE; np = 0; // restart if (remaining > 0) { data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); positions = data->Positions; colors = data->Colors; } } } // The Primitive Drawing if (remaining) em->DrawPrimitive(dev, FALSE, VX_POINTLIST, (WORD*)NULL, remaining, data); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetState(VXRENDERSTATE_POINTSPRITEENABLE, FALSE); dev->SetWorldTransformationMatrix(oldmatrix); return 0; } // FixedPointSprite (Hardware) Render int RenderParticles_FPS(CKRenderContext *dev,CKRenderObject *obj,void *arg) { CK3dEntity* mov = (CK3dEntity *)obj; ParticleEmitter *em = (ParticleEmitter*)arg; #ifdef USE_THR ThreadWaitForCompletion(em,"PointSprite"); #endif if (!IsRenderingNeeded(em)) return CKBR_OK; const int VBUFFERSIZE = 4000; int pc = em->particleCount; VxDrawPrimitiveData* data = dev->GetDrawPrimitiveStructure(CKRST_DPFLAGS(CKRST_DP_TR_CL_VC|CKRST_DP_VBUFFER), (pc>VBUFFERSIZE)?VBUFFERSIZE:pc); VxMatrix oldmatrix = dev->GetWorldTransformationMatrix(); // To manage reflection dev->SetWorldTransformationMatrix(oldmatrix*mov->GetInverseWorldMatrix()); // we don't let write to the ZBuffer // TMP TMP em->SetState(dev); float averageSize = em->m_StartSize * 2.0f; float minSize = 0.0f; float maxSize = 4096.0f; dev->SetState(VXRENDERSTATE_POINTSPRITEENABLE, TRUE); dev->SetState(VXRENDERSTATE_POINTSIZE, *(DWORD*)&averageSize); dev->SetState(VXRENDERSTATE_POINTSIZE_MIN,*(DWORD*)&minSize); dev->SetState(VXRENDERSTATE_POINTSIZE_MAX,*(DWORD*)&maxSize); dev->SetState(VXRENDERSTATE_POINTSCALEENABLE, FALSE); Particle* p = em->getParticles(); XPtrStrided positions(data->Positions); XPtrStrided colors(data->Colors); int np = 0; int remaining = pc; unsigned int (*ColorConvertor) (const VxColor*); if (dev->GetRasterizerContext()->m_Driver->m_3DCaps.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_VERTEXCOLORABGR) ColorConvertor = &BGRAFTOCOLOR; else ColorConvertor = &RGBAFTOCOLOR; while (p) { *positions = p->pos; *colors = (*ColorConvertor)(&(p->m_Color)); // next point p = p->next; ++colors; ++positions; ++np; if (np == VBUFFERSIZE) { // need to flush the vertex buffer // The Primitive Drawing em->DrawPrimitive(dev, FALSE, VX_POINTLIST, (WORD*)0, np, data); remaining -= VBUFFERSIZE; np = 0; // restart if (remaining > 0) { data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VCT|CKRST_DP_VBUFFER),(remaining>VBUFFERSIZE)?VBUFFERSIZE:remaining); positions = data->Positions; colors = data->Colors; } } } // The Primitive Drawing if (remaining) em->DrawPrimitive(dev, FALSE, VX_POINTLIST, (WORD*)NULL, remaining, data); // we let write to the ZBuffer dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE); dev->SetState(VXRENDERSTATE_POINTSPRITEENABLE, FALSE); dev->SetWorldTransformationMatrix(oldmatrix); return 0; }