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/>.
21 #include "nel/3d/water_env_map.h"
22 #include "nel/3d/vertex_buffer.h"
23 #include "nel/3d/index_buffer.h"
24 #include "nel/3d/material.h"
25 #include "nel/3d/driver.h"
26 #include "nel/3d/u_water_env_map.h"
27 #include "nel/misc/common.h"
28 #include "nel/3d/viewport.h"
39 CVertexBuffer
CWaterEnvMap::_FlattenVB
; // vb to map cube map top hemisphere to a 2D map
40 CIndexBuffer
CWaterEnvMap::_FlattenIB("CWaterEnvMap::_FlattenIB");
41 bool CWaterEnvMap::_FlattenVBInitialized
;
42 CMaterial
CWaterEnvMap::_MaterialPassThru
;
43 CMaterial
CWaterEnvMap::_MaterialPassThruZTest
;
44 CVertexBuffer
CWaterEnvMap::_TestVB
;
45 CIndexBuffer
CWaterEnvMap::_TestIB("CWaterEnvMap::_TestIB");
48 static const uint FVB_NUM_SIDES
= 32;
49 static const uint FVB_NUM_SECTIONS
= 10;
50 static const uint FVB_NUM_VERTS
= FVB_NUM_SIDES
* (FVB_NUM_SECTIONS
+ 1);
51 static const uint FVB_NUM_TRIS
= 2 * FVB_NUM_SIDES
* FVB_NUM_SECTIONS
;
53 // tmp : test vertex buffer
54 static const uint TEST_VB_NUM_SEGMENT
= 16;
55 static const uint TEST_VB_NUM_SLICE
= 16;
56 static const uint TEST_VB_NUM_TRIS
= 2 * TEST_VB_NUM_SEGMENT
* TEST_VB_NUM_SLICE
;
58 // Get index of a vertex in the flatten vb from its side an section
59 static uint32
inline getFVBVertex(uint section
, uint side
)
61 nlassert(section
<= FVB_NUM_SECTIONS
);
62 return (uint32
) (section
+ (side
% FVB_NUM_SIDES
) * (FVB_NUM_SECTIONS
+ 1));
66 const uint NUM_FACES_TO_RENDER
= 5;
69 // *******************************************************************************
70 CWaterEnvMap::CWaterEnvMap()
75 _NumRenderedFaces
= 0;
79 _StartRenderTime
= -1;
83 // *******************************************************************************
84 void CWaterEnvMap::init(uint cubeMapSize
, uint projection2DSize
, TGlobalAnimationTime updateTime
, IDriver
&driver
)
87 // a cubic texture with no sharing allowed
88 class CTextureCubeUnshared
: public CTextureCube
91 virtual bool supportSharing() const {return false;}
92 virtual uint32
getWidth(uint32 numMipMap
= 0) const
94 nlassert(numMipMap
== 0);
97 virtual uint32
getHeight(uint32 numMipMap
= 0) const
99 nlassert(numMipMap
== 0);
105 class CTexture2DUnshared
: public CTextureBlank
108 virtual bool supportSharing() const {return false;}
109 virtual uint32
getWidth(uint32 numMipMap
= 0) const
111 nlassert(numMipMap
== 0);
114 virtual uint32
getHeight(uint32 numMipMap
= 0) const
116 nlassert(numMipMap
== 0);
121 nlassert(cubeMapSize
> 0);
122 nlassert(NLMISC::isPowerOf2(cubeMapSize
));
123 nlassert(projection2DSize
> 0);
124 nlassert(NLMISC::isPowerOf2(projection2DSize
));
125 CTextureCubeUnshared
*envCubic
= new CTextureCubeUnshared
;
126 _EnvCubic
= envCubic
;
127 _EnvCubic
->setRenderTarget(true); // we will render to the texture
128 _EnvCubic
->setWrapS(ITexture::Clamp
);
129 _EnvCubic
->setWrapT(ITexture::Clamp
);
130 _EnvCubic
->setFilterMode(ITexture::Linear
, ITexture::LinearMipMapOff
);
131 CTexture2DUnshared
*tb
= new CTexture2DUnshared
;
132 tb
->resize(cubeMapSize
, cubeMapSize
); // Unfortunately, must allocate memory in order for the driver to figure out the size
133 // that it needs to allocate for the texture, though its datas are never used (it is a render target)
134 tb
->Size
= cubeMapSize
;
135 tb
->setFilterMode(ITexture::Linear
, ITexture::LinearMipMapOff
);
136 for(uint k
= 0; k
< 6; ++k
)
138 _EnvCubic
->setTexture((CTextureCube::TFace
) k
, tb
);
139 _EnvCubic
->getTexture((CTextureCube::TFace
) k
)->setRenderTarget(true);
141 envCubic
->Size
= cubeMapSize
;
142 // setup the texture to force the driver to allocate vram for it
143 driver
.setupTexture(*_EnvCubic
);
145 // Allocate projection 2D map
146 CTexture2DUnshared
*env2D
= new CTexture2DUnshared
;
148 _Env2D
->resize(projection2DSize
, projection2DSize
);
149 env2D
->Size
= projection2DSize
;
150 _Env2D
->setWrapS(ITexture::Clamp
);
151 _Env2D
->setWrapT(ITexture::Clamp
);
152 _Env2D
->setRenderTarget(true); // we will render to the texture
153 _Env2D
->setFilterMode(ITexture::Linear
, ITexture::LinearMipMapOff
);
154 driver
.setupTexture(*_Env2D
); // allocate vram
156 _UpdateTime
= updateTime
;
157 _LastRenderTime
= -1;
159 _NumRenderedFaces
= 0;
160 _EnvCubicSize
= cubeMapSize
;
161 _Env2DSize
= projection2DSize
;
164 // *******************************************************************************
165 void CWaterEnvMap::update(TGlobalAnimationTime time
, IDriver
&driver
)
167 if (_LastRenderTime
== time
) return;
168 _LastRenderTime
= time
;
169 // First five updates are used to render the cubemap faces (bottom face is not rendered)
170 // Sixth update project the cubemap into a 2D texture
174 uint64 currRenderTick
= (uint64
) (time
/ (_UpdateTime
/ (NUM_FACES_TO_RENDER
+ 1)));
175 numTexToRender
= (uint
) (currRenderTick
- _LastRenderTick
);
176 _LastRenderTick
= currRenderTick
;
180 numTexToRender
= NUM_FACES_TO_RENDER
+ 1;
182 if (!numTexToRender
) return;
183 if (_NumRenderedFaces
== 0)
185 _StartRenderTime
= time
;
187 uint lastCubeFacesToRender
= std::min((uint
) NUM_FACES_TO_RENDER
, _NumRenderedFaces
+ numTexToRender
); // we don't render negative Z (only top hemisphere is used)
188 for(uint k
= _NumRenderedFaces
; k
< lastCubeFacesToRender
; ++k
)
190 driver
.setRenderTarget(_EnvCubic
, 0, 0, _EnvCubicSize
, _EnvCubicSize
, 0, (uint32
) k
);
191 render((CTextureCube::TFace
) k
, _StartRenderTime
);
193 _NumRenderedFaces
= lastCubeFacesToRender
;
194 if (_NumRenderedFaces
== NUM_FACES_TO_RENDER
&& (_NumRenderedFaces
+ numTexToRender
) > NUM_FACES_TO_RENDER
)
197 driver
.setRenderTarget(_Env2D
, 0, 0, _Env2DSize
, _Env2DSize
);
200 driver
.activeVertexProgram(NULL
);
201 driver
.activeVertexBuffer(_FlattenVB
);
202 driver
.activeIndexBuffer(_FlattenIB
);
203 driver
.setFrustum(-1.f
, 1.f
, -1.f
, 1.f
, 0.f
, 1.f
, false);
204 driver
.setupViewMatrix(CMatrix::Identity
);
207 driver
.setupModelMatrix(mat
);
208 _MaterialPassThru
.setTexture(0, _EnvCubic
);
209 _MaterialPassThru
.texConstantColor(0, CRGBA(255, 255, 255, _Alpha
));
210 driver
.renderTriangles(_MaterialPassThru
, 0, FVB_NUM_TRIS
);
211 _NumRenderedFaces
= 0; // start to render again
213 driver
.setRenderTarget(NULL
);
216 // *******************************************************************************
217 void CWaterEnvMap::doInit()
219 if (!_FlattenVBInitialized
)
223 _FlattenVBInitialized
= true;
224 _MaterialPassThru
.setLighting(false);
225 _MaterialPassThru
.texEnvOpRGB(0, CMaterial::Replace
);
226 _MaterialPassThru
.texEnvArg0RGB(0, CMaterial::Texture
, CMaterial::SrcColor
);
227 _MaterialPassThru
.texEnvOpAlpha(0, CMaterial::Replace
);
228 _MaterialPassThru
.texEnvArg0Alpha(0, CMaterial::Constant
, CMaterial::SrcAlpha
);
229 _MaterialPassThru
.texConstantColor(0, CRGBA(255, 255, 255, 255));
230 _MaterialPassThru
.setDoubleSided(true);
231 _MaterialPassThruZTest
= _MaterialPassThru
;
232 _MaterialPassThru
.setZWrite(false);
233 _MaterialPassThru
.setZFunc(CMaterial::always
);
237 static const char *testMeshVPstr =
239 DP4 o[HPOS].x, c[0], v[0]; \n\
240 DP4 o[HPOS].y, c[1], v[0]; \n\
241 DP4 o[HPOS].z, c[2], v[0]; \n\
242 DP4 o[HPOS].w, c[3], v[0]; \n\
243 MAD o[COL0], v[8], c[4].xxxx, c[4].yyyy; \n\
244 MOV o[TEX0], v[8]; \n\
248 class CVertexProgramTestMeshVP : public CVertexProgram
253 uint ProgramConstant0;
255 CVertexProgramTestMeshVP()
259 CSource *source = new CSource();
260 source->Profile = nelvp;
261 source->DisplayName = "testMeshVP/nelvp";
262 source->setSourcePtr(testMeshVPstr);
263 source->ParamIndices["modelViewProjection"] = 0;
264 source->ParamIndices["programConstant0"] = 4;
268 virtual ~CVertexProgramTestMeshVP()
272 virtual void buildInfo()
274 m_Idx.ProgramConstant0 = getUniformIndex("programConstant0");
275 nlassert(m_Idx.ProgramConstant0 != ~0);
277 inline const CIdx &idx() { return m_Idx; }
283 static NLMISC::CSmartPtr<CVertexProgramTestMeshVP> testMeshVP;
287 // *******************************************************************************
288 void CWaterEnvMap::renderTestMesh(IDriver &driver)
292 testMeshVP = new CVertexProgramTestMeshVP();
297 testMat.setLighting(false);
298 testMat.texEnvOpRGB(0, CMaterial::Modulate);
299 testMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
300 testMat.texEnvArg0RGB(1, CMaterial::Diffuse, CMaterial::SrcColor);
301 testMat.texEnvOpAlpha(0, CMaterial::Replace);
302 testMat.texEnvArg0Alpha(0, CMaterial::Constant, CMaterial::SrcAlpha);
303 testMat.texConstantColor(0, CRGBA(255, 255, 255, 255));
304 testMat.setDoubleSided(true);
305 testMat.setZWrite(false);
306 testMat.setZFunc(CMaterial::always);
307 // tmp : test cubemap
308 driver.activeVertexProgram(testMeshVP);
309 driver.activeVertexBuffer(_TestVB);
310 driver.activeIndexBuffer(_TestIB);
311 _MaterialPassThruZTest.setTexture(0, _EnvCubic);
312 driver.setUniformMatrix(IDriver::VertexProgram, testMeshVP->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity);
313 driver.setUniform2f(IDriver::VertexProgram, testMeshVP->idx().ProgramConstant0, 2.f, 1.f);
314 //driver.renderTriangles(testMat, 0, TEST_VB_NUM_TRIS);
315 driver.renderTriangles(_MaterialPassThruZTest, 0, TEST_VB_NUM_TRIS);
316 driver.activeVertexProgram(NULL);
319 // *******************************************************************************
320 void CWaterEnvMap::initFlattenVB()
322 _FlattenVB
.setPreferredMemory(CVertexBuffer::AGPPreferred
, true);
323 _FlattenVB
.setName("Flatten VB");
324 _FlattenVB
.clearValueEx();
325 _FlattenVB
.addValueEx (CVertexBuffer::Position
, CVertexBuffer::Float3
);
326 _FlattenVB
.addValueEx (CVertexBuffer::TexCoord0
, CVertexBuffer::Float3
);
328 nlctassert(FVB_NUM_SIDES
% 4 == 0); // number of sides must be a multiple of 4 so that sections sides will align with corners
329 _FlattenVB
.setNumVertices(FVB_NUM_VERTS
);
330 _FlattenIB
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
331 _FlattenIB
.setNumIndexes(3 * FVB_NUM_TRIS
);
333 CVertexBufferReadWrite vbrw
;
334 CIndexBufferReadWrite ibrw
;
335 _FlattenVB
.lock(vbrw
);
336 _FlattenIB
.lock(ibrw
);
337 for(uint l
= 0; l
< FVB_NUM_SIDES
; ++l
)
339 double angle
= NLMISC::Pi
* 0.25 + 2 * NLMISC::Pi
* (double) l
/ (double) FVB_NUM_SIDES
;
340 for(uint k
= 0; k
< FVB_NUM_SECTIONS
+ 1; ++k
)
342 double radius
= (double) k
/ (double) (FVB_NUM_SECTIONS
- 1);
343 float x
= (float) (radius
* cos(angle
));
344 float y
= (float) (radius
* sin(angle
));
345 if (k
< FVB_NUM_SECTIONS
)
347 ibrw
.setTri(3 * 2 * (k
+ (l
* FVB_NUM_SECTIONS
)), getFVBVertex(k
, l
), getFVBVertex(k
+ 1, l
+ 1), getFVBVertex(k
+ 1, l
));
348 ibrw
.setTri(3 * (2 * (k
+ (l
* FVB_NUM_SECTIONS
)) + 1), getFVBVertex(k
, l
), getFVBVertex(k
, l
+ 1), getFVBVertex(k
+ 1, l
+ 1));
352 uint side
= l
/ (FVB_NUM_SIDES
/ 4);
377 //dir.sphericToCartesian(1.f, (float) angle, (float) (NLMISC::Pi * 0.5 * acos(std::max(0.f, (1.f - (float) k / (FVB_NUM_SECTIONS - 1))))));
378 dir
.sphericToCartesian(1.f
, (float) angle
, (float) acos(std::min(1.f
, (float) k
/ (FVB_NUM_SECTIONS
- 1))));
379 vbrw
.setValueFloat3Ex(CVertexBuffer::Position
, getFVBVertex(k
, l
), x
, 0.5f
, y
);
380 vbrw
.setValueFloat3Ex(CVertexBuffer::TexCoord0
, getFVBVertex(k
, l
), -dir
.x
, dir
.z
, -dir
.y
);
386 // *******************************************************************************
387 void CWaterEnvMap::invalidate()
389 _LastRenderTime
= -1;
390 _StartRenderTime
= -1;
391 if (_UpdateTime
== 0)
393 _LastRenderTick
= std::numeric_limits
<uint64
>::max();
397 _LastRenderTick
-= (NUM_FACES_TO_RENDER
+ 1);
399 _NumRenderedFaces
= 0;
402 // *******************************************************************************
403 void CWaterEnvMap::initTestVB()
405 _TestVB
.setPreferredMemory(CVertexBuffer::AGPPreferred
, true);
406 _TestVB
.setName("TestVB");
407 _TestVB
.clearValueEx();
408 _TestVB
.addValueEx (CVertexBuffer::Position
, CVertexBuffer::Float3
);
409 _TestVB
.addValueEx (CVertexBuffer::TexCoord0
, CVertexBuffer::Float3
);
411 _TestVB
.setNumVertices(TEST_VB_NUM_SEGMENT
* 2 * (TEST_VB_NUM_SLICE
+ 1));
412 _TestIB
.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT
);
413 _TestIB
.setNumIndexes(3 * TEST_VB_NUM_TRIS
);
415 CVertexBufferReadWrite vbrw
;
416 CIndexBufferReadWrite ibrw
;
420 for(uint k
= 0; k
< TEST_VB_NUM_SEGMENT
; ++k
)
422 float theta
= 2 * (float) (NLMISC::Pi
* (double) k
/ (double) TEST_VB_NUM_SEGMENT
);
423 for(uint l
= 0; l
<= TEST_VB_NUM_SLICE
; ++l
)
425 float phi
= (float) (NLMISC::Pi
/ 2 * (1 - 2 * (double) l
/ (double) TEST_VB_NUM_SLICE
));
427 pos
.sphericToCartesian(1.f
, theta
, phi
);
428 #define VERT_INDEX(k, l) ((l) + ((k) % TEST_VB_NUM_SEGMENT) * (TEST_VB_NUM_SLICE + 1))
429 vbrw
.setVertexCoord(VERT_INDEX(k
, l
), pos
);
430 vbrw
.setValueFloat3Ex(CVertexBuffer::TexCoord0
, VERT_INDEX(k
, l
), pos
);
431 if (l
!= TEST_VB_NUM_SLICE
)
433 ibrw
.setTri(3 * triIndex
++, VERT_INDEX(k
, l
), VERT_INDEX(k
+ 1, l
), VERT_INDEX(k
+ 1, l
+ 1));
434 ibrw
.setTri(3 * triIndex
++, VERT_INDEX(k
, l
), VERT_INDEX(k
+ 1, l
+ 1), VERT_INDEX(k
, l
+ 1));
438 nlassert(triIndex
== TEST_VB_NUM_TRIS
);