Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / water_env_map.cpp
blobcb41c8d259b0a8a03dbd8cd8cb57369406cd3422
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 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"
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"
30 #ifdef DEBUG_NEW
31 #define new DEBUG_NEW
32 #endif
34 namespace NL3D
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");
47 // flatten vb params
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()
72 _UpdateTime = 0;
73 _LastRenderTick = 0;
74 invalidate();
75 _NumRenderedFaces = 0;
76 _EnvCubicSize = 0;
77 _Env2DSize = 0;
78 _LastRenderTime = -1;
79 _StartRenderTime = -1;
80 _Alpha = 255;
83 // *******************************************************************************
84 void CWaterEnvMap::init(uint cubeMapSize, uint projection2DSize, TGlobalAnimationTime updateTime, IDriver &driver)
86 // Allocate cube map
87 // a cubic texture with no sharing allowed
88 class CTextureCubeUnshared : public CTextureCube
90 public:
91 virtual bool supportSharing() const {return false;}
92 virtual uint32 getWidth(uint32 numMipMap = 0) const
94 nlassert(numMipMap == 0);
95 return Size;
97 virtual uint32 getHeight(uint32 numMipMap = 0) const
99 nlassert(numMipMap == 0);
100 return Size;
102 uint32 Size;
104 // a 2D testure
105 class CTexture2DUnshared : public CTextureBlank
107 public:
108 virtual bool supportSharing() const {return false;}
109 virtual uint32 getWidth(uint32 numMipMap = 0) const
111 nlassert(numMipMap == 0);
112 return Size;
114 virtual uint32 getHeight(uint32 numMipMap = 0) const
116 nlassert(numMipMap == 0);
117 return Size;
119 uint32 Size;
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);
144 tb->reset();
145 // Allocate projection 2D map
146 CTexture2DUnshared *env2D = new CTexture2DUnshared;
147 _Env2D = env2D;
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
155 _Env2D->reset();
156 _UpdateTime = updateTime;
157 _LastRenderTime = -1;
158 invalidate();
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
171 uint numTexToRender;
172 if (_UpdateTime > 0)
174 uint64 currRenderTick = (uint64) (time / (_UpdateTime / (NUM_FACES_TO_RENDER + 1)));
175 numTexToRender = (uint) (currRenderTick - _LastRenderTick);
176 _LastRenderTick = currRenderTick;
178 else
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)
196 // render to 2D map
197 driver.setRenderTarget(_Env2D, 0, 0, _Env2DSize, _Env2DSize);
198 doInit();
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);
205 CMatrix mat;
206 //mat.scale(0.8f);
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)
221 initFlattenVB();
222 initTestVB();
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 =
238 "!!VP1.0\n\
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\
245 END";
248 class CVertexProgramTestMeshVP : public CVertexProgram
250 public:
251 struct CIdx
253 uint ProgramConstant0;
255 CVertexProgramTestMeshVP()
257 // nelvp
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;
265 addSource(source);
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; }
278 private:
279 CIdx m_Idx;
283 static NLMISC::CSmartPtr<CVertexProgramTestMeshVP> testMeshVP;
287 // *******************************************************************************
288 void CWaterEnvMap::renderTestMesh(IDriver &driver)
290 if (!testMeshVP)
292 testMeshVP = new CVertexProgramTestMeshVP();
295 doInit();
296 CMaterial testMat;
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);
327 _FlattenVB.initEx();
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));
350 else
352 uint side = l / (FVB_NUM_SIDES / 4);
353 switch(side)
355 case 0: // top
356 x /= y;
357 y = 1.f;
358 break;
359 case 1: // left
360 y /= -x;
361 x = -1.f;
362 break;
363 case 2: // bottom
364 x /= -y;
365 y = -1.f;
366 break;
367 case 3: // right
368 y /= x;
369 x = 1.f;
370 break;
371 default:
372 nlassert(0);
373 break;
376 CVector dir;
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();
395 else
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);
410 _TestVB.initEx();
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;
417 _TestVB.lock(vbrw);
418 _TestIB.lock(ibrw);
419 uint triIndex = 0;
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));
426 CVector pos;
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);
443 } // NL3D