//**********************************************************************// // Filename: LightningGenerator.h // // Last Modified: 04/10/10 by RayJ // // Comment: Contains declaration of the LightningGenerator and the // // various structs it utilizes // //**********************************************************************// #include "LightningGenerator.h" #include LightningGenerator::LightningGenerator() : m_bInitialized(false), m_pSavedAlphasBranch(NULL), m_pSavedAlphasMain(NULL) { } LightningGenerator::~LightningGenerator(void) { SAFE_DELETE_ARRAY(m_pSavedAlphasMain); SAFE_DELETE_ARRAY(m_pSavedAlphasBranch); } bool LightningGenerator::Init(LightningGeneratorProperties& _properties, LPDIRECT3DVERTEXBUFFER9*& _vertBuff, LPDIRECT3DVERTEXBUFFER9*& _branchBuffer) { if (_properties.AllSafetyChecksGood()) { m_Properties = _properties; static AngelVertexDeclaration* pointDecl = NULL; if (pointDecl == NULL) { vector decls; decls.push_back(Declaration(DECLUSAGE_POSITION, DECLTYPE_FLOAT3)); decls.push_back(Declaration(DECLUSAGE_COLOR, DECLTYPE_U32COLOR)); decls.push_back(Declaration(DECLUSAGE_PSIZE, DECLTYPE_FLOAT1)); pointDecl = g_AngelHardwareBufferManagerInstance->CreateVertexDeclaration(decls); pointDecl->Release(); } if (!_vertBuff) { // point sprite vertex buffer, one constant size, made once _vertBuff = g_AngelHardwareBufferManagerInstance->CreateVertexBuffer( _properties.BufferSize, pointDecl, Usage::USAGE_VERTEXBUFFER_POINTSPRITES ); if (!_vertBuff) return false; } if (!_branchBuffer) { _branchBuffer = g_AngelHardwareBufferManagerInstance->CreateVertexBuffer( _properties.BranchBufferSize, pointDecl, Usage::USAGE_VERTEXBUFFER_POINTSPRITES); if (!_branchBuffer) return false; } } else { #ifdef _DEBUG MessageBox(NULL, "Some Lightning Generator properties not set!", "Generator not initialized!", MB_OK); #endif return false; } m_bInitialized = true; return true; } bool LightningGenerator::CreateLightning(LPDIRECT3DVERTEXBUFFER9*& _vertBuff, LPDIRECT3DVERTEXBUFFER9*& _branchBuff) { assert(_vertBuff && _branchBuff); if (!m_bInitialized) // make sure generator properties have been initialized { #ifdef _DEBUG MessageBox(NULL, "Lightning Generator not initialized!", "Error!", MB_OK); #endif return false; } // Clear any old generated points if(m_vGeneratedPoints.size()) m_vGeneratedPoints.clear(); if (m_vGeneratedBranchPoints.size()) m_vGeneratedBranchPoints.clear(); // The vectors to and from start to end points. D3DXVECTOR3 vStartToEnd = m_Properties.EndPt - m_Properties.StartPt; D3DXVECTOR3 vDir; D3DXVec3Normalize(&vDir, &vStartToEnd); // Find the distances between start and end points float fDistance = D3DXVec3Length(&vStartToEnd); // add a little variety in segment lengths between bolts float fSegmentLength = RAND_FLOAT(m_Properties.MinSegLen, m_Properties.MaxSegLen); D3DXVECTOR3 vStartPos = m_Properties.StartPt; D3DXVECTOR3 vNewPt = D3DXVECTOR3(VECTOR3_ZERO), vPrevSegEndPt = vStartPos; // add first point to vector m_vGeneratedPoints.push_back(LightningPointVert(vStartPos, AngelColor::White, m_Properties.MaxPSSize)); // how many segments to make unsigned numSegments = unsigned(fDistance / fSegmentLength); bool lastSegBranched = false; ////////////////////////////////////////////////////////////////////////// // setup first segment ////////////////////////////////////////////////////////////////////////// // generate the next point // move the new point from the old point in the direction of the end point first vNewPt = vPrevSegEndPt + (vDir * fSegmentLength); // save the new point for the next point to start from // before we offset it vPrevSegEndPt = vNewPt; // offset new point from its position on the line ( more accurately, a ray, in "vDir") to a random point vNewPt.x += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation); vNewPt.y += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation) * 0.65f; // don't move y as much vNewPt.z += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation); GenerateBetweenPositions(vStartPos, vNewPt); vStartPos = vNewPt; // update the current startPt to be the new point ( the current segment's endpoint ) ////////////////////////////////////////////////////////////////////////// bool signs[3]; // used to make sure we continue to go the same direction for (unsigned i = 1; i < numSegments - 1; ++i) { // generate the next point // move the new point from the old point in the direction of the end point first vNewPt = vPrevSegEndPt + (vDir * fSegmentLength); // save the new point for the next point to start from // before we offset it vPrevSegEndPt = vNewPt; // offset new point from its position on the line ( more accurately, a ray, in "vDir") to a random point vNewPt.x += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation); vNewPt.y += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation) * 0.65f; // don't move y as much vNewPt.z += RAND_FLOAT(m_Properties.MinEndPtDeviation, m_Properties.MaxEndPtDeviation); GenerateBetweenPositions(vStartPos, vNewPt); // space branching out along the main bolt, only allow branching every other if (RAND_FLOAT(0.0f, 1.0f) <= m_Properties.BranchProbability && !lastSegBranched) { lastSegBranched = true; unsigned totalBranchSegs = 1; // main branch segments // determine how many branches we'll end up having so we can alpha out appropriately for (unsigned n = totalBranchSegs; n < m_Properties.MaxBranchDepth; ++n) if (RAND_FLOAT(0.0f, 1.0f) <= m_Properties.ConsecutiveBranchProbability) ++totalBranchSegs; // vNewPt here can be thought of as the start point of the next branch segment GenerateBranch(vNewPt, totalBranchSegs, totalBranchSegs, m_Properties.MaxBranchColor, signs); // clr starts at max } else if (lastSegBranched) lastSegBranched = false; vStartPos = vNewPt; // update the current startPt to be the new point ( the current segment's endpoint ) } vNewPt = m_Properties.EndPt; // finish final segment GenerateBetweenPositions(vStartPos, vNewPt); // since it will never quite exactly reach the final point, we need to add it manually m_vGeneratedPoints.push_back(LightningPointVert(vNewPt, (AngelColor)m_Properties.MaxColor, m_Properties.MaxPSSize)); // fill out the vertex buffers with the newly made point data FillBuffers(_vertBuff, _branchBuff); return true; } #define X 0 #define Y 1 #define Z 2 void LightningGenerator::GenerateBranch( const D3DXVECTOR3& _vNewPt, unsigned _numBranchesLeft, unsigned _totalBranchSegs, AngelFloatColor& _startColor, bool _signs[3] ) { // generate a branch, starting with the new point static D3DXVECTOR3 branchEnd; D3DXVECTOR3 startPt = _vNewPt; branchEnd = startPt; // randomize the new endpoint if (_numBranchesLeft == _totalBranchSegs) // means we just started branching (from main bolt) { D3DXVECTOR3 temp = RAND_D3DXVECTOR3(m_Properties.MinBranchEndPtDeviation, m_Properties.MaxBranchEndPtDeviation); temp.y *= 0.65f; branchEnd += temp; // sign bools for the three axes, false = negative _signs[X] = temp.x > 0.0f; _signs[Y] = temp.y > 0.0f; _signs[Z] = temp.z > 0.0f; } else { if (_signs[X]) // positive branchEnd.x += RAND_FLOAT(0.0f, m_Properties.MaxBranchEndPtDeviation.x); else branchEnd.x += RAND_FLOAT(m_Properties.MinBranchEndPtDeviation.x, 0.0f); if (_signs[Y]) // positive branchEnd.y += RAND_FLOAT(0.0f, m_Properties.MaxBranchEndPtDeviation.y); else branchEnd.y += RAND_FLOAT(m_Properties.MinBranchEndPtDeviation.y, 0.0f); if (_signs[Z]) // positive branchEnd.z += RAND_FLOAT(0.0f, m_Properties.MaxBranchEndPtDeviation.z); else branchEnd.z += RAND_FLOAT(m_Properties.MinBranchEndPtDeviation.z, 0.0f); } // determine color and size based on how far along the segments to be generated we are unsigned totalPts = _totalBranchSegs + 1; float percent = float(totalPts - _numBranchesLeft + 1) / float(totalPts); AngelFloatColor endColor; endColor = _startColor; endColor.a = lerp(m_Properties.MinBranchColor.a, m_Properties.MaxBranchColor.a, 1.0f - percent); endColor.r = lerp(m_Properties.MinBranchColor.r, m_Properties.MaxBranchColor.r, 1.0f - percent); endColor.g = lerp(m_Properties.MinBranchColor.g, m_Properties.MaxBranchColor.g, 1.0f - percent); endColor.b = lerp(m_Properties.MinBranchColor.b, m_Properties.MaxBranchColor.b, 1.0f - percent); float beginPSSize = lerp(m_Properties.MinBranchPSSize, m_Properties.MaxBranchPSSize, 1.0f - (float(_totalBranchSegs - _numBranchesLeft + 1) / float(totalPts))); float endPSSize = lerp(m_Properties.MinBranchPSSize, m_Properties.MaxBranchPSSize, 1.0f - percent); // add the endpoint m_vGeneratedBranchPoints.push_back(LightningPointVert(branchEnd, (AngelColor)endColor, endPSSize)); GenerateBetweenPositionsBranch(startPt, branchEnd, _startColor, endColor, beginPSSize, endPSSize); startPt = branchEnd; // recursively make more branches until we've made them all if (--_numBranchesLeft > 0) GenerateBranch(branchEnd, _numBranchesLeft, _totalBranchSegs, endColor, _signs); } void LightningGenerator::GenerateBetweenPositions(const D3DXVECTOR3& _vStart, const D3DXVECTOR3& _vEnd) { D3DXVECTOR3 vToEndPt = _vEnd - _vStart; D3DXVECTOR3 vDir; D3DXVec3Normalize(&vDir, &vToEndPt); float d = D3DXVec3Length(&vToEndPt); unsigned numPtsToGen = unsigned(D3DXVec3Length(&vToEndPt) / m_Properties.DistToParticleRatio) + 1; D3DXVECTOR3 offsetIncrement = vDir * m_Properties.DistToParticleRatio; D3DXVECTOR3 newPt = _vStart; for (unsigned i = 1; i < numPtsToGen; ++i) // start at 1, don't create the start point a second time { // always give the main bolt max color and size m_vGeneratedPoints.push_back(LightningPointVert(newPt, (AngelColor)m_Properties.MaxColor, m_Properties.MaxPSSize)); // push new point by the offset amount // in the direction of the new point newPt += offsetIncrement; } } void LightningGenerator::GenerateBetweenPositionsBranch(const D3DXVECTOR3& _vStart, const D3DXVECTOR3& _vEnd, AngelFloatColor& _startColor, AngelFloatColor& _endColor, float _startPSSize, float _endPSSize) { D3DXVECTOR3 vToEndPt = _vEnd - _vStart; D3DXVECTOR3 vDir; D3DXVec3Normalize(&vDir, &vToEndPt); float d = D3DXVec3Length(&vToEndPt); unsigned numPtsToGen = unsigned(D3DXVec3Length(&vToEndPt) / m_Properties.DistToParticleRatioBranch) + 1; D3DXVECTOR3 offsetIncrement = vDir * m_Properties.DistToParticleRatioBranch; AngelFloatColor currPtColor; currPtColor = _startColor; D3DXVECTOR4 colorStep; colorStep = ( *((D3DXVECTOR4*)&_endColor) - *((D3DXVECTOR4*)&_startColor) ) / (float)numPtsToGen; float sizeStep = (_startPSSize - _endPSSize) / (float)numPtsToGen; D3DXVECTOR3 newPt = _vStart; for (unsigned i = 1; i < numPtsToGen; ++i) // start at 1, don't create the start point a second time { // use i as offset increment, push new point (i times) by the offset amount // in the direction of the new point newPt += offsetIncrement; // the farther down the branch we go, the less visible the streak is *((D3DXVECTOR4*)&currPtColor) += colorStep; _startPSSize += sizeStep; m_vGeneratedBranchPoints.push_back(LightningPointVert(newPt, (AngelColor)currPtColor, _startPSSize)); } } void LightningGenerator::FillBuffers( LPDIRECT3DVERTEXBUFFER9*& _vertBuff, LPDIRECT3DVERTEXBUFFER9*& _branchBuffer ) { D3DVERTEXBUFFER_DESC desc; _vertBuff->GetDesc(desc); if (m_vGeneratedPoints.size() > desc.Size) MessageBox(NULL, "Vertex Buffer for main bolt too small", "Vertex Buffer error", MB_OK); SAFE_DELETE_ARRAY(m_pSavedAlphasMain); SAFE_DELETE_ARRAY(m_pSavedAlphasBranch); m_pSavedAlphasMain = new unsigned[m_vGeneratedPoints.size()]; m_pSavedAlphasBranch = new unsigned[m_vGeneratedBranchPoints.size()]; unsigned i = 0; // regular bolt vertex buffer LightningPointVert* lock = (LightningPointVert*)_vertBuff->Lock(AngelLockFlags::LOCK_DISCARD); LVertsIter itr = m_vGeneratedPoints.begin(); LVertsIter end = m_vGeneratedPoints.end(); for ( ; itr != end; ++itr, ++lock, ++i) { m_pSavedAlphasMain[i] = (*itr).Color; *lock = (*itr); } _vertBuff.Unlock(); _branchBuffer->GetDesc(desc); if (m_vGeneratedBranchPoints.size() > desc.Size) MessageBox(NULL, "Vertex Buffer for branches too small", "Vertex Buffer error", MB_OK); // branch segments vertex buffer LightningPointVert* lock2 = (LightningPointVert*)_branchBuffer->Lock(AngelLockFlags::LOCK_DISCARD); end = m_vGeneratedBranchPoints.end(); itr = m_vGeneratedBranchPoints.begin(); for (i = 0; itr != end; ++itr, ++lock2, ++i) { m_pSavedAlphasBranch[i] = (*itr).Color; *lock2 = (*itr); } _branchBuffer->Unlock(); } float LightningGenerator::RandFloatMultiple(float _min, float _max, float _multipleOf) { float retVal; // we can't even generate the multipleOf value, it's too high/low, return 0.0f if (_multipleOf > _max || _multipleOf < _min) retVal = 0.0f; else { // get the random number float f = RAND_FLOAT(_min, _max); // keep track if it's pos/neg bool signNeg = false; if (f < 0.0f) signNeg = true; float fAbs = fabs(f) + 1.0f; // make sure we round up float quotient = fAbs / _multipleOf; if (quotient < 1.0f) // this means our random value is between 0.0f and _multipleOf, return _multipleOf retVal = _multipleOf; else { // otherwise, calculate the closest multiple and return it retVal = ((int)quotient * _multipleOf); } if (signNeg) retVal = -retVal; } return retVal; } void LightningGenerator::clampLightningMovement(const D3DXVECTOR3& _min, const D3DXVECTOR3& _max, D3DXVECTOR3& _x, D3DXVECTOR3& _moveBy) { ///////////////////////////clamp if lower/////////////////////////////////// if (_x.x < _min.x) { _x.x = _min.x - _moveBy.x; _moveBy.x = -_moveBy.x; } if (_x.y < _min.y) { _x.y = _min.y - _moveBy.y; _moveBy.y = -_moveBy.y; } if (_x.z < _min.z) { _x.z = _min.z - _moveBy.z; _moveBy.z = -_moveBy.z; } ////////////////////////////clamp if higher///////////////////////////////// if (_x.x > _max.x) { _x.x = _max.x - _moveBy.x; _moveBy.x = -_moveBy.x; } if (_x.y > _max.y) { _x.y = _max.y - _moveBy.y; _moveBy.y = -_moveBy.y; } if (_x.z > _max.z) { _x.z = _max.z - _moveBy.z; _moveBy.z = -_moveBy.z; } }