//////////////////////////////////////////////////////////////////////////////////
080605_HowToUseTSHeightTerrainSceneNode.txt
Code: Select all
Documented By: Do-young Chung
Documented Date: 080605
Title: How to use TSTerrainSceneNode
0. Please note.
@ TSTerrainSceneNode was developed in Irrlicht 1.3.1.
@ TSTerrainSceneNode's actual goal is importing the height map created by Earthsculptor( http://www.earthsculptor.com/ ).
and it has only been tested with the height maps that were genereted by Earthsculptor. Thus, to get the best result,
please use the height map generated by Earthsculptor.
@ Only for DirectX 9.
1. To use the TSTerrainSceneNode, Irrlicht must be modified to support 7 textures.
The unmodified Irrlicht only supports 4 textures, however TSTerrainSceneNode requires
4 detail textures, 1 detail map texture, 1 light map texture and 1 color map texture. I think about combining 4 detail
textures to 1, therefore make Irrlicht not be necessary to modify. However I decided to not go that far for the current version.
// Modifying Irrlicht To Make Enable Using 8 Textures ------------------------------------------------------------------ //
At SMaterial.h (Line: 310) ===============================================
From -------->
//! Maximum number of texture an SMaterial can have.
const u32 MATERIAL_MAX_TEXTURES = 4;
To <----------
//! Maximum number of texture an SMaterial can have.
/*
const u32 MATERIAL_MAX_TEXTURES = 4;
*/
// Modified By DC
const u32 MATERIAL_MAX_TEXTURES = 7;
==========================================================================
NOTE: This change enables to set more than 4 textures to the scene::ISceneNode's inherited SceneNode classes
by calling scene::ISceneNode::void setMaterialTexture(u32 textureLayer, video::ITexture* texture) method.
At S3DVertex.h(Line: 127) ================================================
From--------->
//! First set of texture coordinates
core::vector2d<f32> TCoords; // Use for detail alpha
//! Second set of texture coordinates
core::vector2d<f32> TCoords2; // Use for 1st texture
Add From Next Line <----
// Added By DC
core::vector2d<f32> TCoords3; // Use for 2nd texture
core::vector2d<f32> TCoords4; // Use for 3rd texture
core::vector2d<f32> TCoords5; // Use for 4th texture
core::vector2d<f32> TCoords6; // Use for light map
core::vector2d<f32> TCoords7; // Use for color map
==========================================================================
NOTE: This change enables to store more than 2 texture coordinates in S3DVertex2TCoords structure, which
is the template type of SMeshBufferLightMap(CMeshBuffer.h). By storing more texture coordinates,
the each detail texture's color can be blended in the Pixel Shader according to the texture coordinates.
CD3D9Driver.cpp(Line 1159) ===============================================
From--------->
case EVT_2TCOORDS:
hr = pID3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2);
break;
To <----------
case EVT_2TCOORDS: // Modified By DC
hr = pID3DDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX7/*D3DFVF_TEX2*/);
break;
==========================================================================
NOTE: This change makes to set vertex type from the vertex with 2 textures to the vertex with 7 textures
in the DirectX 9.
// End Modifying Irrlicht To Make Enable Using 8 Textures --------------------------------------------------------------- //
2. Now, Irrlicht needs to be modified for being able to create video::IImage from video::ITexture.
NOTE: This modification is posted by tonic( http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=26986&highlight=virtual+iimage%2A+createimage+iimage%2A+imagetocopy )
// Modifying Irrlicht To Make Enable To Create video::IImage From video::ITexture --------------------------------------- //
IImage.h(Line 8) =========================================================
From---------->
#include "IUnknown.h"
#include "position2d.h"
To <----------
#include "IUnknown.h"
// Modified By DC: For using TSTerrainSceneNode
//#include "position2d.h"
#include "rect.h"
IImage.h(Line 103) =======================================================
From--------->
virtual u32 getPitch() const = 0;
Add From Next Line <----
// Modified By DC: For using TSHeightTerrainSceneNode
//! fills the surface with given color
virtual void fill(const SColor &color) = 0;
//! copies this surface into another
virtual void copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect=0) = 0;
IVideoDriver.h(Line 714) =================================================
From--------->
virtual IImage* createImageFromData(ECOLOR_FORMAT format,
const core::dimension2d<s32>& size, void *data,
bool ownForeignMemory=false,
bool deleteMemory = true) = 0;
Add From Next Line <----
// Modified By DC: For using TSHeightTerrainSceneNode
//! Creates an empty software image.
/**
\param format: Desired color format of the image.
\param size: Size of the image to create.
\return Returns the created image.
If you no longer need the image, you should call IImage::drop().
See IReferenceCounted::drop() for more information. */
virtual IImage* createImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size) = 0;
//! Creates a software image by converting it to given format from another image.
/**
\param format: Desired color format of the image.
\param imageToCopy: image to copy to the new image.
\return Returns the created image.
If you no longer need the image, you should call IImage::drop().
See IReferenceCounted::drop() for more information. */
virtual IImage* createImage(ECOLOR_FORMAT format, IImage *imageToCopy) = 0;
//! Creates a software image from a part of another image.
/**
\param imageToCopy: image to copy the the new image in part.
\param pos: position of rectangle to copy.
\param size: extents of rectangle to copy.
\return Returns the created image.
If you no longer need the image, you should call IImage::drop().
See IReferenceCounted::drop() for more information. */
virtual IImage* createImage(IImage* imageToCopy,
const core::position2d<s32>& pos, const core::dimension2d<s32>& size) = 0;
CNullDriver.h(Line 286) ==================================================
--From------->
virtual IImage* createImageFromData(ECOLOR_FORMAT format,
const core::dimension2d<s32>& size, void *data,
bool ownForeignMemory=true, bool deleteForeignMemory = true);
Add From Next Line <----
// Modified By DC: For using TSTerrainSceneNode
virtual IImage* createImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size);
virtual IImage* createImage(ECOLOR_FORMAT format, IImage *imageToCopy);
virtual IImage* createImage(IImage* imageToCopy,
const core::position2d<s32>& pos, const core::dimension2d<s32>& size);
CNullDriver.cpp(Line 1213) ===============================================
From--------->
//! Creates a software image from a byte array.
IImage* CNullDriver::createImageFromData(ECOLOR_FORMAT format,
const core::dimension2d<s32>& size, void *data,
bool ownForeignMemory,
bool deleteMemory)
{
return new CImage(format, size, data, ownForeignMemory, deleteMemory);
}
Add From Next Line <----
// Modified By DC: For using TSTerrainSceneNode
IImage* CNullDriver::createImage(ECOLOR_FORMAT format, const core::dimension2d<s32>& size)
{
return new CImage(format, size);
}
IImage* CNullDriver::createImage(ECOLOR_FORMAT format, IImage *imageToCopy)
{
return new CImage(format, imageToCopy);
}
IImage* CNullDriver::createImage(IImage* imageToCopy, const core::position2d<s32>& pos, const core::dimension2d<s32>& size)
{
return new CImage(imageToCopy, pos, size);
}
// End Modifying Irrlicht To Make Enable To Create video::IImage From video::ITexture ----------------------------------- //
3. Now, modifying Irrlicht is completed and it is ready to use TSTerrainSceneNode. Thus BUILD IT.
4. To Use the TSTerrainSceneNode
<1> Before main
#include "TSTerrainSceneNode.h"
<2> In main
IrrlichtDevice *irrDevice = createDevice(video::EDT_DIRECT3D9, core::dimension2d<s32>(1024, 768));
video::IVideoDriver *videoDriver = irrDevice->getVideoDriver();
scene::ISceneManager *sceneManager = irrDevice->getSceneManager();
.............
// Create TSTerrainSceneNode
TSTerrainSceneNode *terrain = new TSTerrainSceneNode(
irrDevice->getLogger(),
sceneManager,
irrDevice->getTimer(),
"test03", // Mesh Name
"test03.png", // HeightMap Image File Name
core::dimension2d<f32>(1.0, 1.0), // Stretch Size :Same with ISceneManager::addTerrainMesh's parameter
250.0f, // Max Height :Same with ISceneManager::addTerrainMesh's parameter
core::dimension2d<s32>(32, 32), // Default Vertex Block Size:Same with ISceneManager::addTerrainMesh's parameter
9.39f, 12.85f, 7.947f, 11.478f); // 1 ~ 4 textures scale :Same With scene::ITerrainSceneNode::ScaleTexture
// Set Material Textures In SPECIFIC ORDER
terrain->setMaterialTexture(0, videoDriver->getTexture("test03_d.png")); // Detail Map
terrain->setMaterialTexture(1, videoDriver->getTexture("grayRock.png")); // 1st Detail Texture
terrain->setMaterialTexture(2, videoDriver->getTexture("hardDirt.png")); // 2nd Detail Texture
terrain->setMaterialTexture(3, videoDriver->getTexture("bigRockFace.png")); // 3rd Detail Texture
terrain->setMaterialTexture(4, videoDriver->getTexture("shortGrass.png")); // 4th Detail Texture
terrain->setMaterialTexture(5, videoDriver->getTexture("test03_l.png")); // Light Map
terrain->setMaterialTexture(6, videoDriver->getTexture("test03_c.png")); // Color Map
.............
terrain->drop();
.............
// Usual main loop
if(irrDevice)
{
while(irrDevice->run())
{
if (irrDevice->isWindowActive())
{
videoDriver->beginScene(true, true, video::SColor(0, 255, 255, 255));
sceneManager->drawAll();
videoDriver->endScene();
}
}
irrDevice->drop();
}
5. That's it~.
//////////////////////////////////////////////////////////////////////////////////
TSTerrainSceneNode.h
Code: Select all
/*
Programmed by Do-young Chung
Programmed Date 080526
For:
TSTerrain(Texture Splatting Height Terrain) Mesh and SceneNode
*/
#pragma once
#include <iostream>
#include <tchar.h>
#include <string>
#include <irrlicht.h>
#pragma comment(lib, "Irrlicht.lib")
using namespace irr;
class TSTerrainSceneNode: public scene::ISceneNode, video::IShaderConstantSetCallBack
{
public:
// Constructor and Deconstructor ------------------------------------------------//
TSTerrainSceneNode(
ILogger *logger,
scene::ISceneManager *sceneManager,
ITimer *timer,
const c8* name,
const c8* heightMapFileName,
const core::dimension2d<f32>& stretchSize,
f32 maxHeight,
const core::dimension2d<s32>& defaultVertexBlockSize,
f32 text1Scale, f32 text2Scale, f32 text3Scale, f32 text4Scale,
scene::ISceneNode* parent = 0, s32 id = -1);
virtual ~TSTerrainSceneNode();
// Inherited From scene::ISceneNode ---------------------------------------------//
virtual video::SMaterial& getMaterial(u32 i);
virtual u32 getMaterialCount();
virtual void OnRegisterSceneNode();
virtual void OnAnimate(u32 timeMs);
virtual void render();
virtual const core::aabbox3d<f32>& getBoundingBox() const;
// Inherited From video::IShaderConstantSetCallBack -----------------------------//
virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData);
private:
// Helper Methods ---------------------------------------------------------------//
bool loadHeightMap(
const c8* name,
video::IImage* heightmap,
const core::dimension2d<f32>& stretchSize,
f32 maxHeight, const core::dimension2d<s32> maxVtxBlockSize,
bool debugBorders);
// Copied From CTerrainSceneNode
void calculateNormals(scene::SMeshBufferLightMap* pMeshBuffer, core::dimension2d<s32> terrainData);
// Attributes -------------------------------------------------------------------//
ILogger *my_logger;
video::IVideoDriver *my_videoDriver;
scene::ISceneManager *my_sceneManager;
s32 my_shaderMaterial;
ITimer *my_timer;
core::array<video::SMaterial> my_materials;
scene::SMesh *my_mesh;
scene::SMeshBufferLightMap my_RenderBuffer;
core::dimension2d<f32> my_size;
f32 my_detailTextScale; // Initial value 1.0
f32 my_lightMapTextScale; // Initial value 1.0
f32 my_colorMapTextScale; // Initial value 1.0
f32 my_text1Scale;
f32 my_text2Scale;
f32 my_text3Scale;
f32 my_text4Scale;
};
//////////////////////////////////////////////////////////////////////////////////
TSTerrainSceneNode.cpp
Code: Select all
/*
Programmed by Do-young Chung
Programmed Date 080526
For:
TSTerrain(Texture Splatting Terrain) SceneNode
*/
#define _USING_SMOOTH_FACTOR
#include "TSTerrainSceneNode.h"
// Constructor and Deconstructor ------------------------------------------------//
TSTerrainSceneNode::TSTerrainSceneNode(
ILogger *logger,
scene::ISceneManager *sceneManager,
ITimer *timer,
const c8* name,
const c8* heightMapFileName,
const core::dimension2d<f32>& stretchSize,
f32 maxHeight,
const core::dimension2d<s32>& defaultVertexBlockSize,
f32 text1Scale, f32 text2Scale, f32 text3Scale, f32 text4Scale,
scene::ISceneNode* parent/* = 0*/, s32 id/* = -1*/):
scene::ISceneNode( parent?parent:sceneManager->getRootSceneNode(), sceneManager, id),
my_sceneManager(sceneManager), my_timer(timer),
my_detailTextScale(1.0f), my_lightMapTextScale(1.0f), my_colorMapTextScale(1.0f),
my_text1Scale(text1Scale), my_text2Scale(text2Scale), my_text3Scale(text3Scale), my_text4Scale(text4Scale)
{
#ifdef _DEBUG
setDebugName("TSTerrainSceneNode");
#endif
my_logger = logger;
my_videoDriver = my_sceneManager->getVideoDriver();
my_mesh = NULL;
my_size;
video::IImage *heightMapImage = this->my_videoDriver->createImageFromFile(heightMapFileName);
if(this->loadHeightMap(name, heightMapImage, stretchSize, maxHeight, defaultVertexBlockSize, false))
{
video::IGPUProgrammingServices* GPUProgrammingServices = my_videoDriver->getGPUProgrammingServices();
std::string textureSplattingPixelShader = "";
std::string textureSplattingVertexShader = "";
if (my_videoDriver->getDriverType() == video::EDT_DIRECT3D9)
{
textureSplattingPixelShader = "textureSplatting_ps.hlsl";
textureSplattingVertexShader = "textureSplatting_vs.hlsl";
}
/*
DC NOTE: For now, it's not working for OpenGL
else if(my_videoDriver->getDriverType() == video::EDT_OPENGL)
{
textureSplattingPixelShader = "textureSplatting_ps.glsl";
textureSplattingVertexShader = "textureSplatting_vs.glsl";
}
*/
my_shaderMaterial = GPUProgrammingServices->addHighLevelShaderMaterialFromFiles(
textureSplattingVertexShader.c_str(), "main", video::EVST_VS_2_0,
textureSplattingPixelShader.c_str(), "main", video::EPST_PS_2_0,
this,
video::EMT_LIGHTMAP);
this->setMaterialType((video::E_MATERIAL_TYPE)my_shaderMaterial);
}
else
{
my_shaderMaterial = -1;
}
if(heightMapImage != NULL)
heightMapImage->drop();
}
TSTerrainSceneNode::~TSTerrainSceneNode()
{
if(my_mesh != NULL)
my_mesh->drop();
}
// Inherited From scene::ISceneNode ---------------------------------------------//
video::SMaterial& TSTerrainSceneNode::getMaterial(u32 i)
{
if (i >= this->my_materials.size())
return ISceneNode::getMaterial(i);
return this->my_materials[i];
}
u32 TSTerrainSceneNode::getMaterialCount()
{
return this->my_materials.size();
}
void TSTerrainSceneNode::OnRegisterSceneNode()
{
if(this->IsVisible)
this->my_sceneManager->registerNodeForRendering(this);
ISceneNode::OnRegisterSceneNode();
}
void TSTerrainSceneNode::OnAnimate(u32 timeMs)
{
if(this->IsVisible)
{
}
ISceneNode::OnAnimate(timeMs);
}
void TSTerrainSceneNode::render()
{
/*
DC NOTE: Code is partially borrowed from CAnimatedMeshSceneNode(.cpp)
*/
if(this->IsVisible && this->my_mesh != NULL)
{
this->my_videoDriver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
bool isTransparentPass =
this->my_sceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
u32 i = 0;
for(i = 0; i < this->my_mesh->getMeshBufferCount(); ++i)
{
video::IMaterialRenderer* rnd = this->my_videoDriver->getMaterialRenderer(my_materials[i].MaterialType);
bool transparent = (rnd && rnd->isTransparent());
// only render transparent buffer if this is the transparent render pass
// and solid only in solid pass
if (transparent == isTransparentPass)
{
scene::IMeshBuffer* mb = this->my_mesh->getMeshBuffer(i);
this->my_videoDriver->setMaterial(my_materials[i]);
this->my_videoDriver->drawMeshBuffer(mb);
}
}
}
}
const core::aabbox3d<f32>& TSTerrainSceneNode::getBoundingBox() const
{
return this->my_mesh->getBoundingBox();
}
// Inherited From video::IShaderConstantSetCallBack -----------------------------//
void TSTerrainSceneNode::OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
{
video::IVideoDriver* driver = services->getVideoDriver();
core::matrix4 projection = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 view = driver->getTransform(video::ETS_VIEW);
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
core::matrix4 worldViewProj = projection;
worldViewProj *= view;
worldViewProj *= world;
services->setVertexShaderConstant("WorldViewProj", worldViewProj.pointer(), 16);
}
// Helper Methods ---------------------------------------------------------------//
bool TSTerrainSceneNode::loadHeightMap(
const c8* name,
video::IImage* heightmap,
const core::dimension2d<f32>& stretchSize,
f32 maxHeight, const core::dimension2d<s32> maxVtxBlockSize,
bool debugBorders)
{
bool result = false;
if(name != NULL && strlen(name) > 0 &&
heightmap != NULL && this->my_timer != NULL && this->my_videoDriver != NULL)
{
u32 startTime = my_timer->getRealTime();
video::SMaterial material;
c8 stringBuffer[256];
c8 textureName[64];
s32 borderSkip = debugBorders ? 0 : 1;
video::S3DVertex2TCoords vtx;
vtx.Color.set(255,255,255,255);
scene::SMesh* mesh = new scene::SMesh();
u32 tm = this->my_timer->getRealTime() / 1000;
core::dimension2d<s32> hMapSize = heightmap->getDimension();
core::dimension2d<s32> tMapSize = heightmap->getDimension();
core::position2d<f32> thRel((f32)tMapSize.Width / hMapSize.Width, (f32)tMapSize.Height / hMapSize.Height);
core::position2d<s32> processed(0,0);
#ifdef _USING_SMOOTH_FACTOR
s32 widthInBlock = 0;
s32 heightInBlock = 0;
bool noMoreWidthCounting = false;
scene::SMeshBufferLightMap *wholeTerrain = new scene::SMeshBufferLightMap();
wholeTerrain->Vertices.set_used(hMapSize.Height * hMapSize.Width);
u32 wholeTerrainIndex = 0;
#endif
while(processed.Y < hMapSize.Height)
{
#ifdef _USING_SMOOTH_FACTOR
heightInBlock++;
#endif
while(processed.X < hMapSize.Width)
{
#ifdef _USING_SMOOTH_FACTOR
if(!noMoreWidthCounting)
widthInBlock++;
#endif
// 1. Calculate the block size
core::dimension2d<s32> blockSize = maxVtxBlockSize;
if (processed.X + blockSize.Width > hMapSize.Width)
blockSize.Width = hMapSize.Width - processed.X;
if (processed.Y + blockSize.Height > hMapSize.Height)
blockSize.Height = hMapSize.Height - processed.Y;
// DC NOTE: For Applying Texture Splatting.
// typedef CMeshBuffer<video::S3DVertex2TCoords> SMeshBufferLightMap
scene::SMeshBufferLightMap* buffer = new scene::SMeshBufferLightMap();
// 2. Add vertices of vertex block
s32 y;
const f32 resDTBySize = this->my_detailTextScale / (f32)(hMapSize.Width - 1);
const f32 resLMBySize = this->my_lightMapTextScale/ (f32)(hMapSize.Width - 1);
const f32 resCMBySize = this->my_colorMapTextScale/ (f32)(hMapSize.Width - 1);
const f32 res1BySize = this->my_text1Scale / (f32)(hMapSize.Width - 1);
const f32 res2BySize = this->my_text2Scale / (f32)(hMapSize.Width - 1);
const f32 res3BySize = this->my_text3Scale / (f32)(hMapSize.Width - 1);
const f32 res4BySize = this->my_text4Scale / (f32)(hMapSize.Width - 1);
f32 xDTval = 0, zDTval = 0;
f32 xLMval = 0, zLMval = 0;
f32 xCMval = 0, zCMval = 0;
f32 x1val = 0, z1val = 0;
f32 x2val = 0, z2val = 0;
f32 x3val = 0, z3val = 0;
f32 x4val = 0, z4val = 0;
for (y=0; y < blockSize.Height; ++y)
{
if(zDTval == 0)
{
zDTval = resDTBySize * (y + processed.Y);
zLMval = resLMBySize * (y + processed.Y);
zCMval = resCMBySize * (y + processed.Y);
z1val = res1BySize * (y + processed.Y);
z2val = res2BySize * (y + processed.Y);
z3val = res3BySize * (y + processed.Y);
z4val = res4BySize * (y + processed.Y);
}
for (s32 x=0; x < blockSize.Width; ++x)
{
if(xDTval == 0)
{
xDTval += resDTBySize * (x + processed.X);
xLMval += resLMBySize * (x + processed.X);
xCMval += resCMBySize * (x + processed.X);
x1val += res1BySize * (x + processed.X);
x2val += res2BySize * (x + processed.X);
x3val += res3BySize * (x + processed.X);
x4val += res4BySize * (x + processed.X);
}
video::SColor clr = heightmap->getPixel(x + processed.X, y + processed.Y);
f32 height = ((clr.getRed() + clr.getGreen() + clr.getBlue()) / 3.0f) / 255.0f * maxHeight;
vtx.Pos.set((f32)(x + processed.X) * stretchSize.Width,
height, (f32)(y + processed.Y) * stretchSize.Height);
vtx.TCoords.set((x + 0.5f) / blockSize.Width,
(y + 0.5f) / blockSize.Height);
vtx.TCoords.X = xDTval;
vtx.TCoords6.X = xLMval;
vtx.TCoords7.X = xCMval;
vtx.TCoords.Y = zDTval;
vtx.TCoords6.Y = zLMval;
vtx.TCoords7.Y = zCMval;
if(this->my_text1Scale == 0)
vtx.TCoords2 = vtx.TCoords;
else
{
vtx.TCoords2.X = x1val;
vtx.TCoords2.Y = z1val;
}
if(this->my_text2Scale == 0)
vtx.TCoords3 = vtx.TCoords;
else
{
vtx.TCoords3.X = x2val;
vtx.TCoords3.Y = z2val;
}
if(this->my_text3Scale == 0)
vtx.TCoords4 = vtx.TCoords;
else
{
vtx.TCoords4.X = x3val;
vtx.TCoords4.Y = z3val;
}
if(this->my_text4Scale == 0)
vtx.TCoords5 = vtx.TCoords;
else
{
vtx.TCoords5.X = x4val;
vtx.TCoords5.Y = z4val;
}
buffer->Vertices.push_back(vtx);
#ifdef _USING_SMOOTH_FACTOR
wholeTerrainIndex = (x + processed.X) * hMapSize.Width + (y + processed.Y);
wholeTerrain->Vertices[wholeTerrainIndex] = vtx;
#endif
xDTval += resDTBySize;
xLMval += resLMBySize;
xCMval += resCMBySize;
x1val += res1BySize;
x2val += res2BySize;
x3val += res3BySize;
x4val += res4BySize;
}
xDTval = xLMval = xCMval = x1val = x2val = x3val = x4val = 0;
zDTval += resDTBySize;
zLMval += resLMBySize;
zCMval += resCMBySize;
z1val += res1BySize;
z2val += res2BySize;
z3val += res3BySize;
z4val += res4BySize;
}
// 3. Add indices of vertex block
for(y = 0; y < blockSize.Height - 1; ++y)
{
for (s32 x = 0; x < blockSize.Width - 1; ++x)
{
s32 c = (y * blockSize.Width) + x;
buffer->Indices.push_back(c);
buffer->Indices.push_back(c + blockSize.Width);
buffer->Indices.push_back(c + 1);
buffer->Indices.push_back(c + 1);
buffer->Indices.push_back(c + blockSize.Width);
buffer->Indices.push_back(c + 1 + blockSize.Width);
}
}
// 4. Recalculate normals
for(s32 i = 0; i < (s32)buffer->Indices.size(); i += 3)
{
core::plane3d<f32> p(
buffer->Vertices[buffer->Indices[i + 0]].Pos,
buffer->Vertices[buffer->Indices[i + 1]].Pos,
buffer->Vertices[buffer->Indices[i + 2]].Pos);
p.Normal.normalize();
buffer->Vertices[buffer->Indices[i + 0]].Normal = p.Normal;
buffer->Vertices[buffer->Indices[i + 1]].Normal = p.Normal;
buffer->Vertices[buffer->Indices[i + 2]].Normal = p.Normal;
}
if (buffer->Vertices.size())
{
// create texture for this block
video::IImage* img = this->my_videoDriver->createImage(heightmap,
core::position2d<s32>((s32)(processed.X * thRel.X), (s32)(processed.Y * thRel.Y)),
core::dimension2d<s32>((s32)(blockSize.Width * thRel.X), (s32)(blockSize.Height * thRel.Y)));
sprintf_s(textureName, 64, "terrain%u_%d", tm, mesh->getMeshBufferCount());
material.Textures[0] = this->my_videoDriver->addTexture(textureName, img);
if (material.Textures[0])
{
sprintf_s(stringBuffer, 256, "Generated terrain texture (%dx%d): %s\n",
material.Textures[0]->getSize().Width,
material.Textures[0]->getSize().Height,
textureName);
this->my_logger->log(stringBuffer);
}
else
printf("Could not create terrain texture.", textureName);
buffer->Material = material;
img->drop();
}
buffer->recalculateBoundingBox();
mesh->addMeshBuffer(buffer);
buffer->drop();
processed.X += maxVtxBlockSize.Width - borderSkip;
} // while(processed.X<hMapSize.Width)
#ifdef _USING_SMOOTH_FACTOR
noMoreWidthCounting = true;
#endif
// keep on processing
processed.X = 0;
processed.Y += maxVtxBlockSize.Height - borderSkip;
} // while (processed.Y<hMapSize.Height)
scene::SAnimatedMesh* animatedMesh = new scene::SAnimatedMesh();
mesh->recalculateBoundingBox();
this->my_mesh = mesh;
#ifdef _USING_SMOOTH_FACTOR
for( s32 i=0; i < 5 ;i++)
{
for( s32 x=0; x < hMapSize.Width ; x++)
{
for( s32 z=0; z < hMapSize.Height ; z++)
{
int counter=1;
if( x>0 && z > 0){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z-1) * hMapSize.Height].Pos.Y;
counter++;
}
if( x>0 ){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z) * hMapSize.Height].Pos.Y;
counter++;
}
if( x>0 && z < hMapSize.Height-1){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x-1) + (z+1) * hMapSize.Height].Pos.Y;
counter++;
}
if( x < hMapSize.Width-1 && z > 0){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z-1) * hMapSize.Height].Pos.Y;
counter++;
}
if( x < hMapSize.Width-1 ){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z) * hMapSize.Height].Pos.Y;
counter++;
}
if( x < hMapSize.Width-1 && z < hMapSize.Height-1){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x+1) + (z+1) * hMapSize.Height].Pos.Y;
counter++;
}
if( z > 0 ){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x) + (z-1) * hMapSize.Height].Pos.Y;
counter++;
}
if( z < hMapSize.Height-1 ){
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y += wholeTerrain->Vertices[(x) + (z+1) * hMapSize.Height].Pos.Y;
counter++;
}
wholeTerrain->Vertices[x + z * hMapSize.Height].Pos.Y /= (float)counter;
}
}
}
this->calculateNormals(wholeTerrain, core::dimension2d<s32>(hMapSize.Width, hMapSize.Height));
// Convert to the Blocks
s32 curBlockIndex = 0;
s32 vertexIndex = 0;
processed.X = processed.Y = 0;
while(processed.Y < hMapSize.Height)
{
while(processed.X < hMapSize.Width)
{
// 1. Calculate the block size
core::dimension2d<s32> blockSize = maxVtxBlockSize;
if (processed.X + blockSize.Width > hMapSize.Width)
blockSize.Width = hMapSize.Width - processed.X;
if (processed.Y + blockSize.Height > hMapSize.Height)
blockSize.Height = hMapSize.Height - processed.Y;
vertexIndex = 0;
s32 y;
for (y=0; y < blockSize.Height; ++y)
{
for (s32 x=0; x < blockSize.Width; ++x)
{
wholeTerrainIndex = (x + processed.X) * hMapSize.Width + (y + processed.Y);
((scene::SMeshBufferLightMap *)my_mesh->getMeshBuffer(curBlockIndex))->Vertices[vertexIndex].Pos
= wholeTerrain->Vertices[wholeTerrainIndex].Pos;
vertexIndex++;
}
}
/*
// 4. Recalculate normals
for(s32 i = 0; i < (s32)buffer->Indices.size(); i += 3)
{
core::plane3d<f32> p(
buffer->Vertices[buffer->Indices[i + 0]].Pos,
buffer->Vertices[buffer->Indices[i + 1]].Pos,
buffer->Vertices[buffer->Indices[i + 2]].Pos);
p.Normal.normalize();
buffer->Vertices[buffer->Indices[i + 0]].Normal = p.Normal;
buffer->Vertices[buffer->Indices[i + 1]].Normal = p.Normal;
buffer->Vertices[buffer->Indices[i + 2]].Normal = p.Normal;
}
*/
curBlockIndex++;
processed.X += maxVtxBlockSize.Width - borderSkip;
} // while(processed.X<hMapSize.Width)
// keep on processing
processed.X = 0;
processed.Y += maxVtxBlockSize.Height - borderSkip;
} // while (processed.Y<hMapSize.Height)
wholeTerrain->drop();
wholeTerrain = NULL;
#endif
video::SMaterial mat;
for (u32 i = 0; i < this->my_mesh->getMeshBufferCount(); ++i)
{
scene::IMeshBuffer* mb = this->my_mesh->getMeshBuffer(i);
if (mb)
mat = mb->getMaterial();
this->my_materials.push_back(mat);
}
u32 endTime = this->my_timer->getRealTime();
sprintf_s(stringBuffer, 256, "Generated terrain data (%dx%d) in %.4f seconds\n",
hMapSize.Width, hMapSize.Height, (endTime - startTime) / 1000.0f);
this->my_logger->log(stringBuffer);
if(this->my_mesh != NULL)
{
this->my_mesh->grab();
result = true;
}
}
return result;
}
// calculate smooth normals
void TSTerrainSceneNode::calculateNormals(scene::SMeshBufferLightMap* pMeshBuffer, core::dimension2d<s32> terrainData)
{
s32 count;
core::vector3df a, b, c, t;
for (s32 x=0; x<terrainData.Width; ++x)
for (s32 z=0; z<terrainData.Height; ++z)
{
count = 0;
core::vector3df normal;
// top left
if (x>0 && z>0)
{
a = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z-1].Pos;
b = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z].Pos;
c = pMeshBuffer->Vertices[x*terrainData.Width+z].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
a = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z-1].Pos;
b = pMeshBuffer->Vertices[x*terrainData.Width+z-1].Pos;
c = pMeshBuffer->Vertices[x*terrainData.Width+z].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
count += 2;
}
// top right
if (x>0 && z<terrainData.Height-1)
{
a = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z].Pos;
b = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z+1].Pos;
c = pMeshBuffer->Vertices[x*terrainData.Width+z+1].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
a = pMeshBuffer->Vertices[(x-1)*terrainData.Width+z].Pos;
b = pMeshBuffer->Vertices[x*terrainData.Width+z+1].Pos;
c = pMeshBuffer->Vertices[x*terrainData.Width+z].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
count += 2;
}
// bottom right
if (x<terrainData.Width-1 && z<terrainData.Width-1)
{
a = pMeshBuffer->Vertices[x*terrainData.Width+z+1].Pos;
b = pMeshBuffer->Vertices[x*terrainData.Width+z].Pos;
c = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z+1].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
a = pMeshBuffer->Vertices[x*terrainData.Width+z+1].Pos;
b = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z+1].Pos;
c = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
count += 2;
}
// bottom left
if (x<terrainData.Width-1 && z>0)
{
a = pMeshBuffer->Vertices[x*terrainData.Width+z-1].Pos;
b = pMeshBuffer->Vertices[x*terrainData.Width+z].Pos;
c = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
a = pMeshBuffer->Vertices[x*terrainData.Width+z-1].Pos;
b = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z].Pos;
c = pMeshBuffer->Vertices[(x+1)*terrainData.Width+z-1].Pos;
b -= a;
c -= a;
t = b.crossProduct ( c );
t.normalize ( );
normal += t;
count += 2;
}
if ( count != 0 )
{
normal.normalize ( );
}
else
{
normal.set( 0.0f, 1.0f, 0.0f );
}
pMeshBuffer->Vertices[x * terrainData.Width + z].Normal = normal;
}
}
//////////////////////////////////////////////////////////////////////////////////
textureSplatting_vs.hlsl
Code: Select all
float4x4 WorldViewProj; // World * View * Projection transformation
struct VS_INPUT
{
float4 Position : POSITION; // vertex position
float2 DetailMapCoord : TEXCOORD0;
float2 TextureOneCoord : TEXCOORD1;
float2 TextureTwoCoord : TEXCOORD2;
float2 TextureThreeCoord : TEXCOORD3;
float2 TextureFourCoord : TEXCOORD4;
float2 LightMapCoord : TEXCOORD5;
float2 ColorMapCoord : TEXCOORD6;
};
// Vertex shader output structure
struct VS_OUTPUT
{
float4 Position : POSITION; // vertex position
float2 DetailMapCoord : TEXCOORD0;
float2 TextureOneCoord : TEXCOORD1;
float2 TextureTwoCoord : TEXCOORD2;
float2 TextureThreeCoord : TEXCOORD3;
float2 TextureFourCoord : TEXCOORD4;
float2 LightMapCoord : TEXCOORD5;
float2 ColorMapCoord : TEXCOORD6;
};
VS_OUTPUT main( VS_INPUT Input )
{
VS_OUTPUT Output;
// transform position to clip space
Output.Position = mul(Input.Position, WorldViewProj);
Output.DetailMapCoord = Input.DetailMapCoord;
Output.TextureOneCoord = Input.TextureOneCoord;
Output.TextureTwoCoord = Input.TextureTwoCoord;
Output.TextureThreeCoord = Input.TextureThreeCoord;
Output.TextureFourCoord = Input.TextureFourCoord;
Output.LightMapCoord = Input.LightMapCoord;
Output.ColorMapCoord = Input.ColorMapCoord;
return Output;
}
//////////////////////////////////////////////////////////////////////////////////
textureSplatting_ps.hlsl
Code: Select all
sampler2D DetailMap;
sampler2D TextureOne;
sampler2D TextureTwo;
sampler2D TextureThree;
sampler2D TextureFour;
sampler2D LightMap;
sampler2D ColorMap;
// Pixel shader output structure
struct PS_OUTPUT
{
float4 Color : COLOR0; // Pixel color
};
struct PS_INPUT
{
float3 Position : POSITION; // vertex position
float3 Normal : NORMAL;
float4 Color : COLOR;
float2 DetailMapCoord : TEXCOORD0;
float2 TextureOneCoord : TEXCOORD1;
float2 TextureTwoCoord : TEXCOORD2;
float2 TextureThreeCoord : TEXCOORD3;
float2 TextureFourCoord : TEXCOORD4;
float2 LightMapCoord : TEXCOORD5;
float2 ColorMapCoord : TEXCOORD6;
};
PS_OUTPUT main( PS_INPUT Input )
{
PS_OUTPUT Output;
float4 detailMapColor = tex2D(DetailMap, Input.DetailMapCoord);
float4 textOneColor = tex2D(TextureOne, Input.TextureOneCoord);
float4 textTwoColor = tex2D(TextureTwo, Input.TextureTwoCoord);
float4 textThreeColor = tex2D(TextureThree, Input.TextureThreeCoord);
float4 textFourColor = tex2D(TextureFour, Input.TextureFourCoord);
float4 lightMapColor = tex2D(LightMap, Input.LightMapCoord);
float4 colorMapColor = tex2D(ColorMap, Input.ColorMapCoord);
textOneColor = textOneColor * detailMapColor.x;
textTwoColor = textOneColor + detailMapColor.y * (textTwoColor - textOneColor);
textThreeColor = textTwoColor + detailMapColor.z * (textThreeColor - textTwoColor);
textFourColor = textThreeColor + detailMapColor.w * (textFourColor - textThreeColor);
colorMapColor = colorMapColor * 1.6;
lightMapColor = lightMapColor + 0.2;
//Output.Color = textFourColor * lightMapColor - colorMapColor;
Output.Color = textFourColor * lightMapColor * colorMapColor;
return Output;
}
That's it~ First text file is pretty important. Most of them are about
modifying Irrlicht 1.3.1. Once you read, i am pretty sure even you
are using Irrlicht 1.4+, you could get the idea how to modify that.
Ok~ That's it for now. If i implement LOD or Using more detailed textures
(EarthSculptor's license version support 8 detailed textures), then
i will update the project.