Реализация взрыва при помощи системы частиц
Автор: Max Stalker
Часть 1
Если вы решили реализовать взрыв какого-нибудь объекта и хотите сделать его не слишком сложным, то можно поступить следующим путем.
Представим, что наша модель сделана из треугольников или квадратов (так как это не существенно). Будем реализовывать взрыв таким образом, чтобы модель распадалась на отдельные части и они бы разлетались в зависимости от следующих параметров :
Положение самой части в пространстве
Положение "взрывного устройства"
Сила взрыва
Направление нормали к поверхности части
Гравитация
Все эти параметры нам нужны для следующего:
В зависимости от положения части мы будем считать направление в котором будет лететь частица.
Положение взрывного устройства также влияет на направление движения части
Сила взрыва влияет на скорость с которой данная часть будет удалятся от начальной позиции
Направление нормали на необходимо для того, чтобы посчитать как будет влиять взрывная волна на данную часть модели. Так как если она повернута к ней перпендикулярно, то влияние взрыва на часть будет гораздо меньше, нежели если эта часть повернута перпендикулярно распространению волны
Под действием гравитации частицы будут лететь к "земле"
Будем действовать последовательно и для начала напишем "геометрическую" состовляющую нашего движка. А будет она выглядеть вот так:
unit GEOMETRY; uses Windows,OpenGL;
Создадим тип для положения в пространстве
type TVector3f=record x:glfloat; y:glfloat; z:glfloat; end;
Опишем также тип геометрических примитивов rTriangle:
rTriangle=record V:array [0..2] of TVector3f; end;
Далее напишем несколько процедур для более удобного обращения с типом Tvector3f. Вот прототипы наших процедур и функций:
procedure Normalize3f (var a:TVector3f); procedure Swap3f ( var a,b:TVector3f); procedure SummVectors3f( var a,b:TVector3f); procedure Multiple3f( var v:TVector3f;cof:glfloat); function CreateVector3f ( x,y,z : glfloat) :TVector3f; function CrossProduct3f ( a,b:TVector3f) :TVector3f; function CalculateNormal3F( P1,P2:TVector3f) :TVector3f; function DotProduct3f( v1,v2:TVector3f) :glfloat; function CalculateDistance3f( P1,P2:TVector3f) :glfloat;
А вот их реализация
procedure Normalize3f(var a:TVector3f); var d:glfloat; begin d:=sqrt( sqr( a.x)+sqr( a.y)+sqr( a.z)); if d = 0 then d :=1; a.x:=a.x/d; a.y:=a.y/d; a.z:=a.z/d; end; procedure Swap3f( var a,b:TVector3f); var temp:TVector3f; begin temp:=a; a:=b; b:=temp; end; procedure SummVectors3f( var a,b:TVector3f); var temp:TVector3f; begin temp.x:=a.x+b.x; temp.y:=a.y+b.y; temp.z:=a.z+b.z; a:=temp; end; procedure Multiple3f( var v:TVector3f;cof:glfloat);overload; begin v.x:=v.x*cof; v.y:=v.y*cof; v.z:=v.z*cof; end; function CreateVector3f( x,y,z:glfloat):TVector3f; begin result.x:=x; result.y:=y; result.z:=z; end; function CrossProduct3f( a,b:TVector3f):TVector3f;overload; begin result.x:= a.y * b.z - a.z * b.y; result.y:= a.z * b.x - a.x * b.z; result.z:= a.x * b.y - a.y * b.x; end; function CalculateNormal3f( P1,P2:TVector3f):TVector3f; var p3:TVector3f; v1,v2,v3,temp:TVector3f; begin p3.x:=p2.x-p1.x; p3.y:=p2.y-p1.y; p3.z:=p2.z-p1.z; v1:=CreateVector3f( p3.x-p1.x,p3.y-p1.y,p3.z-p1.z); v2:=CreateVector3f( p3.x-p2.x,p3.y-p2.y,p3.z-p2.z); temp:=CrossProduct3f( v1,v2); Normalize3f( temp); result:=temp; end; function DotProduct3f( v1,v2:TVector3f):glfloat; begin result :=V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z; end; function CalculateDistance3f( P1,P2:TVector3f):real ;overload; var t:TVector3f; begin t:=CalcDifference3f( P1,P2); result:=sqrt( sqr( t.x)+sqr( t.y)+sqr( t.z)); end;
Напишем простенький класс, который будет считывать из текстового файла координаты вершин модели и затем рисовать ее на экране. Вот основа нашего класса
unit MODEL; interface uses Windows, OpenGL, GEOMETRY, SysUtils; {GEOMETRY - юнит,который мы описали в предыдущем пункте } type CGeomModel=class public TrioAmount :integer; Triangles :array of rTriangle; constructor Init; procedure LoadFromFile(FileName:string); procedure Render; end; function ConvertStrings( s1,s2,s3:string) :TVector3f;
Функция ConvertStrings берет три значения считанных из файла и делает из них вектор положения в пространстве.
Опишем сначала функцию,а затем все методы класса.
var F:text; {файловая переменная,она нам понадобится позже} function ConvertStrings(s1,s2,s3:string) :TVector3f; begin result.x:=StrToFloat( s1); result.y:=StrToFloat( s2); result.z:=StrToFloat( s3); end;
А теперь все методы по порядку. Начнем с конструктора. Он ничем не отличается от конструктора класс TObject и нужен лишь с эстетической точки зрения.
constructor CGeomModel.Init; begin inherited Create; end;
Далее у нас следует метод
procedure CGeomModel.LoadFromFile(FileName:string); var ch:char; i,j:integer; s:array [0..8] of string; st:string; begin AssignFile( F,FileName); Reset( F); While not SeekEof( F) do begin inc( TrioAmount); SetLength( Triangles,TrioAmount*SizeOF( rTriangle)); i:=0; for j:=0 to 8 do s[j]:=''; st:=''; j:=0; while not SeekEoln( F) do begin read( f,st); for i:=1 to Length( st) do begin if st[i]=' ' then inc( j); s[j]:=s[j]+st[i]; end; end; Triangles[TrioAmount].V[0]:=ConvertStrings( s[0],s[1],s[2]); Triangles[TrioAmount].V[1]:=ConvertStrings( s[3],s[4],s[5]); Triangles[TrioAmount].V[2]:=ConvertStrings( s[6],s[7],s[8]); end; end;
В принципе, данный метод считывания не является быстрым. Лучше использовать BlockRead для чтения и BlockWrite для записи. Они побыстрее будут. Но здесь я хотел сделать считывание из текстового файла, так как оно самое простое. В это методе мы сначала считываем ВСЮ строку из файла, затем разбиваем ее на подстроки поиском пробела, преобразуем подстроки в значения при помощи процедуры ConvertStrings, увеличиваем размер динамического массива и записываем в него значения вершин треугольника, переходим на следующую строку. Если мы достигли конца файла, то тогда просто выходим их процедуры
procedure CGeomModel.Render; var i:integer; begin glBegin(gl_Triangles); for i:=1 to TrioAmount do begin glVertex3fv( @triangles[i].V[0]); glVertex3fv( @triangles[i].V[1]); glVertex3fv( @triangles[i].V[2]); end; glEnd; end; end. {Завершающий END в данном юните}
Пожалуй пора рассказать о том, как мы будем реализовывать наш взрыв. А будем мы его реализовывать при помощи системы частиц. Все просто ! Когда взрыв не активен мы рисуем нашу модель, как только стал активен взрыв, мы начинаем рисовать частицы. Для того, чтобы все это выглядело похожим на изначальную модель мы из массива треугольников модели сделаем массив частиц. В принципе, можно изменять и координаты вершин МОДЕЛИ. Здесь, как кому нравится. Я считаю, что так будет пожалуй проще работать.
Итак, создадим класс для частиц
unit BlowParticles; interface uses OpenGL,Windows,GEOMETRY,MODEL; type rModelParticle=record Trio :rTriangle; {"частичный" треугольник} Direction :TVector3f; {Направление движения} Life :glfloat; {Жизнь частицы.Нужно для затухания} Size :glfloat; {Текущий размер частицы} end; CParticleSystem=class private Active :boolean; {Активен ли движок} Particles :array of rModelParticle; {Массив частиц} procedure Swap(var a,b:rModelParticle); public Gravity :TVector3f; {Вектор гравитации} Amount :integer; {Число частиц} Texture :gluint; {текстура} Color :TVector3f; {Цвет} Fade :glfloat; {Скорость затухания} procedure Add( v1,v2,v3:TVector3f;Direct:TVector3f); procedure Delete( index:integer); procedure Change; procedure DrawSprite( index:integer); procedure Render; end; procedure glBindTexture( target: GLenum; texture: GLuint); stdcall; external opengl32;
Начинаем описывать методы. Метод Swap. Метод нужен для замены одной частицы другой. Он нужен будет нам при удалении частиц.
procedure CParticleSystem.Swap(var a,b:rModelParticle); var temp:rModelParticle; begin temp:=a; a:=b; b:=temp; end;
Частицы будем добавлять по трем вершинам и направлению. Так как у нас пока нет ни направления, ни вершин (ни будут задаваться в "взрывном" движке), то мы просто представим, что они у нас есть и запишем метод.
procedure CParticleSystem.Add(v1,v2,v3:TVector3f;Direct:TVector3f); begin Inc( Amount); SetLength( Particles,Amount*SizeOf( rModelParticle)); Particles[Amount].Trio.v[0]:=v1; Particles[Amount].Trio.v[1]:=v2; Particles[Amount].Trio.v[2]:=v3; Particles[Amount].Direction:=Direct; Particles[Amount].Life:=2; Particles[Amount].Size:=Size; end;
#Delphi, #эффекты, #particle system
18 октября 2003 (Обновление: 22 июня 2009)
Комментарии [2]