Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / ps_face.cpp
blob43bd921ab2c6c6e7672e3c773f5032acc00eb8c8
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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/>.
17 #include "std3d.h"
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"
28 #ifdef DEBUG_NEW
29 #define new DEBUG_NEW
30 #endif
32 namespace NL3D
35 using NLMISC::CQuat;
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 :)
46 class CPSFaceHelper
48 public:
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)
53 PARTICLES_CHECK_MEM;
54 nlassert(f._Owner);
55 IDriver *driver = f.getDriver();
57 CVertexBuffer &vb = f.getNeededVB(*driver);
58 f.updateMatBeforeRendering(driver, vb);
60 uint8 *currVertex;
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];
67 float *ptSize;
68 T endPosIt;
70 // if constant size is used, the pointer points always the same float
71 uint32 ptSizeIncrement = f._SizeScheme ? 1 : 0;
73 if (f._ColorScheme)
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;
86 vb.lock (vba);
87 currVertex = (uint8 *) vba.getVertexCoordPointer() ;
88 if (f._SizeScheme)
90 ptSize = (float *) (f._SizeScheme->make(f._Owner, size - leftFaces, sizeBuf, sizeof(float), toProcess, true, srcStep));
92 else
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;
126 ++indexIt;
127 ++posIt;
129 while (posIt != endPosIt);
131 driver->activeVertexBuffer(vb),
132 driver->renderRawQuads(f._Mat, 0, toProcess);
133 leftFaces -= toProcess;
135 while (leftFaces);
137 else
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;
150 vb.lock (vba);
151 currVertex = (uint8 *) vba.getVertexCoordPointer() ;
152 if (f._SizeScheme)
154 ptSize = (float *) (f._SizeScheme->make(f._Owner, size - leftFaces, sizeBuf, sizeof(float), toProcess, true, srcStep));
156 else
158 ptSize = &f._ParticleSize;
161 if (f._PlaneBasisScheme)
163 currBasis = (CPlaneBasis *) (f._PlaneBasisScheme->make(f._Owner, size - leftFaces, planeBasis, sizeof(CPlaneBasis), toProcess, true, srcStep));
165 else
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;
178 currVertex += vSize;
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;
184 currVertex += vSize;
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;
190 currVertex += vSize;
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;
196 currVertex += vSize;
197 ptSize += ptSizeIncrement;
198 ++posIt;
199 currBasis += ptPlaneBasisIncrement;
201 while (posIt != endPosIt);
203 driver->activeVertexBuffer(vb);
204 driver->renderRawQuads(f._Mat, 0, toProcess);
205 leftFaces -= toProcess;
207 while (leftFaces);
209 PARTICLES_CHECK_MEM;
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
227 showTool();
228 return;
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...
239 CMatrix mat;
240 mat.rotate(CQuat(it->Axis, CParticleSystem::EllapsedTime * it->AngularVelocity));
241 CVector n = mat * it->Basis.getNormal();
242 it->Basis = CPlaneBasis(n);
245 return;
247 else // check this is the right pass
248 if (!
249 ( (pass == PSBlendRender && hasTransparentFaces())
250 || (pass == PSSolidRender && hasOpaqueFaces())
254 return;
259 if (!_Owner->getSize()) return;
260 uint32 step;
261 uint numToProcess;
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();
271 /// draw the faces
272 CPSFaceHelper::drawFaces(_Owner->getPos().begin(),
273 indexIt,
274 *this,
275 numToProcess,
276 step
279 else
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),
285 indexIt,
286 *this,
287 numToProcess,
288 step
295 ///======================================================================================
296 void CPSFace::serial(NLMISC::IStream &f)
298 NL_PS_FUNC(CPSFace_IStream )
299 f.serialVersion(1);
300 CPSQuad::serial(f);
301 CPSRotated3DPlaneParticle::serialPlaneBasisScheme(f);
303 if (f.isReading())
305 uint32 nbConfigurations;
306 f.serial(nbConfigurations);
307 if (nbConfigurations)
309 f.serial(_MinAngularVelocity, _MaxAngularVelocity);
311 hintRotateTheSame(nbConfigurations, _MinAngularVelocity, _MaxAngularVelocity);
313 init();
315 else
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)
336 v.normalize();
337 return v;
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);
350 if (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();
372 if (_Owner)
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);
421 } // NL3D