// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/3d/ps_face_look_at.h"
#include "nel/3d/ps_macro.h"
#include "nel/3d/driver.h"
#include "nel/3d/ps_iterator.h"
#include "nel/3d/particle_system.h"
#include "nel/misc/fast_floor.h"
namespace NL3D
{
/** vector giving the orientation of look at
*/
struct CLookAtAlign
{
CVector I;
CVector K;
};
uint64 PSLookAtRenderTime = 0;
//////////////////////////////////
// CPSFaceLookAt implementation //
//////////////////////////////////
/** Well, we could have put a method template in CPSFaceLookAt, but some compilers
* want the definition of the methods in the header, and some compilers
* don't want friend with function template, so we use a static method template of a friend class instead,
* which gives us the same result :)
*/
class CPSFaceLookAtHelper
{
public:
/** compute orientation vectors depending on speed
*/
template
static void computeOrientationVectors(T speedIt, const CVector &I, const CVector &K, CLookAtAlign *dest, uint size)
{
NL_PS_FUNC(CPSFaceLookAtHelper_computeOrientationVectors)
nlassert(size > 0);
const CLookAtAlign *endDest = dest + size;
do
{
// tmp unoptimized slow version
CVector normedSpeed = (*speedIt).normed();
float iProj = normedSpeed * I;
float kProj = normedSpeed * K;
dest->I = iProj * I + kProj * K;
dest->K = (- kProj * I + iProj * K).normed();
++ speedIt;
++ dest;
}
while(dest != endDest);
}
/** Draw look at and align them on motion
*/
template
static void drawLookAtAlignOnMotion(T it, T speedIt, CPSFaceLookAt &la, uint size, uint32 srcStep)
{
PARTICLES_CHECK_MEM;
nlassert(la._Owner);
IDriver *driver = la.getDriver();
if (la._ColorScheme)
{
la._ColorScheme->setColorType(driver->getVertexColorFormat());
}
CVertexBuffer &vb = la.getNeededVB(*driver);
la.updateMatBeforeRendering(driver, vb);
la._Owner->incrementNbDrawnParticles(size); // for benchmark purpose
la.setupDriverModelMatrix();
//driver->activeVertexBuffer(vb);
const CVector I = la.computeI();
const CVector K = la.computeK();
const float *rotTable = CPSRotated2DParticle::getRotTable();
// for each the particle can be constantly rotated or have an independant rotation for each particle
// number of face left, and number of face to process at once
uint32 leftToDo = size, toProcess;
float pSizes[CPSQuad::quadBufSize]; // the sizes to use
float pSecondSizes[CPSQuad::quadBufSize]; // the second sizes to use
uint8 laAlignRaw[sizeof(CLookAtAlign) * CPSQuad::quadBufSize]; // orientation computed from motion for each particle
CLookAtAlign *laAlign = (CLookAtAlign *) laAlignRaw; // cast to avoid unilined ctor calls
float *currentSize;
uint32 currentSizeStep = la._SizeScheme ? 1 : 0;
// point the vector part in the current vertex
uint8 *ptPos;
// strides to go from one vertex to another one
const uint32 stride = vb.getVertexSize();
if (!la._Angle2DScheme)
{
// constant rotation case
do
{
toProcess = leftToDo <= (uint32) CPSQuad::quadBufSize ? leftToDo : (uint32) CPSQuad::quadBufSize;
vb.setNumVertices(4 * toProcess);
// restart at the beginning of the vertex buffer
CVertexBufferReadWrite vba;
vb.lock (vba);
ptPos = (uint8 *) vba.getVertexCoordPointer();
if (la._SizeScheme)
{
currentSize = (float *) la._SizeScheme->make(la._Owner, size- leftToDo, pSizes, sizeof(float), toProcess, true, srcStep);
}
else
{
currentSize = &la._ParticleSize;
}
computeOrientationVectors(speedIt, I, K, laAlign, toProcess);
speedIt = speedIt + toProcess;
const CLookAtAlign *currAlign = laAlign;
la.updateVbColNUVForRender(vb, size - leftToDo, toProcess, srcStep, *driver);
T endIt = it + toProcess;
if (!la._IndependantSizes)
{
const uint32 tabIndex = (((uint32) la._Angle2D) & 0xff) << 2;
CVector v1;
CVector v2;
// TODO : optimize if necessary
while (it != endIt)
{
v1 = rotTable[tabIndex] * currAlign->I + rotTable[tabIndex + 1] * currAlign->K;
v2 = rotTable[tabIndex + 2] * currAlign->I + rotTable[tabIndex + 3] * currAlign->K;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v2.z;
ptPos += stride;
++it;
++currAlign;
currentSize += currentSizeStep;
}
}
else // independant sizes
{
float *currentSize2;
float secondSize;
uint32 currentSizeStep2;
if (la._SecondSize.getSizeScheme())
{
currentSize2 = (float *) la._SecondSize.getSizeScheme()->make(la._Owner, size- leftToDo, pSecondSizes, sizeof(float), toProcess, true, srcStep);
currentSizeStep2 = 1;
}
else
{
secondSize = la._SecondSize.getSize();
currentSize2 = &secondSize;
currentSizeStep2 = 0;
}
CVector v1;
CVector v2;
// TODO : optimize if necessary
while (it != endIt)
{
v1 = CPSUtil::getCos((sint32) la._Angle2D) * currAlign->I + CPSUtil::getSin((sint32) la._Angle2D) * currAlign->K;
v2 = - CPSUtil::getSin((sint32) la._Angle2D) * currAlign->I + CPSUtil::getCos((sint32) la._Angle2D) * currAlign->K;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
++it;
++currAlign;
currentSize += currentSizeStep;
currentSize2 += currentSizeStep2;
}
}
// uint64 startTick = NLMISC::CTime::getPerformanceTime();
vba.unlock();
driver->activeVertexBuffer(vb);
driver->renderRawQuads(la._Mat, 0, toProcess);
// PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;
leftToDo -= toProcess;
}
while (leftToDo);
}
else
{
float pAngles[CPSQuad::quadBufSize]; // the angles to use
float *currentAngle;
do
{
toProcess = leftToDo <= (uint32) CPSQuad::quadBufSize ? leftToDo : (uint32) CPSQuad::quadBufSize;
vb.setNumVertices(4 * toProcess);
// restart at the beginning of the vertex buffer
CVertexBufferReadWrite vba;
vb.lock (vba);
ptPos = (uint8 *) vba.getVertexCoordPointer();
if (la._SizeScheme)
{
currentSize = (float *) la._SizeScheme->make(la._Owner, size - leftToDo, pSizes, sizeof(float), toProcess, true, srcStep);
}
else
{
currentSize = &la._ParticleSize;
}
computeOrientationVectors(speedIt, I, K, laAlign, toProcess);
speedIt = speedIt + toProcess;
const CLookAtAlign *currAlign = laAlign;
currentAngle = (float *) la._Angle2DScheme->make(la._Owner, size - leftToDo, pAngles, sizeof(float), toProcess, true, srcStep);
la.updateVbColNUVForRender(vb, size - leftToDo, toProcess, srcStep, *driver);
T endIt = it + toProcess;
CVector v1, v2;
NLMISC::OptFastFloorBegin();
if (!la._IndependantSizes)
{
while (it != endIt)
{
const uint32 tabIndex = ((NLMISC::OptFastFloor(*currentAngle)) & 0xff) << 2;
// lets avoid some ctor calls
v1.x = *currentSize * (rotTable[tabIndex] * currAlign->I.x + rotTable[tabIndex + 1] * currAlign->K.x);
v1.y = *currentSize * (rotTable[tabIndex] * currAlign->I.y + rotTable[tabIndex + 1] * currAlign->K.y);
v1.z = *currentSize * (rotTable[tabIndex] * currAlign->I.z + rotTable[tabIndex + 1] * currAlign->K.z);
v2.x = *currentSize * (rotTable[tabIndex + 2] * currAlign->I.x + rotTable[tabIndex + 3] * currAlign->K.x);
v2.y = *currentSize * (rotTable[tabIndex + 2] * currAlign->I.y + rotTable[tabIndex + 3] * currAlign->K.y);
v2.z = *currentSize * (rotTable[tabIndex + 2] * currAlign->I.z + rotTable[tabIndex + 3] * currAlign->K.z);
CHECK_VERTEX_BUFFER(vb, ptPos);
CHECK_VERTEX_BUFFER(vb, ptPos + stride);
CHECK_VERTEX_BUFFER(vb, ptPos + stride2);
CHECK_VERTEX_BUFFER(vb, ptPos + stride3);
((CVector *) ptPos)->x = (*it).x + v1.x;
((CVector *) ptPos)->y = (*it).y + v1.y;
((CVector *) ptPos)->z = (*it).z + v1.z;
ptPos += stride;
((CVector *) ptPos)->x = (*it).x + v2.x;
((CVector *) ptPos)->y = (*it).y + v2.y;
((CVector *) ptPos)->z = (*it).z + v2.z;
ptPos += stride;
((CVector *) ptPos)->x = (*it).x - v1.x;
((CVector *) ptPos)->y = (*it).y - v1.y;
((CVector *) ptPos)->z = (*it).z - v1.z;
ptPos += stride;
((CVector *) ptPos)->x = (*it).x - v2.x;
((CVector *) ptPos)->y = (*it).y - v2.y;
((CVector *) ptPos)->z = (*it).z - v2.z;
ptPos += stride;
++it;
++ currAlign;
currentSize += currentSizeStep;
++currentAngle;
}
}
else // independant size, and non-constant rotation
{
float *currentSize2;
float secondSize;
uint32 currentSizeStep2;
if (la._SecondSize.getSizeScheme())
{
currentSize2 = (float *) la._SecondSize.getSizeScheme()->make(la._Owner, size- leftToDo, pSecondSizes, sizeof(float), toProcess, true, srcStep);
currentSizeStep2 = 1;
}
else
{
secondSize = la._SecondSize.getSize();
currentSize2 = &secondSize;
currentSizeStep2 = 0;
}
float cosAngle, sinAngle;
while (it != endIt)
{
cosAngle = CPSUtil::getCos((sint32) *currentAngle);
sinAngle = CPSUtil::getSin((sint32) *currentAngle);
v1 = cosAngle * currAlign->I + sinAngle * currAlign->K;
v2 = - sinAngle * currAlign->I + cosAngle * currAlign->K;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
++it;
++currentAngle;
++ currAlign;
currentSize += currentSizeStep;
currentSize2 += currentSizeStep2;
}
}
NLMISC::OptFastFloorEnd();
//tmp
// uint64 startTick = NLMISC::CTime::getPerformanceTime();
vba.unlock();
driver->activeVertexBuffer(vb);
driver->renderRawQuads(la._Mat, 0, toProcess);
// PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;
leftToDo -= toProcess;
}
while (leftToDo);
}
PARTICLES_CHECK_MEM;
}
/** render look at, but dont align on motion
*/
template
static void drawLookAt(T it, T speedIt, CPSFaceLookAt &la, uint size, uint32 srcStep)
{
//uint64 startTick = NLMISC::CTime::getPerformanceTime();
PARTICLES_CHECK_MEM;
nlassert(la._Owner);
IDriver *driver = la.getDriver();
if (la._ColorScheme)
{
la._ColorScheme->setColorType(driver->getVertexColorFormat());
}
CVertexBuffer &vb = la.getNeededVB(*driver);
la.updateMatBeforeRendering(driver, vb);
la._Owner->incrementNbDrawnParticles(size); // for benchmark purpose
la.setupDriverModelMatrix();
//driver->activeVertexBuffer(vb);
CVector I;
CVector J;
CVector K;
if (!la._AlignOnZAxis)
{
I = la.computeI();
J = la.computeJ();
K = la.computeK();
}
else
{
I = la.computeIWithZAxisAligned();
K = la.computeKWithZAxisAligned();
J = K ^ I;
}
const float *rotTable = CPSRotated2DParticle::getRotTable();
// for each the particle can be constantly rotated or have an independant rotation for each particle
// number of face left, and number of face to process at once
uint32 leftToDo = size, toProcess;
float pSizes[CPSQuad::quadBufSize]; // the sizes to use
float pSecondSizes[CPSQuad::quadBufSize]; // the second sizes to use
float *currentSize;
uint32 currentSizeStep = la._SizeScheme ? 1 : 0;
// point the vector part in the current vertex
uint8 *ptPos;
// strides to go from one vertex to another one
const uint32 stride = vb.getVertexSize(), stride2 = stride << 1, stride3 = stride + stride2, stride4 = stride << 2;
//PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;
if (!la._Angle2DScheme)
{
// constant rotation case
do
{
toProcess = leftToDo <= (uint32) CPSQuad::quadBufSize ? leftToDo : (uint32) CPSQuad::quadBufSize;
vb.setNumVertices(4 * toProcess);
// restart at the beginning of the vertex buffer
CVertexBufferReadWrite vba;
vb.lock (vba);
ptPos = (uint8 *) vba.getVertexCoordPointer();
if (la._SizeScheme)
{
currentSize = (float *) la._SizeScheme->make(la._Owner, size- leftToDo, pSizes, sizeof(float), toProcess, true, srcStep);
}
else
{
currentSize = &la._ParticleSize;
}
la.updateVbColNUVForRender(vb, size - leftToDo, toProcess, srcStep, *driver);
T endIt = it + toProcess;
if (la._MotionBlurCoeff == 0.f)
{
if (!la._IndependantSizes)
{
const uint32 tabIndex = (((uint32) la._Angle2D) & 0xff) << 2;
const CVector v1 = rotTable[tabIndex] * I + rotTable[tabIndex + 1] * K;
const CVector v2 = rotTable[tabIndex + 2] * I + rotTable[tabIndex + 3] * K;
if (currentSizeStep)
{
while (it != endIt)
{
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v2.z;
ptPos += stride;
++it;
currentSize += currentSizeStep;
}
}
else
{
// constant size
const CVector myV1 = *currentSize * v1;
const CVector myV2 = *currentSize * v2;
while (it != endIt)
{
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + myV1.x;
((CVector *) ptPos)->y = (*it).y + myV1.y;
((CVector *) ptPos)->z = (*it).z + myV1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + myV2.x;
((CVector *) ptPos)->y = (*it).y + myV2.y;
((CVector *) ptPos)->z = (*it).z + myV2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - myV1.x;
((CVector *) ptPos)->y = (*it).y - myV1.y;
((CVector *) ptPos)->z = (*it).z - myV1.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - myV2.x;
((CVector *) ptPos)->y = (*it).y - myV2.y;
((CVector *) ptPos)->z = (*it).z - myV2.z;
ptPos += stride;
++it;
}
}
}
else // independant sizes
{
const CVector v1 = CPSUtil::getCos((sint32) la._Angle2D) * I + CPSUtil::getSin((sint32) la._Angle2D) * K;
const CVector v2 = - CPSUtil::getSin((sint32) la._Angle2D) * I + CPSUtil::getCos((sint32) la._Angle2D) * K;
float *currentSize2;
float secondSize;
uint32 currentSizeStep2;
if (la._SecondSize.getSizeScheme())
{
currentSize2 = (float *) la._SecondSize.getSizeScheme()->make(la._Owner, size- leftToDo, pSecondSizes, sizeof(float), toProcess, true, srcStep);
currentSizeStep2 = 1;
}
else
{
secondSize = la._SecondSize.getSize();
currentSize2 = &secondSize;
currentSizeStep2 = 0;
}
while (it != endIt)
{
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
++it;
currentSize += currentSizeStep;
currentSize2 += currentSizeStep2;
}
}
//tmp
//uint64 startTick = NLMISC::CTime::getPerformanceTime();
vba.unlock();
driver->activeVertexBuffer(vb);
driver->renderRawQuads(la._Mat, 0, toProcess);
//PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;
}
else
{
// perform motion, blur, we need an iterator on speed
// independant sizes and rotation not supported for now with motion blur
const CVector v1 = I + K;
const CVector v2 = K - I;
CVector startV, endV, mbv1, mbv1n, mbv12, mbv2;
// norme of the v1 vect
float n;
const float epsilon = 10E-5f;
const float normEpsilon = 10E-6f;
CMatrix tMat = la.getViewMat() * la._Owner->getLocalToWorldMatrix();
while (it != endIt)
{
// project the speed in the projection plane
// this give us the v1 vect
startV = tMat * *it ;
endV = tMat * (*it + *speedIt);
if (startV.y > epsilon || endV.y > epsilon)
{
if (startV.y < epsilon)
{
if (fabsf(endV.y - startV.y) > normEpsilon)
{
startV = endV + (endV.y - epsilon) / (endV.y - startV.y) * (startV - endV);
}
startV.y = epsilon;
}
else if (endV.y < epsilon)
{
if (fabsf(endV.y - startV.y) > normEpsilon)
{
endV = startV + (startV.y - epsilon) / (startV.y - endV.y) * (endV - startV);
}
endV.y = epsilon;
}
mbv1 = (startV.x / startV.y - endV.x / endV.y) * I
+ (startV.z / startV.y - endV.z / endV.y) * K ;
n = mbv1.norm();
if (n > la._Threshold)
{
mbv1 *= la._Threshold / n;
n = la._Threshold;
}
if (n > normEpsilon)
{
mbv1n = mbv1 / n;
mbv2 = *currentSize * (J ^ mbv1n);
mbv12 = -*currentSize * mbv1n;
mbv1 *= *currentSize * (1 + la._MotionBlurCoeff * n * n) / n;
*(CVector *) ptPos = *it - mbv2;
*(CVector *) (ptPos + stride) = *it + mbv1;
*(CVector *) (ptPos + stride2) = *it + mbv2;
*(CVector *) (ptPos + stride3) = *it + mbv12;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - mbv2.x;
((CVector *) ptPos)->y = (*it).y - mbv2.y;
((CVector *) ptPos)->z = (*it).z - mbv2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride);
((CVector *) (ptPos + stride))->x = (*it).x + mbv1.x;
((CVector *) (ptPos + stride))->y = (*it).y + mbv1.y;
((CVector *) (ptPos + stride))->z = (*it).z + mbv1.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride2);
((CVector *) (ptPos + stride2))->x = (*it).x + mbv2.x;
((CVector *) (ptPos + stride2))->y = (*it).y + mbv2.y;
((CVector *) (ptPos + stride2))->z = (*it).z + mbv2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride3);
((CVector *) (ptPos + stride3))->x = (*it).x + mbv12.x;
((CVector *) (ptPos + stride3))->y = (*it).y + mbv12.y;
((CVector *) (ptPos + stride3))->z = (*it).z + mbv12.z;
}
else // speed too small, we must avoid imprecision
{
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride);
((CVector *) (ptPos + stride))->x = (*it).x + *currentSize * v1.x;
((CVector *) (ptPos + stride))->y = (*it).y + *currentSize * v1.y;
((CVector *) (ptPos + stride))->z = (*it).z + *currentSize * v1.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride2);
((CVector *) (ptPos + stride2))->x = (*it).x + *currentSize * v2.x;
((CVector *) (ptPos + stride2))->y = (*it).y + *currentSize * v2.y;
((CVector *) (ptPos + stride2))->z = (*it).z + *currentSize * v2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride3);
((CVector *) (ptPos + stride3))->x = (*it).x - *currentSize * v1.x;
((CVector *) (ptPos + stride3))->y = (*it).y - *currentSize * v1.y;
((CVector *) (ptPos + stride3))->z = (*it).z - *currentSize * v1.z;
}
}
else
{
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride);
((CVector *) (ptPos + stride))->x = (*it).x + *currentSize * v1.x;
((CVector *) (ptPos + stride))->y = (*it).y + *currentSize * v1.y;
((CVector *) (ptPos + stride))->z = (*it).z + *currentSize * v1.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride2);
((CVector *) (ptPos + stride2))->x = (*it).x + *currentSize * v2.x;
((CVector *) (ptPos + stride2))->y = (*it).y + *currentSize * v2.y;
((CVector *) (ptPos + stride2))->z = (*it).z + *currentSize * v2.z;
CHECK_VERTEX_BUFFER(vb, ptPos + stride3);
((CVector *) (ptPos + stride3))->x = (*it).x - *currentSize * v1.x;
((CVector *) (ptPos + stride3))->y = (*it).y - *currentSize * v1.y;
((CVector *) (ptPos + stride3))->z = (*it).z - *currentSize * v1.z;
}
ptPos += stride4;
++it;
++speedIt;
currentSize += currentSizeStep;
}
//uint64 startTick = NLMISC::CTime::getPerformanceTime();
vba.unlock();
driver->activeVertexBuffer(vb);
driver->renderRawQuads(la._Mat, 0, toProcess);
//PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;
}
leftToDo -= toProcess;
}
while (leftToDo);
}
else
{
float pAngles[CPSQuad::quadBufSize]; // the angles to use
float *currentAngle;
do
{
toProcess = leftToDo <= (uint32) CPSQuad::quadBufSize ? leftToDo : (uint32) CPSQuad::quadBufSize;
vb.setNumVertices(4 * toProcess);
// restart at the beginning of the vertex buffer
CVertexBufferReadWrite vba;
vb.lock (vba);
ptPos = (uint8 *) vba.getVertexCoordPointer();
if (la._SizeScheme)
{
currentSize = (float *) la._SizeScheme->make(la._Owner, size - leftToDo, pSizes, sizeof(float), toProcess, true, srcStep);
}
else
{
currentSize = &la._ParticleSize;
}
currentAngle = (float *) la._Angle2DScheme->make(la._Owner, size - leftToDo, pAngles, sizeof(float), toProcess, true, srcStep);
la.updateVbColNUVForRender(vb, size - leftToDo, toProcess, srcStep, *driver);
/*
static bool fakeColors = false;
if (fakeColors)
{
uint8 *col = (uint8 *) vba.getColorPointer();
uint left = toProcess;
while(left--)
{
* (CRGBA *) col = CRGBA::Red;
col += stride;
* (CRGBA *) col = CRGBA::Red;
col += stride;
* (CRGBA *) col = CRGBA::Red;
col += stride;
* (CRGBA *) col = CRGBA::Red;
col += stride;
}
}
*/
//nlinfo("======= %s", la._Name.c_str());
T endIt = it + toProcess;
CVector v1, v2;
NLMISC::OptFastFloorBegin();
if (!la._IndependantSizes)
{
while (it != endIt)
{
const uint32 tabIndex = ((NLMISC::OptFastFloor(*currentAngle)) & 0xff) << 2;
// lets avoid some ctor calls
v1.x = *currentSize * (rotTable[tabIndex] * I.x + rotTable[tabIndex + 1] * K.x);
v1.y = *currentSize * (rotTable[tabIndex] * I.y + rotTable[tabIndex + 1] * K.y);
v1.z = *currentSize * (rotTable[tabIndex] * I.z + rotTable[tabIndex + 1] * K.z);
v2.x = *currentSize * (rotTable[tabIndex + 2] * I.x + rotTable[tabIndex + 3] * K.x);
v2.y = *currentSize * (rotTable[tabIndex + 2] * I.y + rotTable[tabIndex + 3] * K.y);
v2.z = *currentSize * (rotTable[tabIndex + 2] * I.z + rotTable[tabIndex + 3] * K.z);
CHECK_VERTEX_BUFFER(vb, ptPos);
CHECK_VERTEX_BUFFER(vb, ptPos + stride);
CHECK_VERTEX_BUFFER(vb, ptPos + stride2);
CHECK_VERTEX_BUFFER(vb, ptPos + stride3);
((CVector *) ptPos)->x = (*it).x + v1.x;
((CVector *) ptPos)->y = (*it).y + v1.y;
((CVector *) ptPos)->z = (*it).z + v1.z;
//nlinfo("** %f, %f, %f", ((CVector *) ptPos)->x, ((CVector *) ptPos)->y, ((CVector *) ptPos)->z);
ptPos += stride;
((CVector *) ptPos)->x = (*it).x + v2.x;
((CVector *) ptPos)->y = (*it).y + v2.y;
((CVector *) ptPos)->z = (*it).z + v2.z;
//nlinfo("%f, %f, %f", ((CVector *) ptPos)->x, ((CVector *) ptPos)->y, ((CVector *) ptPos)->z);
ptPos += stride;
((CVector *) ptPos)->x = (*it).x - v1.x;
((CVector *) ptPos)->y = (*it).y - v1.y;
((CVector *) ptPos)->z = (*it).z - v1.z;
//nlinfo("%f, %f, %f", ((CVector *) ptPos)->x, ((CVector *) ptPos)->y, ((CVector *) ptPos)->z);
ptPos += stride;
((CVector *) ptPos)->x = (*it).x - v2.x;
((CVector *) ptPos)->y = (*it).y - v2.y;
((CVector *) ptPos)->z = (*it).z - v2.z;
//nlinfo("%f, %f, %f", ((CVector *) ptPos)->x, ((CVector *) ptPos)->y, ((CVector *) ptPos)->z);
ptPos += stride;
++it;
currentSize += currentSizeStep;
++currentAngle;
}
}
else // independant size, and non-constant rotation
{
float *currentSize2;
float secondSize;
uint32 currentSizeStep2;
if (la._SecondSize.getSizeScheme())
{
currentSize2 = (float *) la._SecondSize.getSizeScheme()->make(la._Owner, size- leftToDo, pSecondSizes, sizeof(float), toProcess, true, srcStep);
currentSizeStep2 = 1;
}
else
{
secondSize = la._SecondSize.getSize();
currentSize2 = &secondSize;
currentSizeStep2 = 0;
}
float cosAngle, sinAngle;
while (it != endIt)
{
cosAngle = CPSUtil::getCos((sint32) *currentAngle);
sinAngle = CPSUtil::getSin((sint32) *currentAngle);
v1 = cosAngle * I + sinAngle * K;
v2 = - sinAngle * I + cosAngle * K;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x + *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y + *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z + *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x + *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y + *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z + *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
CHECK_VERTEX_BUFFER(vb, ptPos);
((CVector *) ptPos)->x = (*it).x - *currentSize * v1.x - *currentSize2 * v2.x;
((CVector *) ptPos)->y = (*it).y - *currentSize * v1.y - *currentSize2 * v2.y;
((CVector *) ptPos)->z = (*it).z - *currentSize * v1.z - *currentSize2 * v2.z;
ptPos += stride;
++it;
++currentAngle;
currentSize += currentSizeStep;
currentSize2 += currentSizeStep2;
}
}
NLMISC::OptFastFloorEnd();
//tmp
// uint64 startTick = NLMISC::CTime::getPerformanceTime();
vba.unlock();
driver->activeVertexBuffer(vb);
driver->renderRawQuads(la._Mat, 0, toProcess);
//PSLookAtRenderTime += NLMISC::CTime::getPerformanceTime() - startTick;*/
leftToDo -= toProcess;
}
while (leftToDo);
}
PARTICLES_CHECK_MEM;
}
};
///===========================================================================================
void CPSFaceLookAt::draw(bool opaque)
{
// if (!FilterPS[2]) return;
NL_PS_FUNC(CPSFaceLookAt_draw)
PARTICLES_CHECK_MEM;
if (!_Owner->getSize()) return;
uint32 step;
uint numToProcess;
computeSrcStep(step, numToProcess);
if (!numToProcess) return;
if (step == (1 << 16))
{
if (!_AlignOnMotion)
{
CPSFaceLookAtHelper::drawLookAt(_Owner->getPos().begin(),
_Owner->getSpeed().begin(),
*this,
numToProcess,
step
);
}
else
{
CPSFaceLookAtHelper::drawLookAtAlignOnMotion(_Owner->getPos().begin(),
_Owner->getSpeed().begin(),
*this,
numToProcess,
step
);
}
}
else
{
if (!_AlignOnMotion)
{
CPSFaceLookAtHelper::drawLookAt(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
TIteratorVectStep1616(_Owner->getSpeed().begin(), 0, step),
*this,
numToProcess,
step
);
}
else
{
CPSFaceLookAtHelper::drawLookAtAlignOnMotion(TIteratorVectStep1616(_Owner->getPos().begin(), 0, step),
TIteratorVectStep1616(_Owner->getSpeed().begin(), 0, step),
*this,
numToProcess,
step
);
}
}
PARTICLES_CHECK_MEM;
}
///===========================================================================================
CPSFaceLookAt::CPSFaceLookAt(CSmartPtr tex) : CPSQuad(tex),
_MotionBlurCoeff(0.f),
_Threshold(0.5f),
_IndependantSizes(false),
_AlignOnMotion(false),
_AlignOnZAxis(false)
{
NL_PS_FUNC(CPSFaceLookAt_CPSFaceLookAt)
_SecondSize.Owner = this;
if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("LookAt");
}
///===========================================================================================
void CPSFaceLookAt::newElement(const CPSEmitterInfo &info)
{
NL_PS_FUNC(CPSFaceLookAt_newElement)
CPSQuad::newElement(info);
newAngle2DElement(info);
}
///===========================================================================================
void CPSFaceLookAt::deleteElement(uint32 index)
{
NL_PS_FUNC(CPSFaceLookAt_deleteElement)
CPSQuad::deleteElement(index);
deleteAngle2DElement(index);
}
///===========================================================================================
void CPSFaceLookAt::resize(uint32 capacity)
{
NL_PS_FUNC(CPSFaceLookAt_resize)
nlassert(capacity < (1 << 16));
CPSQuad::resize(capacity);
resizeAngle2D(capacity);
}
///===========================================================================================
void CPSFaceLookAt::serial(NLMISC::IStream &f) throw(NLMISC::EStream)
{
NL_PS_FUNC(CPSFaceLookAt_serial)
// version 4 : added 'align on z-axis' flag
// version 3 : added 'align on motion' flag
sint ver = f.serialVersion(4);
CPSQuad::serial(f);
CPSRotated2DParticle::serialAngle2DScheme(f);
f.serial(_MotionBlurCoeff);
if (_MotionBlurCoeff != 0)
{
f.serial(_Threshold);
}
if (ver > 1)
{
f.serial(_IndependantSizes);
if (_IndependantSizes)
{
_SecondSize.serialSizeScheme(f);
}
}
if (ver >= 3)
{
f.serial(_AlignOnMotion);
}
if (ver >= 4)
{
f.serial(_AlignOnZAxis);
}
if (f.isReading())
{
init();
}
}
} // NL3D