// 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/meshvp_wind_tree.h" #include "nel/3d/mesh_base_instance.h" #include "nel/3d/scene.h" #include "nel/3d/driver.h" #include #include "nel/misc/common.h" #include "nel/3d/render_trav.h" using namespace NLMISC; using namespace std; namespace NL3D { // *************************************************************************** // Light VP fragment constants start at 24 static const uint VPLightConstantStart = 24; // *************************************************************************** NLMISC::CSmartPtr CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp]; static const char* WindTreeVPCodeWave= "!!VP1.0 \n\ # extract from color.R the 3 factors into R0.xyz \n\ MAD R0, v[3].x, c[9].x, c[9].yzww; # col.R*3 \n\ MIN R0, R0, c[8].yyyy; # clamp each to 0,1 \n\ MAX R0, R0, c[8].xxxx; \n\ \n\ # Add influence of Bone Level1 \n\ MAD R5, c[15], R0.x, v[0]; \n\ \n\ # Sample LevelPhase into R7.yz: 0 to 3. \n\ MUL R7, v[3].xyzw, c[10].x; \n\ \n\ # Add influence of Bone Level2 \n\ ARL A0.x, R7.y; \n\ MAD R5, c[A0.x+16], R0.y, R5; \n\ \n\ # Add influence of Bone Level3 \n\ ARL A0.x, R7.z; \n\ MAD R5, c[A0.x+20], R0.z, R5; \n\ \n\ # Get normal in R6 for lighting. \n\ MOV R6, v[2]; \n\ "; static const char* WindTreeVPCodeEnd= " # compute in Projection space \n\ DP4 o[HPOS].x, c[0], R5; \n\ DP4 o[HPOS].y, c[1], R5; \n\ DP4 o[HPOS].z, c[2], R5; \n\ DP4 o[HPOS].w, c[3], R5; \n\ MOV o[TEX0], v[8]; \n\ # hulud : remove this line for the moment because it doesn't work under d3d, if it is needed, we will have to create 2 CVertexProgram objects.\n\ #MOV o[TEX1], v[9]; \n\ DP4 o[FOGC].x, c[6], R5; \n\ END \n\ "; class CVertexProgramWindTree : public CVertexProgramLighted { public: struct CIdx { uint ProgramConstants[3]; uint WindLevel1; uint WindLevel2[4]; uint WindLevel3[4]; }; CVertexProgramWindTree(uint numPls, bool specular, bool normalize); virtual ~CVertexProgramWindTree() { }; virtual void buildInfo(); const CIdx &idx() const { return m_Idx; } bool PerMeshSetup; private: CIdx m_Idx; }; CVertexProgramWindTree::CVertexProgramWindTree(uint numPls, bool specular, bool normalize) { // lighted settings m_FeaturesLighted.SupportSpecular = specular; m_FeaturesLighted.NumActivePointLights = numPls; m_FeaturesLighted.Normalize = normalize; m_FeaturesLighted.CtStartNeLVP = VPLightConstantStart; // constants cache PerMeshSetup = false; // nelvp { std::string vpCode = std::string(WindTreeVPCodeWave) + CRenderTrav::getLightVPFragmentNeLVP(numPls, VPLightConstantStart, specular, normalize) + WindTreeVPCodeEnd; CSource *source = new CSource(); source->DisplayName = NLMISC::toString("nelvp/MeshVPWindTree/%i/%s/%s", numPls, specular ? "spec" : "nospec", normalize ? "normalize" : "nonormalize"); source->Profile = CVertexProgram::nelvp; source->setSource(vpCode); source->ParamIndices["modelViewProjection"] = 0; source->ParamIndices["fog"] = 6; addSource(source); } // TODO_VP_GLSL } void CVertexProgramWindTree::buildInfo() { CVertexProgramLighted::buildInfo(); if (profile() == nelvp) { m_Idx.ProgramConstants[0] = 8; m_Idx.ProgramConstants[1] = 9; m_Idx.ProgramConstants[2] = 10; m_Idx.WindLevel1 = 15; m_Idx.WindLevel2[0] = 16; m_Idx.WindLevel2[1] = 17; m_Idx.WindLevel2[2] = 18; m_Idx.WindLevel2[3] = 19; m_Idx.WindLevel3[0] = 20; m_Idx.WindLevel3[1] = 21; m_Idx.WindLevel3[2] = 22; m_Idx.WindLevel3[3] = 23; } else { // TODO_VP_GLSL } } // *************************************************************************** float CMeshVPWindTree::speedCos(float angle) { // \todo yoyo TODO_OPTIM return cosf(angle * 2*(float)Pi); } // *************************************************************************** CMeshVPWindTree::CMeshVPWindTree() { for(uint i=0; i_VPWindTreePhase= frand(1); } // *************************************************************************** inline void CMeshVPWindTree::setupPerMesh(IDriver *driver, CScene *scene) { // process current times and current power. Only one time per render() and per CMeshVPWindTree. if(scene->getCurrentTime() != _LastSceneTime) { // Get info from scene float windPower= scene->getGlobalWindPower(); float dt= (float)(scene->getCurrentTime() - _LastSceneTime); _LastSceneTime= scene->getCurrentTime(); // Update each boneLevel time according to frequency. uint i; for(i=0; igetGlobalWindDirection() * PowerXY[i] * windPower; _MaxDeltaPos[i].z= PowerZ[i] * windPower; } /* Update the Max amplitude distance in world space, since maxdeltaPos are applied in world space, see setupPerInstanceConstants() */ _MaxVertexMove= 0; for(i=0; isetUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[0], 0, 1, 0.5f, 2); // c[9] take other useful constants. driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[1], 3.f, 0.f, -1.f, -2.f); // c[10] take Number of phase (4) for level2 and 3. -0.01 to avoid int value == 4. driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[2], 4-0.01f, 0, 0, 0); } // *************************************************************************** inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat) { CVertexProgramWindTree *program = _ActiveVertexProgram; nlassert(program); // get instance info float instancePhase= mbi->_VPWindTreePhase; // maxDeltaPos in ObjectSpace. So same world Wind direction is applied to all objects static CMatrix invWorldMatrix; // Keep only rotation part. (just need it and faster invert) invWorldMatrix.setRot(mbi->getWorldMatrix()); invWorldMatrix.invert(); static CVector maxDeltaPosOS[HrcDepth]; for(uint i=0; isetUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); // c[4..7] take the ModelView Matrix. After setupModelMatrix();00 driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::Fog)); // c[15] take Wind of level 0. float f; f= _CurrentTime[0] + instancePhase; f= speedCos(f) + Bias[0]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel1, maxDeltaPosOS[0]*f ); // c[16-19] take Wind of level 1. // Unrolled. float instTime1= _CurrentTime[1] + instancePhase; // phase 0. f= speedCos( instTime1+0 ) + Bias[1]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[0], maxDeltaPosOS[1]*f); // phase 1. f= speedCos( instTime1+0.25f ) + Bias[1]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[1], maxDeltaPosOS[1]*f); // phase 2. f= speedCos( instTime1+0.50f ) + Bias[1]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[2], maxDeltaPosOS[1]*f); // phase 3. f= speedCos( instTime1+0.75f ) + Bias[1]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[3], maxDeltaPosOS[1]*f); // c[20, 23] take Wind of level 2. // Unrolled. float instTime2= _CurrentTime[2] + instancePhase; // phase 0. f= speedCos( instTime2+0 ) + Bias[2]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[0], maxDeltaPosOS[2]*f); // phase 1. f= speedCos( instTime2+0.25f ) + Bias[2]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[1], maxDeltaPosOS[2]*f); // phase 2. f= speedCos( instTime2+0.50f ) + Bias[2]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[2], maxDeltaPosOS[2]*f); // phase 3. f= speedCos( instTime2+0.75f ) + Bias[2]; driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[3], maxDeltaPosOS[2]*f); } // *************************************************************************** bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat, const NLMISC::CVector & /*viewerPos*/) { if (driver->isVertexProgramEmulated()) return false; // Activate the good VertexProgram //=============== // Get how many pointLights are setuped now. nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); renderTrav->prepareVPLightSetup(); sint numPls= renderTrav->getNumVPLights()-1; clamp(numPls, 0, CRenderTrav::MaxVPLight-1); // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting" uint idVP= (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ; // correct VP id for correct unmber of pls. idVP= numPls*4 + idVP; // activate VP. if (driver->activeVertexProgram(_VertexProgram[idVP])) { _ActiveVertexProgram = _VertexProgram[idVP]; } else { // vertex program not supported _ActiveVertexProgram = NULL; return false; } // precompute mesh setupPerMesh(driver, scene); // Setup instance constants setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); return true; } // *************************************************************************** void CMeshVPWindTree::end(IDriver *driver) { // Disable the VertexProgram driver->activeVertexProgram(NULL); _ActiveVertexProgram = NULL; } // *************************************************************************** // tool fct static inline void SetupForMaterial(const CMaterial &mat, CScene *scene) { CRenderTrav *renderTrav= &scene->getRenderTrav(); renderTrav->changeVPLightSetupMaterial(mat, false /* don't exclude strongest */); } // *************************************************************************** void CMeshVPWindTree::setupForMaterial(const CMaterial &mat, IDriver *drv, CScene *scene, CVertexBuffer *) { SetupForMaterial(mat, scene); } // *************************************************************************** void CMeshVPWindTree::setupLighting(CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat) { nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); // setup cte for lighting CVertexProgramWindTree *program = _ActiveVertexProgram; renderTrav->beginVPLightSetup(program, invertedModelMat); } // *************************************************************************** // *************************************************************************** // MBR interface // *************************************************************************** // *************************************************************************** // *************************************************************************** bool CMeshVPWindTree::supportMeshBlockRendering() const { return true; } // *************************************************************************** bool CMeshVPWindTree::isMBRVpOk(IDriver *driver) const { initVertexPrograms(); if (driver->isVertexProgramEmulated()) { return false; } for (uint i = 0; i < NumVp; ++i) { if (!driver->compileVertexProgram(_VertexProgram[i])) { return false; } } return true; } // *************************************************************************** void CMeshVPWindTree::beginMBRMesh(IDriver *driver, CScene *scene) { /* Since need a VertexProgram Activation before activeVBHard, activate a default one bet the common one will be "NoPointLight, NoSpecular, No ForceNormalize" => 0. */ _LastMBRIdVP = 0; // activate VP. driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP]; // precompute mesh setupPerMesh(driver, scene); _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true; } // *************************************************************************** void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat) { // Get how many pointLights are setuped now. nlassert(scene != NULL); CRenderTrav *renderTrav= &scene->getRenderTrav(); renderTrav->prepareVPLightSetup(); sint numPls= renderTrav->getNumVPLights()-1; clamp(numPls, 0, CRenderTrav::MaxVPLight-1); // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting" uint idVP = (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ; // correct VP id for correct number of pls. idVP = numPls*4 + idVP; // re-activate VP if idVP different from last setup if (idVP != _LastMBRIdVP) { _LastMBRIdVP= idVP; driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]); _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP]; if (!_VertexProgram[_LastMBRIdVP]->PerMeshSetup) { // precompute mesh setupPerMesh(driver, scene); _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true; } } // setup first constants for this instance setupPerInstanceConstants(driver, scene, mbi, invertedModelMat); } // *************************************************************************** void CMeshVPWindTree::endMBRMesh(IDriver *driver) { // Disable the VertexProgram driver->activeVertexProgram(NULL); _ActiveVertexProgram = NULL; } // *************************************************************************** float CMeshVPWindTree::getMaxVertexMove() { return _MaxVertexMove; } } // NL3D