// This file is distributed under a BSD license. See LICENSE.txt for details. #include "geneffectdebris.hpp" #include "genminmesh.hpp" #include "genblobspline.hpp" #include "genscene.hpp" #include "genmaterial.hpp" #include "engine.hpp" #if !sPLAYER extern sBool SceneWireframe; extern sInt SceneWireFlags; extern sU32 SceneWireMask; #endif /****************************************************************************/ /*** ***/ /*** Walk the spline ***/ /*** ***/ /****************************************************************************/ KObject * __stdcall Init_Effect_WalkTheSpline(class GenMinMesh *mesh,class GenSpline *spline,sF32 animpos,sF32 animwalk,sInt count,sInt seed,sF32 side,sF32 middle) { EngMesh *engmesh; if(spline) spline->Release(); if(mesh && mesh->ClassId==KC_MINMESH) { #if !sPLAYER if(SceneWireframe) { mesh->PrepareWire(SceneWireFlags,SceneWireMask); engmesh = mesh->WireMesh; } else { mesh->UnPrepareWire(); mesh->Prepare(); engmesh = mesh->PreparedMesh; } #else mesh->Prepare(); engmesh = mesh->PreparedMesh; #endif #if sPLAYER engmesh->Preload(); #endif } if(mesh) mesh->Release(); return new GenScene; } /****************************************************************************/ void __stdcall Exec_Effect_WalkTheSpline(KOp *op,KEnvironment *kenv,sF32 animpos,sF32 animwalk,sInt count,sInt seed,sF32 side,sF32 middle) { GenMinMesh *mesh = (GenMinMesh *)op->GetInput(0)->Cache; GenSpline *spline = (GenSpline *) op->GetInput(1)->Cache; sVERIFY(mesh); sVERIFY(spline); sMatrix mat,mat0,mat1; sF32 dummy,a,s; if(mesh && mesh->ClassId==KC_MINMESH) { EngMesh *engmesh; #if !sPLAYER if(SceneWireframe) engmesh = mesh->WireMesh; else engmesh = mesh->PreparedMesh; #else engmesh = mesh->PreparedMesh; #endif if(engmesh) { sSetRndSeed(seed); animpos = sFMod(animpos,1); mat0 = kenv->ExecStack.Top(); for(sInt i=0;i1.0f) a = a-1.0f; if(s<0) { spline->Eval(1-a,0,mat1,dummy); mat.MulA(mat1,mat0); mat.l.AddScale3(mat.i,side*s - middle); mat.i.Scale3(-1); mat.k.Scale3(-1); Engine->AddPaintJob(engmesh,mat,animwalk,0); } else { spline->Eval(a,0,mat1,dummy); mat.MulA(mat1,mat0); mat.l.AddScale3(mat.i,side*s + middle); Engine->AddPaintJob(engmesh,mat,animwalk,0); } } } } } /****************************************************************************/ /*** ***/ /*** Chain Line ***/ /*** ***/ /****************************************************************************/ KObject * __stdcall Init_Effect_ChainLine(GenMaterial *mtrl, sF323 posa,sF323 posb,sInt marka,sInt markb,sF32 dist,sF32 thick,sF32 gravity,sF32 damp,sF32 spring, sF32 rip,sF32 windspeed,sF32 windforce,sInt flags,sF323 basewind,sInt rippoint) { return MakeEffect(mtrl); } /****************************************************************************/ static const sInt CLMax = 32; struct CLVerlet { sVector OldPos; sVector Pos; sVector NewPos; }; struct CLData : public KInstanceMem { CLVerlet Lines[CLMax]; sInt Ripped; sInt ColAxis; sInt TimeCounter; sInt FirstCycles; sVector OldA,OldB; }; void __stdcall Exec_Effect_ChainLine(KOp *op,KEnvironment *kenv, sF323 _posa,sF323 _posb,sInt marka,sInt markb,sF32 dist,sF32 thick,sF32 gravity,sF32 damp,sF32 spring, sF32 rip,sF32 windspeed,sF32 windforce,sInt flags,sF323 basewind,sInt rippoint) { GenMaterial *mtrl; sVector posa,posb; sVector d,tempv; sF32 len; sVector wind; sVector winddir; sVector NewA,NewB; sF32 ColMin; sF32 ColMax; // check and convert parameters sVERIFY(marka>=0 && markaMarkers)); sVERIFY(markb>=0 && markbMarkers)); NewA.Init3(_posa.x,_posa.y,_posa.z); NewB.Init3(_posb.x,_posb.y,_posb.z); NewA.Rotate34(kenv->Markers[marka]); NewB.Rotate34(kenv->Markers[markb]); dist = dist / (CLMax-1); gravity = gravity * gravity; // initialize instance data CLData *mem = kenv->GetInst(op); if(mem->Reset) { d.Sub3(NewA,NewB); if(flags & 4) { for(sInt i=0;iLines[i].Pos = NewA; mem->Lines[i].Pos.y -= i*dist; mem->Lines[i].OldPos = mem->Lines[i].Pos; } mem->Ripped = CLMax-1; } if(sFAbs(d.x) > sFAbs(d.y)) { if(sFAbs(d.x) > sFAbs(d.z)) mem->ColAxis = 0; else mem->ColAxis = 2; } else { if(sFAbs(d.y) > sFAbs(d.z)) mem->ColAxis = 1; else mem->ColAxis = 2; } mem->TimeCounter = 0; mem->FirstCycles = 250; mem->OldA = NewA; mem->OldB = NewB; } if((mem->Reset && !(flags & 4)) || (flags & 8)) // when physical simulation is switched off, always calculate curve { sF32 h; sF32 delta; d.Sub3(NewA,NewB); delta = d.Dot3(d); h = (dist*(CLMax-1)*dist*(CLMax-1))-delta; if(h<=0) h = 0; else h = sFSqrt(h)*0.4f; for(sInt i=0;iLines[i].Pos.Lin3(NewA,NewB,n); mem->Lines[i].Pos.y -= (1-m*m)*h; if(mem->Lines[i].Pos.y<0) mem->Lines[i].Pos.y = 0; mem->Lines[i].OldPos = mem->Lines[i].Pos; } mem->Ripped = -1; } // physical simulation sInt timeslices = kenv->TimeSlices; #if !sPLAYER if(timeslices > 50) timeslices = 50; #endif if(!(flags & 8)) for(sInt t=0;tOldA,NewA,(t+1)*1.0f/(timeslices*10)); posb.Lin3(mem->OldB,NewB,(t+1)*1.0f/(timeslices*10)); ColMin = posa[mem->ColAxis]; ColMax = posb[mem->ColAxis]; if(ColMin>ColMax) sSwap(ColMin,ColMax); sF32 wtimer = (mem->TimeCounter + t)*windspeed; // wind speed d.Init(wtimer*1.0f,wtimer*1.1f,wtimer*1.2f); sPerlin3D(d,wind); wind.Scale3(windforce); wind.y *= 0.5f; wind.x += basewind.x; wind.y += basewind.y; wind.z += basewind.z; winddir = wind; winddir.UnitSafe3(); for(sInt i=0;iLines[i]; v->NewPos = v->Pos; // verlet step if(mem->FirstCycles==0) { v->NewPos.AddScale3(v->Pos,damp); v->NewPos.AddScale3(v->OldPos,-damp); } else { v->NewPos.AddScale3(v->Pos,0.975f); v->NewPos.AddScale3(v->OldPos,-0.975f); } v->NewPos.y -= gravity; // gravity } for(sInt i=0;iLines[i]; CLVerlet *b = &mem->Lines[i+1]; d.Sub3(b->Pos,a->Pos); tempv.Cross3(d,winddir); sF32 wf = tempv.Abs3(); // wind a->NewPos.AddScale3(wind,wf*0.0001f); if(i!=mem->Ripped) { len = d.UnitAbs3(); // spring force if(len>dist) { a->NewPos.AddScale3(d,(len-dist)*spring); b->NewPos.AddScale3(d,-(len-dist)*spring); } } } for(sInt i=0;iLines[i]; if(v->NewPos.y < 0) // ground v->NewPos.y = 0; sInt j = mem->ColAxis; switch(flags & 3) { case 0: break; case 1: if(v->NewPos[j]Pos[j] -= v->NewPos[j]-ColMin; v->NewPos[j] = ColMin; } if(v->NewPos[j]>ColMax) { v->Pos[j] -= v->NewPos[j]-ColMax; v->NewPos[j] = ColMax; } break; case 2: if(v->NewPos[j]NewPos[j] = ColMin; if(v->NewPos[j]>ColMax) v->NewPos[j] = ColMax; break; } } mem->Lines[0].NewPos = posa; // endpoint constraints if(!(flags & 4)) mem->Lines[CLMax-1].NewPos = posb; if(mem->Ripped==-1) { d.Sub3(posa,posb); if(d.Dot3(d)>rip*rip) mem->Ripped = rippoint; } for(sInt i=0;iLines[i]; v->OldPos = v->Pos; v->Pos = v->NewPos; } if(mem->FirstCycles>0) mem->FirstCycles--; } mem->TimeCounter += kenv->TimeSlices*10; mem->OldA = NewA; mem->OldB = NewB; // drawing mtrl = (GenMaterial *) op->GetLinkCache(0); if(mtrl && mtrl->Passes.Count>0) { sInt geo; sF32 *fp; sU16 *ip; sVector v,s,a,b,d; sMatrix view; sSystem->GetTransform(sGT_MODELVIEW,view); s.Init(view.i.z,view.j.z,view.k.z); s.Unit3(); kenv->CurrentCam.CameraSpace.Init(); mtrl->Passes[0].Mtrl->Set(kenv->CurrentCam); geo = sSystem->GeoAdd(sFVF_TSPACE3,sGEO_TRI); sSystem->GeoBegin(geo,CLMax*2,(CLMax-1)*6,&fp,(void **)&ip); for(sInt i=0;iLines[sMax(0,i-1)].Pos; b = mem->Lines[sMin(CLMax-1,i+1)].Pos; d.Sub3(a,b); d.UnitSafe3(); a.Cross3(d,s); a.Scale3(thick/2); v = mem->Lines[i].Pos; fp[0] = v.x+a.x; fp[1] = v.y+a.y; fp[2] = v.z+a.z; fp[3] = 0; fp[4] = 0; *((sU32 *)(&fp[5])) = 0xff404040; fp[6] = i; fp[7] = 0; fp += 8; fp[0] = v.x-a.x; fp[1] = v.y-a.y; fp[2] = v.z-a.z; fp[3] = 0; fp[4] = 0; *((sU32 *)(&fp[5])) = 0xff404040; fp[6] = i; fp[7] = 1; fp += 8; } for(sInt i=0;iRipped) { sQuad(ip,i*2+3,i*2+2,i*2+0,i*2+1); } else { sQuad(ip,i*2+0,i*2+1,i*2+0,i*2+1); } } sSystem->GeoEnd(geo); sSystem->GeoDraw(geo); sSystem->GeoRem(geo); } } /****************************************************************************/ /*** ***/ /*** BillboardField ***/ /*** ***/ /****************************************************************************/ struct BFData : public KInstanceMem { sInt Count; sInt Max; sInt Seed; sF32 MinDist; sF32 HeightRnd; sVector *Data; }; KObject * __stdcall Init_Effect_BillboardField(class GenMaterial *mtrl,sF32 size,sF32 aspect,sF32 maxdist,sInt mode,sF323 r0,sF323 r1,sF32 anim,sInt count,sInt seed,sF32 heightrnd,sF32 mindist) { sInt pass = 0; if(mtrl) { mtrl->Release(); pass = mtrl->Passes[0].Pass; } GenEffect *eff = new GenEffect; eff->Pass = pass; return eff; } void __stdcall Exec_Effect_BillboardField(KOp *op,KEnvironment *kenv,sF32 size,sF32 aspect,sF32 maxdist,sInt mode,sF323 r0,sF323 r1,sF32 anim,sInt max,sInt seed,sF32 heightrnd,sF32 mindist) { GenMaterial *mtrl; sVector v; sMatrix mat,mat0,mat1; sInt count; sF32 dist,dpos; static const sInt MAXBILL = 10240; static const sInt MAXOT = 1024; static BillboardOTElem Buffer[MAXBILL]; static BillboardOTElem *OT[MAXOT]; BFData *mem = kenv->GetInst(op); if(mem->Reset || mem->MinDist!=mindist || mem->Seed!=seed || mem->Max!=max || mem->HeightRnd!=heightrnd) { if(!mem->Reset) delete[] mem->Data; mem->Max = max; mem->MinDist = mindist; mem->HeightRnd = heightrnd; mem->Seed = seed; mem->Count = 0; mem->Data = new sVector[max]; mem->DeleteArray = mem->Data; sSetRndSeed(seed); for(sInt i=0;iMax;j++) { sF32 x = (mem->Data[j].x-v.x)*(r1.x-r0.x); sF32 y = (mem->Data[j].y-v.y)*(r1.y-r0.y); sF32 z = (mem->Data[j].z-v.z)*(r1.z-r0.z); if(x*x+y*y+z*zData[mem->Count++] = v; skip:; } } mtrl = (GenMaterial *) op->GetInput(0)->Cache; if(mtrl && mtrl->Passes.Count>0) { mat0 = kenv->ExecStack.Top(); // model matrix mat1 = kenv->CurrentCam.CameraSpace; // camera matrix sSystem->GetTransform(sGT_MODELVIEW,mat); // align the billboards by this mat.Trans3(); dpos = mat1.l.x*mat1.k.x + mat1.l.y*mat1.k.y + mat1.l.z*mat1.k.z; sSetRndSeed(17); if(anim>0) anim = sFMod(anim,1); count = 0; for(sInt i=0;iCount && iData[i]; v.z += anim; if(v.z>0) { if(v.z>1) v.z -= 1; v.x = v.x * (r1.x-r0.x)+r0.x; v.y = v.y * (r1.y-r0.y)+r0.y; v.z = v.z * (r1.z-r0.z)+r0.z; e->size = v.w; v.Rotate34(mat0); e->x = v.x; e->y = v.y; e->z = v.z; e->uvval = i&7; dist = - dpos + (e->x*mat1.k.x + e->y*mat1.k.y + e->z*mat1.k.z); sInt id = sInt(dist * (MAXOT/maxdist)); if(id>=0 && idNext = OT[id]; OT[id] = e; count++; } } } if(count>0) { mtrl->Passes[0].Mtrl->Set(kenv->CurrentCam); PaintBillboards(kenv,count,OT,MAXOT,size,aspect,mode); } } } /****************************************************************************/ /*** ***/ /*** ***/ /*** ***/ /****************************************************************************/