397 lines
14 KiB
C++
397 lines
14 KiB
C++
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "std3d.h"
|
||
|
#include "nel/3d/water_env_map.h"
|
||
|
#include "nel/3d/vertex_buffer.h"
|
||
|
#include "nel/3d/index_buffer.h"
|
||
|
#include "nel/3d/material.h"
|
||
|
#include "nel/3d/driver.h"
|
||
|
#include "nel/3d/u_water_env_map.h"
|
||
|
#include "nel/misc/common.h"
|
||
|
#include "nel/3d/viewport.h"
|
||
|
|
||
|
namespace NL3D
|
||
|
|
||
|
{
|
||
|
|
||
|
|
||
|
CVertexBuffer CWaterEnvMap::_FlattenVB; // vb to map cube map top hemisphere to a 2D map
|
||
|
CIndexBuffer CWaterEnvMap::_FlattenIB("CWaterEnvMap::_FlattenIB");
|
||
|
bool CWaterEnvMap::_FlattenVBInitialized;
|
||
|
CMaterial CWaterEnvMap::_MaterialPassThru;
|
||
|
CMaterial CWaterEnvMap::_MaterialPassThruZTest;
|
||
|
CVertexBuffer CWaterEnvMap::_TestVB;
|
||
|
CIndexBuffer CWaterEnvMap::_TestIB("CWaterEnvMap::_TestIB");
|
||
|
|
||
|
// flatten vb params
|
||
|
static const uint FVB_NUM_SIDES = 32;
|
||
|
static const uint FVB_NUM_SECTIONS = 10;
|
||
|
static const uint FVB_NUM_VERTS = FVB_NUM_SIDES * (FVB_NUM_SECTIONS + 1);
|
||
|
static const uint FVB_NUM_TRIS = 2 * FVB_NUM_SIDES * FVB_NUM_SECTIONS;
|
||
|
|
||
|
// tmp : test vertex buffer
|
||
|
static const uint TEST_VB_NUM_SEGMENT = 16;
|
||
|
static const uint TEST_VB_NUM_SLICE = 16;
|
||
|
static const uint TEST_VB_NUM_TRIS = 2 * TEST_VB_NUM_SEGMENT * TEST_VB_NUM_SLICE;
|
||
|
|
||
|
// Get index of a vertex in the flatten vb from its side an section
|
||
|
static uint32 inline getFVBVertex(uint section, uint side)
|
||
|
{
|
||
|
nlassert(section <= FVB_NUM_SECTIONS);
|
||
|
return (uint32) (section + (side % FVB_NUM_SIDES) * (FVB_NUM_SECTIONS + 1));
|
||
|
}
|
||
|
|
||
|
|
||
|
const uint NUM_FACES_TO_RENDER = 5;
|
||
|
|
||
|
|
||
|
//*******************************************************************************
|
||
|
CWaterEnvMap::CWaterEnvMap()
|
||
|
{
|
||
|
_UpdateTime = 0;
|
||
|
_LastRenderTick = 0;
|
||
|
invalidate();
|
||
|
_NumRenderedFaces = 0;
|
||
|
_EnvCubicSize = 0;
|
||
|
_Env2DSize = 0;
|
||
|
_LastRenderTime = -1;
|
||
|
_StartRenderTime = -1;
|
||
|
_Alpha = 255;
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::init(uint cubeMapSize, uint projection2DSize, TGlobalAnimationTime updateTime, IDriver &driver)
|
||
|
{
|
||
|
// Allocate cube map
|
||
|
// a cubic texture with no sharing allowed
|
||
|
class CTextureCubeUnshared : public CTextureCube
|
||
|
{
|
||
|
public:
|
||
|
virtual bool supportSharing() const {return false;}
|
||
|
virtual uint32 getWidth(uint32 numMipMap = 0) const
|
||
|
{
|
||
|
nlassert(numMipMap == 0);
|
||
|
return Size;
|
||
|
}
|
||
|
virtual uint32 getHeight(uint32 numMipMap = 0) const
|
||
|
{
|
||
|
nlassert(numMipMap == 0);
|
||
|
return Size;
|
||
|
}
|
||
|
uint32 Size;
|
||
|
};
|
||
|
// a 2D testure
|
||
|
class CTexture2DUnshared : public CTextureBlank
|
||
|
{
|
||
|
public:
|
||
|
virtual bool supportSharing() const {return false;}
|
||
|
virtual uint32 getWidth(uint32 numMipMap = 0) const
|
||
|
{
|
||
|
nlassert(numMipMap == 0);
|
||
|
return Size;
|
||
|
}
|
||
|
virtual uint32 getHeight(uint32 numMipMap = 0) const
|
||
|
{
|
||
|
nlassert(numMipMap == 0);
|
||
|
return Size;
|
||
|
}
|
||
|
uint32 Size;
|
||
|
};
|
||
|
nlassert(cubeMapSize > 0);
|
||
|
nlassert(NLMISC::isPowerOf2(cubeMapSize));
|
||
|
nlassert(projection2DSize > 0);
|
||
|
nlassert(NLMISC::isPowerOf2(projection2DSize));
|
||
|
CTextureCubeUnshared *envCubic = new CTextureCubeUnshared;
|
||
|
_EnvCubic = envCubic;
|
||
|
_EnvCubic->setRenderTarget(true); // we will render to the texture
|
||
|
_EnvCubic->setWrapS(ITexture::Clamp);
|
||
|
_EnvCubic->setWrapT(ITexture::Clamp);
|
||
|
_EnvCubic->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
|
||
|
CTexture2DUnshared *tb = new CTexture2DUnshared;
|
||
|
tb->resize(cubeMapSize, cubeMapSize); // Unfortunately, must allocate memory in order for the driver to figure out the size
|
||
|
// that it needs to allocate for the texture, though its datas are never used (it is a render target)
|
||
|
tb->Size = cubeMapSize;
|
||
|
tb->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
|
||
|
for(uint k = 0; k < 6; ++k)
|
||
|
{
|
||
|
_EnvCubic->setTexture((CTextureCube::TFace) k, tb);
|
||
|
_EnvCubic->getTexture((CTextureCube::TFace) k)->setRenderTarget(true);
|
||
|
}
|
||
|
envCubic->Size = cubeMapSize;
|
||
|
// setup the texture to force the driver to allocate vram for it
|
||
|
driver.setupTexture(*_EnvCubic);
|
||
|
tb->reset();
|
||
|
// Allocate projection 2D map
|
||
|
CTexture2DUnshared *env2D = new CTexture2DUnshared;
|
||
|
_Env2D = env2D;
|
||
|
_Env2D->resize(projection2DSize, projection2DSize);
|
||
|
env2D->Size = projection2DSize;
|
||
|
_Env2D->setWrapS(ITexture::Clamp);
|
||
|
_Env2D->setWrapT(ITexture::Clamp);
|
||
|
_Env2D->setRenderTarget(true); // we will render to the texture
|
||
|
_Env2D->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff);
|
||
|
driver.setupTexture(*_Env2D); // allocate vram
|
||
|
_Env2D->reset();
|
||
|
_UpdateTime = updateTime;
|
||
|
_LastRenderTime = -1;
|
||
|
invalidate();
|
||
|
_NumRenderedFaces = 0;
|
||
|
_EnvCubicSize = cubeMapSize;
|
||
|
_Env2DSize = projection2DSize;
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::update(TGlobalAnimationTime time, IDriver &driver)
|
||
|
{
|
||
|
if (_LastRenderTime == time) return;
|
||
|
_LastRenderTime = time;
|
||
|
// First five updates are used to render the cubemap faces (bottom face is not rendered)
|
||
|
// Sixth update project the cubemap into a 2D texture
|
||
|
uint numTexToRender;
|
||
|
if (_UpdateTime > 0)
|
||
|
{
|
||
|
uint64 currRenderTick = (uint64) (time / (_UpdateTime / (NUM_FACES_TO_RENDER + 1)));
|
||
|
numTexToRender = (uint) (currRenderTick - _LastRenderTick);
|
||
|
_LastRenderTick = currRenderTick;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
numTexToRender = NUM_FACES_TO_RENDER + 1;
|
||
|
}
|
||
|
if (!numTexToRender) return;
|
||
|
if (_NumRenderedFaces == 0)
|
||
|
{
|
||
|
_StartRenderTime = time;
|
||
|
}
|
||
|
uint lastCubeFacesToRender = std::min((uint) NUM_FACES_TO_RENDER, _NumRenderedFaces + numTexToRender); // we don't render negative Z (only top hemisphere is used)
|
||
|
for(uint k = _NumRenderedFaces; k < lastCubeFacesToRender; ++k)
|
||
|
{
|
||
|
driver.setRenderTarget(_EnvCubic, 0, 0, _EnvCubicSize, _EnvCubicSize, 0, (uint32) k);
|
||
|
render((CTextureCube::TFace) k, _StartRenderTime);
|
||
|
}
|
||
|
_NumRenderedFaces = lastCubeFacesToRender;
|
||
|
if (_NumRenderedFaces == NUM_FACES_TO_RENDER && (_NumRenderedFaces + numTexToRender) > NUM_FACES_TO_RENDER)
|
||
|
{
|
||
|
// render to 2D map
|
||
|
driver.setRenderTarget(_Env2D, 0, 0, _Env2DSize, _Env2DSize);
|
||
|
doInit();
|
||
|
//
|
||
|
driver.activeVertexProgram(NULL);
|
||
|
driver.activeVertexBuffer(_FlattenVB);
|
||
|
driver.activeIndexBuffer(_FlattenIB);
|
||
|
driver.setFrustum(-1.f, 1.f, -1.f, 1.f, 0.f, 1.f, false);
|
||
|
driver.setupViewMatrix(CMatrix::Identity);
|
||
|
CMatrix mat;
|
||
|
//mat.scale(0.8f);
|
||
|
driver.setupModelMatrix(mat);
|
||
|
_MaterialPassThru.setTexture(0, _EnvCubic);
|
||
|
_MaterialPassThru.texConstantColor(0, CRGBA(255, 255, 255, _Alpha));
|
||
|
driver.renderTriangles(_MaterialPassThru, 0, FVB_NUM_TRIS);
|
||
|
_NumRenderedFaces = 0; // start to render again
|
||
|
}
|
||
|
driver.setRenderTarget(NULL);
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::doInit()
|
||
|
{
|
||
|
if (!_FlattenVBInitialized)
|
||
|
{
|
||
|
initFlattenVB();
|
||
|
initTestVB();
|
||
|
_FlattenVBInitialized = true;
|
||
|
_MaterialPassThru.setLighting(false);
|
||
|
_MaterialPassThru.texEnvOpRGB(0, CMaterial::Replace);
|
||
|
_MaterialPassThru.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
|
||
|
_MaterialPassThru.texEnvOpAlpha(0, CMaterial::Replace);
|
||
|
_MaterialPassThru.texEnvArg0Alpha(0, CMaterial::Constant, CMaterial::SrcAlpha);
|
||
|
_MaterialPassThru.texConstantColor(0, CRGBA(255, 255, 255, 255));
|
||
|
_MaterialPassThru.setDoubleSided(true);
|
||
|
_MaterialPassThruZTest = _MaterialPassThru;
|
||
|
_MaterialPassThru.setZWrite(false);
|
||
|
_MaterialPassThru.setZFunc(CMaterial::always);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static CVertexProgram testMeshVP(
|
||
|
"!!VP1.0\n\
|
||
|
DP4 o[HPOS].x, c[0], v[0]; \n\
|
||
|
DP4 o[HPOS].y, c[1], v[0]; \n\
|
||
|
DP4 o[HPOS].z, c[2], v[0]; \n\
|
||
|
DP4 o[HPOS].w, c[3], v[0]; \n\
|
||
|
MAD o[COL0], v[8], c[4].xxxx, c[4].yyyy; \n\
|
||
|
MOV o[TEX0], v[8]; \n\
|
||
|
END"
|
||
|
);
|
||
|
|
||
|
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::renderTestMesh(IDriver &driver)
|
||
|
{
|
||
|
doInit();
|
||
|
CMaterial testMat;
|
||
|
testMat.setLighting(false);
|
||
|
testMat.texEnvOpRGB(0, CMaterial::Modulate);
|
||
|
testMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
|
||
|
testMat.texEnvArg0RGB(1, CMaterial::Diffuse, CMaterial::SrcColor);
|
||
|
testMat.texEnvOpAlpha(0, CMaterial::Replace);
|
||
|
testMat.texEnvArg0Alpha(0, CMaterial::Constant, CMaterial::SrcAlpha);
|
||
|
testMat.texConstantColor(0, CRGBA(255, 255, 255, 255));
|
||
|
testMat.setDoubleSided(true);
|
||
|
testMat.setZWrite(false);
|
||
|
testMat.setZFunc(CMaterial::always);
|
||
|
// tmp : test cubemap
|
||
|
driver.activeVertexProgram(&testMeshVP);
|
||
|
driver.activeVertexBuffer(_TestVB);
|
||
|
driver.activeIndexBuffer(_TestIB);
|
||
|
driver.setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity); // tmp
|
||
|
_MaterialPassThruZTest.setTexture(0, _EnvCubic);
|
||
|
driver.setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity);
|
||
|
driver.setConstant(4, 2.f, 1.f, 0.f, 0.f);
|
||
|
//driver.renderTriangles(testMat, 0, TEST_VB_NUM_TRIS);
|
||
|
driver.renderTriangles(_MaterialPassThruZTest, 0, TEST_VB_NUM_TRIS);
|
||
|
driver.activeVertexProgram(NULL);
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::initFlattenVB()
|
||
|
{
|
||
|
_FlattenVB.setPreferredMemory(CVertexBuffer::AGPPreferred, true);
|
||
|
_FlattenVB.setName("Flatten VB");
|
||
|
_FlattenVB.clearValueEx();
|
||
|
_FlattenVB.addValueEx (CVertexBuffer::Position, CVertexBuffer::Float3);
|
||
|
_FlattenVB.addValueEx (CVertexBuffer::TexCoord0, CVertexBuffer::Float3);
|
||
|
_FlattenVB.initEx();
|
||
|
nlctassert(FVB_NUM_SIDES % 4 == 0); // number of sides must be a multiple of 4 so that sections sides will align with corners
|
||
|
_FlattenVB.setNumVertices(FVB_NUM_VERTS);
|
||
|
_FlattenIB.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
|
||
|
_FlattenIB.setNumIndexes(3 * FVB_NUM_TRIS);
|
||
|
{
|
||
|
CVertexBufferReadWrite vbrw;
|
||
|
CIndexBufferReadWrite ibrw;
|
||
|
_FlattenVB.lock(vbrw);
|
||
|
_FlattenIB.lock(ibrw);
|
||
|
for(uint l = 0; l < FVB_NUM_SIDES; ++l)
|
||
|
{
|
||
|
double angle = NLMISC::Pi * 0.25 + 2 * NLMISC::Pi * (double) l / (double) FVB_NUM_SIDES;
|
||
|
for(uint k = 0; k < FVB_NUM_SECTIONS + 1; ++k)
|
||
|
{
|
||
|
double radius = (double) k / (double) (FVB_NUM_SECTIONS - 1);
|
||
|
float x = (float) (radius * cos(angle));
|
||
|
float y = (float) (radius * sin(angle));
|
||
|
if (k < FVB_NUM_SECTIONS)
|
||
|
{
|
||
|
ibrw.setTri(3 * 2 * (k + (l * FVB_NUM_SECTIONS)), getFVBVertex(k, l), getFVBVertex(k + 1, l + 1), getFVBVertex(k + 1, l));
|
||
|
ibrw.setTri(3 * (2 * (k + (l * FVB_NUM_SECTIONS)) + 1), getFVBVertex(k, l), getFVBVertex(k, l + 1), getFVBVertex(k + 1, l + 1));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint side = l / (FVB_NUM_SIDES / 4);
|
||
|
switch(side)
|
||
|
{
|
||
|
case 0: // top
|
||
|
x /= y;
|
||
|
y = 1.f;
|
||
|
break;
|
||
|
case 1: // left
|
||
|
y /= -x;
|
||
|
x = -1.f;
|
||
|
break;
|
||
|
case 2: // bottom
|
||
|
x /= -y;
|
||
|
y = -1.f;
|
||
|
break;
|
||
|
case 3: // right
|
||
|
y /= x;
|
||
|
x = 1.f;
|
||
|
break;
|
||
|
default:
|
||
|
nlassert(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
CVector dir;
|
||
|
//dir.sphericToCartesian(1.f, (float) angle, (float) (NLMISC::Pi * 0.5 * acos(std::max(0.f, (1.f - (float) k / (FVB_NUM_SECTIONS - 1))))));
|
||
|
dir.sphericToCartesian(1.f, (float) angle, (float) acos(std::min(1.f, (float) k / (FVB_NUM_SECTIONS - 1))));
|
||
|
vbrw.setValueFloat3Ex(CVertexBuffer::Position, getFVBVertex(k, l), x, 0.5f, y);
|
||
|
vbrw.setValueFloat3Ex(CVertexBuffer::TexCoord0, getFVBVertex(k, l), -dir.x, dir.z, -dir.y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::invalidate()
|
||
|
{
|
||
|
_LastRenderTime = -1;
|
||
|
_StartRenderTime = -1;
|
||
|
if (_UpdateTime == 0)
|
||
|
{
|
||
|
_LastRenderTick = UINT64_CONSTANT(~0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_LastRenderTick -= (NUM_FACES_TO_RENDER + 1);
|
||
|
}
|
||
|
_NumRenderedFaces = 0;
|
||
|
}
|
||
|
|
||
|
//*******************************************************************************
|
||
|
void CWaterEnvMap::initTestVB()
|
||
|
{
|
||
|
_TestVB.setPreferredMemory(CVertexBuffer::AGPPreferred, true);
|
||
|
_TestVB.setName("TestVB");
|
||
|
_TestVB.clearValueEx();
|
||
|
_TestVB.addValueEx (CVertexBuffer::Position, CVertexBuffer::Float3);
|
||
|
_TestVB.addValueEx (CVertexBuffer::TexCoord0, CVertexBuffer::Float3);
|
||
|
_TestVB.initEx();
|
||
|
_TestVB.setNumVertices(TEST_VB_NUM_SEGMENT * 2 * (TEST_VB_NUM_SLICE + 1));
|
||
|
_TestIB.setFormat(NL_DEFAULT_INDEX_BUFFER_FORMAT);
|
||
|
_TestIB.setNumIndexes(3 * TEST_VB_NUM_TRIS);
|
||
|
{
|
||
|
CVertexBufferReadWrite vbrw;
|
||
|
CIndexBufferReadWrite ibrw;
|
||
|
_TestVB.lock(vbrw);
|
||
|
_TestIB.lock(ibrw);
|
||
|
uint triIndex = 0;
|
||
|
for(uint k = 0; k < TEST_VB_NUM_SEGMENT; ++k)
|
||
|
{
|
||
|
float theta = 2 * (float) (NLMISC::Pi * (double) k / (double) TEST_VB_NUM_SEGMENT);
|
||
|
for(uint l = 0; l <= TEST_VB_NUM_SLICE; ++l)
|
||
|
{
|
||
|
float phi = (float) (NLMISC::Pi / 2 * (1 - 2 * (double) l / (double) TEST_VB_NUM_SLICE));
|
||
|
CVector pos;
|
||
|
pos.sphericToCartesian(1.f, theta, phi);
|
||
|
#define VERT_INDEX(k, l) ((l) + ((k) % TEST_VB_NUM_SEGMENT) * (TEST_VB_NUM_SLICE + 1))
|
||
|
vbrw.setVertexCoord(VERT_INDEX(k, l), pos);
|
||
|
vbrw.setValueFloat3Ex(CVertexBuffer::TexCoord0, VERT_INDEX(k, l), pos);
|
||
|
if (l != TEST_VB_NUM_SLICE)
|
||
|
{
|
||
|
ibrw.setTri(3 * triIndex++, VERT_INDEX(k, l), VERT_INDEX(k + 1, l), VERT_INDEX(k + 1, l + 1));
|
||
|
ibrw.setTri(3 * triIndex++, VERT_INDEX(k, l), VERT_INDEX(k + 1, l + 1), VERT_INDEX(k, l + 1));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
nlassert(triIndex == TEST_VB_NUM_TRIS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
} // NL3D
|