1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2017 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
27 #include "nel/misc/common.h"
28 #include "nel/3d/render_trav.h"
31 using namespace NLMISC
;
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
=
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\
57 # Add influence of Bone Level1 \n\
58 MAD R5, c[15], R0.x, v[0]; \n\
60 # Sample LevelPhase into R7.yz: 0 to 3. \n\
61 MUL R7, v[3].xyzw, c[10].x; \n\
63 # Add influence of Bone Level2 \n\
65 MAD R5, c[A0.x+16], R0.y, R5; \n\
67 # Add influence of Bone Level3 \n\
69 MAD R5, c[A0.x+20], R0.z, R5; \n\
71 # Get normal in R6 for lighting. \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\
89 class CVertexProgramWindTree
: public CVertexProgramLighted
94 uint ProgramConstants
[3];
99 CVertexProgramWindTree(uint numPls
, bool specular
, bool normalize
);
100 virtual ~CVertexProgramWindTree() { };
101 virtual void buildInfo();
102 const CIdx
&idx() const { return m_Idx
; }
111 CVertexProgramWindTree::CVertexProgramWindTree(uint numPls
, bool specular
, bool normalize
)
114 m_FeaturesLighted
.SupportSpecular
= specular
;
115 m_FeaturesLighted
.NumActivePointLights
= numPls
;
116 m_FeaturesLighted
.Normalize
= normalize
;
117 m_FeaturesLighted
.CtStartNeLVP
= VPLightConstantStart
;
120 PerMeshSetup
= false;
124 std::string vpCode
= std::string(WindTreeVPCodeWave
)
125 + CRenderTrav::getLightVPFragmentNeLVP(numPls
, VPLightConstantStart
, specular
, normalize
)
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;
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;
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
++)
179 FrequencyWindFactor
[i
]= 0;
186 SpecularLighting
= false;
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
]);
213 f
.serial(SpecularLighting
);
216 void CMeshVPWindTree::initVertexPrograms()
218 // init the vertexProgram code.
219 static bool vpCreated
= false;
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
232 // FIXME: normalize=true makes trees dance, workaround for issue #160
233 bool normalize
= false; //(i&1)!=0;
234 bool specular
= (i
&2)!=0;
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.
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()
283 for(i
=0; i
<HrcDepth
; i
++)
285 _MaxVertexMove
+= _MaxDeltaPos
[i
].norm();
289 CVertexProgramWindTree
*program
= _ActiveVertexProgram
;
292 // Setup common constants for each instances.
293 // c[8] take useful constants.
294 driver
->setUniform4f(IDriver::VertexProgram
, program
->idx().ProgramConstants
[0],
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],
304 // ***************************************************************************
305 inline void CMeshVPWindTree::setupPerInstanceConstants(IDriver
*driver
, CScene
*scene
, CMeshBaseInstance
*mbi
, const NLMISC::CMatrix
&invertedModelMat
)
307 CVertexProgramWindTree
*program
= _ActiveVertexProgram
;
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.
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.
346 float instTime1
= _CurrentTime
[1] + instancePhase
;
348 f
= speedCos( instTime1
+0 ) + Bias
[1];
349 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel2
[0],
352 f
= speedCos( instTime1
+0.25f
) + Bias
[1];
353 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel2
[1],
356 f
= speedCos( instTime1
+0.50f
) + Bias
[1];
357 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel2
[2],
360 f
= speedCos( instTime1
+0.75f
) + Bias
[1];
361 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel2
[3],
365 // c[20, 23] take Wind of level 2.
367 float instTime2
= _CurrentTime
[2] + instancePhase
;
369 f
= speedCos( instTime2
+0 ) + Bias
[2];
370 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel3
[0],
373 f
= speedCos( instTime2
+0.25f
) + Bias
[2];
374 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel3
[1],
377 f
= speedCos( instTime2
+0.50f
) + Bias
[2];
378 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel3
[2],
381 f
= speedCos( instTime2
+0.75f
) + Bias
[2];
382 driver
->setUniform3f(IDriver::VertexProgram
, program
->idx().WindLevel3
[3],
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
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
;
407 if (driver
->activeVertexProgram(_VertexProgram
[idVP
]))
409 _ActiveVertexProgram
= _VertexProgram
[idVP
];
413 // vertex program not supported
414 _ActiveVertexProgram
= NULL
;
420 setupPerMesh(driver
, scene
);
422 // Setup instance constants
423 setupPerInstanceConstants(driver
, scene
, mbi
, invertedModelMat
);
431 // ***************************************************************************
432 void CMeshVPWindTree::end(IDriver
*driver
)
434 // Disable the VertexProgram
435 driver
->activeVertexProgram(NULL
);
436 _ActiveVertexProgram
= NULL
;
439 // ***************************************************************************
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
,
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 // ***************************************************************************
470 // ***************************************************************************
471 // ***************************************************************************
474 // ***************************************************************************
475 bool CMeshVPWindTree::supportMeshBlockRendering() const
480 // ***************************************************************************
481 bool CMeshVPWindTree::isMBRVpOk(IDriver
*driver
) const
483 initVertexPrograms();
485 if (driver
->isVertexProgramEmulated())
489 for (uint i
= 0; i
< NumVp
; ++i
)
491 if (!driver
->compileVertexProgram(_VertexProgram
[i
]))
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.
508 driver
->activeVertexProgram(_VertexProgram
[_LastMBRIdVP
]);
509 _ActiveVertexProgram
= _VertexProgram
[_LastMBRIdVP
];
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
)
535 driver
->activeVertexProgram(_VertexProgram
[_LastMBRIdVP
]);
536 _ActiveVertexProgram
= _VertexProgram
[_LastMBRIdVP
];
538 if (!_VertexProgram
[_LastMBRIdVP
]->PerMeshSetup
)
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
;