Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / meshvp_wind_tree.cpp
blob55b39ce8b8c649ef8b563d80d37a2d6d5a13d573
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
22 #include "nel/3d/meshvp_wind_tree.h"
23 #include "nel/3d/mesh_base_instance.h"
24 #include "nel/3d/scene.h"
25 #include "nel/3d/driver.h"
26 #include <cmath>
27 #include "nel/misc/common.h"
28 #include "nel/3d/render_trav.h"
31 using namespace NLMISC;
32 using namespace std;
34 #ifdef DEBUG_NEW
35 #define new DEBUG_NEW
36 #endif
38 namespace NL3D
42 // ***************************************************************************
43 // Light VP fragment constants start at 24
44 static const uint VPLightConstantStart = 24;
47 // ***************************************************************************
48 NLMISC::CSmartPtr<CVertexProgramWindTree> CMeshVPWindTree::_VertexProgram[CMeshVPWindTree::NumVp];
50 static const char* WindTreeVPCodeWave=
51 "!!VP1.0 \n\
52 # extract from color.R the 3 factors into R0.xyz \n\
53 MAD R0, v[3].x, c[9].x, c[9].yzww; # col.R*3 \n\
54 MIN R0, R0, c[8].yyyy; # clamp each to 0,1 \n\
55 MAX R0, R0, c[8].xxxx; \n\
56 \n\
57 # Add influence of Bone Level1 \n\
58 MAD R5, c[15], R0.x, v[0]; \n\
59 \n\
60 # Sample LevelPhase into R7.yz: 0 to 3. \n\
61 MUL R7, v[3].xyzw, c[10].x; \n\
62 \n\
63 # Add influence of Bone Level2 \n\
64 ARL A0.x, R7.y; \n\
65 MAD R5, c[A0.x+16], R0.y, R5; \n\
66 \n\
67 # Add influence of Bone Level3 \n\
68 ARL A0.x, R7.z; \n\
69 MAD R5, c[A0.x+20], R0.z, R5; \n\
70 \n\
71 # Get normal in R6 for lighting. \n\
72 MOV R6, v[2]; \n\
75 static const char* WindTreeVPCodeEnd=
76 " # compute in Projection space \n\
77 DP4 o[HPOS].x, c[0], R5; \n\
78 DP4 o[HPOS].y, c[1], R5; \n\
79 DP4 o[HPOS].z, c[2], R5; \n\
80 DP4 o[HPOS].w, c[3], R5; \n\
81 MOV o[TEX0], v[8]; \n\
82 # 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\
83 #MOV o[TEX1], v[9]; \n\
84 DP4 o[FOGC].x, c[6], R5; \n\
85 END \n\
89 class CVertexProgramWindTree : public CVertexProgramLighted
91 public:
92 struct CIdx
94 uint ProgramConstants[3];
95 uint WindLevel1;
96 uint WindLevel2[4];
97 uint WindLevel3[4];
99 CVertexProgramWindTree(uint numPls, bool specular, bool normalize);
100 virtual ~CVertexProgramWindTree() { };
101 virtual void buildInfo();
102 const CIdx &idx() const { return m_Idx; }
104 bool PerMeshSetup;
106 private:
107 CIdx m_Idx;
111 CVertexProgramWindTree::CVertexProgramWindTree(uint numPls, bool specular, bool normalize)
113 // lighted settings
114 m_FeaturesLighted.SupportSpecular = specular;
115 m_FeaturesLighted.NumActivePointLights = numPls;
116 m_FeaturesLighted.Normalize = normalize;
117 m_FeaturesLighted.CtStartNeLVP = VPLightConstantStart;
119 // constants cache
120 PerMeshSetup = false;
122 // nelvp
124 std::string vpCode = std::string(WindTreeVPCodeWave)
125 + CRenderTrav::getLightVPFragmentNeLVP(numPls, VPLightConstantStart, specular, normalize)
126 + WindTreeVPCodeEnd;
128 CSource *source = new CSource();
129 source->DisplayName = NLMISC::toString("nelvp/MeshVPWindTree/%i/%s/%s", numPls, specular ? "spec" : "nospec", normalize ? "normalize" : "nonormalize");
130 source->Profile = CVertexProgram::nelvp;
131 source->setSource(vpCode);
132 source->ParamIndices["modelViewProjection"] = 0;
133 source->ParamIndices["fog"] = 6;
134 addSource(source);
137 // TODO_VP_GLSL
140 void CVertexProgramWindTree::buildInfo()
142 CVertexProgramLighted::buildInfo();
143 if (profile() == nelvp)
145 m_Idx.ProgramConstants[0] = 8;
146 m_Idx.ProgramConstants[1] = 9;
147 m_Idx.ProgramConstants[2] = 10;
148 m_Idx.WindLevel1 = 15;
149 m_Idx.WindLevel2[0] = 16;
150 m_Idx.WindLevel2[1] = 17;
151 m_Idx.WindLevel2[2] = 18;
152 m_Idx.WindLevel2[3] = 19;
153 m_Idx.WindLevel3[0] = 20;
154 m_Idx.WindLevel3[1] = 21;
155 m_Idx.WindLevel3[2] = 22;
156 m_Idx.WindLevel3[3] = 23;
158 else
160 // TODO_VP_GLSL
165 // ***************************************************************************
166 float CMeshVPWindTree::speedCos(float angle)
168 // \todo yoyo TODO_OPTIM
169 return cosf(angle * 2*(float)Pi);
173 // ***************************************************************************
174 CMeshVPWindTree::CMeshVPWindTree()
176 for(uint i=0; i<HrcDepth; i++)
178 Frequency[i]= 1;
179 FrequencyWindFactor[i]= 0;
180 PowerXY[i]= 0;
181 PowerZ[i]= 0;
182 Bias[i]= 0;
183 // Init currentTime.
184 _CurrentTime[i]= 0;
186 SpecularLighting= false;
188 _LastSceneTime= 0;
189 _MaxVertexMove= 0;
193 // ***************************************************************************
194 CMeshVPWindTree::~CMeshVPWindTree()
199 // ***************************************************************************
200 void CMeshVPWindTree::serial(NLMISC::IStream &f)
202 (void)f.serialVersion(0);
204 nlassert(HrcDepth==3);
205 for(uint i=0; i<HrcDepth; i++)
207 f.serial(Frequency[i]);
208 f.serial(FrequencyWindFactor[i]);
209 f.serial(PowerXY[i]);
210 f.serial(PowerZ[i]);
211 f.serial(Bias[i]);
213 f.serial(SpecularLighting);
216 void CMeshVPWindTree::initVertexPrograms()
218 // init the vertexProgram code.
219 static bool vpCreated= false;
221 if(!vpCreated)
223 vpCreated= true;
224 // All vpcode and begin() written for HrcDepth==3
225 nlassert(HrcDepth==3);
227 // For all possible VP.
228 for(uint i=0;i<NumVp;i++)
230 // setup of the VPLight fragment
231 uint numPls= i/4;
232 // FIXME: normalize=true makes trees dance, workaround for issue #160
233 bool normalize= false; //(i&1)!=0;
234 bool specular= (i&2)!=0;
236 // combine
237 _VertexProgram[i] = new CVertexProgramWindTree(numPls, normalize, specular);
242 // ***************************************************************************
243 void CMeshVPWindTree::initInstance(CMeshBaseInstance *mbi)
245 initVertexPrograms();
247 // init a random phase.
248 mbi->_VPWindTreePhase= frand(1);
251 // ***************************************************************************
252 inline void CMeshVPWindTree::setupPerMesh(IDriver *driver, CScene *scene)
254 // process current times and current power. Only one time per render() and per CMeshVPWindTree.
255 if(scene->getCurrentTime() != _LastSceneTime)
257 // Get info from scene
258 float windPower= scene->getGlobalWindPower();
260 float dt= (float)(scene->getCurrentTime() - _LastSceneTime);
261 _LastSceneTime= scene->getCurrentTime();
263 // Update each boneLevel time according to frequency.
264 uint i;
265 for(i=0; i<HrcDepth; i++)
267 _CurrentTime[i]+= dt*(Frequency[i] + FrequencyWindFactor[i]*windPower);
268 // get it between 0 and 1. Important for float precision problems.
269 _CurrentTime[i]= (float)fmod(_CurrentTime[i], 1);
272 // Update each boneLevel maximum amplitude vector.
273 for(i=0; i<HrcDepth; i++)
275 _MaxDeltaPos[i]= scene->getGlobalWindDirection() * PowerXY[i] * windPower;
276 _MaxDeltaPos[i].z= PowerZ[i] * windPower;
279 /* Update the Max amplitude distance
280 in world space, since maxdeltaPos are applied in world space, see setupPerInstanceConstants()
282 _MaxVertexMove= 0;
283 for(i=0; i<HrcDepth; i++)
285 _MaxVertexMove+= _MaxDeltaPos[i].norm();
289 CVertexProgramWindTree *program = _ActiveVertexProgram;
290 nlassert(program);
292 // Setup common constants for each instances.
293 // c[8] take useful constants.
294 driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[0],
295 0, 1, 0.5f, 2);
296 // c[9] take other useful constants.
297 driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[1],
298 3.f, 0.f, -1.f, -2.f);
299 // c[10] take Number of phase (4) for level2 and 3. -0.01 to avoid int value == 4.
300 driver->setUniform4f(IDriver::VertexProgram, program->idx().ProgramConstants[2],
301 4-0.01f, 0, 0, 0);
304 // ***************************************************************************
305 inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat)
307 CVertexProgramWindTree *program = _ActiveVertexProgram;
308 nlassert(program);
310 // get instance info
311 float instancePhase= mbi->_VPWindTreePhase;
314 // maxDeltaPos in ObjectSpace. So same world Wind direction is applied to all objects
315 static CMatrix invWorldMatrix;
316 // Keep only rotation part. (just need it and faster invert)
317 invWorldMatrix.setRot(mbi->getWorldMatrix());
318 invWorldMatrix.invert();
319 static CVector maxDeltaPosOS[HrcDepth];
320 for(uint i=0; i<HrcDepth; i++)
322 maxDeltaPosOS[i]= invWorldMatrix.mulVector(_MaxDeltaPos[i]);
326 // Setup lighting and lighting constants
327 setupLighting(scene, mbi, invertedModelMat);
329 // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix();
330 driver->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::ModelViewProjection),
331 IDriver::ModelViewProjection, IDriver::Identity);
332 // c[4..7] take the ModelView Matrix. After setupModelMatrix();00
333 driver->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::Fog));
336 // c[15] take Wind of level 0.
337 float f;
338 f= _CurrentTime[0] + instancePhase;
339 f= speedCos(f) + Bias[0];
340 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel1,
341 maxDeltaPosOS[0]*f );
344 // c[16-19] take Wind of level 1.
345 // Unrolled.
346 float instTime1= _CurrentTime[1] + instancePhase;
347 // phase 0.
348 f= speedCos( instTime1+0 ) + Bias[1];
349 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[0],
350 maxDeltaPosOS[1]*f);
351 // phase 1.
352 f= speedCos( instTime1+0.25f ) + Bias[1];
353 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[1],
354 maxDeltaPosOS[1]*f);
355 // phase 2.
356 f= speedCos( instTime1+0.50f ) + Bias[1];
357 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[2],
358 maxDeltaPosOS[1]*f);
359 // phase 3.
360 f= speedCos( instTime1+0.75f ) + Bias[1];
361 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel2[3],
362 maxDeltaPosOS[1]*f);
365 // c[20, 23] take Wind of level 2.
366 // Unrolled.
367 float instTime2= _CurrentTime[2] + instancePhase;
368 // phase 0.
369 f= speedCos( instTime2+0 ) + Bias[2];
370 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[0],
371 maxDeltaPosOS[2]*f);
372 // phase 1.
373 f= speedCos( instTime2+0.25f ) + Bias[2];
374 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[1],
375 maxDeltaPosOS[2]*f);
376 // phase 2.
377 f= speedCos( instTime2+0.50f ) + Bias[2];
378 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[2],
379 maxDeltaPosOS[2]*f);
380 // phase 3.
381 f= speedCos( instTime2+0.75f ) + Bias[2];
382 driver->setUniform3f(IDriver::VertexProgram, program->idx().WindLevel3[3],
383 maxDeltaPosOS[2]*f);
386 // ***************************************************************************
387 bool CMeshVPWindTree::begin(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat, const NLMISC::CVector & /*viewerPos*/)
389 if (driver->isVertexProgramEmulated()) return false;
392 // Activate the good VertexProgram
393 //===============
395 // Get how many pointLights are setuped now.
396 nlassert(scene != NULL);
397 CRenderTrav *renderTrav= &scene->getRenderTrav();
398 renderTrav->prepareVPLightSetup();
399 sint numPls= renderTrav->getNumVPLights()-1;
400 clamp(numPls, 0, CRenderTrav::MaxVPLight-1);
402 // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting"
403 uint idVP= (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ;
404 // correct VP id for correct unmber of pls.
405 idVP= numPls*4 + idVP;
406 // activate VP.
407 if (driver->activeVertexProgram(_VertexProgram[idVP]))
409 _ActiveVertexProgram = _VertexProgram[idVP];
411 else
413 // vertex program not supported
414 _ActiveVertexProgram = NULL;
415 return false;
419 // precompute mesh
420 setupPerMesh(driver, scene);
422 // Setup instance constants
423 setupPerInstanceConstants(driver, scene, mbi, invertedModelMat);
428 return true;
431 // ***************************************************************************
432 void CMeshVPWindTree::end(IDriver *driver)
434 // Disable the VertexProgram
435 driver->activeVertexProgram(NULL);
436 _ActiveVertexProgram = NULL;
439 // ***************************************************************************
440 // tool fct
441 static inline void SetupForMaterial(const CMaterial &mat, CScene *scene)
443 CRenderTrav *renderTrav= &scene->getRenderTrav();
444 renderTrav->changeVPLightSetupMaterial(mat, false /* don't exclude strongest */);
447 // ***************************************************************************
448 void CMeshVPWindTree::setupForMaterial(const CMaterial &mat,
449 IDriver *drv,
450 CScene *scene,
451 CVertexBuffer *)
453 SetupForMaterial(mat, scene);
456 // ***************************************************************************
457 void CMeshVPWindTree::setupLighting(CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat)
459 nlassert(scene != NULL);
460 CRenderTrav *renderTrav= &scene->getRenderTrav();
461 // setup cte for lighting
462 CVertexProgramWindTree *program = _ActiveVertexProgram;
463 renderTrav->beginVPLightSetup(program, invertedModelMat);
467 // ***************************************************************************
468 // ***************************************************************************
469 // MBR interface
470 // ***************************************************************************
471 // ***************************************************************************
474 // ***************************************************************************
475 bool CMeshVPWindTree::supportMeshBlockRendering() const
477 return true;
480 // ***************************************************************************
481 bool CMeshVPWindTree::isMBRVpOk(IDriver *driver) const
483 initVertexPrograms();
485 if (driver->isVertexProgramEmulated())
487 return false;
489 for (uint i = 0; i < NumVp; ++i)
491 if (!driver->compileVertexProgram(_VertexProgram[i]))
493 return false;
496 return true;
499 // ***************************************************************************
500 void CMeshVPWindTree::beginMBRMesh(IDriver *driver, CScene *scene)
502 /* Since need a VertexProgram Activation before activeVBHard, activate a default one
503 bet the common one will be "NoPointLight, NoSpecular, No ForceNormalize" => 0.
505 _LastMBRIdVP = 0;
507 // activate VP.
508 driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]);
509 _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP];
511 // precompute mesh
512 setupPerMesh(driver, scene);
513 _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true;
516 // ***************************************************************************
517 void CMeshVPWindTree::beginMBRInstance(IDriver *driver, CScene *scene, CMeshBaseInstance *mbi, const NLMISC::CMatrix &invertedModelMat)
519 // Get how many pointLights are setuped now.
520 nlassert(scene != NULL);
521 CRenderTrav *renderTrav= &scene->getRenderTrav();
522 renderTrav->prepareVPLightSetup();
523 sint numPls= renderTrav->getNumVPLights()-1;
524 clamp(numPls, 0, CRenderTrav::MaxVPLight-1);
526 // Enable normalize only if requested by user. Because lighting don't manage correct "scale lighting"
527 uint idVP = (SpecularLighting?2:0) + (driver->isForceNormalize()?1:0) ;
528 // correct VP id for correct number of pls.
529 idVP = numPls*4 + idVP;
531 // re-activate VP if idVP different from last setup
532 if (idVP != _LastMBRIdVP)
534 _LastMBRIdVP= idVP;
535 driver->activeVertexProgram(_VertexProgram[_LastMBRIdVP]);
536 _ActiveVertexProgram = _VertexProgram[_LastMBRIdVP];
538 if (!_VertexProgram[_LastMBRIdVP]->PerMeshSetup)
540 // precompute mesh
541 setupPerMesh(driver, scene);
542 _VertexProgram[_LastMBRIdVP]->PerMeshSetup = true;
546 // setup first constants for this instance
547 setupPerInstanceConstants(driver, scene, mbi, invertedModelMat);
550 // ***************************************************************************
551 void CMeshVPWindTree::endMBRMesh(IDriver *driver)
553 // Disable the VertexProgram
554 driver->activeVertexProgram(NULL);
555 _ActiveVertexProgram = NULL;
558 // ***************************************************************************
559 float CMeshVPWindTree::getMaxVertexMove()
561 return _MaxVertexMove;
565 } // NL3D