#include "StdAfx.h" #include "SprayGenerator.h" #include "NiMath.h" #include "NiTextureTransformController.h" #include "NiLinFloatKey.h" #include "NiFloatData.h" #include "NiFloatInterpolator.h" #include "Utility.h" #ifndef CODE_INGAME using namespace SceneCore; //--------------------------------------------------------------- CSprayGenerator::CSprayGenerator() { } //--------------------------------------------------------------- CSprayGenerator::~CSprayGenerator(void) { m_spSprayMesh = NULL; for (unsigned int i=0; iSetFilename(szTmp); if (m_spSprayTexture->GetHeight() <= 0) { return false; } return true; } //--------------------------------------------------------------- // 生成排浪几何体 NiGeometryPtr CSprayGenerator::GenerateSprayGeometry() { /************************ //1. 提取海岸线 *************************/ // 1.1 提取线段 _ExtractCoastEdge(); // 1.2 连接线段 _ConnectCoastEdge(); /************************ //2. 挤压海岸线,生成排浪几何体 *************************/ // 2.1 生成挤压顶点 _ExtrudeCoastEdge(); return m_spSprayMesh; } //--------------------------------------------------------------- NiGeometryPtr CSprayGenerator::GenerateSprayGeometry(std::set& gridSet) { /************************ //1. 提取海岸线 *************************/ // 1.1 提取线段 _ExtractCoastEdge(gridSet); // 1.2 连接线段 _ConnectCoastEdge(); /************************ //2. 挤压海岸线,生成排浪几何体 *************************/ // 2.1 生成挤压顶点 _ExtrudeCoastEdge(); // 2.2 创建 mesh _CreateMesh(); /************************ //3. 为排浪面片设置纹理动画 *************************/ _SetTextureAnimation(); //int iNumVert = 0; //for (int i=0; isize(); //} //NiPoint3* pVert = NiNew NiPoint3[iNumVert]; //NiColorA* pColor = NiNew NiColorA[iNumVert]; //NiPoint2* pTexture = NiNew NiPoint2[iNumVert]; //NiPoint3* pV = pVert; //for (int i=0; i::iterator iter = m_ExtrudeEdgeList[i]->begin(); // while(iter != m_ExtrudeEdgeList[i]->end()) // { // *(pV++) = *iter; // iter++; // } //} //m_spSprayMesh = NiNew NiLines(iNumVert, pVert, pColor, pTexture, 1, NiGeometryData::NBT_METHOD_NONE, NULL); //m_spSprayMesh->UpdateEffects(); //m_spSprayMesh->UpdateProperties(); //m_spSprayMesh->Update(0.0f); return m_spSprayMesh; } //--------------------------------------------------------------- // 获取已生成的排浪几何体 NiGeometryPtr CSprayGenerator::GetSprayGeometry() { return m_spSprayMesh; } void CSprayGenerator::_ExtractCoastEdge(std::set& gridSet) { // 遍历所有 grid,找出和海平面有相交的,计算出交线段 const NiPoint3* pVertices = m_pTerrain->GetVertices(); // 地形所有顶点 int iNumGridX = m_pTerrain->GetChunkNumX()*GRIDINCHUNK; // Grid 数量 int iNumGridY = m_pTerrain->GetChunkNumY()*GRIDINCHUNK; int iNumVertX = iNumGridX + 1; // 每行顶点数 std::set::iterator iter = gridSet.begin(); while (iter != gridSet.end()) { int iGridIdx = (*iter); int x = iGridIdx % iNumGridX; int y = iGridIdx / iNumGridX; /* 1__ 2 | /| |/_| 0 3 */ // 获取 grid 四个顶点索引 static int idx[4]; idx[0] = y*iNumVertX + x; idx[1] = idx[0] + iNumVertX; idx[2] = idx[1] + 1; idx[3] = idx[0] + 1; // 判断四个顶点与海平面的关系 static bool bAbove[4]; for (int i=0; i<4; i++) { if (pVertices[idx[i]].z > m_fSeaLevel) { bAbove[i] = true; } else { bAbove[i] = false; } } // 找出两个三角形和 海平面交线 // 0 1 2 三角形 { // 找出顶角和两个底角 int iTopIdx = -1; int iBottomIdx0 = -1, iBottomIdx1 = -1; // 找出与另两个顶点不再同一侧的顶点 if (bAbove[0]!=bAbove[1] && bAbove[0]!=bAbove[2]) { iTopIdx = idx[0]; iBottomIdx0 = idx[1]; iBottomIdx1 = idx[2]; } else if (bAbove[1]!=bAbove[0] && bAbove[1]!=bAbove[2]) { iTopIdx = idx[1]; iBottomIdx0 = idx[0]; iBottomIdx1 = idx[2]; } else if (bAbove[2]!=bAbove[0] && bAbove[2]!=bAbove[1]) { iTopIdx = idx[2]; iBottomIdx0 = idx[0]; iBottomIdx1 = idx[1]; } // idx[0] 顶点 与 1, 2 顶点不在同一边 if (iTopIdx > 0) { float fInterValue0 = (m_fSeaLevel - pVertices[iTopIdx].z) / (pVertices[iBottomIdx0].z - pVertices[iTopIdx].z); NiPoint3 kP0 = _Lerp(pVertices[iTopIdx], pVertices[iBottomIdx0], fInterValue0); float fInterValue1 = (m_fSeaLevel - pVertices[iTopIdx].z) / (pVertices[iBottomIdx1].z - pVertices[iTopIdx].z); NiPoint3 kP1 = _Lerp(pVertices[iTopIdx], pVertices[iBottomIdx1], fInterValue1); m_EdgeSegmentList.push_back(kP0); m_EdgeSegmentList.push_back(kP1); } } // 0 2 3 三角形 { // 找出顶角和两个底角 int iTopIdx = -1; int iBottomIdx0 = -1, iBottomIdx1 = -1; // 找出与另两个顶点不再同一侧的顶点 if (bAbove[0]!=bAbove[2] && bAbove[0]!=bAbove[3]) { iTopIdx = idx[0]; iBottomIdx0 = idx[2]; iBottomIdx1 = idx[3]; } else if (bAbove[2]!=bAbove[0] && bAbove[2]!=bAbove[3]) { iTopIdx = idx[2]; iBottomIdx0 = idx[0]; iBottomIdx1 = idx[3]; } else if (bAbove[3]!=bAbove[0] && bAbove[3]!=bAbove[2]) { iTopIdx = idx[3]; iBottomIdx0 = idx[0]; iBottomIdx1 = idx[2]; } // idx[0] 顶点 与 1, 2 顶点不在同一边 if (iTopIdx > 0) { float fInterValue0 = (m_fSeaLevel - pVertices[iTopIdx].z) / (pVertices[iBottomIdx0].z - pVertices[iTopIdx].z); NiPoint3 kP0 = _Lerp(pVertices[iTopIdx], pVertices[iBottomIdx0], fInterValue0); float fInterValue1 = (m_fSeaLevel - pVertices[iTopIdx].z) / (pVertices[iBottomIdx1].z - pVertices[iTopIdx].z); NiPoint3 kP1 = _Lerp(pVertices[iTopIdx], pVertices[iBottomIdx1], fInterValue1); m_EdgeSegmentList.push_back(kP0); m_EdgeSegmentList.push_back(kP1); } } iter++; } } //--------------------------------------------------------------- void CSprayGenerator::_ExtractCoastEdge() { const NiPoint3* pVertices = m_pTerrain->GetVertices(); // 地形所有顶点 int iNumGridX = m_pTerrain->GetChunkNumX()*GRIDINCHUNK; // Grid 数量 int iNumGridY = m_pTerrain->GetChunkNumY()*GRIDINCHUNK; int iNumVertX = iNumGridX + 1; // 每行顶点数 std::set gridSet; for (int i=0; i0) { list* pSprayLine = new list; // 遍历主表若干次,直到主表中找不到能连接到 pSprayLine 的线段为止 while (true) { int iNumAdded = 0; // 从主表中添加到 pSprayLine 中的线段数量 list::iterator iter = m_EdgeSegmentList.begin(); while (iter != m_EdgeSegmentList.end()) { if (pSprayLine->size()==0) { // 如果 pSprayLine 为空,将m_CoastEdgeList 第一个线段添加进去 pSprayLine->push_back(*iter); iter = m_EdgeSegmentList.erase(iter); pSprayLine->push_back(*iter); iter = m_EdgeSegmentList.erase(iter); } else { list::iterator iterSegBegin = iter; iter++; list::iterator iterSegEnd = iter; iter++; // *iter - new segment begin // *(iter+1) - new segment end // 取得 pSprayLine 起点和终点 NiPoint3& kSprayBegin = *(pSprayLine->begin()); NiPoint3& kSprayEnd = pSprayLine->back(); if (_SimpleEqule(*iterSegBegin, kSprayBegin)) { // 如果起点与 pSprayLine 起点相等, 将线段终点插到 pSprayLine 起点前边 pSprayLine->push_front(*iterSegEnd); } else if(_SimpleEqule(*iterSegEnd, kSprayBegin)) { // 如果终点与 pSprayLine 起点相等, 将线段起点插到 pSprayLine 起点前边 pSprayLine->push_front(*iterSegBegin); } else if (_SimpleEqule(*iterSegBegin, kSprayEnd)) { // 如果起点与 pSprayLine 终点相等, 将线段终点插到 pSprayLine 终点后边 pSprayLine->push_back(*iterSegEnd); } else if(_SimpleEqule(*iterSegEnd, kSprayEnd)) { // 如果终点与 pSprayLine 终点相等, 将线段起点插到 pSprayLine 终点后边 pSprayLine->push_back(*iterSegBegin); } else { continue; } m_EdgeSegmentList.erase(iterSegBegin); m_EdgeSegmentList.erase(iterSegEnd); iNumAdded++; } } if (iNumAdded == 0) { break; } } // 删除距离过近的点 list::iterator iter = pSprayLine->begin(); while (true) { list::iterator iterNext = iter; if (iterNext==pSprayLine->end() || ++iterNext==pSprayLine->end()) { break; } if (((*iterNext)-(*iter)).Length() <= 1.0f) { iter = pSprayLine->erase(iterNext); } else { iter++; } } if (pSprayLine->size() > 1) { m_CoastEdgeList.push_back(pSprayLine); } } } //--------------------------------------------------------------- // 挤压海岸线生成排浪面片 void CSprayGenerator::_ExtrudeCoastEdge() { // 将向量延 Z 轴旋转90度的矩阵 NiMatrix3 kMatRot; kMatRot.MakeZRotation(NI_HALF_PI); // 遍历所有 m_CoastEdgeList, 生成每个顶点的挤压顶点 for (unsigned i=0; i& coastLine = *(m_CoastEdgeList[i]); if (coastLine.size() <= 1) { continue; } list* pExtrudeLine = new list; list::iterator iterLast = coastLine.begin(); // 上一个顶点 list::iterator iter = coastLine.begin(); // 当前顶点 list::iterator iterNext = coastLine.begin(); // 下一个顶点 NiPoint3 kLastExtrudeDir; // 上个顶点的挤压方向 while (true) { NiPoint3 kDir; if (iter == coastLine.begin()) { // 如果是第一个顶点, 延线段垂直方向在 XY 平面上挤压 iterNext++; NiPoint3 kLine = (*iterNext) - (*iter); kLine.Unitize(); kDir = kMatRot * kLine; // 积压方向旋转 PI/2 // 检查挤压方向是否合法 if (!_CheckExtrudeDirection(*iter, kDir)) { kDir = -kDir; } } else if(iterNext == coastLine.end()) { // 如果是最后一个顶点, 延线段在 XY 平面垂直方向上挤压 NiPoint3 kLine = (*iterLast) - (*iter); kLine.Unitize(); kDir = kMatRot * kLine; // 积压方向旋转 PI/2 if (kDir.Dot(kLastExtrudeDir) < 0) { kDir = -kDir; } } else { // 如果不是第一个顶点也不是最后一个顶点, 计算两相邻线段的 half vector NiPoint3 kV0 = (*iterLast)-(*iter); kV0.Unitize(); NiPoint3 kV1 = (*iterNext)-(*iter); kV1.Unitize(); kDir = kV0 + kV1; kDir.Unitize(); if (kDir.Dot(kLastExtrudeDir) < 0) { // 保证挤压方向与上顶点挤压方向夹角为锐角,方向正确 kDir = -kDir; } } // 延 kDir 方向挤压顶点. kLastExtrudeDir = kDir; NiPoint3 kExtrudeVert = (*iter) + kDir*m_fWidth; pExtrudeLine->push_back(kExtrudeVert); // 向后偏移 iterator iterLast = iter; ++iter = iterNext; if (iter == coastLine.end()) { break; } iterNext++; } // 将挤压结果放入列表. m_ExtrudeEdgeList.push_back(pExtrudeLine); } } void CSprayGenerator::_CreateMesh() { // 统计顶点个数, 三角形个数 unsigned int uiNumVert = 0; unsigned int uiNumTri = 0; for (unsigned i=0; isize(); uiNumTri += (m_CoastEdgeList[i]->size()-1) * 2; } uiNumVert*=2; // 填充顶点数据 NiPoint3* pVert = NiNew NiPoint3[uiNumVert]; // 顶点坐标 NiPoint3* pNormals = NiNew NiPoint3[uiNumVert]; // 法线 NiColorA* pColors = NiNew NiColorA[uiNumVert]; // 顶点色 NiPoint2* pTextures = NiNew NiPoint2[uiNumVert];// 顶点UV unsigned short* pusTriList = (unsigned short*)NiMalloc(sizeof(unsigned short)*(uiNumTri*3)); // 保证原线段和挤压后线段数量相同 assert(m_CoastEdgeList.size() == m_ExtrudeEdgeList.size()); NiPoint3* pV = pVert; // 用来遍历填充顶点坐标的指针 // 拷贝顶点位置 for (unsigned i=0; isize() == m_ExtrudeEdgeList[i]->size()); list::iterator iter = m_CoastEdgeList[i]->begin(); list::iterator iterEx = m_ExtrudeEdgeList[i]->begin(); while (iter != m_CoastEdgeList[i]->end()) { //NiPoint3 kOffset(NiRand()%100*0.005f, NiRand()%100*0.005f, 0.0f); *(pV) = *iter; pV++; //+ kOffset *(pV) = *iterEx ; pV++; //+ kOffset iter++; iterEx++; } } for (unsigned i=0; isize()-1); // 这段的grid个数 // 0 2 // 口 // 1 3 for (int k=0; ksize()*2; // 偏移顶点索引 } m_spSprayMesh = NiNew NiTriShape(uiNumVert, pVert, pNormals, pColors, pTextures, 1, NiGeometryData::NBT_METHOD_NONE, uiNumTri, pusTriList); NiStencilProperty* pkStencilProp = NiNew NiStencilProperty(); pkStencilProp->SetDrawMode(NiStencilProperty::DRAW_BOTH); m_spSprayMesh->AttachProperty(pkStencilProp); NiAlphaProperty* pkAlphaProp = NiNew NiAlphaProperty(); pkAlphaProp->SetAlphaBlending(true); pkAlphaProp->SetAlphaTesting(false); pkAlphaProp->SetSrcBlendMode(NiAlphaProperty::ALPHA_SRCALPHA); pkAlphaProp->SetDestBlendMode(NiAlphaProperty::ALPHA_INVSRCALPHA); m_spSprayMesh->AttachProperty(pkAlphaProp); NiVertexColorProperty* pVCProp = NiNew NiVertexColorProperty; pVCProp->SetLightingMode( NiVertexColorProperty::LIGHTING_E); pVCProp->SetSourceMode( NiVertexColorProperty::SOURCE_EMISSIVE); m_spSprayMesh->AttachProperty(pVCProp); NiZBufferProperty* pZProp = NiNew NiZBufferProperty; pZProp->SetZBufferTest(true); pZProp->SetTestFunction(NiZBufferProperty::TEST_LESSEQUAL); pZProp->SetZBufferWrite(false); m_spSprayMesh->AttachProperty(pZProp); m_spSprayMesh->UpdateEffects(); m_spSprayMesh->UpdateProperties(); m_spSprayMesh->Update(0.0f); } // 为排浪面片设置纹理动画 void CSprayGenerator::_SetTextureAnimation() { // 创建 NiTexturingProperty* pkTexProp = NiNew NiTexturingProperty(); m_spSprayMesh->AttachProperty(pkTexProp); pkTexProp->SetBaseTexture(m_spSprayTexture); pkTexProp->SetBaseClampMode(NiTexturingProperty::CLAMP_S_CLAMP_T); pkTexProp->SetApplyMode(NiTexturingProperty::APPLY_MODULATE); NiTexturingProperty::Map* pkMap = pkTexProp->GetBaseMap(); // 设置 纹理 UV 动画 NiTextureTransformController* pkTexController = NiNew NiTextureTransformController(pkTexProp, pkMap, NiTextureTransformController::TT_TRANSLATE_V); pkTexController->SetBeginKeyTime( 0.0 ); pkTexController->SetEndKeyTime(m_fT3); pkTexController->SetCycleType( NiTimeController::LOOP ); NiLinFloatKey * pFloatKey = NiNew NiLinFloatKey[4]; pFloatKey[0].SetTime(m_fT0); pFloatKey[0].SetValue(m_fV0); pFloatKey[1].SetTime(m_fT1); pFloatKey[1].SetValue(m_fV1); pFloatKey[2].SetTime(m_fT2); pFloatKey[2].SetValue(m_fV2); pFloatKey[3].SetTime(m_fT3); pFloatKey[3].SetValue(m_fV3); NiFloatData * pFloatData = NiNew NiFloatData(); pFloatData->SetAnim( pFloatKey, 4, NiAnimationKey::LINKEY ); NiFloatInterpolator * pFloatInter = NiNew NiFloatInterpolator( pFloatData ); pkTexController->SetInterpolator( pFloatInter ); pkTexController->SetActive(true); pkTexController->Start(); m_spSprayMesh->SetSelectiveUpdate(true); m_spSprayMesh->SetSelectiveUpdatePropertyControllers(true); m_spSprayMesh->Update(0.0f); m_spSprayMesh->UpdateProperties(); m_spSprayMesh->UpdateControllers(0.0f); } #endif