1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 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_per_pixel_light.h"
23 #include "nel/3d/mesh_base_instance.h"
24 #include "nel/3d/driver.h"
25 #include "nel/3d/scene.h"
26 #include "nel/3d/render_trav.h"
27 #include "nel/3d/render_trav.h"
28 #include "nel/3d/vertex_program_parse.h"
42 NLMISC::CSmartPtr
<CVertexProgramPerPixelLight
> CMeshVPPerPixelLight::_VertexProgram
[NumVp
];
44 // ***************************************************************************
45 // Light VP fragment constants start at 24
46 static const uint VPLightConstantStart
= 24;
48 // ***************************************************************************
49 // ***************************************************************************
55 /** We store the first tangent vector of the tangent space in v[8].
56 * The third vector can be computed using a cross product.
57 * We assume that normal and tangent are normalized.
58 * The position of light must be expressed in object space, and is stored in c[4]
60 // omni light + specular, no normalization
61 static const char* PPLightingVPCodeBegin
=
63 #compute B = N ^ T \n\
65 MUL R1, R6.yzxw, v[9].zxyw; \n\
66 MAD R1, v[9].yzxw, -R6.zxyw, R1; \n\
67 #vector in tangent space = [ T B N ] * L \n\
68 ADD R2, c[4], -v[0]; # compute L \n\
69 DP3 R3, R2, R2; # get L normalized \n\
72 DP3 o[TEX0].x, v[9], R2; # get x of light vector in tangent space \n\
73 DP3 o[TEX0].y, R1, R2; # get y \n\
74 DP3 o[TEX0].z, R6, R2; # get z \n\
76 ADD R3, c[5], - v[0]; # compute V (return to eye) \n\
77 #compute inverse norm of V \n\
80 #we normalize V and add it to L \n\
81 MAD R2, R4, R3, R2; #H in R1 \n\
87 #compute H in tangent space \n\
88 DP3 o[TEX2].x, v[9], R2; \n\
89 DP3 o[TEX2].y, R1, R2; \n\
90 DP3 o[TEX2].z, R6, R2; \n\
91 # Position in R5 for additionnal lighting \n\
93 # Normal is in R6 for additionnal lighting \n\
96 /// The same as above, but for skin / MRM : This normalize the tangent basis.
97 static const char* PPLightingVPNormalizeCodeBegin
=
99 #normalize the normal \n\
100 DP3 R1, v[2], v[2]; \n\
102 MUL R6, v[2], R1; \n\
104 #normalize the second vector \n\
105 DP3 R1, R6, v[9]; \n\
106 MAD R1, R6, -R1, v[9]; #subtract the normal component \n\
109 MUL R5, R1, R2; #second basis vector in R5 \n\
110 #compute B = N ^ T \n\
111 MUL R1, R6.yzxw, R5.zxyw; \n\
112 MAD R1, R5.yzxw, -R6.zxyw, R1; #third basis vector in R1 \n\
114 #vector in tangent space = [ T B N ] * L \n\
115 ADD R2, c[4], -v[0]; # compute L \n\
116 DP3 R3, R2, R2; # get L normalized \n\
119 DP3 o[TEX0].x, R5, R2; # get x of light vector in tangent space \n\
120 DP3 o[TEX0].y, R1, R2; # get y \n\
121 DP3 o[TEX0].z, R6, R2; # get z \n\
124 ADD R3, c[5], - v[0]; # compute V (return to eye) \n\
125 #compute inverse norm of V \n\
128 #we normalize V and add it to L \n\
129 MAD R2, R4, R3, R2; #H in R1 \n\
135 #compute H in tangent space \n\
136 DP3 o[TEX2].x, R5, R2; \n\
137 DP3 o[TEX2].y, R1, R2; \n\
138 DP3 o[TEX2].z, R6, R2; \n\
139 # Position in R5 for additionnal lighting \n\
141 # Normal is in R6 for additionnal lighting \n\
145 // Omni light, no specular
146 static const char* PPLightingNoSpecVPCodeBegin
=
148 #compute B = N ^ T \n\
150 MUL R1, R6.yzxw, v[9].zxyw; \n\
151 MAD R1, v[9].yzxw, -R6.zxyw, R1; \n\
153 #vector in tangent space = [ T B N ] * L \n\
154 ADD R2, c[4], -v[0]; # compute L \n\
155 DP3 R3, R2, R2; # get L normalized \n\
158 DP3 o[TEX0].x, v[9], R2; # get x of light vector in tangent space \n\
159 DP3 o[TEX0].y, R1, R2; # get y \n\
160 DP3 o[TEX0].z, R6, R2; # get z \n\
163 # Normal is in R6 for additionnal lighting \n\
164 # Position in R5 for additionnal lighting \n\
168 /// Omni light with normalization and no specular
169 static const char* PPLightingVPNormalizeNoSpecCodeBegin
=
171 #normalize the normal \n\
172 DP3 R1, v[2], v[2]; \n\
174 MUL R6, v[2], R1; \n\
176 #normalize the second vector \n\
177 DP3 R1, R6, v[9]; \n\
178 MAD R1, R6, -R1, v[9]; #subtract the normal component \n\
181 MUL R5, R1, R2; #second basis vector in R5 \n\
182 #compute B = N ^ T \n\
183 MUL R1, R6.yzxw, R5.zxyw; \n\
184 MAD R1, R5.yzxw, -R6.zxyw, R1; #third basis vector in R1 \n\
186 #vector in tangent space = [ T B N ] * L \n\
187 ADD R2, c[4], -v[0]; # compute L \n\
188 DP3 R3, R2, R2; # get L normalized \n\
191 DP3 o[TEX0].x, R5, R2; # get x of light vector in tangent space \n\
192 DP3 o[TEX0].y, R1, R2; # get y \n\
193 DP3 o[TEX0].z, R6, R2; # get z \n\
195 # Normal is in R6 for additionnal lighting \n\
196 # Position in R5 for additionnal lighting \n\
201 /////////////////////////
202 // directionnal lights //
203 /////////////////////////
205 /// We store the direction of the light rather than its position
206 /// The direction must be normalized and expressed in model space.
208 // directionnal, no normalization
209 static const char* PPLightingDirectionnalVPCodeBegin
=
211 #compute B = N ^ T \n\
213 MUL R1, R6.yzxw, v[9].zxyw; \n\
214 MAD R1, v[9].yzxw, -R6.zxyw, R1; \n\
216 #vector in tangent space = [ T B N ] * L \n\
217 DP3 o[TEX0].x, v[9], -c[4]; # get x of light vector in tangent space \n\
218 DP3 o[TEX0].y, R1, -c[4]; # get y \n\
219 DP3 o[TEX0].z, R6, -c[4]; # get z \n\
222 ADD R3, c[5], - v[0]; # compute V (return to eye) \n\
223 #compute inverse norm of V \n\
226 #we normalize V and add it to L. It gives H unnormalized \n\
227 MAD R2, R4, R3, -c[4]; #H in R1 \n\
233 #compute H in tangent space \n\
234 DP3 o[TEX2].x, v[9], R2; \n\
235 DP3 o[TEX2].y, R1, R2; \n\
236 DP3 o[TEX2].z, R6, R2; \n\
238 # Normal is in R6 for additionnal lighting \n\
239 # Position in R5 for additionnal lighting \n\
243 // directionnal + normalization
244 static const char* PPLightingDirectionnalVPNormalizeCodeBegin
=
246 #normalize the normal \n\
247 DP3 R1, v[2], v[2]; \n\
249 MUL R6, v[2], R1; \n\
251 #normalize the second vector \n\
252 DP3 R1, R6, v[9]; \n\
253 MAD R1, R6, -R1, v[9]; #subtract the normal component \n\
256 MUL R5, R1, R2; #second basis vector in R5 \n\
257 #compute B = N ^ T \n\
258 MUL R1, R6.yzxw, R5.zxyw; \n\
259 MAD R1, R5.yzxw, -R6.zxyw, R1; #third basis vector in R1 \n\
261 #vector in tangent space = [ T B N ] * L \n\
262 DP3 o[TEX0].x, R5, -c[4]; # get x of light vector in tangent space \n\
263 DP3 o[TEX0].y, R1, -c[4]; # get y \n\
264 DP3 o[TEX0].z, R6, -c[4]; # get z \n\
267 ADD R3, c[5], - v[0]; # compute V (return to eye) \n\
268 #compute inverse norm of V \n\
271 #we normalize V and add it to L \n\
272 MAD R2, R4, R3, -c[4]; #H in R1 \n\
278 #compute H in tangent space \n\
279 DP3 o[TEX2].x, R5, R2; \n\
280 DP3 o[TEX2].y, R1, R2; \n\
281 DP3 o[TEX2].z, R6, R2; \n\
282 # Normal is in R6 for additionnal lighting \n\
283 # Position in R5 for additionnal lighting \n\
288 // directionnal, no normalization, no specular
289 static const char* PPLightingDirectionnalNoSpecVPCodeBegin
=
291 #compute B = N ^ T \n\
293 MUL R1, R6.yzxw, v[9].zxyw; \n\
294 MAD R1, v[9].yzxw, -R6.zxyw, R1; \n\
296 #vector in tangent space = [ T B N ] * L \n\
297 DP3 o[TEX0].x, v[9], -c[4]; # get x of light vector in tangent space \n\
298 DP3 o[TEX0].y, R1, -c[4]; # get y \n\
299 DP3 o[TEX0].z, R6, -c[4]; # get z \n\
300 # Normal is in R6 for additionnal lighting \n\
301 # Position in R5 for additionnal lighting \n\
305 // directionnal + normalization, no specular
306 static const char* PPLightingDirectionnalNoSpecVPNormalizeCodeBegin
=
308 #normalize the normal \n\
309 DP3 R1, v[2], v[2]; \n\
311 MUL R6, v[2], R1; \n\
313 #normalize the second vector \n\
314 DP3 R1, R6, v[9]; \n\
315 MAD R1, R6, -R1, v[9]; #subtract the normal component \n\
318 MUL R5, R1, R2; #second basis vector in R5 \n\
319 #compute B = N ^ T \n\
320 MUL R1, R6.yzxw, R5.zxyw; \n\
321 MAD R1, R5.yzxw, -R6.zxyw, R1; #third basis vector in R1 \n\
323 #vector in tangent space = [ T B N ] * L \n\
324 DP3 o[TEX0].x, R5, -c[4]; # get x of light vector in tangent space \n\
325 DP3 o[TEX0].y, R1, -c[4]; # get y \n\
326 DP3 o[TEX0].z, R6, -c[4]; # get z \n\
328 # Normal is in R6 for additionnal lighting \n\
329 # Position in R5 for additionnal lighting \n\
335 // End of per pixel lighting code : compute pos and setup texture
336 static const char* PPLightingVPCodeEnd
=
337 " # compute in Projection space \n\
338 DP4 o[HPOS].x, c[0], v[0]; \n\
339 DP4 o[HPOS].y, c[1], v[0]; \n\
340 DP4 o[HPOS].z, c[2], v[0]; \n\
341 DP4 o[HPOS].w, c[3], v[0]; \n\
342 MOV o[TEX1], v[8]; \n\
346 /***************************************************************/
347 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
348 /***************************************************************
350 // Test code : map the tangent space vector as the diffuse color
351 static const char* PPLightingVPCodeTest =
353 # compute in Projection space \n\
354 DP4 o[HPOS].x, c[0], v[0]; \n\
355 DP4 o[HPOS].y, c[1], v[0]; \n\
356 DP4 o[HPOS].z, c[2], v[0]; \n\
357 DP4 o[HPOS].w, c[3], v[0]; \n\
358 MOV o[COL0], v[9]; \n\
361 ***************************************************************/
363 class CVertexProgramPerPixelLight
: public CVertexProgramLighted
368 /// Position or direction of strongest light
373 CVertexProgramPerPixelLight(uint vp
);
374 virtual ~CVertexProgramPerPixelLight() { };
375 virtual void buildInfo();
376 const CIdx
&idx() const { return m_Idx
; }
383 CVertexProgramPerPixelLight::CVertexProgramPerPixelLight(uint vp
)
386 m_FeaturesLighted
.SupportSpecular
= (vp
& 2) != 0;
387 m_FeaturesLighted
.NumActivePointLights
= MaxLight
- 1;
388 m_FeaturesLighted
.Normalize
= false;
389 m_FeaturesLighted
.CtStartNeLVP
= VPLightConstantStart
;
393 // Gives each vp name
394 // Bit 0 : 1 when it is a directionnal light
395 // Bit 1 : 1 when specular is needed
396 // Bit 2 : 1 when normalization of the tangent space is needed
397 static const char *vpName
[] =
400 PPLightingDirectionnalNoSpecVPCodeBegin
,
401 PPLightingNoSpecVPCodeBegin
,
403 PPLightingDirectionnalVPCodeBegin
,
404 PPLightingVPCodeBegin
,
405 /////////////// normalized versions
407 PPLightingDirectionnalNoSpecVPNormalizeCodeBegin
,
408 PPLightingVPNormalizeNoSpecCodeBegin
,
410 PPLightingDirectionnalVPNormalizeCodeBegin
,
411 PPLightingVPNormalizeCodeBegin
,
414 uint numvp
= sizeof(vpName
) / sizeof(const char *);
415 nlassert(CMeshVPPerPixelLight::NumVp
== numvp
); // make sure that it is in sync with header..todo : compile time assert :)
417 // \todo yoyo TODO_OPTIM Manage different number of pointLights
418 // NB: never call getLightVPFragmentNeLVP() with normalize, because already done by PerPixel fragment before.
419 std::string vpCode
= std::string(vpName
[vp
])
420 + std::string("# ***************") // temp for debug
421 + CRenderTrav::getLightVPFragmentNeLVP(
422 m_FeaturesLighted
.NumActivePointLights
,
423 m_FeaturesLighted
.CtStartNeLVP
,
424 m_FeaturesLighted
.SupportSpecular
,
425 m_FeaturesLighted
.Normalize
)
426 + std::string("# ***************") // temp for debug
427 + std::string(PPLightingVPCodeEnd
);
429 /** For test : parse those programs before they are used.
430 * As a matter of fact some program will works with the NV_VERTEX_PROGRAM extension,
431 * but won't with EXT_vertex_shader, because there are some limitations (can't read a temp
432 * register that hasn't been written before..)
435 CVPParser::TProgram result
;
436 std::string parseOutput
;
437 if (!vpParser
.parse(vpCode
.c_str(), result
, parseOutput
))
439 nlwarning(parseOutput
.c_str());
444 CSource
*source
= new CSource();
445 source
->DisplayName
= NLMISC::toString("nelvp/MeshVPPerPixel/%i", vp
);
446 source
->Profile
= CVertexProgram::nelvp
;
447 source
->setSource(vpCode
);
448 source
->ParamIndices
["modelViewProjection"] = 0;
458 void CVertexProgramPerPixelLight::buildInfo()
460 CVertexProgramLighted::buildInfo();
461 if (profile() == nelvp
)
463 m_Idx
.StrongestLight
= 4;
464 if (m_FeaturesLighted
.SupportSpecular
)
470 m_Idx
.ViewerPos
= std::numeric_limits
<uint
>::max();
477 nlassert(m_Idx
.StrongestLight
!= std::numeric_limits
<uint
>::max());
478 if (m_FeaturesLighted
.SupportSpecular
)
480 nlassert(m_Idx
.ViewerPos
!=std::numeric_limits
<uint
>::max());
485 //=================================================================================
486 void CMeshVPPerPixelLight::initInstance(CMeshBaseInstance
*mbi
)
488 // init the vertexProgram code.
489 static bool vpCreated
= false;
494 for (uint vp
= 0; vp
< NumVp
; ++vp
)
496 _VertexProgram
[vp
] = new CVertexProgramPerPixelLight(vp
);
501 //=================================================================================
502 bool CMeshVPPerPixelLight::begin(IDriver
*drv
,
503 CScene
*scene
, CMeshBaseInstance
*mbi
,
504 const NLMISC::CMatrix
&invertedModelMat
,
505 const NLMISC::CVector
&viewerPos
)
507 // test if supported by driver
508 if (drv
->isVertexProgramEmulated()
509 || !drv
->supportPerPixelLighting(SpecularLighting
))
514 enable(true, drv
); // must enable the vertex program before the vb is activated
515 CVertexProgramPerPixelLight
*program
= _ActiveVertexProgram
;
518 // failed to compile vertex program
522 CRenderTrav
*renderTrav
= &scene
->getRenderTrav();
523 /// Setup for gouraud lighting
524 renderTrav
->prepareVPLightSetup();
525 renderTrav
->beginVPLightSetup(program
, invertedModelMat
);
527 sint strongestLightIndex
= renderTrav
->getStrongestLightIndex();
528 if (strongestLightIndex
== -1) return false; // if no strongest light, disable this vertex program
529 // setup the strongest light
530 ///\todo disabling of specular lighting with this shader
531 const CLight
&strongestLight
= renderTrav
->getDriverLight(strongestLightIndex
);
533 switch (strongestLight
.getMode())
535 case CLight::DirectionalLight
:
537 // put light direction in object space
538 NLMISC::CVector lPos
= invertedModelMat
.mulVector(strongestLight
.getDirection());
539 drv
->setUniform3f(IDriver::VertexProgram
, program
->idx().StrongestLight
, lPos
);
540 _IsPointLight
= false;
543 case CLight::PointLight
:
545 // put light in object space
546 NLMISC::CVector lPos
= invertedModelMat
* strongestLight
.getPosition();
547 drv
->setUniform3f(IDriver::VertexProgram
, program
->idx().StrongestLight
, lPos
);
548 _IsPointLight
= true;
557 if (SpecularLighting
)
559 // viewer pos in object space
560 NLMISC::CVector vPos
= invertedModelMat
* viewerPos
;
561 drv
->setUniform3f(IDriver::VertexProgram
, program
->idx().ViewerPos
, vPos
);
564 // c[0..3] take the ModelViewProjection Matrix. After setupModelMatrix();
565 drv
->setUniformMatrix(IDriver::VertexProgram
, program
->getUniformIndex(CProgramIndex::ModelViewProjection
), IDriver::ModelViewProjection
, IDriver::Identity
);
571 //=================================================================================
572 void CMeshVPPerPixelLight::end(IDriver
*drv
)
578 //=================================================================================
579 void CMeshVPPerPixelLight::serial(NLMISC::IStream
&f
)
581 (void)f
.serialVersion(0);
582 f
.serial(SpecularLighting
);
586 //=================================================================================
587 void CMeshVPPerPixelLight::enable(bool enabled
, IDriver
*drv
)
589 if (enabled
!= _Enabled
)
594 /* for (uint k = 0; k < NumVp; ++k)
596 nlinfo("test vp %d", k);
597 drv->activeVertexProgram(_VertexProgram[k].get());
599 uint idVP
= (drv
->isForceNormalize() ? 4 : 0)
600 | (SpecularLighting
? 2 : 0)
601 | (_IsPointLight
? 1 : 0);
603 if (drv
->activeVertexProgram((CVertexProgramPerPixelLight
*)_VertexProgram
[idVP
]))
605 _ActiveVertexProgram
= _VertexProgram
[idVP
];
609 _ActiveVertexProgram
= NULL
;
614 drv
->activeVertexProgram(NULL
);
615 _ActiveVertexProgram
= NULL
;
621 //=================================================================================
622 bool CMeshVPPerPixelLight::setupForMaterial(const CMaterial
&mat
,
627 bool enabled
= (mat
.getShader() == CMaterial::PerPixelLighting
|| mat
.getShader() == CMaterial::PerPixelLightingNoSpec
);
628 bool change
= (enabled
!= _Enabled
);
629 enable(enabled
, drv
); // enable disable the vertex program (for material that don't have the right shader)
632 CRenderTrav
*renderTrav
= &scene
->getRenderTrav();
633 renderTrav
->changeVPLightSetupMaterial(mat
, true /* exclude strongest*/);
635 NLMISC::CRGBA pplDiffuse
, pplSpecular
;
636 renderTrav
->getStrongestLightColors(pplDiffuse
, pplSpecular
);
637 drv
->setPerPixelLightingLight(pplDiffuse
, pplSpecular
, mat
.getShininess());
641 //=================================================================================
642 void CMeshVPPerPixelLight::setupForMaterial(const CMaterial
&mat
,
648 if (setupForMaterial(mat
, drv
, scene
)) // a switch from v.p enabled / disabled force to reactivate the vertex buffer.
650 drv
->activeVertexBuffer(*vb
);