add basic version of 3d tilemap sources
This commit is contained in:
parent
ad3dd47192
commit
6e90860293
77
src/tilemap/chunkrenderer.cpp
Normal file
77
src/tilemap/chunkrenderer.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "chunkrenderer.h"
|
||||
|
||||
#include "../framework/graphics/blendstate.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/graphics/graphicsdevice.h"
|
||||
#include "../framework/graphics/renderstate.h"
|
||||
#include "../framework/graphics/textureatlas.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/matrix4x4.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilemap.h"
|
||||
#include "tilemeshcollection.h"
|
||||
|
||||
ChunkRenderer::ChunkRenderer(GraphicsDevice *graphicsDevice)
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_graphicsDevice = graphicsDevice;
|
||||
|
||||
m_renderState = new RENDERSTATE_DEFAULT;
|
||||
ASSERT(m_renderState != NULL);
|
||||
|
||||
m_defaultBlendState = new BLENDSTATE_DEFAULT;
|
||||
ASSERT(m_defaultBlendState != NULL);
|
||||
|
||||
m_alphaBlendState = new BLENDSTATE_ALPHABLEND;
|
||||
ASSERT(m_alphaBlendState != NULL);
|
||||
}
|
||||
|
||||
ChunkRenderer::~ChunkRenderer()
|
||||
{
|
||||
STACK_TRACE;
|
||||
SAFE_DELETE(m_renderState);
|
||||
SAFE_DELETE(m_defaultBlendState);
|
||||
SAFE_DELETE(m_alphaBlendState);
|
||||
}
|
||||
|
||||
uint32_t ChunkRenderer::Render(const TileChunk *chunk)
|
||||
{
|
||||
STACK_TRACE;
|
||||
const Texture *texture = chunk->GetTileMap()->GetMeshes()->GetTextureAtlas()->GetTexture();
|
||||
|
||||
uint32_t numVertices = chunk->GetNumVertices();
|
||||
|
||||
m_renderState->Apply();
|
||||
m_defaultBlendState->Apply();
|
||||
m_graphicsDevice->BindTexture(texture);
|
||||
m_graphicsDevice->BindVertexBuffer(chunk->GetVertices());
|
||||
m_graphicsDevice->RenderTriangles(0, numVertices / 3);
|
||||
m_graphicsDevice->UnbindVertexBuffer();
|
||||
|
||||
return numVertices;
|
||||
}
|
||||
|
||||
uint32_t ChunkRenderer::RenderAlpha(const TileChunk *chunk)
|
||||
{
|
||||
STACK_TRACE;
|
||||
uint32_t numVertices = 0;
|
||||
|
||||
if (chunk->IsAlphaEnabled())
|
||||
{
|
||||
const Texture *texture = chunk->GetTileMap()->GetMeshes()->GetTextureAtlas()->GetTexture();
|
||||
|
||||
numVertices = chunk->GetNumAlphaVertices();
|
||||
|
||||
m_renderState->Apply();
|
||||
m_alphaBlendState->Apply();
|
||||
m_graphicsDevice->BindTexture(texture);
|
||||
m_graphicsDevice->BindVertexBuffer(chunk->GetAlphaVertices());
|
||||
m_graphicsDevice->RenderTriangles(0, numVertices / 3);
|
||||
m_graphicsDevice->UnbindVertexBuffer();
|
||||
}
|
||||
|
||||
return numVertices;
|
||||
}
|
||||
|
28
src/tilemap/chunkrenderer.h
Normal file
28
src/tilemap/chunkrenderer.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef __TILEMAP_CHUNKRENDERER_H_INCLUDED__
|
||||
#define __TILEMAP_CHUNKRENDERER_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
class BlendState;
|
||||
class GraphicsDevice;
|
||||
class RenderState;
|
||||
class TileChunk;
|
||||
|
||||
class ChunkRenderer
|
||||
{
|
||||
public:
|
||||
ChunkRenderer(GraphicsDevice *graphicsDevice);
|
||||
virtual ~ChunkRenderer();
|
||||
|
||||
uint32_t Render(const TileChunk *chunk);
|
||||
uint32_t RenderAlpha(const TileChunk *chunk);
|
||||
|
||||
private:
|
||||
GraphicsDevice *m_graphicsDevice;
|
||||
RenderState *m_renderState;
|
||||
BlendState *m_defaultBlendState;
|
||||
BlendState *m_alphaBlendState;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
251
src/tilemap/chunkvertexgenerator.cpp
Normal file
251
src/tilemap/chunkvertexgenerator.cpp
Normal file
|
@ -0,0 +1,251 @@
|
|||
#include "../framework/debug.h"
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "chunkvertexgenerator.h"
|
||||
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/matrix4x4.h"
|
||||
#include "../framework/math/point3.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
#include "cubetilemesh.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilemap.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshcollection.h"
|
||||
#include "tilemeshdefs.h"
|
||||
|
||||
ChunkVertexGenerator::ChunkVertexGenerator()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
ChunkVertexGenerator::~ChunkVertexGenerator()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
void ChunkVertexGenerator::Generate(TileChunk *chunk, uint32_t &numVertices, uint32_t &numAlphaVertices)
|
||||
{
|
||||
STACK_TRACE;
|
||||
numVertices = 0;
|
||||
numAlphaVertices = 0;
|
||||
|
||||
chunk->GetVertices()->MoveToStart();
|
||||
if (chunk->IsAlphaEnabled())
|
||||
chunk->GetAlphaVertices()->MoveToStart();
|
||||
|
||||
const TileMap *tileMap = chunk->GetTileMap();
|
||||
|
||||
for (uint32_t y = 0; y < chunk->GetHeight(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < chunk->GetDepth(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < chunk->GetWidth(); ++x)
|
||||
{
|
||||
Tile *tile = chunk->Get(x, y, z);
|
||||
if (tile->tile == NO_TILE)
|
||||
continue;
|
||||
|
||||
const TileMesh *mesh = chunk->GetTileMap()->GetMeshes()->Get(tile);
|
||||
|
||||
// enable alpha for this chunk if this tile has an alpha-enabled
|
||||
// mesh and we haven't done that already
|
||||
// TODO: optimize so that we only do this if the tile is visible
|
||||
if (mesh->IsAlpha())
|
||||
{
|
||||
if (!chunk->IsAlphaEnabled())
|
||||
chunk->EnableAlphaVertices(TRUE);
|
||||
}
|
||||
|
||||
// "tilemap space" position that this tile is at
|
||||
Point3 position;
|
||||
position.x = x + (int32_t)chunk->GetPosition().x;
|
||||
position.y = y + (int32_t)chunk->GetPosition().y;
|
||||
position.z = z + (int32_t)chunk->GetPosition().z;
|
||||
|
||||
const Matrix4x4 *transform = tile->GetTransformationMatrix();
|
||||
|
||||
// tile color
|
||||
Color color;
|
||||
if (tile->HasCustomColor())
|
||||
color = Color::FromInt(tile->color);
|
||||
else
|
||||
color = mesh->GetColor();
|
||||
|
||||
if (mesh->GetType() == TILEMESH_CUBE)
|
||||
{
|
||||
CubeTileMesh *cubeMesh = (CubeTileMesh*)mesh;
|
||||
|
||||
// determine what's next to each cube face
|
||||
Tile *left = chunk->GetWithinSelfOrNeighbourSafe(x - 1, y, z);
|
||||
Tile *right = chunk->GetWithinSelfOrNeighbourSafe(x + 1, y, z);
|
||||
Tile *forward = chunk->GetWithinSelfOrNeighbourSafe(x, y, z - 1);
|
||||
Tile *backward = chunk->GetWithinSelfOrNeighbourSafe(x, y, z + 1);
|
||||
Tile *down = chunk->GetWithinSelfOrNeighbourSafe(x, y - 1, z);
|
||||
Tile *up = chunk->GetWithinSelfOrNeighbourSafe(x, y + 1, z);
|
||||
|
||||
// evaluate each face's visibility and add it's vertices if needed one at a time
|
||||
if ((left == NULL || left->tile == NO_TILE || !tileMap->GetMeshes()->Get(left)->IsOpaque(SIDE_RIGHT)) && cubeMesh->HasFace(SIDE_LEFT))
|
||||
{
|
||||
// left face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetLeftFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetLeftFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
if ((right == NULL || right->tile == NO_TILE || !tileMap->GetMeshes()->Get(right)->IsOpaque(SIDE_LEFT)) && cubeMesh->HasFace(SIDE_RIGHT))
|
||||
{
|
||||
// right face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetRightFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetRightFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
if ((forward == NULL || forward->tile == NO_TILE || !tileMap->GetMeshes()->Get(forward)->IsOpaque(SIDE_BACK)) && cubeMesh->HasFace(SIDE_FRONT))
|
||||
{
|
||||
// front face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetFrontFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetFrontFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
if ((backward == NULL || backward->tile == NO_TILE || !tileMap->GetMeshes()->Get(backward)->IsOpaque(SIDE_FRONT)) && cubeMesh->HasFace(SIDE_BACK))
|
||||
{
|
||||
// back face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetBackFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetBackFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
if ((down == NULL || down->tile == NO_TILE || !tileMap->GetMeshes()->Get(down)->IsOpaque(SIDE_TOP)) && cubeMesh->HasFace(SIDE_BOTTOM))
|
||||
{
|
||||
// bottom face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetBottomFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetBottomFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
if ((up == NULL || up->tile == NO_TILE || !tileMap->GetMeshes()->Get(up)->IsOpaque(SIDE_BOTTOM)) && cubeMesh->HasFace(SIDE_TOP))
|
||||
{
|
||||
// top face is visible
|
||||
if (cubeMesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(cubeMesh, chunk, TRUE, position, transform, color, cubeMesh->GetTopFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
else
|
||||
numVertices += AddMesh(cubeMesh, chunk, FALSE, position, transform, color, cubeMesh->GetTopFaceVertexOffset(), CUBE_VERTICES_PER_FACE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL visible = FALSE;
|
||||
|
||||
// visibility determination. we check for at least one
|
||||
// adjacent empty space / non-opaque tile
|
||||
Tile *left = chunk->GetWithinSelfOrNeighbourSafe(x - 1, y, z);
|
||||
Tile *right = chunk->GetWithinSelfOrNeighbourSafe(x + 1, y, z);
|
||||
Tile *forward = chunk->GetWithinSelfOrNeighbourSafe(x, y, z - 1);
|
||||
Tile *backward = chunk->GetWithinSelfOrNeighbourSafe(x, y, z + 1);
|
||||
Tile *down = chunk->GetWithinSelfOrNeighbourSafe(x, y - 1, z);
|
||||
Tile *up = chunk->GetWithinSelfOrNeighbourSafe(x, y + 1, z);
|
||||
|
||||
// NULL == empty space (off the edge of the entire map)
|
||||
if (
|
||||
(left == NULL || left->tile == NO_TILE || !tileMap->GetMeshes()->Get(left)->IsOpaque(SIDE_RIGHT)) ||
|
||||
(right == NULL || right->tile == NO_TILE || !tileMap->GetMeshes()->Get(right)->IsOpaque(SIDE_LEFT)) ||
|
||||
(forward == NULL || forward->tile == NO_TILE || !tileMap->GetMeshes()->Get(forward)->IsOpaque(SIDE_BACK)) ||
|
||||
(backward == NULL || backward->tile == NO_TILE || !tileMap->GetMeshes()->Get(backward)->IsOpaque(SIDE_FRONT)) ||
|
||||
(up == NULL || up->tile == NO_TILE || !tileMap->GetMeshes()->Get(up)->IsOpaque(SIDE_BOTTOM)) ||
|
||||
(down == NULL || down->tile == NO_TILE || !tileMap->GetMeshes()->Get(down)->IsOpaque(SIDE_TOP))
|
||||
)
|
||||
visible = TRUE;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
if (mesh->IsAlpha())
|
||||
numAlphaVertices += AddMesh(mesh, chunk, TRUE, position, transform, color, 0, mesh->GetBuffer()->GetNumElements());
|
||||
else
|
||||
numVertices += AddMesh(mesh, chunk, FALSE, position, transform, color, 0, mesh->GetBuffer()->GetNumElements());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numAlphaVertices == 0)
|
||||
chunk->EnableAlphaVertices(FALSE);
|
||||
}
|
||||
|
||||
uint32_t ChunkVertexGenerator::AddMesh(const TileMesh *mesh, TileChunk *chunk, BOOL isAlpha, const Point3 &position, const Matrix4x4 *transform, const Color &color, uint32_t firstVertex, uint32_t numVertices)
|
||||
{
|
||||
STACK_TRACE;
|
||||
VertexBuffer *sourceBuffer = mesh->GetBuffer();
|
||||
sourceBuffer->MoveToStart();
|
||||
|
||||
VertexBuffer *destBuffer;
|
||||
if (isAlpha)
|
||||
destBuffer = chunk->GetAlphaVertices();
|
||||
else
|
||||
destBuffer = chunk->GetVertices();
|
||||
|
||||
ASSERT(firstVertex < sourceBuffer->GetNumElements());
|
||||
ASSERT((firstVertex + numVertices - 1) < sourceBuffer->GetNumElements());
|
||||
|
||||
// ensure there is enough space in the destination buffer
|
||||
uint32_t verticesToAdd = numVertices;
|
||||
if (destBuffer->GetRemainingSpace() < verticesToAdd)
|
||||
{
|
||||
// not enough space, need to resize the destination buffer
|
||||
// resize by the exact amount needed making sure there's no wasted space at the end
|
||||
destBuffer->Extend(verticesToAdd - destBuffer->GetRemainingSpace());
|
||||
ASSERT(destBuffer->GetRemainingSpace() >= verticesToAdd);
|
||||
}
|
||||
|
||||
// adjust position by the tilemesh offset. TileMesh's are modeled using the
|
||||
// origin (0,0,0) as the center and are 1 unit wide/deep/tall. So, their
|
||||
// max extents are from -0.5,-0.5,-0.5 to 0.5,0.5,0.5. For rendering
|
||||
// purposes in a chunk, we want the extents to be 0,0,0 to 1,1,1. This
|
||||
// adjustment fixes that
|
||||
Vector3 positionOffset = TILEMESH_OFFSET;
|
||||
positionOffset.x += (float)position.x;
|
||||
positionOffset.y += (float)position.y;
|
||||
positionOffset.z += (float)position.z;
|
||||
|
||||
// copy vertices
|
||||
sourceBuffer->MoveTo(firstVertex);
|
||||
for (uint32_t i = 0; i < numVertices; ++i)
|
||||
{
|
||||
CopyVertex(chunk, sourceBuffer, destBuffer, positionOffset, transform, color);
|
||||
|
||||
sourceBuffer->MoveNext();
|
||||
destBuffer->MoveNext();
|
||||
}
|
||||
|
||||
return verticesToAdd;
|
||||
}
|
||||
|
||||
void ChunkVertexGenerator::CopyVertex(const TileChunk *chunk, VertexBuffer *sourceBuffer, VertexBuffer *destBuffer, const Vector3 &positionOffset, const Matrix4x4 *transform, const Color &color)
|
||||
{
|
||||
Vector3 v = sourceBuffer->GetCurrentPosition3();
|
||||
Vector3 n = sourceBuffer->GetCurrentNormal();
|
||||
|
||||
if (transform != NULL)
|
||||
{
|
||||
// need to transform the vertex + normal first before copying it
|
||||
v = v * (*transform);
|
||||
n = n * (*transform);
|
||||
}
|
||||
|
||||
// translate vertex into "tilemap space"
|
||||
v += positionOffset;
|
||||
|
||||
// copy to destination
|
||||
destBuffer->SetCurrentPosition3(v);
|
||||
destBuffer->SetCurrentNormal(n);
|
||||
|
||||
// just directly copy the tex coord as-is
|
||||
destBuffer->SetCurrentTexCoord(sourceBuffer->GetCurrentTexCoord());
|
||||
|
||||
// color is the same for the entire mesh
|
||||
destBuffer->SetCurrentColor(color);
|
||||
}
|
||||
|
31
src/tilemap/chunkvertexgenerator.h
Normal file
31
src/tilemap/chunkvertexgenerator.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef __TILEMAP_CHUNKVERTEXGENERATOR_H_INCLUDED__
|
||||
#define __TILEMAP_CHUNKVERTEXGENERATOR_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
class CubeTileMesh;
|
||||
class StaticTileMesh;
|
||||
class TileMesh;
|
||||
class VertexBuffer;
|
||||
struct Color;
|
||||
struct Matrix4x4;
|
||||
struct Point3;
|
||||
struct Vector3;
|
||||
|
||||
class TileChunk;
|
||||
|
||||
class ChunkVertexGenerator
|
||||
{
|
||||
public:
|
||||
ChunkVertexGenerator();
|
||||
virtual ~ChunkVertexGenerator();
|
||||
|
||||
void Generate(TileChunk *chunk, uint32_t &numVertices, uint32_t &numAlphaVertices);
|
||||
|
||||
private:
|
||||
uint32_t AddMesh(const TileMesh *mesh, TileChunk *chunk, BOOL isAlpha, const Point3 &position, const Matrix4x4 *transform, const Color &color, uint32_t firstVertex, uint32_t numVertices);
|
||||
virtual void CopyVertex(const TileChunk *chunk, VertexBuffer *sourceBuffer, VertexBuffer *destBuffer, const Vector3 &positionOffset, const Matrix4x4 *transform, const Color &color);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
282
src/tilemap/cubetilemesh.cpp
Normal file
282
src/tilemap/cubetilemesh.cpp
Normal file
|
@ -0,0 +1,282 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "cubetilemesh.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/mathhelpers.h"
|
||||
#include "../framework/math/rectf.h"
|
||||
#include "../framework/math/vector2.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
|
||||
CubeTileMesh::CubeTileMesh(CUBE_FACES faces, const RectF *textureAtlasTileBoundaries, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_faces = faces;
|
||||
|
||||
SetOpaque(opaqueSides);
|
||||
SetAlpha(alpha);
|
||||
SetColor(color);
|
||||
SetTranslucency(translucency);
|
||||
SetLight(lightValue);
|
||||
|
||||
m_topFaceVertexOffset = 0;
|
||||
m_bottomFaceVertexOffset = 0;
|
||||
m_frontFaceVertexOffset = 0;
|
||||
m_backFaceVertexOffset = 0;
|
||||
m_leftFaceVertexOffset = 0;
|
||||
m_rightFaceVertexOffset = 0;
|
||||
|
||||
uint32_t numVertices = 0;
|
||||
|
||||
if (HasFace(SIDE_TOP))
|
||||
{
|
||||
m_topFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
if (HasFace(SIDE_BOTTOM))
|
||||
{
|
||||
m_bottomFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
if (HasFace(SIDE_FRONT))
|
||||
{
|
||||
m_frontFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
if (HasFace(SIDE_BACK))
|
||||
{
|
||||
m_backFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
if (HasFace(SIDE_LEFT))
|
||||
{
|
||||
m_leftFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
if (HasFace(SIDE_RIGHT))
|
||||
{
|
||||
m_rightFaceVertexOffset = numVertices;
|
||||
numVertices += CUBE_VERTICES_PER_FACE;
|
||||
}
|
||||
|
||||
m_vertices = new VertexBuffer(BUFFEROBJECT_USAGE_STATIC);
|
||||
ASSERT(m_vertices != NULL);
|
||||
m_vertices->AddAttribute(VERTEX_POS_3D);
|
||||
m_vertices->AddAttribute(VERTEX_NORMAL);
|
||||
m_vertices->AddAttribute(VERTEX_COLOR);
|
||||
m_vertices->AddAttribute(VERTEX_TEXCOORD);
|
||||
m_vertices->Create(numVertices);
|
||||
|
||||
SetupFaceVertices(textureAtlasTileBoundaries);
|
||||
SetupCollisionVertices();
|
||||
}
|
||||
|
||||
CubeTileMesh::~CubeTileMesh()
|
||||
{
|
||||
STACK_TRACE;
|
||||
SAFE_DELETE(m_vertices);
|
||||
}
|
||||
|
||||
void CubeTileMesh::SetupFaceVertices(const RectF *textureAtlasTileBoundaries)
|
||||
{
|
||||
STACK_TRACE;
|
||||
uint32_t pos = 0;
|
||||
Vector3 a(-0.5f, -0.5f, -0.5f);
|
||||
Vector3 b(0.5f, 0.5f, 0.5f);
|
||||
|
||||
if (HasFace(SIDE_TOP))
|
||||
{
|
||||
pos = m_topFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(a.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos, UP);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(b.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 1, UP);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(a.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 2, UP);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(b.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 3, UP);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(b.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 4, UP);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(a.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 5, UP);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
|
||||
if (HasFace(SIDE_BOTTOM))
|
||||
{
|
||||
pos = m_bottomFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(b.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos, DOWN);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(a.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 1, DOWN);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(b.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 2, DOWN);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(a.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 3, DOWN);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(a.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 4, DOWN);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(b.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 5, DOWN);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
|
||||
if (HasFace(SIDE_FRONT))
|
||||
{
|
||||
pos = m_frontFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(b.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos, FORWARD);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(a.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 1, FORWARD);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(b.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 2, FORWARD);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(a.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 3, FORWARD);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(a.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 4, FORWARD);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(b.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 5, FORWARD);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
|
||||
if (HasFace(SIDE_BACK))
|
||||
{
|
||||
pos = m_backFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(a.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(b.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 1, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(a.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 2, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(b.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 3, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(b.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 4, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(a.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 5, BACKWARD);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
|
||||
if (HasFace(SIDE_LEFT))
|
||||
{
|
||||
pos = m_leftFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(a.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos, LEFT);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(a.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 1, LEFT);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(a.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 2, LEFT);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(a.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos + 3, LEFT);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(a.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 4, LEFT);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(a.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 5, LEFT);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
|
||||
if (HasFace(SIDE_RIGHT))
|
||||
{
|
||||
pos = m_rightFaceVertexOffset;
|
||||
|
||||
m_vertices->SetPosition3(pos, Vector3(b.x, a.y, b.z));
|
||||
m_vertices->SetNormal(pos, RIGHT);
|
||||
m_vertices->SetTexCoord(pos, ScaleTexCoord(Vector2(0.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 1, Vector3(b.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 1, RIGHT);
|
||||
m_vertices->SetTexCoord(pos + 1, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 2, Vector3(b.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 2, RIGHT);
|
||||
m_vertices->SetTexCoord(pos + 2, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 3, Vector3(b.x, a.y, a.z));
|
||||
m_vertices->SetNormal(pos + 3, RIGHT);
|
||||
m_vertices->SetTexCoord(pos + 3, ScaleTexCoord(Vector2(1.0f, 1.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 4, Vector3(b.x, b.y, a.z));
|
||||
m_vertices->SetNormal(pos + 4, RIGHT);
|
||||
m_vertices->SetTexCoord(pos + 4, ScaleTexCoord(Vector2(1.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
|
||||
m_vertices->SetPosition3(pos + 5, Vector3(b.x, b.y, b.z));
|
||||
m_vertices->SetNormal(pos + 5, RIGHT);
|
||||
m_vertices->SetTexCoord(pos + 5, ScaleTexCoord(Vector2(0.0f, 0.0f), *textureAtlasTileBoundaries));
|
||||
}
|
||||
}
|
||||
|
||||
void CubeTileMesh::SetupCollisionVertices()
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_numCollisionVertices = m_vertices->GetNumElements();
|
||||
|
||||
m_collisionVertices = new Vector3[m_numCollisionVertices];
|
||||
ASSERT(m_collisionVertices != NULL);
|
||||
|
||||
for (uint32_t i = 0; i < m_numCollisionVertices; ++i)
|
||||
m_collisionVertices[i] = m_vertices->GetPosition3(i);
|
||||
}
|
||||
|
||||
inline Vector2 CubeTileMesh::ScaleTexCoord(const Vector2 &texCoord, const RectF &tileBoundaries) const
|
||||
{
|
||||
Vector2 out;
|
||||
out.x = ScaleRange(texCoord.x, 0.0f, 1.0f, tileBoundaries.left, tileBoundaries.right);
|
||||
out.y = ScaleRange(texCoord.y, 0.0f, 1.0f, tileBoundaries.top, tileBoundaries.bottom);
|
||||
return out;
|
||||
}
|
||||
|
57
src/tilemap/cubetilemesh.h
Normal file
57
src/tilemap/cubetilemesh.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef __TILEMAP_CUBETILEMESH_H_INCLUDED__
|
||||
#define __TILEMAP_CUBETILEMESH_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
|
||||
class VertexBuffer;
|
||||
struct RectF;
|
||||
struct Vector2;
|
||||
struct Vector3;
|
||||
|
||||
class CubeTileMesh : public TileMesh
|
||||
{
|
||||
public:
|
||||
CubeTileMesh(CUBE_FACES faces, const RectF *textureAtlasTileBoundaries, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
virtual ~CubeTileMesh();
|
||||
|
||||
VertexBuffer* GetBuffer() const { return m_vertices; }
|
||||
uint32_t GetTopFaceVertexOffset() const { return m_topFaceVertexOffset; }
|
||||
uint32_t GetBottomFaceVertexOffset() const { return m_bottomFaceVertexOffset; }
|
||||
uint32_t GetFrontFaceVertexOffset() const { return m_frontFaceVertexOffset; }
|
||||
uint32_t GetBackFaceVertexOffset() const { return m_backFaceVertexOffset; }
|
||||
uint32_t GetLeftFaceVertexOffset() const { return m_leftFaceVertexOffset; }
|
||||
uint32_t GetRightFaceVertexOffset() const { return m_rightFaceVertexOffset; }
|
||||
|
||||
uint32_t GetNumCollisionVertices() const { return m_numCollisionVertices; }
|
||||
const Vector3* GetCollisionVertices() const { return m_collisionVertices; }
|
||||
|
||||
CUBE_FACES GetFaces() const { return m_faces; }
|
||||
BOOL HasFace(CUBE_FACES face) const { return IsBitSet(face, m_faces); }
|
||||
|
||||
TILEMESH_TYPE GetType() const { return TILEMESH_CUBE; }
|
||||
|
||||
private:
|
||||
void SetupFaceVertices(const RectF *textureAtlasTileBoundaries);
|
||||
void SetupCollisionVertices();
|
||||
|
||||
Vector2 ScaleTexCoord(const Vector2 &texCoord, const RectF &tileBoundaries) const;
|
||||
|
||||
VertexBuffer *m_vertices;
|
||||
uint32_t m_topFaceVertexOffset;
|
||||
uint32_t m_bottomFaceVertexOffset;
|
||||
uint32_t m_frontFaceVertexOffset;
|
||||
uint32_t m_backFaceVertexOffset;
|
||||
uint32_t m_leftFaceVertexOffset;
|
||||
uint32_t m_rightFaceVertexOffset;
|
||||
uint32_t m_numCollisionVertices;
|
||||
Vector3 *m_collisionVertices;
|
||||
CUBE_FACES m_faces;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
91
src/tilemap/litchunkvertexgenerator.cpp
Normal file
91
src/tilemap/litchunkvertexgenerator.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "litchunkvertexgenerator.h"
|
||||
#include "tile.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemap.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/matrix4x4.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
|
||||
LitChunkVertexGenerator::LitChunkVertexGenerator()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
LitChunkVertexGenerator::~LitChunkVertexGenerator()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
void LitChunkVertexGenerator::CopyVertex(const TileChunk *chunk, VertexBuffer *sourceBuffer, VertexBuffer *destBuffer, const Vector3 &positionOffset, const Matrix4x4 *transform, const Color &color)
|
||||
{
|
||||
// figure out what the default lighting value is for this chunk
|
||||
TILE_LIGHT_VALUE defaultLightValue = chunk->GetTileMap()->GetSkyLightValue();
|
||||
if (chunk->GetTileMap()->GetAmbientLightValue() > defaultLightValue)
|
||||
defaultLightValue = chunk->GetTileMap()->GetAmbientLightValue();
|
||||
|
||||
Vector3 v = sourceBuffer->GetCurrentPosition3();
|
||||
Vector3 n = sourceBuffer->GetCurrentNormal();
|
||||
|
||||
if (transform != NULL)
|
||||
{
|
||||
// need to transform the vertex + normal first before copying it
|
||||
v = v * (*transform);
|
||||
n = n * (*transform);
|
||||
}
|
||||
|
||||
// translate vertex into "tilemap space"
|
||||
v += positionOffset;
|
||||
|
||||
destBuffer->SetCurrentPosition3(v);
|
||||
destBuffer->SetCurrentNormal(n);
|
||||
|
||||
// just directly copy the tex coord as-is
|
||||
destBuffer->SetCurrentTexCoord(sourceBuffer->GetCurrentTexCoord());
|
||||
|
||||
// the color we set to the destination determines the brightness (lighting)
|
||||
|
||||
// use the tile that's adjacent to this one in the direction that
|
||||
// this vertex's normal is pointing as the light source
|
||||
Vector3 lightSource = positionOffset + n;
|
||||
|
||||
// if the light source position is off the bounds of the entire world
|
||||
// then use the default light value.
|
||||
// the below call to TileChunk::GetWithinSelfOrNeighbour() actually does
|
||||
// do bounds checking, but we would need to cast from float to int
|
||||
// first. this causes some issues when the one or more of the
|
||||
// lightSource x/y/z values are between 0 and -1 (rounds up to 0 when
|
||||
// using a cast). rather then do some weird custom rounding, we just
|
||||
// check for negatives to ensure we catch them and handle it properly
|
||||
// NOTE: this is only a problem currently because world coords are
|
||||
// always >= 0. this will need to be adjusted if that changes
|
||||
float brightness;
|
||||
if (lightSource.x < 0.0f || lightSource.y < 0.0f || lightSource.z < 0.0f)
|
||||
brightness = Tile::GetBrightness(defaultLightValue);
|
||||
else
|
||||
{
|
||||
// light source is within the boundaries of the world, get the
|
||||
// actual tile (may or may not be in a neighbouring chunk)
|
||||
int32_t lightX = (int32_t)lightSource.x - chunk->GetX();
|
||||
int32_t lightY = (int32_t)lightSource.y - chunk->GetY();
|
||||
int32_t lightZ = (int32_t)lightSource.z - chunk->GetZ();
|
||||
|
||||
const Tile *lightTile = chunk->GetWithinSelfOrNeighbourSafe(lightX, lightY, lightZ);
|
||||
if (lightTile == NULL)
|
||||
brightness = Tile::GetBrightness(defaultLightValue);
|
||||
else
|
||||
brightness = lightTile->GetBrightness();
|
||||
}
|
||||
|
||||
Color resultingColor;
|
||||
resultingColor.r = color.r * brightness;
|
||||
resultingColor.g = color.g * brightness;
|
||||
resultingColor.b = color.b * brightness;
|
||||
resultingColor.a = color.a;
|
||||
|
||||
destBuffer->SetCurrentColor(resultingColor);
|
||||
}
|
||||
|
23
src/tilemap/litchunkvertexgenerator.h
Normal file
23
src/tilemap/litchunkvertexgenerator.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef __TILEMAP_LITCHUNKVERTEXGENERATOR_H_INCLUDED__
|
||||
#define __TILEMAP_LITCHUNKVERTEXGENERATOR_H_INCLUDED__
|
||||
|
||||
#include "chunkvertexgenerator.h"
|
||||
|
||||
class TileChunk;
|
||||
class VertexBuffer;
|
||||
struct Color;
|
||||
struct Matrix4x4;
|
||||
struct Vector3;
|
||||
|
||||
class LitChunkVertexGenerator : public ChunkVertexGenerator
|
||||
{
|
||||
public:
|
||||
LitChunkVertexGenerator();
|
||||
virtual ~LitChunkVertexGenerator();
|
||||
|
||||
private:
|
||||
void CopyVertex(const TileChunk *chunk, VertexBuffer *sourceBuffer, VertexBuffer *destBuffer, const Vector3 &positionOffset, const Matrix4x4 *transform, const Color &color);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
235
src/tilemap/positionandskytilemaplighter.cpp
Normal file
235
src/tilemap/positionandskytilemaplighter.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "positionandskytilemaplighter.h"
|
||||
#include "tile.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemap.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshcollection.h"
|
||||
#include "tilemeshdefs.h"
|
||||
|
||||
PositionAndSkyTileMapLighter::PositionAndSkyTileMapLighter()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
PositionAndSkyTileMapLighter::~PositionAndSkyTileMapLighter()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::Light(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ResetLightValues(tileMap);
|
||||
SetupSkyLight(tileMap);
|
||||
ApplyLighting(tileMap);
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::ResetLightValues(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
for (uint32_t y = 0; y < tileMap->GetHeight(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepth(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < tileMap->GetWidth(); ++x)
|
||||
{
|
||||
Tile *tile = tileMap->Get(x, y, z);
|
||||
|
||||
// sky lighting will be recalculated, and other types of light sources
|
||||
// info stays as they were
|
||||
ClearBit(TILE_LIGHT_SKY, tile->flags);
|
||||
tile->skyLight = 0;
|
||||
tile->tileLight = tileMap->GetAmbientLightValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::SetupSkyLight(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
// NOTE: ResetLightValues() clears sky light data in such a way that
|
||||
// doesn't require us to flood-fill 0 light values here for everything
|
||||
// that isn't sky-lit
|
||||
// go through each vertical column one at a time from top to bottom
|
||||
for (uint32_t x = 0; x < tileMap->GetWidth(); ++x)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepth(); ++z)
|
||||
{
|
||||
BOOL stillSkyLit = TRUE;
|
||||
TILE_LIGHT_VALUE currentSkyLightValue = tileMap->GetSkyLightValue();
|
||||
|
||||
for (int32_t y = tileMap->GetHeight() - 1; y >= 0 && stillSkyLit; --y)
|
||||
{
|
||||
Tile *tile = tileMap->Get(x, y, z);
|
||||
const TileMesh *mesh = tileMap->GetMeshes()->Get(tile);
|
||||
if (mesh == NULL || (mesh != NULL && !mesh->IsOpaque(SIDE_TOP) && !mesh->IsOpaque(SIDE_BOTTOM)))
|
||||
{
|
||||
// tile is partially transparent or this tile is empty space
|
||||
SetBit(TILE_LIGHT_SKY, tile->flags);
|
||||
|
||||
if (mesh != NULL)
|
||||
Tile::AdjustLightForTranslucency(currentSkyLightValue, mesh->GetTranslucency());
|
||||
|
||||
tile->skyLight = currentSkyLightValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// tile is present and is fully solid, sky lighting stops
|
||||
// at the tile above this one
|
||||
stillSkyLit = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::ApplyLighting(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
// for each light source (sky or not), recursively go through and set
|
||||
// appropriate lighting for each adjacent tile
|
||||
for (uint32_t y = 0; y < tileMap->GetHeight(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepth(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < tileMap->GetWidth(); ++x)
|
||||
{
|
||||
Tile *tile = tileMap->Get(x, y, z);
|
||||
if (tile->IsEmptySpace())
|
||||
{
|
||||
if (tile->IsSkyLit())
|
||||
SpreadSkyLight(x, y, z, tile, tile->skyLight, tileMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
const TileMesh *mesh = tileMap->GetMeshes()->Get(tile);
|
||||
if (mesh->IsLightSource())
|
||||
SpreadTileLight(x, y, z, tile, mesh->GetLightValue(), tileMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::SpreadSkyLight(int32_t x, int32_t y, int32_t z, Tile *tile, TILE_LIGHT_VALUE light, TileMap *tileMap)
|
||||
{
|
||||
if (light > 0)
|
||||
{
|
||||
tile->skyLight = light;
|
||||
--light;
|
||||
|
||||
Tile *left = tileMap->GetSafe(x - 1, y, z);
|
||||
Tile *right = tileMap->GetSafe(x + 1, y, z);
|
||||
Tile *forward = tileMap->GetSafe(x, y, z - 1);
|
||||
Tile *backward = tileMap->GetSafe(x, y, z + 1);
|
||||
Tile *up = tileMap->GetSafe(x, y + 1, z);
|
||||
Tile *down = tileMap->GetSafe(x, y - 1, z);
|
||||
|
||||
if (left != NULL && (left->IsEmptySpace() || !tileMap->GetMeshes()->Get(left)->IsOpaque(SIDE_RIGHT)) && left->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!left->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(left)->GetTranslucency());
|
||||
SpreadSkyLight(x - 1, y, z, left, spreadLight, tileMap);
|
||||
}
|
||||
if (right != NULL && (right->IsEmptySpace() || !tileMap->GetMeshes()->Get(right)->IsOpaque(SIDE_LEFT)) && right->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!right->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(right)->GetTranslucency());
|
||||
SpreadSkyLight(x + 1, y, z, right, spreadLight, tileMap);
|
||||
}
|
||||
if (forward != NULL && (forward->IsEmptySpace() || !tileMap->GetMeshes()->Get(forward)->IsOpaque(SIDE_BACK)) && forward->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!forward->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(forward)->GetTranslucency());
|
||||
SpreadSkyLight(x, y, z - 1, forward, spreadLight, tileMap);
|
||||
}
|
||||
if (backward != NULL && (backward->IsEmptySpace() || !tileMap->GetMeshes()->Get(backward)->IsOpaque(SIDE_FRONT)) && backward->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!backward->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(backward)->GetTranslucency());
|
||||
SpreadSkyLight(x, y, z + 1, backward, spreadLight, tileMap);
|
||||
}
|
||||
if (up != NULL && (up->IsEmptySpace() || !tileMap->GetMeshes()->Get(up)->IsOpaque(SIDE_BOTTOM)) && up->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!up->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(up)->GetTranslucency());
|
||||
SpreadSkyLight(x, y + 1, z, up, spreadLight, tileMap);
|
||||
}
|
||||
if (down != NULL && (down->IsEmptySpace() || !tileMap->GetMeshes()->Get(down)->IsOpaque(SIDE_TOP)) && down->skyLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!down->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(down)->GetTranslucency());
|
||||
SpreadSkyLight(x, y - 1, z, down, spreadLight, tileMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PositionAndSkyTileMapLighter::SpreadTileLight(int32_t x, int32_t y, int32_t z, Tile *tile, TILE_LIGHT_VALUE light, TileMap *tileMap)
|
||||
{
|
||||
if (light > 0)
|
||||
{
|
||||
tile->tileLight = light;
|
||||
--light;
|
||||
|
||||
Tile *left = tileMap->GetSafe(x - 1, y, z);
|
||||
Tile *right = tileMap->GetSafe(x + 1, y, z);
|
||||
Tile *forward = tileMap->GetSafe(x, y, z - 1);
|
||||
Tile *backward = tileMap->GetSafe(x, y, z + 1);
|
||||
Tile *up = tileMap->GetSafe(x, y + 1, z);
|
||||
Tile *down = tileMap->GetSafe(x, y - 1, z);
|
||||
|
||||
if (left != NULL && (left->IsEmptySpace() || !tileMap->GetMeshes()->Get(left)->IsOpaque(SIDE_RIGHT)) && left->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!left->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(left)->GetTranslucency());
|
||||
SpreadTileLight(x - 1, y, z, left, spreadLight, tileMap);
|
||||
}
|
||||
if (right != NULL && (right->IsEmptySpace() || !tileMap->GetMeshes()->Get(right)->IsOpaque(SIDE_LEFT)) && right->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!right->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(right)->GetTranslucency());
|
||||
SpreadTileLight(x + 1, y, z, right, spreadLight, tileMap);
|
||||
}
|
||||
if (forward != NULL && (forward->IsEmptySpace() || !tileMap->GetMeshes()->Get(forward)->IsOpaque(SIDE_BACK)) && forward->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!forward->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(forward)->GetTranslucency());
|
||||
SpreadTileLight(x, y, z - 1, forward, spreadLight, tileMap);
|
||||
}
|
||||
if (backward != NULL && (backward->IsEmptySpace() || !tileMap->GetMeshes()->Get(backward)->IsOpaque(SIDE_FRONT)) && backward->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!backward->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(backward)->GetTranslucency());
|
||||
SpreadTileLight(x, y, z + 1, backward, spreadLight, tileMap);
|
||||
}
|
||||
if (up != NULL && (up->IsEmptySpace() || !tileMap->GetMeshes()->Get(up)->IsOpaque(SIDE_BOTTOM)) && up->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!up->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(up)->GetTranslucency());
|
||||
SpreadTileLight(x, y + 1, z, up, spreadLight, tileMap);
|
||||
}
|
||||
if (down != NULL && (down->IsEmptySpace() || !tileMap->GetMeshes()->Get(down)->IsOpaque(SIDE_TOP)) && down->tileLight < light)
|
||||
{
|
||||
TILE_LIGHT_VALUE spreadLight = light;
|
||||
if (!down->IsEmptySpace())
|
||||
spreadLight = Tile::AdjustLightForTranslucency(spreadLight, tileMap->GetMeshes()->Get(down)->GetTranslucency());
|
||||
SpreadTileLight(x, y - 1, z, down, spreadLight, tileMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
src/tilemap/positionandskytilemaplighter.h
Normal file
29
src/tilemap/positionandskytilemaplighter.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef __TILEMAP_POSITIONANDSKYTILEMAPLIGHTER_H_INCLUDED__
|
||||
#define __TILEMAP_POSITIONANDSKYTILEMAPLIGHTER_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemaplighter.h"
|
||||
|
||||
class TileMap;
|
||||
|
||||
class PositionAndSkyTileMapLighter : public TileMapLighter
|
||||
{
|
||||
public:
|
||||
PositionAndSkyTileMapLighter();
|
||||
virtual ~PositionAndSkyTileMapLighter();
|
||||
|
||||
void Light(TileMap *tileMap);
|
||||
|
||||
private:
|
||||
void ResetLightValues(TileMap *tileMap);
|
||||
void SetupSkyLight(TileMap *tileMap);
|
||||
void ApplyLighting(TileMap *tileMap);
|
||||
void SpreadSkyLight(int32_t x, int32_t y, int32_t z, Tile *tile, TILE_LIGHT_VALUE light, TileMap *tileMap);
|
||||
void SpreadTileLight(int32_t x, int32_t y, int32_t z, Tile *tile, TILE_LIGHT_VALUE light, TileMap *tileMap);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
87
src/tilemap/simpletilemaplighter.cpp
Normal file
87
src/tilemap/simpletilemaplighter.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "simpletilemaplighter.h"
|
||||
#include "tile.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemap.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshcollection.h"
|
||||
#include "tilemeshdefs.h"
|
||||
|
||||
SimpleTileMapLighter::SimpleTileMapLighter()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
SimpleTileMapLighter::~SimpleTileMapLighter()
|
||||
{
|
||||
STACK_TRACE;
|
||||
}
|
||||
|
||||
void SimpleTileMapLighter::Light(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ResetLightValues(tileMap);
|
||||
ApplySkyLight(tileMap);
|
||||
}
|
||||
|
||||
void SimpleTileMapLighter::ResetLightValues(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
for (uint32_t y = 0; y < tileMap->GetHeight(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepth(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < tileMap->GetWidth(); ++x)
|
||||
{
|
||||
Tile *tile = tileMap->Get(x, y, z);
|
||||
|
||||
// sky lighting will be recalculated, and other types of light sources
|
||||
// info stays as they were
|
||||
ClearBit(TILE_LIGHT_SKY, tile->flags);
|
||||
tile->skyLight = 0;
|
||||
tile->tileLight = tileMap->GetAmbientLightValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleTileMapLighter::ApplySkyLight(TileMap *tileMap)
|
||||
{
|
||||
STACK_TRACE;
|
||||
// NOTE: ResetLightValues() clears sky light data in such a way that
|
||||
// doesn't require us to flood-fill 0 light values here for everything
|
||||
// that isn't sky-lit
|
||||
// go through each vertical column one at a time from top to bottom
|
||||
for (uint32_t x = 0; x < tileMap->GetWidth(); ++x)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepth(); ++z)
|
||||
{
|
||||
BOOL stillSkyLit = TRUE;
|
||||
TILE_LIGHT_VALUE currentSkyLightValue = tileMap->GetSkyLightValue();
|
||||
|
||||
for (int32_t y = tileMap->GetHeight() - 1; y >= 0 && stillSkyLit; --y)
|
||||
{
|
||||
Tile *tile = tileMap->Get(x, y, z);
|
||||
const TileMesh *mesh = tileMap->GetMeshes()->Get(tile);
|
||||
if (mesh == NULL || (mesh != NULL && !mesh->IsOpaque(SIDE_TOP) && !mesh->IsOpaque(SIDE_BOTTOM)))
|
||||
{
|
||||
// tile is partially transparent or this tile is empty space
|
||||
SetBit(TILE_LIGHT_SKY, tile->flags);
|
||||
|
||||
if (mesh != NULL)
|
||||
Tile::AdjustLightForTranslucency(currentSkyLightValue, mesh->GetTranslucency());
|
||||
|
||||
tile->skyLight = tileMap->GetSkyLightValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// tile is present and is fully solid, sky lighting stops
|
||||
// at the tile above this one
|
||||
stillSkyLit = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/tilemap/simpletilemaplighter.h
Normal file
25
src/tilemap/simpletilemaplighter.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef __TILEMAP_SIMPLETILEMAPLIGHTER_H_INCLUDED__
|
||||
#define __TILEMAP_SIMPLETILEMAPLIGHTER_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "tilemaplighter.h"
|
||||
|
||||
class TileMap;
|
||||
|
||||
class SimpleTileMapLighter : public TileMapLighter
|
||||
{
|
||||
public:
|
||||
SimpleTileMapLighter();
|
||||
virtual ~SimpleTileMapLighter();
|
||||
|
||||
void Light(TileMap *tileMap);
|
||||
|
||||
private:
|
||||
void ResetLightValues(TileMap *tileMap);
|
||||
void ApplySkyLight(TileMap *tileMap);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
116
src/tilemap/statictilemesh.cpp
Normal file
116
src/tilemap/statictilemesh.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "statictilemesh.h"
|
||||
#include "../framework/assets/static/staticmesh.h"
|
||||
#include "../framework/assets/static/staticmeshsubset.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/mathhelpers.h"
|
||||
#include "../framework/math/rectf.h"
|
||||
|
||||
StaticTileMesh::StaticTileMesh(const StaticMesh *mesh, const RectF *textureAtlasTileBoundaries, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color, const StaticMesh *collisionMesh)
|
||||
{
|
||||
STACK_TRACE;
|
||||
// only work with the first mesh subset
|
||||
StaticMeshSubset *subset = mesh->GetSubset(0);
|
||||
|
||||
// copy the source buffer
|
||||
m_vertices = new VertexBuffer(BUFFEROBJECT_USAGE_STATIC);
|
||||
ASSERT(m_vertices != NULL);
|
||||
m_vertices->CreateCopyOf(subset->GetVertices());
|
||||
|
||||
SetOpaque(opaqueSides);
|
||||
SetAlpha(alpha);
|
||||
SetColor(color);
|
||||
SetTranslucency(translucency);
|
||||
SetLight(lightValue);
|
||||
|
||||
// adjust texture coordinates, which are likely all within the full range 0.0f to 1.0f
|
||||
// to fit into the texture atlas tile's texture coordinate boundaries
|
||||
for (uint32_t i = 0; i < m_vertices->GetNumElements(); ++i)
|
||||
{
|
||||
Vector2 texCoord = m_vertices->GetTexCoord(i);
|
||||
texCoord.x = ScaleRange(texCoord.x, 0.0f, 1.0f, textureAtlasTileBoundaries->left, textureAtlasTileBoundaries->right);
|
||||
texCoord.y = ScaleRange(texCoord.y, 0.0f, 1.0f, textureAtlasTileBoundaries->top, textureAtlasTileBoundaries->bottom);
|
||||
m_vertices->SetTexCoord(i, texCoord);
|
||||
}
|
||||
|
||||
SetupCollisionVertices(collisionMesh);
|
||||
}
|
||||
|
||||
StaticTileMesh::StaticTileMesh(const StaticMesh *mesh, const RectF *textureAtlasTileBoundaries, uint32_t numTiles, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color, const StaticMesh *collisionMesh)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(numTiles > 0);
|
||||
ASSERT(mesh->GetNumSubsets() >= numTiles);
|
||||
|
||||
// count up all the vertices needed for the number of subsets we're going to use
|
||||
uint32_t numVertices = 0;
|
||||
for (uint32_t i = 0; i < numTiles; ++i)
|
||||
numVertices += mesh->GetSubset(i)->GetVertices()->GetNumElements();
|
||||
|
||||
// create the vertex buffer using the same attribs as the source mesh
|
||||
// (assuming all subsets have the same attribs in the mesh)
|
||||
m_vertices = new VertexBuffer(BUFFEROBJECT_USAGE_STATIC);
|
||||
ASSERT(m_vertices != NULL);
|
||||
m_vertices->CopyAttributesFrom(mesh->GetSubset(0)->GetVertices());
|
||||
m_vertices->Create(numVertices);
|
||||
|
||||
SetOpaque(opaqueSides);
|
||||
SetAlpha(alpha);
|
||||
SetColor(color);
|
||||
SetTranslucency(translucency);
|
||||
SetLight(lightValue);
|
||||
|
||||
uint32_t currentVertex = 0;
|
||||
for (uint32_t i = 0; i < numTiles; ++i)
|
||||
{
|
||||
StaticMeshSubset *subset = mesh->GetSubset(i);
|
||||
const RectF *tileBoundaries = &textureAtlasTileBoundaries[i];
|
||||
|
||||
// copy all this subset's vertices
|
||||
m_vertices->Copy(subset->GetVertices(), currentVertex);
|
||||
|
||||
// now we need to adjust the copied texture coordinates to match the tile boundaries
|
||||
for (uint32_t t = 0; t < subset->GetVertices()->GetNumElements(); ++t)
|
||||
{
|
||||
uint32_t position = t + currentVertex;
|
||||
|
||||
Vector2 texCoord = m_vertices->GetTexCoord(position);
|
||||
texCoord.x = ScaleRange(texCoord.x, 0.0f, 1.0f, tileBoundaries->left, tileBoundaries->right);
|
||||
texCoord.y = ScaleRange(texCoord.y, 0.0f, 1.0f, tileBoundaries->top, tileBoundaries->bottom);
|
||||
m_vertices->SetTexCoord(position, texCoord);
|
||||
}
|
||||
|
||||
currentVertex += subset->GetVertices()->GetNumElements();
|
||||
}
|
||||
|
||||
SetupCollisionVertices(collisionMesh);
|
||||
}
|
||||
|
||||
void StaticTileMesh::SetupCollisionVertices(const StaticMesh *collisionMesh)
|
||||
{
|
||||
STACK_TRACE;
|
||||
const VertexBuffer *srcCollisionVertices;
|
||||
if (collisionMesh != NULL)
|
||||
srcCollisionVertices = collisionMesh->GetSubset(0)->GetVertices();
|
||||
else
|
||||
srcCollisionVertices = m_vertices;
|
||||
|
||||
m_numCollisionVertices = srcCollisionVertices->GetNumElements();
|
||||
|
||||
m_collisionVertices = new Vector3[m_numCollisionVertices];
|
||||
ASSERT(m_collisionVertices != NULL);
|
||||
|
||||
for (uint32_t i = 0; i < m_numCollisionVertices; ++i)
|
||||
m_collisionVertices[i] = srcCollisionVertices->GetPosition3(i);
|
||||
}
|
||||
|
||||
StaticTileMesh::~StaticTileMesh()
|
||||
{
|
||||
STACK_TRACE;
|
||||
SAFE_DELETE(m_vertices);
|
||||
SAFE_DELETE(m_collisionVertices);
|
||||
}
|
||||
|
||||
|
||||
|
37
src/tilemap/statictilemesh.h
Normal file
37
src/tilemap/statictilemesh.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef __TILEMAP_STATICTILEMESH_H_INCLUDED__
|
||||
#define __TILEMAP_STATICTILEMESH_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
|
||||
class StaticMesh;
|
||||
class VertexBuffer;
|
||||
struct RectF;
|
||||
struct Vector3;
|
||||
|
||||
class StaticTileMesh : public TileMesh
|
||||
{
|
||||
public:
|
||||
StaticTileMesh(const StaticMesh *mesh, const RectF *textureAtlasTileBoundaries, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency = 1.0f, const Color &color = COLOR_WHITE, const StaticMesh *collisionMesh = NULL);
|
||||
StaticTileMesh(const StaticMesh *mesh, const RectF *textureAtlasTileBoundaries, uint32_t numTiles, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency = 1.0f, const Color &color = COLOR_WHITE, const StaticMesh *collisionMesh = NULL);
|
||||
virtual ~StaticTileMesh();
|
||||
|
||||
VertexBuffer* GetBuffer() const { return m_vertices; }
|
||||
uint32_t GetNumCollisionVertices() const { return m_numCollisionVertices; }
|
||||
const Vector3* GetCollisionVertices() const { return m_collisionVertices; }
|
||||
|
||||
TILEMESH_TYPE GetType() const { return TILEMESH_STATIC; }
|
||||
|
||||
private:
|
||||
void SetupCollisionVertices(const StaticMesh *collisionMesh);
|
||||
|
||||
VertexBuffer *m_vertices;
|
||||
uint32_t m_numCollisionVertices;
|
||||
Vector3 *m_collisionVertices;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
29
src/tilemap/tile.cpp
Normal file
29
src/tilemap/tile.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "../framework/common.h"
|
||||
#include "../framework/math/common.h"
|
||||
#include "../framework/math/matrix4x4.h"
|
||||
|
||||
const Matrix4x4* Tile::GetTransformationMatrix() const
|
||||
{
|
||||
// static so that we only need to set them up once, since this method will
|
||||
// be used inside inner loops and such
|
||||
// **IMPORTANT**: these rotations assume every TileMesh is modeled facing north!
|
||||
static Matrix4x4 faceNorth = Matrix4x4::CreateRotationY(RADIANS_0);
|
||||
static Matrix4x4 faceEast = Matrix4x4::CreateRotationY(RADIANS_90);
|
||||
static Matrix4x4 faceSouth = Matrix4x4::CreateRotationY(RADIANS_180);
|
||||
static Matrix4x4 faceWest = Matrix4x4::CreateRotationY(RADIANS_270);
|
||||
|
||||
if (IsBitSet(TILE_FACE_NORTH, flags))
|
||||
return &faceNorth;
|
||||
else if (IsBitSet(TILE_FACE_EAST, flags))
|
||||
return &faceEast;
|
||||
else if (IsBitSet(TILE_FACE_SOUTH, flags))
|
||||
return &faceSouth;
|
||||
else if (IsBitSet(TILE_FACE_WEST, flags))
|
||||
return &faceWest;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
161
src/tilemap/tile.h
Normal file
161
src/tilemap/tile.h
Normal file
|
@ -0,0 +1,161 @@
|
|||
#ifndef __TILEMAP_TILE_H_INCLUDED__
|
||||
#define __TILEMAP_TILE_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/math/mathhelpers.h"
|
||||
#include <math.h>
|
||||
|
||||
struct Matrix4x4;
|
||||
|
||||
typedef uint16_t TILE_INDEX;
|
||||
typedef uint16_t TILE_FLAG_BITS;
|
||||
|
||||
const TILE_INDEX NO_TILE = 0;
|
||||
|
||||
enum TILE_FLAGS
|
||||
{
|
||||
TILE_COLLIDABLE = 1,
|
||||
TILE_FACE_NORTH = 2,
|
||||
TILE_FACE_EAST = 4,
|
||||
TILE_FACE_SOUTH = 8,
|
||||
TILE_FACE_WEST = 16,
|
||||
TILE_CUSTOM_COLOR = 32,
|
||||
TILE_FRICTION_SLIPPERY = 64,
|
||||
TILE_LIGHT_SKY = 128,
|
||||
TILE_WALKABLE_SURFACE = 256
|
||||
};
|
||||
|
||||
struct Tile
|
||||
{
|
||||
Tile();
|
||||
|
||||
void Set(TILE_INDEX tile);
|
||||
void Set(TILE_INDEX tile, TILE_FLAG_BITS flags);
|
||||
void Set(TILE_INDEX tile, TILE_FLAG_BITS flags, const Color &color);
|
||||
void Set(TILE_INDEX tile, TILE_FLAG_BITS flags, uint32_t color);
|
||||
const Matrix4x4* GetTransformationMatrix() const;
|
||||
|
||||
void SetCustomColor(const Color &color);
|
||||
void SetCustomColor(uint32_t color);
|
||||
void ClearCustomColor();
|
||||
|
||||
float GetBrightness() const;
|
||||
static float GetBrightness(TILE_LIGHT_VALUE light);
|
||||
static TILE_LIGHT_VALUE AdjustLightForTranslucency(TILE_LIGHT_VALUE light, float translucency);
|
||||
|
||||
BOOL IsEmptySpace() const;
|
||||
BOOL IsCollideable() const;
|
||||
BOOL HasCustomColor() const;
|
||||
BOOL IsSlippery() const;
|
||||
BOOL IsSkyLit() const;
|
||||
|
||||
TILE_INDEX tile;
|
||||
TILE_FLAG_BITS flags;
|
||||
TILE_LIGHT_VALUE tileLight;
|
||||
TILE_LIGHT_VALUE skyLight;
|
||||
uint32_t color; // save some memory (4 byte int, versus 16 byte Color obj)
|
||||
};
|
||||
|
||||
inline Tile::Tile()
|
||||
{
|
||||
tile = NO_TILE;
|
||||
flags = 0;
|
||||
tileLight = 0;
|
||||
skyLight = 0;
|
||||
color = 0;
|
||||
}
|
||||
|
||||
inline void Tile::Set(TILE_INDEX tile)
|
||||
{
|
||||
this->tile = tile;
|
||||
}
|
||||
|
||||
inline void Tile::Set(TILE_INDEX tile, TILE_FLAG_BITS flags)
|
||||
{
|
||||
this->tile = tile;
|
||||
this->flags = flags;
|
||||
}
|
||||
|
||||
inline void Tile::Set(TILE_INDEX tile, TILE_FLAG_BITS flags, const Color &color)
|
||||
{
|
||||
Set(tile, flags, color.ToInt());
|
||||
}
|
||||
|
||||
inline void Tile::Set(TILE_INDEX tile, TILE_FLAG_BITS flags, uint32_t color)
|
||||
{
|
||||
SetBit(TILE_CUSTOM_COLOR, flags);
|
||||
|
||||
this->tile = tile;
|
||||
this->flags = flags;
|
||||
this->color = color;
|
||||
}
|
||||
|
||||
inline void Tile::SetCustomColor(const Color &color)
|
||||
{
|
||||
SetCustomColor(color.ToInt());
|
||||
}
|
||||
|
||||
inline void Tile::SetCustomColor(uint32_t color)
|
||||
{
|
||||
SetBit(TILE_CUSTOM_COLOR, this->flags);
|
||||
this->color = color;
|
||||
}
|
||||
|
||||
inline void Tile::ClearCustomColor()
|
||||
{
|
||||
ClearBit(TILE_CUSTOM_COLOR, this->flags);
|
||||
this->color = 0;
|
||||
}
|
||||
|
||||
inline float Tile::GetBrightness() const
|
||||
{
|
||||
if (this->tileLight > this->skyLight)
|
||||
return GetBrightness(this->tileLight);
|
||||
else
|
||||
return GetBrightness(this->skyLight);
|
||||
}
|
||||
|
||||
inline float Tile::GetBrightness(TILE_LIGHT_VALUE light)
|
||||
{
|
||||
// this is a copy of the brightness formula listed here:
|
||||
// http://gamedev.stackexchange.com/a/21247
|
||||
|
||||
const float BASE_BRIGHTNESS = 0.086f;
|
||||
float normalizedLightValue = (float)light / (float)(TILE_LIGHT_VALUE_MAX + 1);
|
||||
return powf(normalizedLightValue, 1.4f) + BASE_BRIGHTNESS;
|
||||
}
|
||||
|
||||
inline TILE_LIGHT_VALUE Tile::AdjustLightForTranslucency(TILE_LIGHT_VALUE light, float translucency)
|
||||
{
|
||||
return (TILE_LIGHT_VALUE)Round((float)light * translucency);
|
||||
}
|
||||
|
||||
inline BOOL Tile::IsEmptySpace() const
|
||||
{
|
||||
return this->tile == NO_TILE;
|
||||
}
|
||||
|
||||
inline BOOL Tile::IsCollideable() const
|
||||
{
|
||||
return IsBitSet(TILE_COLLIDABLE, this->flags);
|
||||
}
|
||||
|
||||
inline BOOL Tile::HasCustomColor() const
|
||||
{
|
||||
return IsBitSet(TILE_CUSTOM_COLOR, this->flags);
|
||||
}
|
||||
|
||||
inline BOOL Tile::IsSlippery() const
|
||||
{
|
||||
return IsBitSet(TILE_FRICTION_SLIPPERY, this->flags);
|
||||
}
|
||||
|
||||
inline BOOL Tile::IsSkyLit() const
|
||||
{
|
||||
return IsBitSet(TILE_LIGHT_SKY, this->flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
417
src/tilemap/tilechunk.cpp
Normal file
417
src/tilemap/tilechunk.cpp
Normal file
|
@ -0,0 +1,417 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "tilechunk.h"
|
||||
|
||||
#include "chunkvertexgenerator.h"
|
||||
#include "tilemap.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshcollection.h"
|
||||
|
||||
#include "../framework/graphics/graphicsdevice.h"
|
||||
#include "../framework/graphics/vertexbuffer.h"
|
||||
#include "../framework/math/intersectiontester.h"
|
||||
#include "../framework/math/mathhelpers.h"
|
||||
#include "../framework/math/ray.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
TileChunk::TileChunk(uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const TileMap *tileMap, GraphicsDevice *graphicsDevice)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(tileMap != NULL);
|
||||
ASSERT(graphicsDevice != NULL);
|
||||
|
||||
m_tileMap = tileMap;
|
||||
m_graphicsDevice = graphicsDevice;
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
m_position = Vector3((float)m_x, (float)m_y, (float)m_z);
|
||||
|
||||
m_bounds.min = Vector3((float)m_x, (float)m_y, (float)m_z);
|
||||
m_bounds.max = Vector3((float)(m_x + m_width), (float)(m_y + m_height), (float)(m_z + m_depth));
|
||||
|
||||
m_data = new Tile[width * height * depth];
|
||||
ASSERT(m_data != NULL);
|
||||
|
||||
// TODO: is 16 a good starting default size?
|
||||
m_vertices = new VertexBuffer(BUFFEROBJECT_USAGE_STATIC);
|
||||
ASSERT(m_vertices != NULL);
|
||||
m_vertices->AddAttribute(VERTEX_POS_3D);
|
||||
m_vertices->AddAttribute(VERTEX_NORMAL);
|
||||
m_vertices->AddAttribute(VERTEX_TEXCOORD);
|
||||
m_vertices->AddAttribute(VERTEX_COLOR);
|
||||
m_vertices->Create(16);
|
||||
m_numVertices = 0;
|
||||
|
||||
// start off assuming we don't have any alpha vertices
|
||||
m_alphaVertices = NULL;
|
||||
m_numAlphaVertices = 0;
|
||||
|
||||
m_vertices->CreateInVRAM();
|
||||
m_graphicsDevice->RegisterManagedResource(m_vertices);
|
||||
}
|
||||
|
||||
TileChunk::~TileChunk()
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_graphicsDevice->UnregisterManagedResource(m_vertices);
|
||||
if (m_alphaVertices != NULL)
|
||||
m_graphicsDevice->UnregisterManagedResource(m_alphaVertices);
|
||||
|
||||
SAFE_DELETE_ARRAY(m_data);
|
||||
SAFE_DELETE(m_vertices);
|
||||
SAFE_DELETE(m_alphaVertices);
|
||||
}
|
||||
|
||||
void TileChunk::GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z, BoundingBox *box) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(box != NULL);
|
||||
|
||||
// "chunk space"
|
||||
box->min = Vector3((float)x, (float)y, (float)z);
|
||||
box->max = Vector3(x + 1.0f, y + 1.0f, z + 1.0f); // 1.0f = tile width
|
||||
|
||||
// move to world space ("tilemap space" maybe .. ?)
|
||||
box->min += m_bounds.min;
|
||||
box->max += m_bounds.min;
|
||||
}
|
||||
|
||||
BoundingBox TileChunk::GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
BoundingBox box;
|
||||
|
||||
// "chunk space"
|
||||
box.min = Vector3((float)x, (float)y, (float)z);
|
||||
box.max = Vector3(x + 1.0f, y + 1.0f, z + 1.0f); // 1.0f = tile width
|
||||
|
||||
// move to world space ("tilemap space" maybe .. ?)
|
||||
box.min += m_bounds.min;
|
||||
box.max += m_bounds.min;
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
BOOL TileChunk::CheckForCollision(const Ray &ray, uint32_t &x, uint32_t &y, uint32_t &z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// make sure that the ray and this chunk can actually collide in the first place
|
||||
Vector3 position;
|
||||
if (!IntersectionTester::Test(ray, m_bounds, &position))
|
||||
return FALSE;
|
||||
|
||||
// convert initial chunk collision point to tile coords (this is in "tilemap space")
|
||||
int32_t currentX = (int32_t)position.x;
|
||||
int32_t currentY = (int32_t)position.y;
|
||||
int32_t currentZ = (int32_t)position.z;
|
||||
|
||||
// make sure the coords are inrange of this chunk. due to some floating
|
||||
// point errors / decimal truncating from the above conversion we could
|
||||
// end up with one or more that are very slightly out of bounds.
|
||||
// this is still in "tilemap space"
|
||||
currentX = Clamp(currentX, (int32_t)GetX(), (int32_t)(GetX() + GetWidth() - 1));
|
||||
currentY = Clamp(currentY, (int32_t)GetY(), (int32_t)(GetY() + GetHeight() - 1));
|
||||
currentZ = Clamp(currentZ, (int32_t)GetZ(), (int32_t)(GetZ() + GetDepth() - 1));
|
||||
|
||||
// convert to "chunk space"
|
||||
currentX -= (int32_t)GetX();
|
||||
currentY -= (int32_t)GetY();
|
||||
currentZ -= (int32_t)GetZ();
|
||||
|
||||
// is the start position colliding with a solid tile?
|
||||
Tile *startTile = Get(currentX, currentY, currentZ);
|
||||
if (IsBitSet(TILE_COLLIDABLE, startTile->flags))
|
||||
{
|
||||
// collision found, set the tile coords of the collision
|
||||
x = currentX;
|
||||
y = currentY;
|
||||
z = currentZ;
|
||||
|
||||
// and we're done
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// no collision initially, continue on with the rest ...
|
||||
|
||||
// step increments in "chunk tile" units
|
||||
int32_t stepX = (int32_t)Sign(ray.direction.x);
|
||||
int32_t stepY = (int32_t)Sign(ray.direction.y);
|
||||
int32_t stepZ = (int32_t)Sign(ray.direction.z);
|
||||
|
||||
// tile boundary (needs to be in "tilemap space")
|
||||
int32_t tileBoundaryX = (int32_t)GetX() + currentX + (stepX > 0 ? 1 : 0);
|
||||
int32_t tileBoundaryY = (int32_t)GetY() + currentY + (stepY > 0 ? 1 : 0);
|
||||
int32_t tileBoundaryZ = (int32_t)GetZ() + currentZ + (stepZ > 0 ? 1 : 0);
|
||||
|
||||
// HACK: for the tMax and tDelta initial calculations below, if any of the
|
||||
// components of ray.direction are zero, it will result in "inf"
|
||||
// components in tMax or tDelta. This is fine, but it has to be
|
||||
// *positive* "inf", not negative. What I found was that sometimes
|
||||
// they would be negative, sometimes positive. So, we force them to be
|
||||
// positive below. Additionally, "nan" components (which will happen
|
||||
// if both sides of the divisions are zero) are bad, and we need to
|
||||
// change that up for "inf" as well.
|
||||
// TODO: check that this is compatible with Visual C++
|
||||
|
||||
// determine how far we can travel along the ray before we hit a tile boundary
|
||||
Vector3 tMax = Vector3(
|
||||
(tileBoundaryX - ray.position.x) / ray.direction.x,
|
||||
(tileBoundaryY - ray.position.y) / ray.direction.y,
|
||||
(tileBoundaryZ - ray.position.z) / ray.direction.z
|
||||
);
|
||||
if (tMax.x == -INFINITY)
|
||||
tMax.x = INFINITY;
|
||||
if (tMax.y == -INFINITY)
|
||||
tMax.y = INFINITY;
|
||||
if (tMax.z == -INFINITY)
|
||||
tMax.z = INFINITY;
|
||||
if (isnan(tMax.x))
|
||||
tMax.x = INFINITY;
|
||||
if (isnan(tMax.y))
|
||||
tMax.y = INFINITY;
|
||||
if (isnan(tMax.z))
|
||||
tMax.z = INFINITY;
|
||||
|
||||
// determine how far we must travel along the ray before we cross a grid cell
|
||||
Vector3 tDelta = Vector3(
|
||||
stepX / ray.direction.x,
|
||||
stepY / ray.direction.y,
|
||||
stepZ / ray.direction.z
|
||||
);
|
||||
if (tDelta.x == -INFINITY)
|
||||
tDelta.x = INFINITY;
|
||||
if (tDelta.y == -INFINITY)
|
||||
tDelta.y = INFINITY;
|
||||
if (tDelta.z == -INFINITY)
|
||||
tDelta.z = INFINITY;
|
||||
if (isnan(tDelta.x))
|
||||
tDelta.x = INFINITY;
|
||||
if (isnan(tDelta.y))
|
||||
tDelta.y = INFINITY;
|
||||
if (isnan(tDelta.z))
|
||||
tDelta.z = INFINITY;
|
||||
|
||||
BOOL collided = FALSE;
|
||||
BOOL outOfChunk = FALSE;
|
||||
while (!outOfChunk)
|
||||
{
|
||||
// step up to the next tile using the lowest step value
|
||||
// (in other words, we figure out on which axis, X, Y, or Z, the next
|
||||
// tile that lies on the ray is closest, and use that axis step increment
|
||||
// to move us up to get to the next tile location)
|
||||
if (tMax.x < tMax.y && tMax.x < tMax.z)
|
||||
{
|
||||
// tMax.x is lowest, the YZ tile boundary plane is closest
|
||||
currentX += stepX;
|
||||
tMax.x += tDelta.x;
|
||||
}
|
||||
else if (tMax.y < tMax.z)
|
||||
{
|
||||
// tMax.y is lowest, the XZ tile boundary plane is closest
|
||||
currentY += stepY;
|
||||
tMax.y += tDelta.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// tMax.z is lowest, the XY tile boundary plane is closest
|
||||
currentZ += stepZ;
|
||||
tMax.z += tDelta.z;
|
||||
}
|
||||
|
||||
// need to figure out if this new position is still inside the bounds of
|
||||
// the chunk before we can attempt to determine if the current tile is
|
||||
// solid
|
||||
if (
|
||||
currentX < 0 || currentX >= (int32_t)GetWidth() ||
|
||||
currentY < 0 || currentY >= (int32_t)GetHeight() ||
|
||||
currentZ < 0 || currentZ >= (int32_t)GetDepth()
|
||||
)
|
||||
outOfChunk = TRUE;
|
||||
else
|
||||
{
|
||||
// still inside and at the next position, test for a solid tile
|
||||
Tile *tile = Get(currentX, currentY, currentZ);
|
||||
if (IsBitSet(TILE_COLLIDABLE, tile->flags))
|
||||
{
|
||||
collided = TRUE;
|
||||
|
||||
// set the tile coords of the collision
|
||||
x = currentX;
|
||||
y = currentY;
|
||||
z = currentZ;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
BOOL TileChunk::CheckForCollision(const Ray &ray, Vector3 &point, uint32_t &x, uint32_t &y, uint32_t &z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// if the ray doesn't collide with any solid tiles in the first place, then
|
||||
// we can skip this more expensive triangle collision check...
|
||||
if (!CheckForCollision(ray, x, y, z))
|
||||
return FALSE;
|
||||
|
||||
// now perform the per-triangle collision check against the tile position
|
||||
// where the ray ended up at the end of the above CheckForCollision() call
|
||||
return CheckForCollisionWithTile(ray, point, x, y, z);
|
||||
}
|
||||
|
||||
BOOL TileChunk::CheckForCollisionWithTile(const Ray &ray, Vector3 &point, uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
const Tile *tile = Get(x, y, z);
|
||||
const TileMesh *mesh = m_tileMap->GetMeshes()->Get(tile);
|
||||
|
||||
uint32_t numVertices = mesh->GetNumCollisionVertices();
|
||||
const Vector3 *vertices = mesh->GetCollisionVertices();
|
||||
|
||||
// world position of this tile, will be used to move each
|
||||
// mesh triangle into world space
|
||||
Vector3 tileWorldPosition = Vector3((float)x, (float)y, (float)z);
|
||||
|
||||
float closestSquaredDistance = FLT_MAX;
|
||||
BOOL collided = FALSE;
|
||||
Vector3 collisionPoint = ZERO_VECTOR;
|
||||
|
||||
for (uint32_t i = 0; i < numVertices; i += 3)
|
||||
{
|
||||
// get the vertices making up this triangle
|
||||
Vector3 a = vertices[i];
|
||||
Vector3 b = vertices[i + 1];
|
||||
Vector3 c = vertices[i + 2];
|
||||
|
||||
// move these vertices into world space
|
||||
a += tileWorldPosition;
|
||||
b += tileWorldPosition;
|
||||
c += tileWorldPosition;
|
||||
|
||||
if (IntersectionTester::Test(ray, a, b, c, &collisionPoint))
|
||||
{
|
||||
collided = TRUE;
|
||||
|
||||
// if this is the closest collision yet, then keep the distance
|
||||
// and point of collision
|
||||
float squaredDistance = Vector3::LengthSquared(collisionPoint - ray.position);
|
||||
if (squaredDistance < closestSquaredDistance)
|
||||
{
|
||||
closestSquaredDistance = squaredDistance;
|
||||
point = collisionPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
BOOL TileChunk::GetOverlappedTiles(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// make sure the given box actually intersects with this chunk in the first place
|
||||
if (!IntersectionTester::Test(m_bounds, box))
|
||||
return FALSE;
|
||||
|
||||
// convert to tile coords (these will be in "tilemap space")
|
||||
// HACK: ceil() calls and "-1"'s keep us from picking up too many/too few
|
||||
// blocks. these were arrived at through observation
|
||||
int32_t minX = (int32_t)box.min.x;
|
||||
int32_t minY = (int32_t)box.min.y;
|
||||
int32_t minZ = (int32_t)box.min.z;
|
||||
int32_t maxX = (int32_t)ceil(box.max.x);
|
||||
int32_t maxY = (int32_t)ceil(box.max.y - 1.0f);
|
||||
int32_t maxZ = (int32_t)ceil(box.max.z);
|
||||
|
||||
// trim off the excess bounds so that we end up with a min-to-max area
|
||||
// that is completely within the bounds of this chunk
|
||||
// HACK: "-1"'s keep us from picking up too many blocks. these were arrived
|
||||
// at through observation
|
||||
minX = Clamp(minX, (int32_t)GetX(), (int32_t)(GetX() + GetWidth()));
|
||||
minY = Clamp(minY, (int32_t)GetY(), (int32_t)(GetY() + GetHeight() - 1));
|
||||
minZ = Clamp(minZ, (int32_t)GetZ(), (int32_t)(GetZ() + GetDepth()));
|
||||
maxX = Clamp(maxX, (int32_t)GetX(), (int32_t)(GetX() + GetWidth()));
|
||||
maxY = Clamp(maxY, (int32_t)GetY(), (int32_t)(GetY() + GetHeight() - 1));
|
||||
maxZ = Clamp(maxZ, (int32_t)GetZ(), (int32_t)(GetZ() + GetDepth()));
|
||||
|
||||
// return the leftover area, converted to "chunk space"
|
||||
x1 = minX - (int32_t)GetX();
|
||||
y1 = minY - (int32_t)GetY();
|
||||
z1 = minZ - (int32_t)GetZ();
|
||||
x2 = maxX - (int32_t)GetX();
|
||||
y2 = maxY - (int32_t)GetY();
|
||||
z2 = maxZ - (int32_t)GetZ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Tile* TileChunk::GetWithinSelfOrNeighbour(int32_t x, int32_t y, int32_t z) const
|
||||
{
|
||||
int32_t checkX = (int32_t)GetX() + x;
|
||||
int32_t checkY = (int32_t)GetY() + y;
|
||||
int32_t checkZ = (int32_t)GetZ() + z;
|
||||
return m_tileMap->Get(checkX, checkY, checkZ);
|
||||
}
|
||||
|
||||
Tile* TileChunk::GetWithinSelfOrNeighbourSafe(int32_t x, int32_t y, int32_t z) const
|
||||
{
|
||||
int32_t checkX = (int32_t)GetX() + x;
|
||||
int32_t checkY = (int32_t)GetY() + y;
|
||||
int32_t checkZ = (int32_t)GetZ() + z;
|
||||
if (!m_tileMap->IsWithinBounds(checkX, checkY, checkZ))
|
||||
return NULL;
|
||||
else
|
||||
return m_tileMap->Get(checkX, checkY, checkZ);
|
||||
}
|
||||
|
||||
void TileChunk::EnableAlphaVertices(BOOL enable)
|
||||
{
|
||||
STACK_TRACE;
|
||||
if (enable)
|
||||
{
|
||||
if (m_alphaVertices != NULL)
|
||||
return;
|
||||
|
||||
// need to create the vertex buffer
|
||||
// TODO: is '16' a good default size? it probably isn't likely that
|
||||
// chunks will have a lot of these. has to be non-zero anyway...
|
||||
m_alphaVertices = new VertexBuffer(BUFFEROBJECT_USAGE_STATIC);
|
||||
ASSERT(m_alphaVertices != NULL);
|
||||
m_alphaVertices->AddAttribute(VERTEX_POS_3D);
|
||||
m_alphaVertices->AddAttribute(VERTEX_NORMAL);
|
||||
m_alphaVertices->AddAttribute(VERTEX_TEXCOORD);
|
||||
m_alphaVertices->AddAttribute(VERTEX_COLOR);
|
||||
m_alphaVertices->Create(16);
|
||||
m_numAlphaVertices = 0;
|
||||
|
||||
m_graphicsDevice->RegisterManagedResource(m_alphaVertices);
|
||||
m_alphaVertices->CreateInVRAM();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_alphaVertices == NULL)
|
||||
return;
|
||||
|
||||
// need to free the vertex buffer
|
||||
m_graphicsDevice->RegisterManagedResource(m_alphaVertices);
|
||||
m_alphaVertices->FreeFromVRAM();
|
||||
SAFE_DELETE(m_alphaVertices);
|
||||
m_numAlphaVertices = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TileChunk::UpdateVertices(ChunkVertexGenerator *vertexGenerator)
|
||||
{
|
||||
STACK_TRACE;
|
||||
vertexGenerator->Generate(this, m_numVertices, m_numAlphaVertices);
|
||||
}
|
||||
|
121
src/tilemap/tilechunk.h
Normal file
121
src/tilemap/tilechunk.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#ifndef __TILEMAP_TILECHUNK_H_INCLUDED__
|
||||
#define __TILEMAP_TILECHUNK_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "../framework/math/boundingbox.h"
|
||||
|
||||
class ChunkVertexGenerator;
|
||||
class GraphicsDevice;
|
||||
class TileMap;
|
||||
class VertexBuffer;
|
||||
struct Ray;
|
||||
struct Vector3;
|
||||
|
||||
class TileChunk
|
||||
{
|
||||
public:
|
||||
TileChunk(uint32_t x, uint32_t y, uint32_t z, uint32_t width, uint32_t height, uint32_t depth, const TileMap *tileMap, GraphicsDevice *graphicsDevice);
|
||||
virtual ~TileChunk();
|
||||
|
||||
void EnableAlphaVertices(BOOL enable);
|
||||
BOOL IsAlphaEnabled() const { return m_alphaVertices != NULL; }
|
||||
void UpdateVertices(ChunkVertexGenerator *vertexGenerator);
|
||||
|
||||
Tile* Get(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
Tile* GetSafe(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
Tile* GetWithinSelfOrNeighbour(int32_t x, int32_t y, int32_t z) const;
|
||||
Tile* GetWithinSelfOrNeighbourSafe(int32_t x, int32_t y, int32_t z) const;
|
||||
void GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z, BoundingBox *box) const;
|
||||
BoundingBox GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
|
||||
BOOL CheckForCollision(const Ray &ray, uint32_t &x, uint32_t &y, uint32_t &z) const;
|
||||
BOOL CheckForCollision(const Ray &ray, Vector3 &point, uint32_t &x, uint32_t &y, uint32_t &z) const;
|
||||
BOOL CheckForCollisionWithTile(const Ray &ray, Vector3 &point, uint32_t x, uint32_t y, uint32_t z) const;
|
||||
BOOL GetOverlappedTiles(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const;
|
||||
|
||||
BOOL IsWithinBounds(int32_t x, int32_t y, int32_t z) const;
|
||||
BOOL IsWithinLocalBounds(int32_t x, int32_t y, int32_t z) const;
|
||||
|
||||
uint32_t GetX() const { return m_x; }
|
||||
uint32_t GetY() const { return m_y; }
|
||||
uint32_t GetZ() const { return m_z; }
|
||||
const Vector3& GetPosition() const { return m_position; }
|
||||
uint32_t GetWidth() const { return m_width; }
|
||||
uint32_t GetHeight() const { return m_height; }
|
||||
uint32_t GetDepth() const { return m_depth; }
|
||||
const BoundingBox& GetBounds() const { return m_bounds; }
|
||||
|
||||
const TileMap* GetTileMap() const { return m_tileMap; }
|
||||
VertexBuffer* GetVertices() const { return m_vertices; }
|
||||
VertexBuffer* GetAlphaVertices() const { return m_alphaVertices; }
|
||||
uint32_t GetNumVertices() const { return m_numVertices; }
|
||||
uint32_t GetNumAlphaVertices() const { return m_numAlphaVertices; }
|
||||
|
||||
private:
|
||||
uint32_t GetIndexOf(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
|
||||
Tile *m_data;
|
||||
const TileMap *m_tileMap;
|
||||
GraphicsDevice *m_graphicsDevice;
|
||||
VertexBuffer *m_vertices;
|
||||
VertexBuffer *m_alphaVertices;
|
||||
uint32_t m_numVertices;
|
||||
uint32_t m_numAlphaVertices;
|
||||
|
||||
uint32_t m_x;
|
||||
uint32_t m_y;
|
||||
uint32_t m_z;
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
uint32_t m_depth;
|
||||
BoundingBox m_bounds;
|
||||
Vector3 m_position;
|
||||
};
|
||||
|
||||
inline Tile* TileChunk::Get(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
uint32_t index = GetIndexOf(x, y, z);
|
||||
return &m_data[index];
|
||||
}
|
||||
|
||||
inline Tile* TileChunk::GetSafe(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
if (!IsWithinLocalBounds((int32_t)x, (int32_t)y, (int32_t)z))
|
||||
return NULL;
|
||||
else
|
||||
return Get(x, y, z);
|
||||
}
|
||||
|
||||
inline uint32_t TileChunk::GetIndexOf(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
return (y * m_width * m_depth) + (z * m_width) + x;
|
||||
}
|
||||
|
||||
inline BOOL TileChunk::IsWithinBounds(int32_t x, int32_t y, int32_t z) const
|
||||
{
|
||||
if (x < (int32_t)GetX() || x >= (int32_t)(GetX() + m_width))
|
||||
return FALSE;
|
||||
if (y < (int32_t)GetY() || y >= (int32_t)(GetY() + m_height))
|
||||
return FALSE;
|
||||
if (z < (int32_t)GetZ() || z >= (int32_t)(GetZ() + m_depth))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
inline BOOL TileChunk::IsWithinLocalBounds(int32_t x, int32_t y, int32_t z) const
|
||||
{
|
||||
if (x < 0 || x >= (int32_t)m_width)
|
||||
return FALSE;
|
||||
if (y < 0 || y >= (int32_t)m_height)
|
||||
return FALSE;
|
||||
if (z < 0 || z >= (int32_t)m_depth)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
13
src/tilemap/tilelightdefs.h
Normal file
13
src/tilemap/tilelightdefs.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef __TILEMAP_TILELIGHTDEFS_H_INCLUDED__
|
||||
#define __TILEMAP_TILELIGHTDEFS_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
typedef uint8_t TILE_LIGHT_VALUE;
|
||||
|
||||
const TILE_LIGHT_VALUE TILE_LIGHT_VALUE_MAX = 15;
|
||||
const TILE_LIGHT_VALUE TILE_LIGHT_VALUE_SKY = TILE_LIGHT_VALUE_MAX;
|
||||
|
||||
|
||||
#endif
|
||||
|
439
src/tilemap/tilemap.cpp
Normal file
439
src/tilemap/tilemap.cpp
Normal file
|
@ -0,0 +1,439 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "tilemap.h"
|
||||
|
||||
#include "chunkvertexgenerator.h"
|
||||
#include "tile.h"
|
||||
#include "tilemaplighter.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshcollection.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/math/intersectiontester.h"
|
||||
#include "../framework/math/mathhelpers.h"
|
||||
#include "../framework/math/ray.h"
|
||||
#include "../framework/math/rectf.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
|
||||
TileMap::TileMap(TileMeshCollection *tileMeshes, ChunkVertexGenerator *vertexGenerator, TileMapLighter *lighter, GraphicsDevice *graphicsDevice)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(tileMeshes != NULL);
|
||||
ASSERT(vertexGenerator != NULL);
|
||||
ASSERT(graphicsDevice != NULL);
|
||||
|
||||
m_tileMeshes = tileMeshes;
|
||||
m_vertexGenerator = vertexGenerator;
|
||||
m_lighter = lighter;
|
||||
m_graphicsDevice = graphicsDevice;
|
||||
|
||||
m_numChunks = 0;
|
||||
m_widthInChunks = 0;
|
||||
m_heightInChunks = 0;
|
||||
m_depthInChunks = 0;
|
||||
m_chunkWidth = 0;
|
||||
m_chunkHeight = 0;
|
||||
m_chunkDepth = 0;
|
||||
m_chunks = NULL;
|
||||
|
||||
m_ambientLightValue = 0;
|
||||
m_skyLightValue = TILE_LIGHT_VALUE_SKY;
|
||||
|
||||
m_bounds.min = ZERO_VECTOR;
|
||||
m_bounds.max = ZERO_VECTOR;
|
||||
}
|
||||
|
||||
TileMap::~TileMap()
|
||||
{
|
||||
STACK_TRACE;
|
||||
Clear();
|
||||
}
|
||||
|
||||
void TileMap::SetSize(uint32_t numChunksX, uint32_t numChunksY, uint32_t numChunksZ, uint32_t chunkSizeX, uint32_t chunkSizeY, uint32_t chunkSizeZ)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(numChunksX > 0);
|
||||
ASSERT(numChunksY > 0);
|
||||
ASSERT(numChunksZ > 0);
|
||||
ASSERT(chunkSizeX > 0);
|
||||
ASSERT(chunkSizeY > 0);
|
||||
ASSERT(chunkSizeZ > 0);
|
||||
|
||||
if (m_numChunks > 0)
|
||||
Clear();
|
||||
|
||||
m_numChunks = numChunksX * numChunksY * numChunksZ;
|
||||
m_widthInChunks = numChunksX;
|
||||
m_heightInChunks = numChunksY;
|
||||
m_depthInChunks = numChunksZ;
|
||||
m_chunkWidth = chunkSizeX;
|
||||
m_chunkHeight = chunkSizeY;
|
||||
m_chunkDepth = chunkSizeZ;
|
||||
|
||||
m_chunks = new TileChunk*[m_numChunks];
|
||||
ASSERT(m_chunks != NULL);
|
||||
|
||||
// set each one up
|
||||
for (uint32_t y = 0; y < numChunksY; ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < numChunksZ; ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < numChunksX; ++x)
|
||||
{
|
||||
TileChunk *chunk = new TileChunk(x * m_chunkWidth, y * m_chunkHeight, z * m_chunkDepth, m_chunkWidth, m_chunkHeight, m_chunkDepth, this, m_graphicsDevice);
|
||||
ASSERT(chunk != NULL);
|
||||
|
||||
uint32_t index = GetChunkIndex(x, y, z);
|
||||
m_chunks[index] = chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_bounds.min = ZERO_VECTOR;
|
||||
m_bounds.max = Vector3((float)GetWidth(), (float)GetHeight(), (float)GetDepth());
|
||||
}
|
||||
|
||||
void TileMap::Clear()
|
||||
{
|
||||
STACK_TRACE;
|
||||
for (uint32_t i = 0; i < m_numChunks; ++i)
|
||||
{
|
||||
TileChunk *chunk = m_chunks[i];
|
||||
SAFE_DELETE(chunk);
|
||||
}
|
||||
SAFE_DELETE_ARRAY(m_chunks);
|
||||
|
||||
m_numChunks = 0;
|
||||
m_widthInChunks = 0;
|
||||
m_heightInChunks = 0;
|
||||
m_depthInChunks = 0;
|
||||
m_chunkWidth = 0;
|
||||
m_chunkHeight = 0;
|
||||
m_chunkDepth = 0;
|
||||
}
|
||||
|
||||
BOOL TileMap::CheckForCollision(const Ray &ray, uint32_t &x, uint32_t &y, uint32_t &z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// make sure that the ray can actually collide with this map in the first place
|
||||
Vector3 position;
|
||||
if (!IntersectionTester::Test(ray, m_bounds, &position))
|
||||
return FALSE;
|
||||
|
||||
// convert initial tilemap collision point to tile coords. this is in
|
||||
// "tilemap space" which is how we want it for the rest of this method
|
||||
int32_t currentX = (int32_t)position.x;
|
||||
int32_t currentY = (int32_t)position.y;
|
||||
int32_t currentZ = (int32_t)position.z;
|
||||
|
||||
// make sure the coords are inrange of the map bounds. due to some floating
|
||||
// point errors / decimal truncating from the above conversion we could
|
||||
// end up with one or more that are very slightly out of bounds.
|
||||
currentX = Clamp(currentX, 0, (int32_t)(GetWidth() - 1));
|
||||
currentY = Clamp(currentY, 0, (int32_t)(GetHeight() - 1));
|
||||
currentZ = Clamp(currentZ, 0, (int32_t)(GetDepth() - 1));
|
||||
|
||||
// is the start position colliding with a solid tile?
|
||||
Tile *startTile = Get(currentX, currentY, currentZ);
|
||||
if (IsBitSet(TILE_COLLIDABLE, startTile->flags))
|
||||
{
|
||||
// collision found set the tile coords of the collision
|
||||
x = currentX;
|
||||
y = currentY;
|
||||
z = currentZ;
|
||||
|
||||
// and we're done
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// no collision initially, continue on with the rest ...
|
||||
|
||||
// step increments in whole tile units
|
||||
int32_t stepX = (int32_t)Sign(ray.direction.x);
|
||||
int32_t stepY = (int32_t)Sign(ray.direction.y);
|
||||
int32_t stepZ = (int32_t)Sign(ray.direction.z);
|
||||
|
||||
// tile boundary
|
||||
int32_t tileBoundaryX = currentX + (stepX > 0 ? 1 : 0);
|
||||
int32_t tileBoundaryY = currentY + (stepY > 0 ? 1 : 0);
|
||||
int32_t tileBoundaryZ = currentZ + (stepZ > 0 ? 1 : 0);
|
||||
|
||||
// HACK: for the tMax and tDelta initial calculations below, if any of the
|
||||
// components of ray.direction are zero, it will result in "inf"
|
||||
// components in tMax or tDelta. This is fine, but it has to be
|
||||
// *positive* "inf", not negative. What I found was that sometimes
|
||||
// they would be negative, sometimes positive. So, we force them to be
|
||||
// positive below. Additionally, "nan" components (which will happen
|
||||
// if both sides of the divisions are zero) are bad, and we need to
|
||||
// change that up for "inf" as well.
|
||||
// TODO: check that this is compatible with Visual C++
|
||||
|
||||
// determine how far we can travel along the ray before we hit a tile boundary
|
||||
Vector3 tMax = Vector3(
|
||||
(tileBoundaryX - ray.position.x) / ray.direction.x,
|
||||
(tileBoundaryY - ray.position.y) / ray.direction.y,
|
||||
(tileBoundaryZ - ray.position.z) / ray.direction.z
|
||||
);
|
||||
if (tMax.x == -INFINITY)
|
||||
tMax.x = INFINITY;
|
||||
if (tMax.y == -INFINITY)
|
||||
tMax.y = INFINITY;
|
||||
if (tMax.z == -INFINITY)
|
||||
tMax.z = INFINITY;
|
||||
if (isnan(tMax.x))
|
||||
tMax.x = INFINITY;
|
||||
if (isnan(tMax.y))
|
||||
tMax.y = INFINITY;
|
||||
if (isnan(tMax.z))
|
||||
tMax.z = INFINITY;
|
||||
|
||||
// determine how far we must travel along the ray before we cross a grid cell
|
||||
Vector3 tDelta = Vector3(
|
||||
stepX / ray.direction.x,
|
||||
stepY / ray.direction.y,
|
||||
stepZ / ray.direction.z
|
||||
);
|
||||
if (tDelta.x == -INFINITY)
|
||||
tDelta.x = INFINITY;
|
||||
if (tDelta.y == -INFINITY)
|
||||
tDelta.y = INFINITY;
|
||||
if (tDelta.z == -INFINITY)
|
||||
tDelta.z = INFINITY;
|
||||
if (isnan(tDelta.x))
|
||||
tDelta.x = INFINITY;
|
||||
if (isnan(tDelta.y))
|
||||
tDelta.y = INFINITY;
|
||||
if (isnan(tDelta.z))
|
||||
tDelta.z = INFINITY;
|
||||
|
||||
BOOL collided = FALSE;
|
||||
BOOL outOfMap = FALSE;
|
||||
while (!outOfMap)
|
||||
{
|
||||
// step up to the next tile using the lowest step value
|
||||
// (in other words, we figure out on which axis, X, Y, or Z, the next
|
||||
// tile that lies on the ray is closest, and use that axis step increment
|
||||
// to move us up to get to the next tile location)
|
||||
if (tMax.x < tMax.y && tMax.x < tMax.z)
|
||||
{
|
||||
// tMax.x is lowest, the YZ tile boundary plane is closest
|
||||
currentX += stepX;
|
||||
tMax.x += tDelta.x;
|
||||
}
|
||||
else if (tMax.y < tMax.z)
|
||||
{
|
||||
// tMax.y is lowest, the XZ tile boundary plane is closest
|
||||
currentY += stepY;
|
||||
tMax.y += tDelta.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// tMax.z is lowest, the XY tile boundary plane is closest
|
||||
currentZ += stepZ;
|
||||
tMax.z += tDelta.z;
|
||||
}
|
||||
|
||||
// need to figure out if this new position is still inside the bounds of
|
||||
// the map before we can attempt to determine if the current tile is
|
||||
// solid
|
||||
if (
|
||||
currentX < 0 || currentX >= (int32_t)GetWidth() ||
|
||||
currentY < 0 || currentY >= (int32_t)GetHeight() ||
|
||||
currentZ < 0 || currentZ >= (int32_t)GetDepth()
|
||||
)
|
||||
outOfMap = TRUE;
|
||||
else
|
||||
{
|
||||
// still inside and at the next position, test for a solid tile
|
||||
Tile *tile = Get(currentX, currentY, currentZ);
|
||||
if (IsBitSet(TILE_COLLIDABLE, tile->flags))
|
||||
{
|
||||
collided = TRUE;
|
||||
|
||||
// set the tile coords of the collision
|
||||
x = currentX;
|
||||
y = currentY;
|
||||
z = currentZ;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
BOOL TileMap::CheckForCollision(const Ray &ray, Vector3 &point, uint32_t &x, uint32_t &y, uint32_t &z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// if the ray doesn't collide with any solid tiles in the first place, then
|
||||
// we can skip this more expensive triangle collision check...
|
||||
if (!CheckForCollision(ray, x, y, z))
|
||||
return FALSE;
|
||||
|
||||
// now perform the per-triangle collision check against the tile position
|
||||
// where the ray ended up at the end of the above CheckForCollision() call
|
||||
return CheckForCollisionWithTile(ray, point, x, y, z);
|
||||
}
|
||||
|
||||
BOOL TileMap::CheckForCollisionWithTile(const Ray &ray, Vector3 &point, uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
const Tile *tile = Get(x, y, z);
|
||||
const TileMesh *mesh = m_tileMeshes->Get(tile);
|
||||
|
||||
uint32_t numVertices = mesh->GetNumCollisionVertices();
|
||||
const Vector3 *vertices = mesh->GetCollisionVertices();
|
||||
|
||||
// world position of this tile, will be used to move each
|
||||
// mesh triangle into world space
|
||||
Vector3 tileWorldPosition = Vector3((float)x, (float)y, (float)z);
|
||||
|
||||
float closestSquaredDistance = FLT_MAX;
|
||||
BOOL collided = FALSE;
|
||||
Vector3 collisionPoint = ZERO_VECTOR;
|
||||
|
||||
for (uint32_t i = 0; i < numVertices; i += 3)
|
||||
{
|
||||
// get the vertices making up this triangle
|
||||
Vector3 a = vertices[i];
|
||||
Vector3 b = vertices[i + 1];
|
||||
Vector3 c = vertices[i + 2];
|
||||
|
||||
// move these vertices into world space
|
||||
a += tileWorldPosition;
|
||||
b += tileWorldPosition;
|
||||
c += tileWorldPosition;
|
||||
|
||||
if (IntersectionTester::Test(ray, a, b, c, &collisionPoint))
|
||||
{
|
||||
collided = TRUE;
|
||||
|
||||
// if this is the closest collision yet, then keep the distance
|
||||
// and point of collision
|
||||
float squaredDistance = Vector3::LengthSquared(collisionPoint - ray.position);
|
||||
if (squaredDistance < closestSquaredDistance)
|
||||
{
|
||||
closestSquaredDistance = squaredDistance;
|
||||
point = collisionPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collided;
|
||||
}
|
||||
|
||||
BOOL TileMap::GetOverlappedTiles(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// make sure the given box actually intersects with the map in the first place
|
||||
if (!IntersectionTester::Test(m_bounds, box))
|
||||
return FALSE;
|
||||
|
||||
// convert to tile coords. this is in "tilemap space" which is how we want it
|
||||
// HACK: ceil() calls and "-1"'s keep us from picking up too many/too few
|
||||
// blocks. these were arrived at through observation
|
||||
int32_t minX = (int32_t)box.min.x;
|
||||
int32_t minY = (int32_t)box.min.y;
|
||||
int32_t minZ = (int32_t)box.min.z;
|
||||
int32_t maxX = (int32_t)ceil(box.max.x);
|
||||
int32_t maxY = (int32_t)ceil(box.max.y - 1.0f);
|
||||
int32_t maxZ = (int32_t)ceil(box.max.z);
|
||||
|
||||
// trim off the excess bounds so that we end up with a min-to-max area
|
||||
// that is completely within the bounds of the map
|
||||
// HACK: "-1"'s keep us from picking up too many blocks. these were arrived
|
||||
// at through observation
|
||||
minX = Clamp(minX, 0, (int32_t)GetWidth());
|
||||
minY = Clamp(minY, 0, (int32_t)(GetHeight() - 1));
|
||||
minZ = Clamp(minZ, 0, (int32_t)GetDepth());
|
||||
maxX = Clamp(maxX, 0, (int32_t)GetWidth());
|
||||
maxY = Clamp(maxY, 0, (int32_t)(GetHeight() - 1));
|
||||
maxZ = Clamp(maxZ, 0, (int32_t)GetDepth());
|
||||
|
||||
// return the leftover area
|
||||
x1 = minX;
|
||||
y1 = minY;
|
||||
z1 = minZ;
|
||||
x2 = maxX;
|
||||
y2 = maxY;
|
||||
z2 = maxZ;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL TileMap::GetOverlappedChunks(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const
|
||||
{
|
||||
STACK_TRACE;
|
||||
// make sure the given box actually intersects with the map in the first place
|
||||
if (!IntersectionTester::Test(m_bounds, box))
|
||||
return FALSE;
|
||||
|
||||
// convert to tile coords. this is in "tilemap space" which is how we want it
|
||||
// HACK: ceil() calls and "-1"'s keep us from picking up too many/too few
|
||||
// blocks. these were arrived at through observation
|
||||
int32_t minX = (int32_t)box.min.x;
|
||||
int32_t minY = (int32_t)box.min.y;
|
||||
int32_t minZ = (int32_t)box.min.z;
|
||||
int32_t maxX = (int32_t)ceil(box.max.x);
|
||||
int32_t maxY = (int32_t)ceil(box.max.y - 1.0f);
|
||||
int32_t maxZ = (int32_t)ceil(box.max.z);
|
||||
|
||||
// now convert to chunk coords
|
||||
int32_t minChunkX = minX / (int32_t)GetChunkWidth();
|
||||
int32_t minChunkY = minY / (int32_t)GetChunkHeight();
|
||||
int32_t minChunkZ = minZ / (int32_t)GetChunkDepth();
|
||||
int32_t maxChunkX = maxX / (int32_t)GetChunkWidth();
|
||||
int32_t maxChunkY = maxY / (int32_t)GetChunkHeight();
|
||||
int32_t maxChunkZ = maxZ / (int32_t)GetChunkDepth();
|
||||
|
||||
// trim off the excess bounds so that we end up with a min-to-max area
|
||||
// that is completely within the chunk bounds of the map
|
||||
// HACK: "-1"'s keep us from picking up too many chunks. these were arrived
|
||||
// at through observation
|
||||
minChunkX = Clamp(minChunkX, 0, (int32_t)GetWidthInChunks());
|
||||
minChunkY = Clamp(minChunkY, 0, (int32_t)(GetHeightInChunks() - 1));
|
||||
minChunkZ = Clamp(minChunkZ, 0, (int32_t)GetDepthInChunks());
|
||||
maxChunkX = Clamp(maxChunkX, 0, (int32_t)GetWidthInChunks());
|
||||
maxChunkY = Clamp(maxChunkY, 0, (int32_t)(GetHeightInChunks() - 1));
|
||||
maxChunkZ = Clamp(maxChunkZ, 0, (int32_t)GetDepthInChunks());
|
||||
|
||||
// return the leftover area
|
||||
x1 = minChunkX;
|
||||
y1 = minChunkY;
|
||||
z1 = minChunkZ;
|
||||
x2 = maxChunkX;
|
||||
y2 = maxChunkY;
|
||||
z2 = maxChunkZ;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void TileMap::UpdateVertices()
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(m_numChunks > 0);
|
||||
|
||||
for (uint32_t i = 0; i < m_numChunks; ++i)
|
||||
{
|
||||
TileChunk *chunk = m_chunks[i];
|
||||
UpdateChunkVertices(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMap::UpdateChunkVertices(TileChunk *chunk)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(m_numChunks > 0);
|
||||
ASSERT(chunk != NULL);
|
||||
|
||||
chunk->UpdateVertices(m_vertexGenerator);
|
||||
}
|
||||
|
||||
void TileMap::UpdateLighting()
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(m_numChunks > 0);
|
||||
|
||||
if (m_lighter != NULL)
|
||||
m_lighter->Light(this);
|
||||
}
|
212
src/tilemap/tilemap.h
Normal file
212
src/tilemap/tilemap.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
#ifndef __TILEMAP_TILEMAP_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMAP_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "tile.h"
|
||||
#include "tilechunk.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "../framework/math/boundingbox.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
|
||||
#include <stl/vector.h>
|
||||
|
||||
class ChunkVertexGenerator;
|
||||
class GraphicsDevice;
|
||||
class TileChunk;
|
||||
class TileMapLighter;
|
||||
class TileMesh;
|
||||
class TileMeshCollection;
|
||||
struct RectF;
|
||||
struct Ray;
|
||||
struct Tile;
|
||||
|
||||
class TileMap
|
||||
{
|
||||
public:
|
||||
TileMap(TileMeshCollection *tileMeshes, ChunkVertexGenerator *vertexGenerator, TileMapLighter *lighter, GraphicsDevice *graphicsDevice);
|
||||
virtual ~TileMap();
|
||||
|
||||
void SetSize(uint32_t numChunksX, uint32_t numChunksY, uint32_t numChunksZ, uint32_t chunkSizeX, uint32_t chunkSizeY, uint32_t chunkSizeZ);
|
||||
|
||||
TileMeshCollection* GetMeshes() const { return m_tileMeshes; }
|
||||
ChunkVertexGenerator* GetVertexGenerator() const { return m_vertexGenerator; }
|
||||
TileMapLighter* GetLighter() const { return m_lighter; }
|
||||
|
||||
Tile* Get(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
Tile* GetSafe(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
TileChunk* GetChunk(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const;
|
||||
TileChunk* GetChunkSafe(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const;
|
||||
TileChunk* GetChunkNextTo(TileChunk *chunk, int32_t offsetX, int32_t offsetY, int32_t offsetZ) const;
|
||||
TileChunk* GetChunkContaining(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
|
||||
void GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z, BoundingBox *box) const;
|
||||
BoundingBox GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
|
||||
BOOL CheckForCollision(const Ray &ray, uint32_t &x, uint32_t &y, uint32_t &z) const;
|
||||
BOOL CheckForCollision(const Ray &ray, Vector3 &point, uint32_t &x, uint32_t &y, uint32_t &z) const;
|
||||
BOOL CheckForCollisionWithTile(const Ray &ray, Vector3 &point, uint32_t x, uint32_t y, uint32_t z) const;
|
||||
BOOL GetOverlappedTiles(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const;
|
||||
BOOL GetOverlappedChunks(const BoundingBox &box, uint32_t &x1, uint32_t &y1, uint32_t &z1, uint32_t &x2, uint32_t &y2, uint32_t &z2) const;
|
||||
|
||||
void UpdateChunkVertices(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ);
|
||||
void UpdateVertices();
|
||||
|
||||
void UpdateLighting();
|
||||
|
||||
BOOL IsWithinBounds(int32_t x, int32_t y, int32_t z) const;
|
||||
|
||||
uint32_t GetWidth() const { return m_widthInChunks * m_chunkWidth; }
|
||||
uint32_t GetHeight() const { return m_heightInChunks * m_chunkHeight; }
|
||||
uint32_t GetDepth() const { return m_depthInChunks * m_chunkDepth; }
|
||||
|
||||
uint32_t GetChunkWidth() const { return m_chunkWidth; }
|
||||
uint32_t GetChunkHeight() const { return m_chunkHeight; }
|
||||
uint32_t GetChunkDepth() const { return m_chunkDepth; }
|
||||
|
||||
uint32_t GetWidthInChunks() const { return m_widthInChunks; }
|
||||
uint32_t GetHeightInChunks() const { return m_heightInChunks; }
|
||||
uint32_t GetDepthInChunks() const { return m_depthInChunks; }
|
||||
const BoundingBox& GetBounds() const { return m_bounds; }
|
||||
|
||||
uint32_t GetNumChunks() const { return m_numChunks; }
|
||||
|
||||
void SetAmbientLightValue(TILE_LIGHT_VALUE value) { m_ambientLightValue = value; }
|
||||
TILE_LIGHT_VALUE GetAmbientLightValue() const { return m_ambientLightValue; }
|
||||
void SetSkyLightValue(TILE_LIGHT_VALUE value) { m_skyLightValue = value; }
|
||||
TILE_LIGHT_VALUE GetSkyLightValue() const { return m_skyLightValue; }
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
||||
uint32_t GetChunkIndexAt(uint32_t x, uint32_t y, uint32_t z) const;
|
||||
uint32_t GetChunkIndex(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const;
|
||||
|
||||
void UpdateChunkVertices(TileChunk *chunk);
|
||||
|
||||
TileMeshCollection *m_tileMeshes;
|
||||
TileChunk **m_chunks;
|
||||
GraphicsDevice *m_graphicsDevice;
|
||||
ChunkVertexGenerator *m_vertexGenerator;
|
||||
TileMapLighter *m_lighter;
|
||||
|
||||
uint32_t m_chunkWidth;
|
||||
uint32_t m_chunkHeight;
|
||||
uint32_t m_chunkDepth;
|
||||
|
||||
uint32_t m_widthInChunks;
|
||||
uint32_t m_heightInChunks;
|
||||
uint32_t m_depthInChunks;
|
||||
BoundingBox m_bounds;
|
||||
|
||||
uint32_t m_numChunks;
|
||||
|
||||
TILE_LIGHT_VALUE m_ambientLightValue;
|
||||
TILE_LIGHT_VALUE m_skyLightValue;
|
||||
};
|
||||
|
||||
inline Tile* TileMap::Get(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
TileChunk *chunk = GetChunkContaining(x, y, z);
|
||||
uint32_t chunkX = x - chunk->GetX();
|
||||
uint32_t chunkY = y - chunk->GetY();
|
||||
uint32_t chunkZ = z - chunk->GetZ();
|
||||
|
||||
return chunk->Get(chunkX, chunkY, chunkZ);
|
||||
}
|
||||
|
||||
inline Tile* TileMap::GetSafe(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
if (!IsWithinBounds((int32_t)x, (int32_t)y, (int32_t)z))
|
||||
return NULL;
|
||||
else
|
||||
return Get(x, y, z);
|
||||
}
|
||||
|
||||
inline TileChunk* TileMap::GetChunk(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const
|
||||
{
|
||||
uint32_t index = GetChunkIndex(chunkX, chunkY, chunkZ);
|
||||
return m_chunks[index];
|
||||
}
|
||||
|
||||
inline TileChunk* TileMap::GetChunkSafe(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const
|
||||
{
|
||||
if (
|
||||
(chunkX >= m_widthInChunks) ||
|
||||
(chunkY >= m_heightInChunks) ||
|
||||
(chunkZ >= m_depthInChunks)
|
||||
)
|
||||
return NULL;
|
||||
else
|
||||
return GetChunk(chunkX, chunkY, chunkZ);
|
||||
}
|
||||
|
||||
inline TileChunk* TileMap::GetChunkNextTo(TileChunk *chunk, int32_t offsetX, int32_t offsetY, int32_t offsetZ) const
|
||||
{
|
||||
int32_t checkX = chunk->GetX() + offsetX;
|
||||
int32_t checkY = chunk->GetY() + offsetY;
|
||||
int32_t checkZ = chunk->GetZ() + offsetZ;
|
||||
|
||||
if (
|
||||
(checkX < 0 || (uint32_t)checkX >= m_widthInChunks) ||
|
||||
(checkY < 0 || (uint32_t)checkY >= m_heightInChunks) ||
|
||||
(checkZ < 0 || (uint32_t)checkZ >= m_depthInChunks)
|
||||
)
|
||||
return NULL;
|
||||
else
|
||||
return GetChunk(checkX, checkY, checkZ);
|
||||
}
|
||||
|
||||
inline TileChunk* TileMap::GetChunkContaining(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
uint32_t index = GetChunkIndexAt(x, y, z);
|
||||
return m_chunks[index];
|
||||
}
|
||||
|
||||
inline void TileMap::GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z, BoundingBox *box) const
|
||||
{
|
||||
TileChunk *chunk = GetChunkContaining(x, y, z);
|
||||
uint32_t chunkX = x - chunk->GetX();
|
||||
uint32_t chunkY = y - chunk->GetY();
|
||||
uint32_t chunkZ = z - chunk->GetZ();
|
||||
|
||||
chunk->GetBoundingBoxFor(chunkX, chunkY, chunkZ, box);
|
||||
}
|
||||
|
||||
inline BoundingBox TileMap::GetBoundingBoxFor(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
BoundingBox box;
|
||||
GetBoundingBoxFor(x, y, z, &box);
|
||||
return box;
|
||||
}
|
||||
|
||||
inline void TileMap::UpdateChunkVertices(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ)
|
||||
{
|
||||
uint32_t index = GetChunkIndex(chunkX, chunkY, chunkZ);
|
||||
TileChunk *chunk = m_chunks[index];
|
||||
UpdateChunkVertices(chunk);
|
||||
}
|
||||
|
||||
inline uint32_t TileMap::GetChunkIndexAt(uint32_t x, uint32_t y, uint32_t z) const
|
||||
{
|
||||
return GetChunkIndex(x / m_chunkWidth, y / m_chunkHeight, z / m_chunkDepth);
|
||||
}
|
||||
|
||||
inline uint32_t TileMap::GetChunkIndex(uint32_t chunkX, uint32_t chunkY, uint32_t chunkZ) const
|
||||
{
|
||||
return (chunkY * m_widthInChunks * m_depthInChunks) + (chunkZ * m_widthInChunks) + chunkX;
|
||||
}
|
||||
|
||||
inline BOOL TileMap::IsWithinBounds(int32_t x, int32_t y, int32_t z) const
|
||||
{
|
||||
if (x < 0 || x >= (int32_t)GetWidth())
|
||||
return FALSE;
|
||||
if (y < 0 || y >= (int32_t)GetHeight())
|
||||
return FALSE;
|
||||
if (z < 0 || z >= (int32_t)GetDepth())
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
16
src/tilemap/tilemaplighter.h
Normal file
16
src/tilemap/tilemaplighter.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef __TILEMAP_TILEMAPLIGHTER_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMAPLIGHTER_H_INCLUDED__
|
||||
|
||||
class TileMap;
|
||||
|
||||
class TileMapLighter
|
||||
{
|
||||
public:
|
||||
TileMapLighter() {}
|
||||
virtual ~TileMapLighter() {}
|
||||
|
||||
virtual void Light(TileMap *tileMap) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
104
src/tilemap/tilemaprenderer.cpp
Normal file
104
src/tilemap/tilemaprenderer.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "tilemaprenderer.h"
|
||||
|
||||
#include "tilemap.h"
|
||||
#include "../framework/graphics/graphicsdevice.h"
|
||||
#include "../framework/graphics/renderstate.h"
|
||||
#include "../framework/graphics/shader.h"
|
||||
#include "../framework/graphics/simplecolortextureshader.h"
|
||||
#include "../framework/graphics/viewcontext.h"
|
||||
#include "../framework/math/camera.h"
|
||||
#include "../framework/math/frustum.h"
|
||||
|
||||
TileMapRenderer::TileMapRenderer(GraphicsDevice *graphicsDevice)
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_graphicsDevice = graphicsDevice;
|
||||
|
||||
m_chunkRenderer = new ChunkRenderer(graphicsDevice);
|
||||
|
||||
m_numChunksRendered = 0;
|
||||
m_numAlphaChunksRendered = 0;
|
||||
m_numVerticesRendered = 0;
|
||||
m_numAlphaVerticesRendered = 0;
|
||||
}
|
||||
|
||||
TileMapRenderer::~TileMapRenderer()
|
||||
{
|
||||
STACK_TRACE;
|
||||
SAFE_DELETE(m_chunkRenderer);
|
||||
}
|
||||
|
||||
void TileMapRenderer::Render(const TileMap *tileMap, Shader *shader)
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_numChunksRendered = 0;
|
||||
m_numVerticesRendered = 0;
|
||||
|
||||
if (shader == NULL)
|
||||
{
|
||||
m_graphicsDevice->BindShader(m_graphicsDevice->GetSimpleColorTextureShader());
|
||||
m_graphicsDevice->GetSimpleColorTextureShader()->SetModelViewMatrix(m_graphicsDevice->GetViewContext()->GetModelViewMatrix());
|
||||
m_graphicsDevice->GetSimpleColorTextureShader()->SetProjectionMatrix(m_graphicsDevice->GetViewContext()->GetProjectionMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(shader->IsReadyForUse() == TRUE);
|
||||
m_graphicsDevice->BindShader(shader);
|
||||
}
|
||||
|
||||
for (uint32_t y = 0; y < tileMap->GetHeightInChunks(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepthInChunks(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < tileMap->GetWidthInChunks(); ++x)
|
||||
{
|
||||
TileChunk *chunk = tileMap->GetChunk(x, y, z);
|
||||
if (m_graphicsDevice->GetViewContext()->GetCamera()->GetFrustum()->Test(chunk->GetBounds()))
|
||||
{
|
||||
m_numVerticesRendered += m_chunkRenderer->Render(chunk);
|
||||
++m_numChunksRendered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_graphicsDevice->UnbindShader();
|
||||
}
|
||||
|
||||
void TileMapRenderer::RenderAlpha(const TileMap *tileMap, Shader *shader)
|
||||
{
|
||||
m_numAlphaChunksRendered = 0;
|
||||
m_numAlphaVerticesRendered = 0;
|
||||
|
||||
if (shader == NULL)
|
||||
{
|
||||
m_graphicsDevice->BindShader(m_graphicsDevice->GetSimpleColorTextureShader());
|
||||
m_graphicsDevice->GetSimpleColorTextureShader()->SetModelViewMatrix(m_graphicsDevice->GetViewContext()->GetModelViewMatrix());
|
||||
m_graphicsDevice->GetSimpleColorTextureShader()->SetProjectionMatrix(m_graphicsDevice->GetViewContext()->GetProjectionMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(shader->IsReadyForUse() == TRUE);
|
||||
m_graphicsDevice->BindShader(shader);
|
||||
}
|
||||
|
||||
for (uint32_t y = 0; y < tileMap->GetHeightInChunks(); ++y)
|
||||
{
|
||||
for (uint32_t z = 0; z < tileMap->GetDepthInChunks(); ++z)
|
||||
{
|
||||
for (uint32_t x = 0; x < tileMap->GetWidthInChunks(); ++x)
|
||||
{
|
||||
TileChunk *chunk = tileMap->GetChunk(x, y, z);
|
||||
if (chunk->IsAlphaEnabled() && m_graphicsDevice->GetViewContext()->GetCamera()->GetFrustum()->Test(chunk->GetBounds()))
|
||||
{
|
||||
m_numAlphaVerticesRendered += m_chunkRenderer->RenderAlpha(chunk);
|
||||
++m_numAlphaChunksRendered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_graphicsDevice->UnbindShader();
|
||||
}
|
40
src/tilemap/tilemaprenderer.h
Normal file
40
src/tilemap/tilemaprenderer.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef __TILEMAP_TILEMAPRENDERER_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMAPRENDERER_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
#include "chunkrenderer.h"
|
||||
|
||||
class GraphicsDevice;
|
||||
class TileMap;
|
||||
class Shader;
|
||||
|
||||
class TileMapRenderer
|
||||
{
|
||||
public:
|
||||
TileMapRenderer(GraphicsDevice *graphicsDevice);
|
||||
virtual ~TileMapRenderer();
|
||||
|
||||
void Render(const TileMap *tileMap, Shader *shader = NULL);
|
||||
void RenderAlpha(const TileMap *tileMap, Shader *shader = NULL);
|
||||
|
||||
uint32_t GetNumVerticesRendered() const { return m_numVerticesRendered; }
|
||||
uint32_t GetNumAlphaVerticesRendered() const { return m_numAlphaVerticesRendered; }
|
||||
uint32_t GetNumChunksRendered() const { return m_numChunksRendered; }
|
||||
uint32_t GetNumAlphaChunksRendered() const { return m_numAlphaChunksRendered; }
|
||||
|
||||
uint32_t GetTotalVerticesRendered() const { return m_numVerticesRendered + m_numAlphaVerticesRendered; }
|
||||
uint32_t GetTotalChunksRendered() const { return m_numChunksRendered + m_numAlphaChunksRendered; }
|
||||
|
||||
private:
|
||||
GraphicsDevice *m_graphicsDevice;
|
||||
ChunkRenderer *m_chunkRenderer;
|
||||
|
||||
uint32_t m_numVerticesRendered;
|
||||
uint32_t m_numAlphaVerticesRendered;
|
||||
uint32_t m_numChunksRendered;
|
||||
uint32_t m_numAlphaChunksRendered;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
56
src/tilemap/tilemesh.h
Normal file
56
src/tilemap/tilemesh.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef __TILEMAP_TILEMESH_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMESH_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/math/vector3.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
|
||||
class VertexBuffer;
|
||||
|
||||
const Vector3 TILEMESH_OFFSET = Vector3(0.5f, 0.5f, 0.5f);
|
||||
|
||||
enum TILEMESH_TYPE
|
||||
{
|
||||
TILEMESH_STATIC,
|
||||
TILEMESH_CUBE
|
||||
};
|
||||
|
||||
class TileMesh
|
||||
{
|
||||
public:
|
||||
TileMesh() {}
|
||||
virtual ~TileMesh() {}
|
||||
|
||||
virtual VertexBuffer* GetBuffer() const = 0;
|
||||
virtual uint32_t GetNumCollisionVertices() const = 0;
|
||||
virtual const Vector3* GetCollisionVertices() const = 0;
|
||||
|
||||
virtual TILEMESH_TYPE GetType() const = 0;
|
||||
|
||||
BOOL IsCompletelyOpaque() const { return m_opaqueSides == SIDE_ALL; }
|
||||
BOOL IsOpaque(MESH_SIDES sides) const { return IsBitSet(sides, m_opaqueSides); }
|
||||
BOOL IsAlpha() const { return m_alpha; }
|
||||
const Color& GetColor() const { return m_color; }
|
||||
float GetTranslucency() const { return m_translucency; }
|
||||
BOOL IsLightSource() const { return m_lightValue > 0; }
|
||||
TILE_LIGHT_VALUE GetLightValue() const { return m_lightValue; }
|
||||
|
||||
protected:
|
||||
void SetOpaque(MESH_SIDES sides) { m_opaqueSides = sides; }
|
||||
void SetAlpha(BOOL alpha) { m_alpha = alpha; }
|
||||
void SetColor(const Color &color) { m_color = color; }
|
||||
void SetTranslucency(float translucency) { m_translucency = translucency; }
|
||||
void SetLight(TILE_LIGHT_VALUE lightValue) { m_lightValue = lightValue; }
|
||||
|
||||
private:
|
||||
MESH_SIDES m_opaqueSides;
|
||||
BOOL m_alpha;
|
||||
Color m_color;
|
||||
float m_translucency;
|
||||
TILE_LIGHT_VALUE m_lightValue;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
87
src/tilemap/tilemeshcollection.cpp
Normal file
87
src/tilemap/tilemeshcollection.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "../framework/debug.h"
|
||||
|
||||
#include "tilemeshcollection.h"
|
||||
#include "cubetilemesh.h"
|
||||
#include "statictilemesh.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemesh.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include "../framework/assets/static/staticmesh.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "../framework/graphics/textureatlas.h"
|
||||
|
||||
TileMeshCollection::TileMeshCollection(const TextureAtlas *textureAtlas)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(textureAtlas != NULL);
|
||||
m_textureAtlas = textureAtlas;
|
||||
|
||||
// the first mesh (index = 0) should always be a NULL one as this has special meaning
|
||||
// in other TileMap-related objects
|
||||
AddMesh(NULL);
|
||||
}
|
||||
|
||||
TileMeshCollection::~TileMeshCollection()
|
||||
{
|
||||
STACK_TRACE;
|
||||
for (size_t i = 0; i < m_meshes.size(); ++i)
|
||||
{
|
||||
TileMesh *mesh = m_meshes[i];
|
||||
SAFE_DELETE(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::Add(const StaticMesh *mesh, uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
return Add(mesh, (StaticMesh*)NULL, textureIndex, opaqueSides, lightValue, alpha, translucency, color);
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::Add(const StaticMesh *mesh, const StaticMesh *collisionMesh, uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
StaticTileMesh *tileMesh = new StaticTileMesh(mesh, &m_textureAtlas->GetTile(textureIndex).texCoords, opaqueSides, lightValue, alpha, translucency, color, collisionMesh);
|
||||
ASSERT(tileMesh != NULL);
|
||||
|
||||
return AddMesh(tileMesh);
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::Add(const StaticMesh *mesh, uint32_t *textureIndexes, uint32_t numIndexes, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
return Add(mesh, (StaticMesh*)NULL, textureIndexes, numIndexes, opaqueSides, lightValue, alpha, translucency, color);
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::Add(const StaticMesh *mesh, const StaticMesh *collisionMesh, uint32_t *textureIndexes, uint32_t numIndexes, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
ASSERT(numIndexes > 0);
|
||||
RectF *tileBoundaries = new RectF[numIndexes];
|
||||
ASSERT(tileBoundaries != NULL);
|
||||
|
||||
for (uint32_t i = 0; i < numIndexes; ++i)
|
||||
tileBoundaries[i] = m_textureAtlas->GetTile(textureIndexes[i]).texCoords;
|
||||
|
||||
StaticTileMesh *tileMesh = new StaticTileMesh(mesh, tileBoundaries, numIndexes, opaqueSides, lightValue, alpha, translucency, color, collisionMesh);
|
||||
ASSERT(tileMesh != NULL);
|
||||
|
||||
SAFE_DELETE_ARRAY(tileBoundaries);
|
||||
|
||||
return AddMesh(tileMesh);
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::AddCube(uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue, BOOL alpha, float translucency, const Color &color)
|
||||
{
|
||||
STACK_TRACE;
|
||||
CubeTileMesh *tileMesh = new CubeTileMesh(SIDE_ALL, &m_textureAtlas->GetTile(textureIndex).texCoords, opaqueSides, lightValue, alpha, translucency, color);
|
||||
ASSERT(tileMesh != NULL);
|
||||
|
||||
return AddMesh(tileMesh);
|
||||
}
|
||||
|
||||
uint32_t TileMeshCollection::AddMesh(TileMesh *mesh)
|
||||
{
|
||||
STACK_TRACE;
|
||||
m_meshes.push_back(mesh);
|
||||
return m_meshes.size() - 1;
|
||||
}
|
42
src/tilemap/tilemeshcollection.h
Normal file
42
src/tilemap/tilemeshcollection.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef __TILEMAP_TILEMESHCOLLECTION_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMESHCOLLECTION_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
#include "../framework/graphics/color.h"
|
||||
#include "tile.h"
|
||||
#include "tilelightdefs.h"
|
||||
#include "tilemeshdefs.h"
|
||||
#include <stl/vector.h>
|
||||
|
||||
class StaticMesh;
|
||||
class TextureAtlas;
|
||||
class TileMesh;
|
||||
|
||||
typedef stl::vector<TileMesh*> TileMeshList;
|
||||
|
||||
class TileMeshCollection
|
||||
{
|
||||
public:
|
||||
TileMeshCollection(const TextureAtlas *textureAtlas);
|
||||
virtual ~TileMeshCollection();
|
||||
|
||||
const TextureAtlas* GetTextureAtlas() const { return m_textureAtlas; }
|
||||
|
||||
uint32_t Add(const StaticMesh *mesh, uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue = 0, BOOL alpha = FALSE, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
uint32_t Add(const StaticMesh *mesh, const StaticMesh *collisionMesh, uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue = 0, BOOL alpha = FALSE, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
uint32_t Add(const StaticMesh *mesh, uint32_t *textureIndexes, uint32_t numIndexes, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue = 0, BOOL alpha = FALSE, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
uint32_t Add(const StaticMesh *mesh, const StaticMesh *collisionMesh, uint32_t *textureIndexes, uint32_t numIndexes, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue = 0, BOOL alpha = FALSE, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
uint32_t AddCube(uint32_t textureIndex, MESH_SIDES opaqueSides, TILE_LIGHT_VALUE lightValue = 0, BOOL alpha = FALSE, float translucency = 1.0f, const Color &color = COLOR_WHITE);
|
||||
|
||||
TileMesh* Get(const Tile *tile) const { return m_meshes[tile->tile]; }
|
||||
TileMesh* Get(uint32_t index) const { return m_meshes[index]; }
|
||||
uint32_t GetCount() const { return m_meshes.size(); }
|
||||
|
||||
private:
|
||||
uint32_t AddMesh(TileMesh *mesh);
|
||||
|
||||
const TextureAtlas *m_textureAtlas;
|
||||
TileMeshList m_meshes;
|
||||
};
|
||||
|
||||
#endif
|
25
src/tilemap/tilemeshdefs.h
Normal file
25
src/tilemap/tilemeshdefs.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef __TILEMAP_TILEMESHDEFS_H_INCLUDED__
|
||||
#define __TILEMAP_TILEMESHDEFS_H_INCLUDED__
|
||||
|
||||
#include "../framework/common.h"
|
||||
|
||||
// random place for global-ish mesh definitions and constants
|
||||
|
||||
enum SIDES
|
||||
{
|
||||
SIDE_TOP = 1,
|
||||
SIDE_BOTTOM = 2,
|
||||
SIDE_FRONT = 4,
|
||||
SIDE_BACK = 8,
|
||||
SIDE_LEFT = 16,
|
||||
SIDE_RIGHT = 32,
|
||||
SIDE_ALL = 255
|
||||
};
|
||||
|
||||
typedef uint8_t CUBE_FACES;
|
||||
typedef uint8_t MESH_SIDES;
|
||||
|
||||
const uint32_t CUBE_VERTICES_PER_FACE = 6;
|
||||
|
||||
#endif
|
||||
|
Reference in a new issue