diff options
Diffstat (limited to 'APEX_1.4/module/iofx/include/ModifierSrc.h')
| -rw-r--r-- | APEX_1.4/module/iofx/include/ModifierSrc.h | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/APEX_1.4/module/iofx/include/ModifierSrc.h b/APEX_1.4/module/iofx/include/ModifierSrc.h new file mode 100644 index 00000000..c293c31a --- /dev/null +++ b/APEX_1.4/module/iofx/include/ModifierSrc.h @@ -0,0 +1,921 @@ +/* + * Copyright (c) 2008-2015, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA CORPORATION and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA CORPORATION is strictly prohibited. + */ + + +#if defined(__CUDACC__) && defined(__CUDA_ARCH__) && __CUDA_ARCH__ < 200 +#define _CUDA_OPT_LOC_MEM_ 1 +#else +#define _CUDA_OPT_LOC_MEM_ 0 +#endif + +#define PI 3.141592653589793f + +//--- Helpers + +APEX_CUDA_CALLABLE PX_INLINE unsigned int binSearch(float val, unsigned int count, const float* data) +{ + unsigned int beg = 0; + unsigned int end = count; + + while (beg < end) + { + unsigned int mid = beg + ((end - beg) >> 1); + if (val < data[mid]) + { + end = mid; + } + else + { + beg = mid + 1; + } + } + return beg; +} + +APEX_CUDA_CALLABLE PX_INLINE void approxAxisAngleToMat33(const PxVec3& axisAngle, PxMat33& rot) +{ + const float x = 0.5f * axisAngle.x; + const float y = 0.5f * axisAngle.y; + const float z = 0.5f * axisAngle.z; + const float xx = x * x; + const float yy = y * y; + const float zz = z * z; + const float xy = x * y; + const float yz = y * z; + const float zx = z * x; + const float twoRecipNorm2 = 2.0f / (1.0f + xx + yy + zz); // w = 1 + rot(0, 0) = 1.0f - twoRecipNorm2 * (yy + zz); + rot(0, 1) = twoRecipNorm2 * (xy - z); + rot(0, 2) = twoRecipNorm2 * (zx + y); + rot(1, 0) = twoRecipNorm2 * (xy + z); + rot(1, 1) = 1.0f - twoRecipNorm2 * (zz + xx); + rot(1, 2) = twoRecipNorm2 * (yz - x); + rot(2, 0) = twoRecipNorm2 * (zx - y); + rot(2, 1) = twoRecipNorm2 * (yz + x); + rot(2, 2) = 1.0f - twoRecipNorm2 * (xx + yy); +} + + +APEX_CUDA_CALLABLE PX_INLINE bool approxEquals(float a, float b, float eps) +{ + const float diff = PxAbs(a - b); + return (diff < eps); +} + +APEX_CUDA_CALLABLE PX_INLINE bool approxEquals(const PxVec3& a, const PxVec3& b, float eps) +{ + return approxEquals(a.x, b.x, eps) && + approxEquals(a.y, b.y, eps) && + approxEquals(a.z, b.z, eps); +} + +APEX_CUDA_CALLABLE PX_INLINE int maxAbsElementIndex(const PxVec3& v) +{ + const PxVec3 a(PxAbs(v.x), PxAbs(v.y), PxAbs(v.z)); + const int m01 = (a.y > a.x); + const int m2 = (a.z > a[(unsigned)m01]); + return (m2 << 1) | (m01 >> m2); +} + + +APEX_CUDA_CALLABLE PX_INLINE void generateRotationMatrix(const PxVec3& srcVec, const PxVec3& dstVec, PxMat33& outRotMat) +{ + if (approxEquals(srcVec, dstVec, 0.0001f)) + { + outRotMat = PxMat33(PxIdentity); + return; + } + + PxVec3 crossResult = srcVec.cross(dstVec); + crossResult *= PxAcos(srcVec.dot(dstVec)); + + approxAxisAngleToMat33(crossResult, outRotMat); +} + +APEX_CUDA_CALLABLE PX_INLINE void generateRandomRotation(const PxVec3& srcVec, PxMat33& outRotMat, nvidia::RandState& randState) +{ + PxVec3 tmpRotVec(srcVec); + tmpRotVec *= randState.nextFloat(0.0f, 2 * PI); + approxAxisAngleToMat33(tmpRotVec, outRotMat); +} + +// ------------------------------------------------------------------------------------------------ + + +//--- Rotation modifier --- + +//struct PARAMS_NAME(Rotation) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(Rotation) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, rollType) \ + INPLACE_TYPE_FIELD(int32_t, rollAxis) \ + INPLACE_TYPE_FIELD(float, rollSign) \ + INPLACE_TYPE_FIELD(float, maxSettleRatePerSec) \ + INPLACE_TYPE_FIELD(float, maxRotationRatePerSec) \ + INPLACE_TYPE_FIELD(float, inAirRotationMultiplier) \ + INPLACE_TYPE_FIELD(float, collisionRotationMultiplier) \ + INPLACE_TYPE_FIELD(uint32_t, includeVerticalDirection) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 3; +}; + +APEX_CUDA_CALLABLE PX_INLINE void chooseUp(PxVec3& outUp, unsigned int rollType, nvidia::RandState& randState) +{ + const float angle = randState.nextFloat(0.0f, 2 * PI); + const float up = angle < PI ? -1.0f : 1.0f; + const float cosAng = PxCos(angle); + const float sinAng = PxSin(angle); + + switch (rollType) + { + default: + outUp = PxVec3(0.0f, 0.0f, 1.0f); + break; + + case nvidia::apex::ApexMeshParticleRollType::FLAT_X: + outUp = PxVec3(up, 0.0f, 0.0f); + break; + case nvidia::apex::ApexMeshParticleRollType::FLAT_Y: + outUp = PxVec3(0.0f, up, 0.0f); + break; + case nvidia::apex::ApexMeshParticleRollType::FLAT_Z: + outUp = PxVec3(0.0f, 0.0f, up); + break; + + case nvidia::apex::ApexMeshParticleRollType::LONG_X: + outUp = PxVec3(0.0f, cosAng, sinAng); + break; + case nvidia::apex::ApexMeshParticleRollType::LONG_Y: + outUp = PxVec3(cosAng, 0.0f, sinAng); + break; + case nvidia::apex::ApexMeshParticleRollType::LONG_Z: + outUp = PxVec3(cosAng, sinAng, 0.0f); + break; + } +} + +APEX_CUDA_CALLABLE PX_INLINE void updateParticleRollBoxFromCollision(const PARAMS_NAME(Rotation)& params, PxVec3& particleAngularDelta, const PxMat33& rot, const PxVec3& collisionNormal, float timeSlice) +{ + const float maxSettle = params.maxSettleRatePerSec * timeSlice; + const float maxSettle2 = maxSettle * maxSettle; // where to compute this? + + if (params.rollType != nvidia::apex::ApexMeshParticleRollType::SPHERICAL) + { + // Settling + PxVec3 a; + if (params.rollAxis < 0) + { + // Cubic rolling, must choose most normal-pointing mRollAxis + PxVec3 overlap; + overlap = rot.transform(collisionNormal); + const int32_t bestAxis = maxAbsElementIndex(overlap); + a = bestAxis == 0 ? rot.column0 : bestAxis == 1 ? rot.column1 : rot.column2; + } + else + { + // Flat or long box, mRollAxis is chosen + a = params.rollAxis == 0 ? rot.column0 : params.rollAxis == 1 ? rot.column1 : rot.column2; + } + + PxVec3 settle = (params.rollSign * a.dot(collisionNormal)) * a.cross(collisionNormal); + if (maxSettle != 0.0f) + { + const float settle2 = settle.magnitudeSquared(); + // Cap how much settling is allowed per simulation tick + if (settle2 > maxSettle2) + { + settle *= maxSettle * PxRecipSqrt(settle2); + } + } + particleAngularDelta += settle; + } +} + +APEX_CUDA_CALLABLE PX_INLINE void updateParticleRollBoxNoCollision(const PARAMS_NAME(Rotation)& params, PxVec3& particleAngularDelta, float timeSlice) +{ + const float maxRotation = params.maxRotationRatePerSec * timeSlice; + const float maxRotation2 = maxRotation * maxRotation; + + if (maxRotation != 0.0f) + { + // Cap the total amount of roll. + const float roll2 = particleAngularDelta.magnitudeSquared(); + if (roll2 > maxRotation2) + { + particleAngularDelta *= maxRotation * PxRecipSqrt(roll2); + } + } +} + + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRotation(const PARAMS_NAME(Rotation)& params, const Input& input, PubState& pubState, PrivState& privState, const nvidia::iofx::ModifierCommonParams& common, nvidia::RandState& randState) +{ + if (usage == nvidia::apex::ModifierUsage_Mesh) + { + // TODO: This should really go into a 'settle' modifier, that says that objects should try to settle with one side up + // or another, but for now it's here. + if (spawn) + { + // Need to come up with a default pose. + PxMat33 rotationOnSpawn; + if (params.rollType == nvidia::apex::ApexMeshParticleRollType::SPHERICAL) + { + PxVec3 randomRotation; + randomRotation.x = randState.nextFloat(-PI, PI); + randomRotation.y = randState.nextFloat(-PI, PI); + randomRotation.z = randState.nextFloat(-PI, PI); + approxAxisAngleToMat33(randomRotation, rotationOnSpawn); + } + else + { + PxVec3 upVector; + chooseUp(upVector, params.rollType, randState); + + PxVec3 unitSystemUp = common.upVector; + unitSystemUp.normalize(); + + PxMat33 rotateToUp, randomRotation; + generateRotationMatrix(upVector, unitSystemUp, rotateToUp); + generateRandomRotation(unitSystemUp, randomRotation, randState); + + rotationOnSpawn = randomRotation * rotateToUp; + } + privState.rotation = rotationOnSpawn; + pubState.rotation = rotationOnSpawn; + } + else + { + PxVec3 rollDelta; + PxVec3 delta = input.velocity * common.deltaTime; + + if (common.inputHasCollision) + { + if (input.collisionFlags != 0) + { + rollDelta = params.collisionRotationMultiplier * input.collisionNormal.cross(delta); + } + else + { + if (params.includeVerticalDirection) + { + // I'd like to use the absolute value of the "up" speed... just check it in first. + rollDelta = params.inAirRotationMultiplier * delta; + } + else + { + rollDelta = params.inAirRotationMultiplier * common.upVector.cross( delta ); + } + } + + updateParticleRollBoxFromCollision(params, rollDelta, privState.rotation, input.collisionNormal, common.deltaTime); + updateParticleRollBoxNoCollision(params, rollDelta, common.deltaTime); + } + else + { + if (params.includeVerticalDirection) + { + // I'd like to use the absolute value of the "up" speed... just check it in first. + rollDelta = params.inAirRotationMultiplier * delta; + } + else + { + rollDelta = params.inAirRotationMultiplier * common.upVector.cross( delta ); + } + updateParticleRollBoxNoCollision(params, rollDelta, common.deltaTime); + } + + if (rollDelta.magnitudeSquared() > 0.0f) + { + // update particle transform + // A) This is because maxAngle isn't really working properly + // B) This should be a configurable parameter of the system + PxMat33 rot; + approxAxisAngleToMat33(rollDelta, rot); + privState.rotation = rot * privState.rotation; + } + pubState.rotation = privState.rotation; + } + } +} + +//--- SimpleScale modifier --- + +//struct PARAMS_NAME(SimpleScale) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(SimpleScale) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(PxVec3, scaleFactor) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierSimpleScale(const PARAMS_NAME(SimpleScale)& params, const Input& /*input*/, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + pubState.scale.x *= params.scaleFactor.x; + pubState.scale.y *= params.scaleFactor.y; + pubState.scale.z *= params.scaleFactor.z; +} + +//--- RandomScale modifier --- + +//struct PARAMS_NAME(RandomScale) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(RandomScale) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, scaleFactorMin) \ + INPLACE_TYPE_FIELD(float, scaleFactorMax) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 1; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRandomScale(const PARAMS_NAME(RandomScale)& params, const Input& /*input*/, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& randState) +{ + const float scaleFactor = randState.nextFloat(params.scaleFactorMin, params.scaleFactorMax); + + pubState.scale *= scaleFactor; +} + +//--- ScaleByMass modifier --- + +//struct PARAMS_NAME(ScaleByMass) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ScaleByMass) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, scaleFactor) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierScaleByMass(const PARAMS_NAME(ScaleByMass)& /*params*/, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + pubState.scale *= input.mass; +} + +//--- ColorVsLife modifier --- + +//struct PARAMS_NAME(ColorVsLife) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ColorVsLife) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, channel) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierColorVsLife(const PARAMS_NAME(ColorVsLife)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ +#define _MODIFIER_CODE_(channel) \ + { \ + pubState.color[channel] *= EVAL_CURVE(params.curve, input.liferemain); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.channel == 0) _MODIFIER_CODE_(0) + else if (params.channel == 1) _MODIFIER_CODE_(1) + else if (params.channel == 2) _MODIFIER_CODE_(2) + else if (params.channel == 3) _MODIFIER_CODE_(3) +#else + _MODIFIER_CODE_(params.channel) +#endif +#undef _MODIFIER_CODE_ + + } + +//--- ColorVsDensity modifier --- + +//struct PARAMS_NAME(ColorVsDensity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ColorVsDensity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, channel) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierColorVsDensity(const PARAMS_NAME(ColorVsDensity)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ +#define _MODIFIER_CODE_(channel) \ + { \ + pubState.color[channel] *= EVAL_CURVE(params.curve, input.density); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.channel == 0) _MODIFIER_CODE_(0) + else if (params.channel == 1) _MODIFIER_CODE_(1) + else if (params.channel == 2) _MODIFIER_CODE_(2) + else if (params.channel == 3) _MODIFIER_CODE_(3) +#else + _MODIFIER_CODE_(params.channel) +#endif +#undef _MODIFIER_CODE_ + } + +//--- ColorVsVelocity modifier --- + +//struct PARAMS_NAME(ColorVsVelocity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ColorVsVelocity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, velocity0) \ + INPLACE_TYPE_FIELD(float, velocity1) \ + INPLACE_TYPE_FIELD(uint32_t, channel) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierColorVsVelocity(const PARAMS_NAME(ColorVsVelocity)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + float velocity = (input.velocity.magnitude() - params.velocity0) / (params.velocity1 - params.velocity0); + velocity = PxClamp(velocity, 0.0f, 1.0f); + +#define _MODIFIER_CODE_(channel) \ + { \ + pubState.color[channel] *= EVAL_CURVE(params.curve, velocity); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.channel == 0) _MODIFIER_CODE_(0) + else if (params.channel == 1) _MODIFIER_CODE_(1) + else if (params.channel == 2) _MODIFIER_CODE_(2) + else if (params.channel == 3) _MODIFIER_CODE_(3) +#else + _MODIFIER_CODE_(params.channel) +#endif +#undef _MODIFIER_CODE_ +} + +//--- SubtextureVsLife modifier --- + +//struct PARAMS_NAME(SubtextureVsLife) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(SubtextureVsLife) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierSubtextureVsLife(const PARAMS_NAME(SubtextureVsLife)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + pubState.subTextureId += EVAL_CURVE(params.curve, input.liferemain); + } +} + +//--- OrientAlongVelocity modifier --- + +APEX_CUDA_CALLABLE PX_INLINE bool buildRotationMatrix(const PxVec3& srcVec, const PxVec3& dstVec, PxMat33& outRotMat) +{ + PxVec3 axis = srcVec.cross(dstVec); + + float cosAngle = srcVec.dot(dstVec); + float angle = PxAcos(cosAngle); + + float axisLen = axis.normalize(); + if (axisLen < 0.0001f) + { + return false; + } + + PxQuat quat(angle, axis); + outRotMat = PxMat33(quat); + return true; +} + +//struct PARAMS_NAME(OrientAlongVelocity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(OrientAlongVelocity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(PxVec3, modelForward) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierOrientAlongVelocity(const PARAMS_NAME(OrientAlongVelocity)& params, const Input& input, PubState& pubState, PrivState& privState, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Mesh) + { + PxVec3 vel = input.velocity; + float velMagnitude = vel.normalize(); // normalize it + // If travelling too slowly, this will be unstable, so don't do anything. + if (PxAbs(velMagnitude) >= 0.0001f) + { + PxMat33 rotMat; + if (buildRotationMatrix(params.modelForward, vel, rotMat)) + { + privState.rotation = rotMat; + } + } + pubState.rotation = privState.rotation; + } +} + +//--- ScaleAlongVelocity modifier --- + +///p,q -> p^q = n (n - must be normalized!) +APEX_CUDA_CALLABLE PX_INLINE void buildPlaneBasis(const PxVec3& n, PxVec3& p, PxVec3& q) +{ + const float SQRT1_2 = PxSqrt(float(0.5)); + if (PxAbs(n.z) > SQRT1_2) + { + // choose p in y-z plane + float k = sqrtf(n.y * n.y + n.z * n.z); + p.x = 0; + p.y = -n.z / k; + p.z = n.y / k; + // set q = n x p + q.x = k; + q.y = -n.x * p.z; + q.z = n.x * p.y; + } + else + { + // choose p in x-y plane + float k = PxSqrt(n.x * n.x + n.y * n.y); + p.x = -n.y / k; + p.y = n.x / k; + p.z = 0; + // set q = n x p + q.x = -n.z * p.y; + q.y = n.z * p.x; + q.z = k; + } +} + +APEX_CUDA_CALLABLE PX_INLINE void buildScaleAlongAxis(const PxVec3& scaleAxis, float scale, PxMat33& scaleMat) +{ + PxVec3 axis0, axis1; + buildPlaneBasis(scaleAxis, axis0, axis1); + + PxMat33 rotToAxisMat(axis0, axis1, scaleAxis); + + scaleMat = rotToAxisMat; + scaleMat.column2 = scaleAxis * scale; + + //scaleMat.setMultiplyTransposeRight(scaleMat, rotToAxisMat); + + float a = scaleMat.column0.dot(rotToAxisMat.column0); + float b = scaleMat(0,0) * rotToAxisMat(0,1) + scaleMat(1,0) * rotToAxisMat(1,1) + scaleMat(2,0) * rotToAxisMat(2,1); + float c = scaleMat(0,0) * rotToAxisMat(0,2) + scaleMat(1,0) * rotToAxisMat(1,2) + scaleMat(2,0) * rotToAxisMat(2,2); + float d = scaleMat(0,1) * rotToAxisMat(0,0) + scaleMat(1,1) * rotToAxisMat(1,0) + scaleMat(2,1) * rotToAxisMat(2,0); + float e = scaleMat(0,1) * rotToAxisMat(0,1) + scaleMat(1,1) * rotToAxisMat(1,1) + scaleMat(2,1) * rotToAxisMat(2,1); + float f = scaleMat(0,1) * rotToAxisMat(0,2) + scaleMat(1,1) * rotToAxisMat(1,2) + scaleMat(2,1) * rotToAxisMat(2,2); + float g = scaleMat(0,2) * rotToAxisMat(0,0) + scaleMat(1,2) * rotToAxisMat(1,0) + scaleMat(2,2) * rotToAxisMat(2,0); + float h = scaleMat(0,2) * rotToAxisMat(0,1) + scaleMat(1,2) * rotToAxisMat(1,1) + scaleMat(2,2) * rotToAxisMat(2,1); + float i = scaleMat(0,2) * rotToAxisMat(0,2) + scaleMat(1,2) * rotToAxisMat(1,2) + scaleMat(2,2) * rotToAxisMat(2,2); + + scaleMat(0,0) = a; + scaleMat(1,0) = b; + scaleMat(2,0) = c; + + scaleMat(0,1) = d; + scaleMat(1,1) = e; + scaleMat(2,1) = f; + + scaleMat(0,2) = g; + scaleMat(1,2) = h; + scaleMat(2,2) = i; +} + +//struct PARAMS_NAME(ScaleAlongVelocity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ScaleAlongVelocity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, scaleFactor) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierScaleAlongVelocity(const PARAMS_NAME(ScaleAlongVelocity)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Mesh) + { + PxVec3 vel = input.velocity; + float velMagnitude = vel.normalize(); // normalize it + + PxMat33 scaleMat(PxIdentity); + if (velMagnitude >= 0.0001f) + { + float scale = 1.0f + velMagnitude * params.scaleFactor; + + buildScaleAlongAxis(vel, scale, scaleMat); + } + pubState.rotation = scaleMat * pubState.rotation; + } +} + +//--- RandomSubtexture modifier --- + +//struct PARAMS_NAME(RandomSubtexture) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(RandomSubtexture) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, subtextureRangeMin) \ + INPLACE_TYPE_FIELD(float, subtextureRangeMax) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 1; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRandomSubtexture(const PARAMS_NAME(RandomSubtexture)& params, const Input& /*input*/, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& randState) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + pubState.subTextureId += randState.nextFloat(params.subtextureRangeMin, params.subtextureRangeMax); + } +} + +//--- RandomRotation modifier --- + +//struct PARAMS_NAME(RandomRotation) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(RandomRotation) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, rotationRangeMin) \ + INPLACE_TYPE_FIELD(float, rotationRangeMax) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 1; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRandomRotation(const PARAMS_NAME(RandomRotation)& params, const Input& /*input*/, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& randState) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + const float DegToRad = PI / 180; + pubState.rotation += DegToRad * randState.nextFloat(params.rotationRangeMin, params.rotationRangeMax); + } +} + +//--- ScaleVsLife modifier --- + +//struct PARAMS_NAME(ScaleVsLife) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ScaleVsLife) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, axis) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierScaleVsLife(const PARAMS_NAME(ScaleVsLife)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ +#define _MODIFIER_CODE_(axis) \ + { \ + pubState.scale[axis] *= EVAL_CURVE(params.curve, input.liferemain); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.axis == 0) _MODIFIER_CODE_(0) + else if (params.axis == 1) _MODIFIER_CODE_(1) + else if (params.axis == 2) _MODIFIER_CODE_(2) +#else + _MODIFIER_CODE_(params.axis) +#endif +#undef _MODIFIER_CODE_ + } + +//--- ScaleVsDensity modifier --- + +//struct PARAMS_NAME(ScaleVsDensity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ScaleVsDensity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, axis) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierScaleVsDensity(const PARAMS_NAME(ScaleVsDensity)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ +#define _MODIFIER_CODE_(axis) \ + { \ + pubState.scale[axis] *= EVAL_CURVE(params.curve, input.density); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.axis == 0) _MODIFIER_CODE_(0) + else if (params.axis == 1) _MODIFIER_CODE_(1) + else if (params.axis == 2) _MODIFIER_CODE_(2) +#else + _MODIFIER_CODE_(params.axis) +#endif +#undef _MODIFIER_CODE_ + } + +//--- ScaleVsCameraDistance modifier --- + +//struct PARAMS_NAME(ScaleVsCameraDistance) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ScaleVsCameraDistance) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(uint32_t, axis) \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierScaleVsCameraDistance(const PARAMS_NAME(ScaleVsCameraDistance)& params, const Input& input, PubState& pubState, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& common, nvidia::RandState& /*nvidia::RandState*/) +{ + float cameraDistance = (input.position - common.eyePosition).magnitude(); + +#define _MODIFIER_CODE_(axis) \ + { \ + pubState.scale[axis] *= EVAL_CURVE(params.curve, cameraDistance); \ + } +#if _CUDA_OPT_LOC_MEM_ + if (params.axis == 0) _MODIFIER_CODE_(0) + else if (params.axis == 1) _MODIFIER_CODE_(1) + else if (params.axis == 2) _MODIFIER_CODE_(2) +#else + _MODIFIER_CODE_(params.axis) +#endif +#undef _MODIFIER_CODE_ + } + +//--- ViewDirectionSorting modifier --- + +//struct PARAMS_NAME(ViewDirectionSorting) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(ViewDirectionSorting) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierViewDirectionSorting(const PARAMS_NAME(ViewDirectionSorting)& /*params*/, const Input& /*input*/, PubState& /*pubState*/, PrivState& /*privState*/, const nvidia::iofx::ModifierCommonParams& /*common*/, nvidia::RandState& /*nvidia::RandState*/) +{ +} + +//--- RotationRate modifier --- + +//struct PARAMS_NAME(RotationRate) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(RotationRate) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, rotationRate) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRotationRate(const PARAMS_NAME(RotationRate)& params, const Input& /*input*/, PubState& pubState, PrivState& privState, const nvidia::iofx::ModifierCommonParams& common, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + const float TwoPi = 2 * PI; + const float rotationDelta = params.rotationRate * TwoPi * common.deltaTime; + privState.rotation += rotationDelta; + pubState.rotation += privState.rotation; + } +} + +//--- RotationRateVsLife modifier --- + +//struct PARAMS_NAME(RotationRateVsLife) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(RotationRateVsLife) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(CURVE_TYPE, curve) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierRotationRateVsLife(const PARAMS_NAME(RotationRateVsLife)& params, const Input& input, PubState& pubState, PrivState& privState, const nvidia::iofx::ModifierCommonParams& common, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + const float TwoPi = 2 * PI; + const float rotationDelta = EVAL_CURVE(params.curve, input.liferemain) * TwoPi * common.deltaTime; + privState.rotation += rotationDelta; + pubState.rotation += privState.rotation; + } +} + +//--- OrientScaleAlongScreenVelocity modifier --- + +//struct PARAMS_NAME(OrientScaleAlongScreenVelocity) +#define INPLACE_TYPE_STRUCT_NAME PARAMS_NAME(OrientScaleAlongScreenVelocity) +#define INPLACE_TYPE_STRUCT_FIELDS \ + INPLACE_TYPE_FIELD(float, scalePerVelocity) \ + INPLACE_TYPE_FIELD(float, scaleChangeLimit) \ + INPLACE_TYPE_FIELD(float, scaleChangeDelay) +#define INPLACE_TYPE_STRUCT_LEAVE_OPEN 1 +#include INPLACE_TYPE_BUILD() + + static const uint32_t RANDOM_COUNT = 0; +}; + +INPLACE_TEMPL_VA_ARGS_DEF(bool spawn, int usage, typename Input, typename PubState, typename PrivState) +MODIFIER_DECL void modifierOrientScaleAlongScreenVelocity(const PARAMS_NAME(OrientScaleAlongScreenVelocity)& params, const Input& input, PubState& pubState, PrivState& privState, const nvidia::iofx::ModifierCommonParams& common, nvidia::RandState& /*nvidia::RandState*/) +{ + if (usage == nvidia::apex::ModifierUsage_Sprite) + { + PxVec3 viewPos, viewVel; // position & velocity in view space + + viewPos.x = (input.position - common.eyePosition).dot(common.eyeAxisX); + viewPos.y = (input.position - common.eyePosition).dot(common.eyeAxisY); + viewPos.z = (input.position - common.eyePosition).dot(common.eyeDirection); // eyeDir = eyeAxisZ + + // 4 is simply a hack that looks decent in UE3 ATM, we should use the FOV to determine when to + // cull particles, otherwise you end up with particles around the zNear plane that have huge + // 'd' values, resulting in bad scaling + if (viewPos.z < common.zNear * 4) + { + pubState.rotation = privState.rotation; + pubState.scale.x = 0.0f; + pubState.scale.y = 0.0f; + return; + } + + viewVel.x = (input.velocity).dot(common.eyeAxisX); + viewVel.y = (input.velocity).dot(common.eyeAxisY); + viewVel.z = (input.velocity).dot(common.eyeDirection); // eyeDir = eyeAxisZ + + // tan(angle) = (Vy*Pz - Py*Vz) / (Vx*Pz - Px*Vz) + const float velX = viewVel.x * viewPos.z - viewPos.x * viewVel.z; + const float velY = viewVel.y * viewPos.z - viewPos.y * viewVel.z; + + const float velLengthMultiplier = 1.0f / viewPos.z; + + const float dx = velX * velLengthMultiplier; + const float dy = velY * velLengthMultiplier; + + const float d = PxSqrt(dx * dx + dy * dy); + + if (d >= 1e-5f) + { + // "Note that the order of arguments is reversed; the function atan2(y,x) computes + // the angle corresponding to the point (x,y)." + // see http://en.wikipedia.org/wiki/Atan2 + privState.rotation = PxAtan2(velY, velX); + + float scale = 1.0f + d * params.scalePerVelocity; + if (params.scaleChangeDelay > 0.0f) + { + const float blendFactor = PxExp(-common.deltaTime / params.scaleChangeDelay); + scale *= (1.0f - blendFactor); + scale += blendFactor * privState.scale; + } + if (params.scaleChangeLimit > 0.0f) + { + const float scaleChangeLimit = params.scaleChangeLimit * common.deltaTime; + const float scaleChange = scale - privState.scale; + if (scaleChange > +scaleChangeLimit) + { + scale = privState.scale + scaleChangeLimit; + } + else if (scaleChange < -scaleChangeLimit) + { + scale = privState.scale - scaleChangeLimit; + } + } + privState.scale = scale; + pubState.scale.x *= scale; + } + pubState.rotation = privState.rotation; + } +} + +#undef _CUDA_OPT_LOC_MEM_ |