/****************************************************************************** This source file is part of Viewizard Game Engine For the latest info, see http://www.viewizard.com/ File name: ParticleSystem.cpp Copyright (c) 2006-2007 Michael Kurinnoy, Viewizard All Rights Reserved. File Version: 3.0 ****************************************************************************** AstroMenace game source code available under "dual licensing" model. The licensing options available are: * Commercial Licensing. This is the appropriate option if you are creating proprietary applications and you are not prepared to distribute and share the source code of your application. Contact us for pricing at viewizard@viewizard.com * Open Source Licensing. This is the appropriate option if you want to share the source code of your application with everyone you distribute it to, and you also want to give them the right to share who uses it. You should have received a copy of the GNU General Public License version 3 with this source codes. If not, see . ******************************************************************************/ #include "ParticleSystem.h" #include "../Camera/Camera.h" #include "../RendererInterface/RendererInterface.h" extern bool ParticleSystemUseGLSL; extern float ParticleSystemQuality; //----------------------------------------------------------------------------- // инициализация класса //----------------------------------------------------------------------------- eParticleSystem::eParticleSystem() { NeedDestroy = false; Age = 0.0f; TimeLastUpdate = -1.0f; EmissionResidue = 0.0f; Angle = Direction = VECTOR3D(0,0,0); Texture = 0; AttractiveValue = 25.0f; IsSuppressed = false; DestroyIfNoParticles = false; // положение системы Location = PrevLocation = VECTOR3D( 0.0f, 0.0f, 0.0f); DeadZone = 0.0f; // нет мертвой зоны Resize = 1.0f; // нет изменений ColorStart.r = 1.00f; ColorStart.g = 1.00f; ColorStart.b = 1.00f; ColorVar.r = 0.00f; ColorVar.g = 0.00f; ColorVar.b = 0.00f; ColorEnd.r = 1.00f; ColorEnd.g = 1.00f; ColorEnd.b = 1.00f; AlphaStart = 1.00f; AlphaVar = 0.00f; AlphaEnd = 1.00f; AlphaShowHide = false; SizeStart = 1.00f; SizeVar = 0.00f; SizeEnd = 1.00f; Speed = 1.00f; SpeedOnCreation = 1.00f; SpeedVar = 1.00f; Theta = 1.00f; Life = 1.00f; LifeVar = 0.00f; NeedStop = false; ParticlesPerSec = 100; IsAttractive = 0; ParticlesCreated = 0; CreationType = 0; CreationSize = VECTOR3D(0.05f,0.05f,0.05f); // начальные установки для мартиц поворотов Matrix33Identity(CurrentRotationMat); Matrix33Identity(OldInvRotationMat); Light = 0; LightNeedDeviation = false; NextLightDeviation = 0.7f+0.3f*vw_Randf1; LightDeviation = 100.0f; LightDeviationSpeed = 3.5f+3.5f*vw_Randf1; // настройка массива Start = 0; End = 0; Next = 0; Prev = 0; vw_AttachParticleSystem(this); // начальныя установка коробок // ставим тут очень большие параметры, вне зоны прорисовки, т.к. в партиклах на функциях перемещения // не учитываем изменение коробки, и координаты в коробках у нас абсолютные (!!!) // использовать относительные координаты бокса нельзя, т.к. можем преносить только центр, а можем // и центр, и все частицы... AABB[0]=AABB[1]=AABB[2]=AABB[3]=AABB[4]=AABB[5]=AABB[6]=AABB[7]= VECTOR3D(-1000000.0f, 1000000.0f, -1000000.0f); } //----------------------------------------------------------------------------- // При разрушении класса //----------------------------------------------------------------------------- eParticleSystem::~eParticleSystem() { // полностью освобождаем память от всех частиц в системе eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; Detach(tmp); delete tmp; tmp = 0; tmp = tmp2; } if (Light != 0){vw_ReleaseLight(Light); Light = 0;} vw_DetachParticleSystem(this); } //----------------------------------------------------------------------------- // подключить частицу к системе //----------------------------------------------------------------------------- void eParticleSystem::Attach(eParticle * NewParticle) { if (NewParticle == 0) return; // первый в списке... if (End == 0) { NewParticle->Prev = 0; NewParticle->Next = 0; Start = NewParticle; End = NewParticle; } else // продолжаем заполнение... { NewParticle->Prev = End; NewParticle->Next = 0; End->Next = NewParticle; End = NewParticle; } } //----------------------------------------------------------------------------- // отключить ее от системы //----------------------------------------------------------------------------- void eParticleSystem::Detach(eParticle * OldParticle) { if (OldParticle == 0) return; // переустанавливаем указатели... if (Start == OldParticle) Start = OldParticle->Next; if (End == OldParticle) End = OldParticle->Prev; if (OldParticle->Next != 0) OldParticle->Next->Prev = OldParticle->Prev; else if (OldParticle->Prev != 0) OldParticle->Prev->Next = 0; if (OldParticle->Prev != 0) OldParticle->Prev->Next = OldParticle->Next; else if (OldParticle->Next != 0) OldParticle->Next->Prev = 0; } //----------------------------------------------------------------------------- // обновление системы //----------------------------------------------------------------------------- bool eParticleSystem::Update(float Time) { // первый раз... просто берем время if (TimeLastUpdate == -1.0f) {TimeLastUpdate = Time;return true;} // Time - это абсолютное время, вычисляем дельту float TimeDelta = Time - TimeLastUpdate; // быстро вызвали еще раз... время не изменилось, или почти не изменилось if (TimeDelta == 0.0f) return true; TimeLastUpdate = Time; // очищаем счетчик подсчета кол-ва действующий частиц ParticlesAlive = 0; // для всех частиц eParticle *tmp = Start; // предварительная инициализация float MinX, MaxX; MinX = MaxX = Location.x; float MinY, MaxY; MinY = MaxY = Location.y; float MinZ, MaxZ; MinZ = MaxZ = Location.z; if (tmp!=0) { MinX = MaxX = tmp->Location.x + tmp->Size; MinY = MaxY = tmp->Location.y + tmp->Size; MinZ = MaxZ = tmp->Location.z + tmp->Size; } while (tmp!=0) { eParticle *tmp2 = tmp->Next; // функция вернет false, если частица уже мертва if (tmp->Update(TimeDelta, Location, IsAttractive, AttractiveValue)) { ParticlesAlive++; // строим AABB if (tmp->Alpha > 0.0f && tmp->Size > 0.0f) { VECTOR3D v; v.x = tmp->Location.x + tmp->Size; if (MaxX < v.x) MaxX = v.x; else { v.y = tmp->Location.y + tmp->Size; if (MaxY < v.y) MaxY = v.y; else { v.z = tmp->Location.z + tmp->Size; if (MaxZ < v.z) MaxZ = v.z; else { v.x = tmp->Location.x - tmp->Size; if (MinX > v.x) MinX = v.x; else { v.y = tmp->Location.y - tmp->Size; if (MinY > v.y) MinY = v.y; else { v.z = tmp->Location.z - tmp->Size; if (MinZ > v.z) MinZ = v.z; } } } } } } } else { Detach(tmp); delete tmp; tmp = 0; } tmp = tmp2; } // запоминаем только то, что нужно - float x, float y, float z, float sizeX, float sizeY, float sizeZ AABB[0] = VECTOR3D(MaxX, MaxY, MaxZ); AABB[1] = VECTOR3D(MinX, MaxY, MaxZ); AABB[2] = VECTOR3D(MinX, MaxY, MinZ); AABB[3] = VECTOR3D(MaxX, MaxY, MinZ); AABB[4] = VECTOR3D(MaxX, MinY, MaxZ); AABB[5] = VECTOR3D(MinX, MinY, MaxZ); AABB[6] = VECTOR3D(MinX, MinY, MinZ); AABB[7] = VECTOR3D(MaxX, MinY, MinZ); // подсчитываем, как много частиц нам нужно создать из ParticlesPerSec float ParticlesNeeded = (ParticlesPerSec/ParticleSystemQuality) * TimeDelta + EmissionResidue; // переводим в целочисленные значения ParticlesCreated = (int)ParticlesNeeded; if ( !IsSuppressed ) { // запоминаем разницу между тем сколько нужно и сколько создадим EmissionResidue = ParticlesNeeded - ParticlesCreated; } else { // ничего создавать не нужно EmissionResidue = ParticlesNeeded; ParticlesCreated = 0; } // если нужно что-то создавать, создаем if ( ParticlesCreated > 0 ) { // если пытаемся создать за один раз больше чем можем в секунду // убираем этот "глюк", видно компьютер тормозит if (ParticlesCreated > ParticlesPerSec) ParticlesCreated = ParticlesPerSec; // пока не создадим все необходимые частицы while (ParticlesCreated > 0) { // создаем новую частицу eParticle *NewParticle = 0; NewParticle = new eParticle; if (NewParticle == 0) return true; // установка жизни новой частици и проверка, что не выходит из диапахона NewParticle->Age = 0.0f; NewParticle->Lifetime = Life + vw_Randf0 * LifeVar; if (NewParticle->Lifetime < 0.0f) NewParticle->Lifetime = 0.0f; // стартовый цвет NewParticle->Color.r = ColorStart.r + vw_Randf0 * ColorVar.r; NewParticle->Color.g = ColorStart.g + vw_Randf0 * ColorVar.g; NewParticle->Color.b = ColorStart.b + vw_Randf0 * ColorVar.b; // мы не используем альфа цвет тут NewParticle->Color.a = 1.0f; // проверяем, чтобы не было переполнения цвета Clamp( NewParticle->Color.r, 0.0f, 1.0f ); Clamp( NewParticle->Color.g, 0.0f, 1.0f ); Clamp( NewParticle->Color.b, 0.0f, 1.0f ); // считаем delta относительно жизни частицы NewParticle->ColorDelta.r = (ColorEnd.r - NewParticle->Color.r) / NewParticle->Lifetime; NewParticle->ColorDelta.g = (ColorEnd.g - NewParticle->Color.g) / NewParticle->Lifetime; NewParticle->ColorDelta.b = (ColorEnd.b - NewParticle->Color.b) / NewParticle->Lifetime; // считаем значение альфы NewParticle->Alpha = AlphaStart + vw_Randf0 * AlphaVar; // убираем переполнение Clamp( NewParticle->Alpha, 0.0f, 1.0f ); // считаем дельту альфы NewParticle->AlphaDelta = (AlphaEnd - NewParticle->Alpha) / NewParticle->Lifetime; // передаем тип изменения альфы NewParticle->AlphaShowHide = AlphaShowHide; // если нужно маргнуть, не обращаем внимания на Alpha вообще if (NewParticle->AlphaShowHide) { NewParticle->AlphaDelta = (2.0f-AlphaEnd*2.0f) / NewParticle->Lifetime; NewParticle->Alpha = AlphaEnd; } // выпускаем частицу возле места нахождения системы if (CreationType == 0) // точка NewParticle->Location = Location + VECTOR3D(vw_Randf0 * CreationSize.x, vw_Randf0 * CreationSize.y, vw_Randf0 * CreationSize.z); if (CreationType == 1) { // в кубе VECTOR3D tmp; float minDist; if (DeadZone != 0.0f) { minDist = CreationSize.x*CreationSize.x+CreationSize.y*CreationSize.y+CreationSize.z*CreationSize.z; // если зона больше чем радиус излучения - выключаем ее if (minDist <= DeadZone*DeadZone) DeadZone = 0.0f; } tmp.x = vw_Randf0 * CreationSize.x; tmp.y = vw_Randf0 * CreationSize.y; tmp.z = vw_Randf0 * CreationSize.z; while (tmp.x*tmp.x + tmp.y*tmp.y + tmp.z*tmp.z < DeadZone*DeadZone) { // ув. радиус VECTOR3D tmp1 = tmp; tmp1.Normalize(); tmp1 = tmp1^(1/100.0f); tmp = tmp + tmp1; } Matrix33CalcPoint(&tmp, CurrentRotationMat); NewParticle->Location = Location + tmp; } // тип 11 только !!! для лазеров-мазеров if (CreationType == 11) { // в кубе VECTOR3D tmp; float minDist; if (DeadZone != 0.0f) { minDist = CreationSize.x*CreationSize.x+CreationSize.y*CreationSize.y+CreationSize.z*CreationSize.z; // если зона больше чем радиус излучения - выключаем ее if (minDist <= DeadZone*DeadZone) DeadZone = 0.0f; } tmp.x = (0.5f - vw_Randf1) * CreationSize.x; tmp.y = (0.5f - vw_Randf1) * CreationSize.y; tmp.z = (0.5f - vw_Randf1) * CreationSize.z; VECTOR3D CreationSizeText = CreationSize^(0.5f); while (tmp.x*tmp.x + tmp.y*tmp.y + tmp.z*tmp.z < DeadZone*DeadZone) { // ув. радиус VECTOR3D tmp1 = tmp; tmp1.Normalize(); tmp1 = tmp1^(1/100.0f); tmp = tmp + tmp1; } if (tmp.x > 0.0f) {if (tmp.x > CreationSizeText.x) tmp.x = CreationSizeText.x;} else {if (tmp.x < -CreationSizeText.x) tmp.x = -CreationSizeText.x;} if (tmp.y > 0.0f) {if (tmp.y > CreationSizeText.y) tmp.y = CreationSizeText.y;} else {if (tmp.y < -CreationSizeText.y) tmp.y = -CreationSizeText.y;} // делаем ручное регулирование if (tmp.z > 0.0f) {if (tmp.z > CreationSizeText.z) tmp.z = CreationSizeText.z;} else {if (tmp.z < -CreationSizeText.z) tmp.z = -CreationSizeText.z;} Matrix33CalcPoint(&tmp, CurrentRotationMat); NewParticle->Location = Location + tmp; } if (CreationType == 2) { // в сфере VECTOR3D tmp; float minDist = CreationSize.x*CreationSize.x+CreationSize.y*CreationSize.y+CreationSize.z*CreationSize.z; // если зона больше чем радиус излучения - выключаем ее if (minDist <= DeadZone*DeadZone) DeadZone = 0.0f; tmp.x = vw_Randf0 * CreationSize.x; tmp.y = vw_Randf0 * CreationSize.y; tmp.z = vw_Randf0 * CreationSize.z; float ParticleDist = tmp.x*tmp.x + tmp.y*tmp.y + tmp.z*tmp.z; while (ParticleDist > minDist || ParticleDist < DeadZone*DeadZone) { if (ParticleDist > minDist) { // ум. радиус VECTOR3D tmp1 = tmp; tmp1.Normalize(); tmp1 = tmp1^(1/100.0f); tmp = tmp - tmp1; } if ( ParticleDist < DeadZone*DeadZone) { // ув. радиус VECTOR3D tmp1 = tmp; tmp1.Normalize(); tmp1 = tmp1^(1/100.0f); tmp = tmp + tmp1; if (tmp.x > 0.0f) {if (tmp.x > CreationSize.x) tmp.x = CreationSize.x;} else {if (tmp.x < -CreationSize.x) tmp.x = -CreationSize.x;} if (tmp.y > 0.0f) {if (tmp.y > CreationSize.y) tmp.y = CreationSize.y;} else {if (tmp.y < -CreationSize.y) tmp.y = -CreationSize.y;} if (tmp.z > 0.0f) {if (tmp.z > CreationSize.z) tmp.z = CreationSize.z;} else {if (tmp.z < -CreationSize.z) tmp.z = -CreationSize.z;} } ParticleDist = tmp.x*tmp.x + tmp.y*tmp.y + tmp.z*tmp.z; } Matrix33CalcPoint(&tmp, CurrentRotationMat); NewParticle->Location = Location + tmp; } // если малый фпс... VECTOR3D L = (Location - PrevLocation)^vw_Randf1; NewParticle->Location = NewParticle->Location - L; // считаем размер частицы NewParticle->Size = SizeStart + vw_Randf0 * SizeVar; if (NewParticle->Size < 0.0f) NewParticle->Size = 0.0f; NewParticle->SizeDelta = (SizeEnd - NewParticle->Size) / NewParticle->Lifetime; // если есть учет расстояния, работаем с ним if (Resize < 1.0f) { // получаем текущее положение камеры VECTOR3D CurrentCameraLocation; vw_GetCameraLocation(&CurrentCameraLocation); // находим расстояние от центра системы до камеры float SystDist = (CurrentCameraLocation.x-Location.x-CreationSize.x)*(CurrentCameraLocation.x-Location.x-CreationSize.x) + (CurrentCameraLocation.y-Location.y-CreationSize.y)*(CurrentCameraLocation.y-Location.y-CreationSize.y) + (CurrentCameraLocation.z-Location.z-CreationSize.z)*(CurrentCameraLocation.z-Location.z-CreationSize.z); float ParticleDist = (CurrentCameraLocation.x-NewParticle->Location.x)*(CurrentCameraLocation.x-NewParticle->Location.x) + (CurrentCameraLocation.y-NewParticle->Location.y)*(CurrentCameraLocation.y-NewParticle->Location.y) + (CurrentCameraLocation.z-NewParticle->Location.z)*(CurrentCameraLocation.z-NewParticle->Location.z); // если частица ближе центра к камере - нужна корректировка if (ParticleDist < SystDist) { float tmpStart = SizeStart - SizeStart*(1.0f-Resize)*(SystDist-ParticleDist)/SystDist; float tmpEnd = SizeEnd - SizeEnd*(1.0f-Resize)*(SystDist-ParticleDist)/SystDist; float tmpVar = SizeVar - SizeVar*(1.0f-Resize)*(SystDist-ParticleDist)/SystDist; NewParticle->Size = tmpStart + vw_Randf0 * tmpVar; if (NewParticle->Size < 0.0f) NewParticle->Size = 0.0f; NewParticle->SizeDelta = (tmpEnd - NewParticle->Size) / NewParticle->Lifetime; } } // испускатель имеет направление. этот код немного добавляет случайности if (Theta == 0.0f) { NewParticle->Velocity = Direction; } else { NewParticle->Velocity = Direction; RotatePoint(&NewParticle->Velocity, VECTOR3D(Theta*vw_Randf0/2.0f, Theta*vw_Randf0/2.0f, 0.0f)); } NewParticle->NeedStop = NeedStop; // находим перемещение float NewSpeed = Speed + vw_Randf0 * SpeedVar; if (NewSpeed < 0.0f) NewSpeed = 0.0f; NewParticle->Velocity *= NewSpeed ; // подключаем частицу к системе Attach(NewParticle); // уменьшаем необходимое количество частиц ParticlesCreated--; } } // работа с источником света (если он есть) if (Light != 0) { // просто работаем if (!IsSuppressed) { if (LightNeedDeviation) { // было выключено, сейчас только включили опять if (LightDeviation > 1.0f) LightDeviation = 0.7f; if (NextLightDeviation < LightDeviation) { LightDeviation -= LightDeviationSpeed*TimeDelta; if (NextLightDeviation >= LightDeviation) { LightDeviation = NextLightDeviation; LightDeviationSpeed = 3.5f+3.5f*vw_Randf1; NextLightDeviation = 0.7+0.3f*vw_Randf1; } } else { LightDeviation += LightDeviationSpeed*TimeDelta; if (NextLightDeviation <= LightDeviation) { LightDeviation = NextLightDeviation; LightDeviationSpeed = 3.5f+3.5f*vw_Randf1; NextLightDeviation = 0.7-0.3f*vw_Randf1; } } } else // было выключено, сейчас только включили опять if (LightDeviation > 1.0f) LightDeviation = 1.0f; } else // было включено, сейчас выключили, нужно сделать исчезание if (IsSuppressed) { if (Life != 0.0f) LightDeviation += (25.0f/Life)*TimeDelta; } if (Light->LinearAttenuationBase != 0.0f) { Light->LinearAttenuation = Light->LinearAttenuationBase + 2.0f*(LightDeviation-0.5f)*Light->LinearAttenuationBase; if (Light->LinearAttenuation < Light->LinearAttenuationBase) Light->LinearAttenuation = Light->LinearAttenuationBase; } if (Light->QuadraticAttenuationBase != 0.0f) { Light->QuadraticAttenuation = Light->QuadraticAttenuationBase + 2.0f*(LightDeviation-0.5f)*Light->QuadraticAttenuationBase; if (Light->QuadraticAttenuation < Light->QuadraticAttenuationBase) Light->QuadraticAttenuation = Light->QuadraticAttenuationBase; } // если уде ничего нет, надо выключить свет, если что-то есть то включить if (ParticlesAlive == 0) Light->On = false; else Light->On = true; } // проверка, если уже ничего нет и нужно выйти - выходим if (DestroyIfNoParticles) if (ParticlesAlive == 0) { NeedDestroy = true; return false; } return true; } //----------------------------------------------------------------------------- // прорисовка системы //----------------------------------------------------------------------------- void eParticleSystem::Draw() { // загрузка текстуры, уже должна быть подключена if (Texture == 0) return; // если не входит - рисовать не нужно if (!vw_BoxInFrustum(AABB[6], AABB[0])) return; // проверяем, если все точки аабб входят - нет смысла проверять каждую частицу bool NeedPartVisibleDetection = false; if (!vw_PointInFrustum(AABB[0])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[1])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[2])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[3])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[4])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[5])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[6])) NeedPartVisibleDetection = true; else if (!vw_PointInFrustum(AABB[7])) NeedPartVisibleDetection = true; // находим реальное кол-во частиц на прорисовку eParticle *tmp = Start; int DrawCount = 0; while (tmp!=0) { eParticle *tmp2 = tmp->Next; DrawCount++; tmp = tmp2; } // если рисовать нечего - выходим if (DrawCount == 0) return; // буфер для последовательности RI_TRIANGLES float *tmpDATA = 0; GLubyte *tmpDATAub = 0; // точное кол-во пар треугольников, с учетом оптимизации int PrimitCount = 0; // номер float'а в последовательности int k=0; // делаем массив для всех элементов // войдет RI_3f_XYZ | RI_2f_TEX | RI_4ub_COLOR tmpDATA = new float[4*(3+2+1)*DrawCount]; tmpDATAub = (GLubyte *)tmpDATA; if (tmpDATA == 0) return; GLubyte R,G,B,A; // шейдеры не поддерживаются, рисуем по старинке if (!ParticleSystemUseGLSL) { // получаем текущее положение камеры VECTOR3D CurrentCameraLocation; vw_GetCameraLocation(&CurrentCameraLocation); tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; bool PartVisible = true; if (NeedPartVisibleDetection) { // небольшая оптимизация, если попадает в сферу - рисуем, нет - значит не видно // проверяем точку, если ее видно, то и частицу видно 100% PartVisible = vw_PointInFrustum(tmp->Location); // если ее не видно, надо посмотреть, может сфера входит if (!PartVisible) PartVisible = vw_SphereInFrustum(tmp->Location, tmp->Size); } if (PartVisible) { // ув. счетчик пар треугольников PrimitCount++; // находим вектор камера-точка VECTOR3D nnTmp; nnTmp = CurrentCameraLocation - tmp->Location; //nnTmp.Normalize();// - это тут не нужно, нам нужны только пропорции // находим перпендикуляр к вектору nnTmp VECTOR3D nnTmp2; nnTmp2.x = 1.0f; nnTmp2.y = 1.0f; nnTmp2.z = -(nnTmp.x + nnTmp.y)/nnTmp.z; nnTmp2.Normalize(); // находим перпендикуляр к векторам nnTmp и nnTmp2 // файтически - a x b = ( aybz - byaz , azbx - bzax , axby - bxay ); VECTOR3D nnTmp3; nnTmp3.x = nnTmp.y*nnTmp2.z - nnTmp2.y*nnTmp.z; nnTmp3.y = nnTmp.z*nnTmp2.x - nnTmp2.z*nnTmp.x; nnTmp3.z = nnTmp.x*nnTmp2.y - nnTmp2.x*nnTmp.y; nnTmp3.Normalize(); // находим VECTOR3D tmpAngle1,tmpAngle2,tmpAngle3,tmpAngle4; tmpAngle1 = nnTmp3^(tmp->Size*1.5f); tmpAngle3 = nnTmp3^(-tmp->Size*1.5f); tmpAngle2 = nnTmp2^(tmp->Size*1.5f); tmpAngle4 = nnTmp2^(-tmp->Size*1.5f); // собираем квадраты R = (GLubyte)(tmp->Color.r*255); G = (GLubyte)(tmp->Color.g*255); B = (GLubyte)(tmp->Color.b*255); A = (GLubyte)(tmp->Alpha*255); tmpDATA[k++] = tmp->Location.x+tmpAngle3.x; tmpDATA[k++] = tmp->Location.y+tmpAngle3.y; tmpDATA[k++] = tmp->Location.z+tmpAngle3.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 0.0f; tmpDATA[k++] = 1.0f; tmpDATA[k++] = tmp->Location.x+tmpAngle2.x; tmpDATA[k++] = tmp->Location.y+tmpAngle2.y; tmpDATA[k++] = tmp->Location.z+tmpAngle2.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 0.0f; tmpDATA[k++] = 0.0f; tmpDATA[k++] = tmp->Location.x+tmpAngle1.x; tmpDATA[k++] = tmp->Location.y+tmpAngle1.y; tmpDATA[k++] = tmp->Location.z+tmpAngle1.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 1.0f; tmpDATA[k++] = 0.0f; tmpDATA[k++] = tmp->Location.x+tmpAngle4.x; tmpDATA[k++] = tmp->Location.y+tmpAngle4.y; tmpDATA[k++] = tmp->Location.z+tmpAngle4.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 1.0f; tmpDATA[k++] = 1.0f; } tmp = tmp2; } } else // иначе работаем с шейдерами { tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; bool PartVisible = true; if (NeedPartVisibleDetection) { // небольшая оптимизация, если попадает в сферу - рисуем, нет - значит не видно // проверяем точку, если ее видно, то и частицу видно 100% PartVisible = vw_PointInFrustum(tmp->Location); // если ее не видно, надо посмотреть, может сфера входит if (!PartVisible) PartVisible = vw_SphereInFrustum(tmp->Location, tmp->Size); } if (PartVisible) { // ув. счетчик пар треугольников PrimitCount++; // собираем квадраты R = (GLubyte)(tmp->Color.r*255); G = (GLubyte)(tmp->Color.g*255); B = (GLubyte)(tmp->Color.b*255); A = (GLubyte)(tmp->Alpha*255); tmpDATA[k++] = tmp->Location.x; tmpDATA[k++] = tmp->Location.y; tmpDATA[k++] = tmp->Location.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 1.0f; tmpDATA[k++] = tmp->Size; tmpDATA[k++] = tmp->Location.x; tmpDATA[k++] = tmp->Location.y; tmpDATA[k++] = tmp->Location.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 2.0f; tmpDATA[k++] = tmp->Size; tmpDATA[k++] = tmp->Location.x; tmpDATA[k++] = tmp->Location.y; tmpDATA[k++] = tmp->Location.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 3.0f; tmpDATA[k++] = tmp->Size; tmpDATA[k++] = tmp->Location.x; tmpDATA[k++] = tmp->Location.y; tmpDATA[k++] = tmp->Location.z; tmpDATAub[k*sizeof(float)] = R; tmpDATAub[k*sizeof(float)+1] = G; tmpDATAub[k*sizeof(float)+2] = B; tmpDATAub[k*sizeof(float)+3] = A; k++; tmpDATA[k++] = 4.0f; tmpDATA[k++] = tmp->Size; } tmp = tmp2; } } if (PrimitCount > 0) { vw_SendVertices(RI_QUADS, 4*PrimitCount, RI_3f_XYZ | RI_4ub_COLOR | RI_1_TEX, tmpDATA, 6*sizeof(float)); } if (tmpDATA != 0){delete [] tmpDATA; tmpDATA = 0;} } //----------------------------------------------------------------------------- // поставить правильный полет частиц, т.е. учет внешней скорости //----------------------------------------------------------------------------- void eParticleSystem::SetStartLocation(VECTOR3D NewLocation) { PrevLocation = NewLocation; Location = NewLocation; if (Light != 0) Light->SetLocation(Location); } //----------------------------------------------------------------------------- // перемещение всех частиц и центра //----------------------------------------------------------------------------- void eParticleSystem::MoveSystem(VECTOR3D NewLocation) { // чтобы не сбивать PrevLocation if (Location == NewLocation) return; PrevLocation = Location; Location = NewLocation; eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; // меняем каждой частице tmp->Location += NewLocation-PrevLocation; tmp = tmp2; } if (Light != 0) Light->SetLocation(Location); } //----------------------------------------------------------------------------- // перемещение центра //----------------------------------------------------------------------------- void eParticleSystem::MoveSystemLocation(VECTOR3D NewLocation) { // чтобы не сбивать PrevLocation if (Location == NewLocation) return; PrevLocation = Location; Location = NewLocation; if (Light != 0) Light->SetLocation(Location); } //----------------------------------------------------------------------------- // установка направления "движения" системы //----------------------------------------------------------------------------- void eParticleSystem::RotateSystemByAngle(VECTOR3D NewAngle) { Angle = NewAngle; // сохраняем старые значения + пересчет новых memcpy(OldInvRotationMat, CurrentRotationMat, 9*sizeof(float)); // делаем инверсную старую матрицу Matrix33InverseRotate(OldInvRotationMat); Matrix33CreateRotate(CurrentRotationMat, Angle); Matrix33CalcPoint(&Direction, OldInvRotationMat); Matrix33CalcPoint(&Direction, CurrentRotationMat); } //----------------------------------------------------------------------------- // установка направления "движения" системы + перенос всех частиц //----------------------------------------------------------------------------- void eParticleSystem::RotateSystemAndParticlesByAngle(VECTOR3D NewAngle) { Angle = NewAngle; // сохраняем старые значения + пересчет новых memcpy(OldInvRotationMat, CurrentRotationMat, 9*sizeof(float)); // делаем инверсную старую матрицу Matrix33InverseRotate(OldInvRotationMat); Matrix33CreateRotate(CurrentRotationMat, Angle); Matrix33CalcPoint(&Direction, OldInvRotationMat); Matrix33CalcPoint(&Direction, CurrentRotationMat); eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; // меняем каждой частице VECTOR3D TMP = tmp->Location - Location; Matrix33CalcPoint(&TMP, OldInvRotationMat); Matrix33CalcPoint(&TMP, CurrentRotationMat); tmp->Location = TMP + Location; tmp = tmp2; } } //----------------------------------------------------------------------------- // поворот частиц на угол без поворота системы //----------------------------------------------------------------------------- void eParticleSystem::RotateParticlesByAngle(VECTOR3D NewAngle) { // делаем обратку для отматывания назад float TmpOldInvRotationMat[9]; memcpy(TmpOldInvRotationMat, CurrentRotationMat, 9*sizeof(float)); Matrix33InverseRotate(TmpOldInvRotationMat); // матрица поворота частиц float TmpRotationMat[9]; Matrix33CreateRotate(TmpRotationMat, NewAngle); eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; // меняем каждой частице VECTOR3D TMP = tmp->Location - Location; Matrix33CalcPoint(&TMP, TmpOldInvRotationMat); // поворачиваем каждую точку Matrix33CalcPoint(&TMP, TmpRotationMat); Matrix33CalcPoint(&TMP, CurrentRotationMat); tmp->Location = TMP + Location; tmp = tmp2; } } //----------------------------------------------------------------------------- // остановить все частицы в системе //----------------------------------------------------------------------------- void eParticleSystem::StopAllParticles() { Speed = 0.0f; IsAttractive = false; eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; // меняем каждой частице tmp->Velocity = VECTOR3D(0.0f,0.0f,0.0f); tmp = tmp2; } } //----------------------------------------------------------------------------- // поставить правильный полет частиц, т.е. учет внешней скорости //----------------------------------------------------------------------------- void eParticleSystem::ChangeSpeed(VECTOR3D Vel) { eParticle *tmp = Start; while (tmp!=0) { eParticle *tmp2 = tmp->Next; // меняем каждой частице tmp->Velocity += Vel; tmp = tmp2; } }