1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/3d/ps_face.h"
20 #include "nel/3d/ps_macro.h"
21 #include "nel/3d/driver.h"
22 #include "nel/3d/ps_iterator.h"
23 #include "nel/3d/particle_system.h"
24 #include "nel/3d/debug_vb.h"
26 #include "nel/misc/quat.h"
37 ////////////////////////////
38 // CPSFace implementation //
39 ////////////////////////////
41 /** Well, we could have put a method template in CPSFace, but some compilers
42 * want the definition of the methods in the header, and some compilers
43 * don't want friend with function template, so we use a static method template of a friend class instead,
44 * which gives us the same result :)
49 template <class T
, class U
>
50 static void drawFaces(T posIt
, U indexIt
, CPSFace
&f
, uint size
, uint32 srcStep
)
52 NL_PS_FUNC(CPSFaceHelper_drawFaces
)
55 IDriver
*driver
= f
.getDriver();
57 CVertexBuffer
&vb
= f
.getNeededVB(*driver
);
58 f
.updateMatBeforeRendering(driver
, vb
);
62 // number of left faces to draw, number of faces to process at once
63 uint32 leftFaces
= size
, toProcess
;
64 f
._Owner
->incrementNbDrawnParticles(size
); // for benchmark purpose
65 f
.setupDriverModelMatrix();
66 float sizeBuf
[CPSQuad::quadBufSize
];
70 // if constant size is used, the pointer points always the same float
71 uint32 ptSizeIncrement
= f
._SizeScheme
? 1 : 0;
75 f
._ColorScheme
->setColorType(driver
->getVertexColorFormat());
78 if (f
._PrecompBasis
.size()) // do we use precomputed basis ?
83 toProcess
= leftFaces
> (uint32
) CPSQuad::quadBufSize
? (uint32
) CPSQuad::quadBufSize
: leftFaces
;
84 vb
.setNumVertices(4 * toProcess
);
85 CVertexBufferReadWrite vba
;
87 currVertex
= (uint8
*) vba
.getVertexCoordPointer() ;
90 ptSize
= (float *) (f
._SizeScheme
->make(f
._Owner
, size
- leftFaces
, sizeBuf
, sizeof(float), toProcess
, true, srcStep
));
94 ptSize
= &f
._ParticleSize
;
96 f
.updateVbColNUVForRender(vb
, size
- leftFaces
, toProcess
, srcStep
, *driver
);
97 const uint32 stride
= vb
.getVertexSize();
98 endPosIt
= posIt
+ toProcess
;
101 const CPlaneBasis
&currBasis
= f
._PrecompBasis
[*indexIt
].Basis
;
102 CHECK_VERTEX_BUFFER(vb
, currVertex
);
103 ((CVector
*) currVertex
)->x
= (*posIt
).x
+ *ptSize
* currBasis
.X
.x
;
104 ((CVector
*) currVertex
)->y
= (*posIt
).y
+ *ptSize
* currBasis
.X
.y
;
105 ((CVector
*) currVertex
)->z
= (*posIt
).z
+ *ptSize
* currBasis
.X
.z
;
106 currVertex
+= stride
;
108 CHECK_VERTEX_BUFFER(vb
, currVertex
);
109 ((CVector
*) currVertex
)->x
= (*posIt
).x
+ *ptSize
* currBasis
.Y
.x
;
110 ((CVector
*) currVertex
)->y
= (*posIt
).y
+ *ptSize
* currBasis
.Y
.y
;
111 ((CVector
*) currVertex
)->z
= (*posIt
).z
+ *ptSize
* currBasis
.Y
.z
;
112 currVertex
+= stride
;
114 CHECK_VERTEX_BUFFER(vb
, currVertex
);
115 ((CVector
*) currVertex
)->x
= (*posIt
).x
- *ptSize
* currBasis
.X
.x
;
116 ((CVector
*) currVertex
)->y
= (*posIt
).y
- *ptSize
* currBasis
.X
.y
;
117 ((CVector
*) currVertex
)->z
= (*posIt
).z
- *ptSize
* currBasis
.X
.z
;
118 currVertex
+= stride
;
120 CHECK_VERTEX_BUFFER(vb
, currVertex
);
121 ((CVector
*) currVertex
)->x
= (*posIt
).x
- *ptSize
* currBasis
.Y
.x
;
122 ((CVector
*) currVertex
)->y
= (*posIt
).y
- *ptSize
* currBasis
.Y
.y
;
123 ((CVector
*) currVertex
)->z
= (*posIt
).z
- *ptSize
* currBasis
.Y
.z
;
124 currVertex
+= stride
;
125 ptSize
+= ptSizeIncrement
;
129 while (posIt
!= endPosIt
);
131 driver
->activeVertexBuffer(vb
),
132 driver
->renderRawQuads(f
._Mat
, 0, toProcess
);
133 leftFaces
-= toProcess
;
139 // must compute each particle basis at each time
140 static CPlaneBasis planeBasis
[CPSQuad::quadBufSize
]; // buffer to compute each particle basis
141 CPlaneBasis
*currBasis
;
142 uint32 ptPlaneBasisIncrement
= f
._PlaneBasisScheme
? 1 : 0;
143 const uint32 vSize
= vb
.getVertexSize();
147 toProcess
= leftFaces
> (uint32
) CPSQuad::quadBufSize
? (uint32
) CPSQuad::quadBufSize
: leftFaces
;
148 vb
.setNumVertices(4 * toProcess
);
149 CVertexBufferReadWrite vba
;
151 currVertex
= (uint8
*) vba
.getVertexCoordPointer() ;
154 ptSize
= (float *) (f
._SizeScheme
->make(f
._Owner
, size
- leftFaces
, sizeBuf
, sizeof(float), toProcess
, true, srcStep
));
158 ptSize
= &f
._ParticleSize
;
161 if (f
._PlaneBasisScheme
)
163 currBasis
= (CPlaneBasis
*) (f
._PlaneBasisScheme
->make(f
._Owner
, size
- leftFaces
, planeBasis
, sizeof(CPlaneBasis
), toProcess
, true, srcStep
));
167 currBasis
= &f
._PlaneBasis
;
169 f
.updateVbColNUVForRender(vb
, size
- leftFaces
, toProcess
, srcStep
, *driver
);
170 endPosIt
= posIt
+ toProcess
;
173 // we use this instead of the + operator, because we avoid 4 constructor calls this way
174 CHECK_VERTEX_BUFFER(vb
, currVertex
);
175 ((CVector
*) currVertex
)->x
= (*posIt
).x
+ *ptSize
* currBasis
->X
.x
;
176 ((CVector
*) currVertex
)->y
= (*posIt
).y
+ *ptSize
* currBasis
->X
.y
;
177 ((CVector
*) currVertex
)->z
= (*posIt
).z
+ *ptSize
* currBasis
->X
.z
;
180 CHECK_VERTEX_BUFFER(vb
, currVertex
);
181 ((CVector
*) currVertex
)->x
= (*posIt
).x
+ *ptSize
* currBasis
->Y
.x
;
182 ((CVector
*) currVertex
)->y
= (*posIt
).y
+ *ptSize
* currBasis
->Y
.y
;
183 ((CVector
*) currVertex
)->z
= (*posIt
).z
+ *ptSize
* currBasis
->Y
.z
;
186 CHECK_VERTEX_BUFFER(vb
, currVertex
);
187 ((CVector
*) currVertex
)->x
= (*posIt
).x
- *ptSize
* currBasis
->X
.x
;
188 ((CVector
*) currVertex
)->y
= (*posIt
).y
- *ptSize
* currBasis
->X
.y
;
189 ((CVector
*) currVertex
)->z
= (*posIt
).z
- *ptSize
* currBasis
->X
.z
;
192 CHECK_VERTEX_BUFFER(vb
, currVertex
);
193 ((CVector
*) currVertex
)->x
= (*posIt
).x
- *ptSize
* currBasis
->Y
.x
;
194 ((CVector
*) currVertex
)->y
= (*posIt
).y
- *ptSize
* currBasis
->Y
.y
;
195 ((CVector
*) currVertex
)->z
= (*posIt
).z
- *ptSize
* currBasis
->Y
.z
;
197 ptSize
+= ptSizeIncrement
;
199 currBasis
+= ptPlaneBasisIncrement
;
201 while (posIt
!= endPosIt
);
203 driver
->activeVertexBuffer(vb
);
204 driver
->renderRawQuads(f
._Mat
, 0, toProcess
);
205 leftFaces
-= toProcess
;
213 ///======================================================================================
214 CPSFace::CPSFace(CSmartPtr
<ITexture
> tex
) : CPSQuad(tex
)
216 NL_PS_FUNC(CPSFace_CPSFace
)
217 if (CParticleSystem::getSerializeIdentifierFlag()) _Name
= std::string("Face");
220 ///======================================================================================
221 void CPSFace::step(TPSProcessPass pass
)
223 // if (!FilterPS[1]) return;
224 NL_PS_FUNC(CPSFace_step
)
225 if (pass
== PSToolRender
) // edition mode only
230 else if (pass
== PSMotion
)
233 if (!_PrecompBasis
.empty()) // do we use precomputed basis ?
235 // rotate all precomputed basis
236 for (CPSVector
< CPlaneBasisPair
>::V::iterator it
= _PrecompBasis
.begin(); it
!= _PrecompBasis
.end(); ++it
)
238 // not optimized at all, but this will apply to very few elements anyway...
240 mat
.rotate(CQuat(it
->Axis
, CParticleSystem::EllapsedTime
* it
->AngularVelocity
));
241 CVector n
= mat
* it
->Basis
.getNormal();
242 it
->Basis
= CPlaneBasis(n
);
247 else // check this is the right pass
249 ( (pass
== PSBlendRender
&& hasTransparentFaces())
250 || (pass
== PSSolidRender
&& hasOpaqueFaces())
259 if (!_Owner
->getSize()) return;
262 computeSrcStep(step
, numToProcess
);
263 if (!numToProcess
) return;
266 if (step
== (1 << 16))
268 /// build index iterator
269 CPSVector
<uint32
>::V::const_iterator indexIt
= _IndexInPrecompBasis
.begin();
272 CPSFaceHelper::drawFaces(_Owner
->getPos().begin(),
281 /// build index iterator
282 CAdvance1616Iterator
<CPSVector
<uint32
>::V::const_iterator
, const uint32
>
283 indexIt(_IndexInPrecompBasis
.begin(), 0, step
);
284 CPSFaceHelper::drawFaces(TIteratorVectStep1616(_Owner
->getPos().begin(), 0, step
),
295 ///======================================================================================
296 void CPSFace::serial(NLMISC::IStream
&f
)
298 NL_PS_FUNC(CPSFace_IStream
)
301 CPSRotated3DPlaneParticle::serialPlaneBasisScheme(f
);
305 uint32 nbConfigurations
;
306 f
.serial(nbConfigurations
);
307 if (nbConfigurations
)
309 f
.serial(_MinAngularVelocity
, _MaxAngularVelocity
);
311 hintRotateTheSame(nbConfigurations
, _MinAngularVelocity
, _MaxAngularVelocity
);
317 uint32 nbConfigurations
= (uint32
)_PrecompBasis
.size();
318 f
.serial(nbConfigurations
);
319 if (nbConfigurations
)
321 f
.serial(_MinAngularVelocity
, _MaxAngularVelocity
);
327 ///======================================================================================
328 /// this produce a random unit vector
329 static CVector
MakeRandomUnitVect(void)
331 NL_PS_FUNC(MakeRandomUnitVect
)
332 CVector
v((float) ((rand() % 20000) - 10000)
333 ,(float) ((rand() % 20000) - 10000)
334 ,(float) ((rand() % 20000) - 10000)
340 ///======================================================================================
341 void CPSFace::hintRotateTheSame(uint32 nbConfiguration
342 , float minAngularVelocity
343 , float maxAngularVelocity
346 NL_PS_FUNC(CPSFace_hintRotateTheSame
)
347 _MinAngularVelocity
= minAngularVelocity
;
348 _MaxAngularVelocity
= maxAngularVelocity
;
349 _PrecompBasis
.resize(nbConfiguration
);
352 // each precomp basis is created randomly;
353 for (uint k
= 0; k
< nbConfiguration
; ++k
)
355 CVector v
= MakeRandomUnitVect();
356 _PrecompBasis
[k
].Basis
= CPlaneBasis(v
);
357 _PrecompBasis
[k
].Axis
= MakeRandomUnitVect();
358 _PrecompBasis
[k
].AngularVelocity
= minAngularVelocity
359 + (rand() % 20000) / 20000.f
* (maxAngularVelocity
- minAngularVelocity
);
362 // we need to do this because nbConfs may have changed
363 fillIndexesInPrecompBasis();
367 ///======================================================================================
368 void CPSFace::fillIndexesInPrecompBasis(void)
370 NL_PS_FUNC(CPSFace_fillIndexesInPrecompBasis
)
371 const uint32 nbConf
= (uint32
)_PrecompBasis
.size();
374 _IndexInPrecompBasis
.resize( _Owner
->getMaxSize() );
376 for (CPSVector
<uint32
>::V::iterator it
= _IndexInPrecompBasis
.begin(); it
!= _IndexInPrecompBasis
.end(); ++it
)
378 *it
= rand() % nbConf
;
382 ///======================================================================================
383 void CPSFace::newElement(const CPSEmitterInfo
&info
)
385 NL_PS_FUNC(CPSFace_newElement
)
386 CPSQuad::newElement(info
);
387 newPlaneBasisElement(info
);
388 const uint32 nbConf
= (uint32
)_PrecompBasis
.size();
389 if (nbConf
) // do we use precomputed basis ?
391 _IndexInPrecompBasis
[_Owner
->getNewElementIndex()] = rand() % nbConf
;
395 ///======================================================================================
396 void CPSFace::deleteElement(uint32 index
)
398 NL_PS_FUNC(CPSFace_deleteElement
)
399 CPSQuad::deleteElement(index
);
400 deletePlaneBasisElement(index
);
401 if (!_PrecompBasis
.empty()) // do we use precomputed basis ?
403 // replace ourself by the last element...
404 _IndexInPrecompBasis
[index
] = _IndexInPrecompBasis
[_Owner
->getSize() - 1];
408 ///======================================================================================
409 void CPSFace::resize(uint32 size
)
411 NL_PS_FUNC(CPSFace_resize
)
412 nlassert(size
< (1 << 16));
413 resizePlaneBasis(size
);
414 if (!_PrecompBasis
.empty()) // do we use precomputed basis ?
416 _IndexInPrecompBasis
.resize(size
);
418 CPSQuad::resize(size
);