2011-03-04 21:02:05 +01:00
|
|
|
/* Copyright (C) 2011 Wildfire Games.
|
2009-04-18 19:00:33 +02:00
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2009-04-18 19:51:05 +02:00
|
|
|
/*
|
|
|
|
* Describes ground via heightmap and array of CPatch.
|
2007-05-07 18:33:24 +02:00
|
|
|
*/
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2004-06-03 20:38:14 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2005-08-12 19:06:53 +02:00
|
|
|
#include "lib/res/graphics/ogl_tex.h"
|
2010-04-30 01:36:05 +02:00
|
|
|
#include "lib/sysdep/cpu.h"
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2006-02-13 04:30:34 +01:00
|
|
|
#include "renderer/Renderer.h"
|
|
|
|
|
2006-11-12 05:02:36 +01:00
|
|
|
#include "TerrainProperties.h"
|
2010-09-04 23:26:52 +02:00
|
|
|
#include "TerrainTextureEntry.h"
|
|
|
|
#include "TerrainTextureManager.h"
|
2006-11-12 05:02:36 +01:00
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include "Terrain.h"
|
2006-06-09 18:44:16 +02:00
|
|
|
#include "Patch.h"
|
2010-01-09 20:20:14 +01:00
|
|
|
#include "maths/FixedVector3D.h"
|
2006-06-02 04:10:27 +02:00
|
|
|
#include "maths/MathUtil.h"
|
2009-10-11 23:27:35 +02:00
|
|
|
#include "ps/CLogger.h"
|
2004-05-29 22:56:24 +02:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CTerrain constructor
|
2006-09-26 03:44:20 +02:00
|
|
|
CTerrain::CTerrain()
|
|
|
|
: m_Heightmap(0), m_Patches(0), m_MapSize(0), m_MapSizePatches(0),
|
|
|
|
m_BaseColour(255, 255, 255, 255)
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CTerrain constructor
|
|
|
|
CTerrain::~CTerrain()
|
|
|
|
{
|
|
|
|
ReleaseData();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ReleaseData: delete any data allocated by this terrain
|
|
|
|
void CTerrain::ReleaseData()
|
|
|
|
{
|
2012-04-18 22:39:00 +02:00
|
|
|
m_HeightMipmap.ReleaseData();
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
delete[] m_Heightmap;
|
|
|
|
delete[] m_Patches;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2009-11-08 17:49:52 +01:00
|
|
|
// Initialise: initialise this terrain to the given size
|
2004-05-29 22:56:24 +02:00
|
|
|
// using given heightmap to setup elevation data
|
2012-04-18 22:39:00 +02:00
|
|
|
bool CTerrain::Initialize(ssize_t patchesPerSide, const u16* data)
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
|
|
|
// clean up any previous terrain
|
|
|
|
ReleaseData();
|
|
|
|
|
|
|
|
// store terrain size
|
2012-04-18 22:39:00 +02:00
|
|
|
m_MapSize = patchesPerSide*PATCH_SIZE+1;
|
|
|
|
m_MapSizePatches = patchesPerSide;
|
2004-05-29 22:56:24 +02:00
|
|
|
// allocate data for new terrain
|
2012-04-18 22:39:00 +02:00
|
|
|
m_Heightmap = new u16[m_MapSize*m_MapSize];
|
|
|
|
m_Patches = new CPatch[m_MapSizePatches*m_MapSizePatches];
|
2004-05-29 22:56:24 +02:00
|
|
|
|
|
|
|
// given a heightmap?
|
2012-04-18 22:39:00 +02:00
|
|
|
if (data)
|
|
|
|
{
|
2004-05-29 22:56:24 +02:00
|
|
|
// yes; keep a copy of it
|
2012-04-18 22:39:00 +02:00
|
|
|
memcpy(m_Heightmap, data, m_MapSize*m_MapSize*sizeof(u16));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-05-29 22:56:24 +02:00
|
|
|
// build a flat terrain
|
2012-04-18 22:39:00 +02:00
|
|
|
memset(m_Heightmap, 0, m_MapSize*m_MapSize*sizeof(u16));
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// setup patch parents, indices etc
|
|
|
|
InitialisePatches();
|
|
|
|
|
2012-04-18 22:39:00 +02:00
|
|
|
// initialise mipmap
|
|
|
|
m_HeightMipmap.Initialize(m_MapSize, m_Heightmap);
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2006-06-09 18:44:16 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2010-05-28 01:31:03 +02:00
|
|
|
CStr8 CTerrain::GetMovementClass(ssize_t i, ssize_t j) const
|
2006-10-08 19:39:46 +02:00
|
|
|
{
|
2010-05-28 01:31:03 +02:00
|
|
|
CMiniPatch* tile = GetTile(i, j);
|
|
|
|
if (tile && tile->GetTextureEntry())
|
2011-02-05 14:13:57 +01:00
|
|
|
return tile->GetTextureEntry()->GetProperties().GetMovementClass();
|
2006-10-08 19:39:46 +02:00
|
|
|
|
2010-05-28 01:31:03 +02:00
|
|
|
return "default";
|
2006-06-09 18:44:16 +02:00
|
|
|
}
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CalcPosition: calculate the world space position of the vertex at (i,j)
|
2011-03-04 21:02:05 +01:00
|
|
|
// If i,j is off the map, it acts as if the edges of the terrain are extended
|
|
|
|
// outwards to infinity
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
void CTerrain::CalcPosition(ssize_t i, ssize_t j, CVector3D& pos) const
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
2011-03-04 21:02:05 +01:00
|
|
|
ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
|
|
|
|
ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
|
|
|
|
u16 height = m_Heightmap[hj*m_MapSize + hi];
|
2012-01-12 13:51:10 +01:00
|
|
|
pos.X = float(i*TERRAIN_TILE_SIZE);
|
2006-04-17 22:02:51 +02:00
|
|
|
pos.Y = float(height*HEIGHT_SCALE);
|
2012-01-12 13:51:10 +01:00
|
|
|
pos.Z = float(j*TERRAIN_TILE_SIZE);
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CalcPositionFixed: calculate the world space position of the vertex at (i,j)
|
|
|
|
void CTerrain::CalcPositionFixed(ssize_t i, ssize_t j, CFixedVector3D& pos) const
|
|
|
|
{
|
2011-03-04 21:02:05 +01:00
|
|
|
ssize_t hi = clamp(i, (ssize_t)0, m_MapSize-1);
|
|
|
|
ssize_t hj = clamp(j, (ssize_t)0, m_MapSize-1);
|
|
|
|
u16 height = m_Heightmap[hj*m_MapSize + hi];
|
2012-01-12 13:51:10 +01:00
|
|
|
pos.X = fixed::FromInt(i) * (int)TERRAIN_TILE_SIZE;
|
2010-05-02 22:32:37 +02:00
|
|
|
pos.Y = fixed::FromInt(height) / (int)HEIGHT_UNITS_PER_METRE;
|
2012-01-12 13:51:10 +01:00
|
|
|
pos.Z = fixed::FromInt(j) * (int)TERRAIN_TILE_SIZE;
|
2010-01-09 20:20:14 +01:00
|
|
|
}
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CalcNormal: calculate the world space normal of the vertex at (i,j)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
void CTerrain::CalcNormal(ssize_t i, ssize_t j, CVector3D& normal) const
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
|
|
|
CVector3D left, right, up, down;
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
// Calculate normals of the four half-tile triangles surrounding this vertex:
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
// get position of vertex where normal is being evaluated
|
|
|
|
CVector3D basepos;
|
2010-01-09 20:20:14 +01:00
|
|
|
CalcPosition(i, j, basepos);
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
if (i > 0) {
|
|
|
|
CalcPosition(i-1, j, left);
|
|
|
|
left -= basepos;
|
|
|
|
left.Normalize();
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
if (i < m_MapSize-1) {
|
|
|
|
CalcPosition(i+1, j, right);
|
|
|
|
right -= basepos;
|
|
|
|
right.Normalize();
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
if (j > 0) {
|
|
|
|
CalcPosition(i, j-1, up);
|
|
|
|
up -= basepos;
|
|
|
|
up.Normalize();
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
if (j < m_MapSize-1) {
|
|
|
|
CalcPosition(i, j+1, down);
|
|
|
|
down -= basepos;
|
|
|
|
down.Normalize();
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CVector3D n0 = up.Cross(left);
|
|
|
|
CVector3D n1 = left.Cross(down);
|
|
|
|
CVector3D n2 = down.Cross(right);
|
2006-02-13 15:18:20 +01:00
|
|
|
CVector3D n3 = right.Cross(up);
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
// Compute the mean of the normals
|
2004-05-29 22:56:24 +02:00
|
|
|
normal = n0 + n1 + n2 + n3;
|
2007-05-02 14:07:08 +02:00
|
|
|
float nlen=normal.Length();
|
2004-05-29 22:56:24 +02:00
|
|
|
if (nlen>0.00001f) normal*=1.0f/nlen;
|
|
|
|
}
|
|
|
|
|
2010-01-09 20:20:14 +01:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CalcNormalFixed: calculate the world space normal of the vertex at (i,j)
|
|
|
|
void CTerrain::CalcNormalFixed(ssize_t i, ssize_t j, CFixedVector3D& normal) const
|
|
|
|
{
|
|
|
|
CFixedVector3D left, right, up, down;
|
|
|
|
|
|
|
|
// Calculate normals of the four half-tile triangles surrounding this vertex:
|
|
|
|
|
|
|
|
// get position of vertex where normal is being evaluated
|
|
|
|
CFixedVector3D basepos;
|
|
|
|
CalcPositionFixed(i, j, basepos);
|
|
|
|
|
|
|
|
if (i > 0) {
|
|
|
|
CalcPositionFixed(i-1, j, left);
|
|
|
|
left -= basepos;
|
|
|
|
left.Normalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i < m_MapSize-1) {
|
|
|
|
CalcPositionFixed(i+1, j, right);
|
|
|
|
right -= basepos;
|
|
|
|
right.Normalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j > 0) {
|
|
|
|
CalcPositionFixed(i, j-1, up);
|
|
|
|
up -= basepos;
|
|
|
|
up.Normalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (j < m_MapSize-1) {
|
|
|
|
CalcPositionFixed(i, j+1, down);
|
|
|
|
down -= basepos;
|
|
|
|
down.Normalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
CFixedVector3D n0 = up.Cross(left);
|
|
|
|
CFixedVector3D n1 = left.Cross(down);
|
|
|
|
CFixedVector3D n2 = down.Cross(right);
|
|
|
|
CFixedVector3D n3 = right.Cross(up);
|
|
|
|
|
|
|
|
// Compute the mean of the normals
|
|
|
|
normal = n0 + n1 + n2 + n3;
|
|
|
|
normal.Normalize();
|
|
|
|
}
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2011-07-30 02:56:45 +02:00
|
|
|
CVector3D CTerrain::CalcExactNormal(float x, float z) const
|
|
|
|
{
|
|
|
|
// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
|
2012-01-12 13:51:10 +01:00
|
|
|
const ssize_t xi = clamp((ssize_t)floor(x/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);
|
|
|
|
const ssize_t zi = clamp((ssize_t)floor(z/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);
|
2011-07-30 02:56:45 +02:00
|
|
|
|
2012-01-12 13:51:10 +01:00
|
|
|
const float xf = clamp(x/TERRAIN_TILE_SIZE-xi, 0.0f, 1.0f);
|
|
|
|
const float zf = clamp(z/TERRAIN_TILE_SIZE-zi, 0.0f, 1.0f);
|
2011-07-30 02:56:45 +02:00
|
|
|
|
|
|
|
float h00 = m_Heightmap[zi*m_MapSize + xi];
|
|
|
|
float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
|
|
|
|
float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
|
|
|
|
float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
|
|
|
|
|
|
|
|
// Determine which terrain triangle this point is on,
|
|
|
|
// then compute the normal of that triangle's plane
|
|
|
|
|
|
|
|
if (GetTriangulationDir(xi, zi))
|
|
|
|
{
|
|
|
|
if (xf + zf <= 1.f)
|
|
|
|
{
|
|
|
|
// Lower-left triangle (don't use h11)
|
2012-01-12 13:51:10 +01:00
|
|
|
return -CVector3D(TERRAIN_TILE_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();
|
2011-07-30 02:56:45 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Upper-right triangle (don't use h00)
|
2012-01-12 13:51:10 +01:00
|
|
|
return -CVector3D(TERRAIN_TILE_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();
|
2011-07-30 02:56:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (xf <= zf)
|
|
|
|
{
|
|
|
|
// Upper-left triangle (don't use h10)
|
2012-01-12 13:51:10 +01:00
|
|
|
return -CVector3D(TERRAIN_TILE_SIZE, (h11-h01)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h01-h00)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();
|
2011-07-30 02:56:45 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Lower-right triangle (don't use h01)
|
2012-01-12 13:51:10 +01:00
|
|
|
return -CVector3D(TERRAIN_TILE_SIZE, (h10-h00)*HEIGHT_SCALE, 0).Cross(CVector3D(0, (h11-h10)*HEIGHT_SCALE, TERRAIN_TILE_SIZE)).Normalized();
|
2011-07-30 02:56:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2006-04-17 22:02:51 +02:00
|
|
|
// GetPatch: return the patch at (i,j) in patch space, or null if the patch is
|
2004-05-29 22:56:24 +02:00
|
|
|
// out of bounds
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
CPatch* CTerrain::GetPatch(ssize_t i, ssize_t j) const
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
2009-11-08 17:49:52 +01:00
|
|
|
// range check (invalid indices are passed in by the culling and
|
|
|
|
// patch blend code because they iterate from 0..#patches and examine
|
|
|
|
// neighbors without checking if they're already on the edge)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
if( (size_t)i >= (size_t)m_MapSizePatches || (size_t)j >= (size_t)m_MapSizePatches )
|
2006-08-26 22:25:37 +02:00
|
|
|
return 0;
|
|
|
|
|
2006-04-17 22:02:51 +02:00
|
|
|
return &m_Patches[(j*m_MapSizePatches)+i];
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2009-11-08 17:49:52 +01:00
|
|
|
// GetTile: return the tile at (i,j) in tile space, or null if the tile is out
|
2004-05-29 22:56:24 +02:00
|
|
|
// of bounds
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
CMiniPatch* CTerrain::GetTile(ssize_t i, ssize_t j) const
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
2009-11-08 17:49:52 +01:00
|
|
|
// see comment above
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
if( (size_t)i >= (size_t)(m_MapSize-1) || (size_t)j >= (size_t)(m_MapSize-1) )
|
2006-08-26 22:25:37 +02:00
|
|
|
return 0;
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2009-11-08 17:49:52 +01:00
|
|
|
CPatch* patch=GetPatch(i/PATCH_SIZE, j/PATCH_SIZE); // can't fail (due to above check)
|
2006-04-17 22:02:51 +02:00
|
|
|
return &patch->m_MiniPatches[j%PATCH_SIZE][i%PATCH_SIZE];
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
float CTerrain::GetVertexGroundLevel(ssize_t i, ssize_t j) const
|
2006-02-13 15:18:20 +01:00
|
|
|
{
|
2009-12-12 18:52:38 +01:00
|
|
|
i = clamp(i, (ssize_t)0, m_MapSize-1);
|
|
|
|
j = clamp(j, (ssize_t)0, m_MapSize-1);
|
2006-04-17 22:02:51 +02:00
|
|
|
return HEIGHT_SCALE * m_Heightmap[j*m_MapSize + i];
|
2005-10-09 05:43:03 +02:00
|
|
|
}
|
|
|
|
|
2010-05-28 01:31:03 +02:00
|
|
|
fixed CTerrain::GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const
|
2006-04-09 02:36:52 +02:00
|
|
|
{
|
2010-05-28 01:31:03 +02:00
|
|
|
i = clamp(i, (ssize_t)0, m_MapSize-1);
|
|
|
|
j = clamp(j, (ssize_t)0, m_MapSize-1);
|
|
|
|
// Convert to fixed metres (being careful to avoid intermediate overflows)
|
|
|
|
return fixed::FromInt(m_Heightmap[j*m_MapSize + i] / 2) / (int)(HEIGHT_UNITS_PER_METRE / 2);
|
|
|
|
}
|
2006-04-09 02:36:52 +02:00
|
|
|
|
2010-05-28 01:31:03 +02:00
|
|
|
fixed CTerrain::GetSlopeFixed(ssize_t i, ssize_t j) const
|
|
|
|
{
|
|
|
|
// Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)
|
|
|
|
i = clamp(i, (ssize_t)0, m_MapSize-2);
|
|
|
|
j = clamp(j, (ssize_t)0, m_MapSize-2);
|
|
|
|
|
|
|
|
u16 h00 = m_Heightmap[j*m_MapSize + i];
|
|
|
|
u16 h01 = m_Heightmap[(j+1)*m_MapSize + i];
|
|
|
|
u16 h10 = m_Heightmap[j*m_MapSize + (i+1)];
|
|
|
|
u16 h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];
|
2006-04-22 07:52:23 +02:00
|
|
|
|
2009-12-12 18:52:38 +01:00
|
|
|
// Difference of highest point from lowest point
|
2010-05-28 01:31:03 +02:00
|
|
|
u16 delta = std::max(std::max(h00, h01), std::max(h10, h11)) -
|
|
|
|
std::min(std::min(h00, h01), std::min(h10, h11));
|
|
|
|
|
|
|
|
// Compute fractional slope (being careful to avoid intermediate overflows)
|
2012-01-12 13:51:10 +01:00
|
|
|
return fixed::FromInt(delta / TERRAIN_TILE_SIZE) / (int)HEIGHT_UNITS_PER_METRE;
|
2006-04-09 02:36:52 +02:00
|
|
|
}
|
2009-11-08 17:49:52 +01:00
|
|
|
|
2012-04-18 22:39:00 +02:00
|
|
|
float CTerrain::GetFilteredGroundLevel(float x, float z, float radius) const
|
|
|
|
{
|
|
|
|
// convert to [0,1] interval
|
|
|
|
float nx = x / (TERRAIN_TILE_SIZE*m_MapSize);
|
|
|
|
float nz = z / (TERRAIN_TILE_SIZE*m_MapSize);
|
|
|
|
float nr = radius / (TERRAIN_TILE_SIZE*m_MapSize);
|
|
|
|
|
|
|
|
// get trilinear filtered mipmap height
|
|
|
|
return HEIGHT_SCALE * m_HeightMipmap.GetTrilinearGroundLevel(nx, nz, nr);
|
|
|
|
}
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
float CTerrain::GetExactGroundLevel(float x, float z) const
|
2004-07-23 12:56:52 +02:00
|
|
|
{
|
2009-12-12 18:52:38 +01:00
|
|
|
// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
|
2012-01-12 13:51:10 +01:00
|
|
|
const ssize_t xi = clamp((ssize_t)floor(x/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);
|
|
|
|
const ssize_t zi = clamp((ssize_t)floor(z/TERRAIN_TILE_SIZE), (ssize_t)0, m_MapSize-2);
|
2009-12-12 18:52:38 +01:00
|
|
|
|
2012-01-12 13:51:10 +01:00
|
|
|
const float xf = clamp(x/TERRAIN_TILE_SIZE-xi, 0.0f, 1.0f);
|
|
|
|
const float zf = clamp(z/TERRAIN_TILE_SIZE-zi, 0.0f, 1.0f);
|
2005-10-07 23:09:29 +02:00
|
|
|
|
2006-04-17 22:02:51 +02:00
|
|
|
float h00 = m_Heightmap[zi*m_MapSize + xi];
|
2009-12-12 18:52:38 +01:00
|
|
|
float h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
|
|
|
|
float h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
|
|
|
|
float h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
|
2011-07-30 02:56:45 +02:00
|
|
|
|
|
|
|
// Determine which terrain triangle this point is on,
|
|
|
|
// then compute the linearly-interpolated height on that triangle's plane
|
|
|
|
|
|
|
|
if (GetTriangulationDir(xi, zi))
|
|
|
|
{
|
|
|
|
if (xf + zf <= 1.f)
|
|
|
|
{
|
|
|
|
// Lower-left triangle (don't use h11)
|
|
|
|
return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h01-h00)*zf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Upper-right triangle (don't use h00)
|
|
|
|
return HEIGHT_SCALE * (h11 + (h01-h11)*(1-xf) + (h10-h11)*(1-zf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (xf <= zf)
|
|
|
|
{
|
|
|
|
// Upper-left triangle (don't use h10)
|
|
|
|
return HEIGHT_SCALE * (h00 + (h11-h01)*xf + (h01-h00)*zf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Lower-right triangle (don't use h01)
|
|
|
|
return HEIGHT_SCALE * (h00 + (h10-h00)*xf + (h11-h10)*zf);
|
|
|
|
}
|
|
|
|
}
|
2004-07-23 12:56:52 +02:00
|
|
|
}
|
2004-05-29 22:56:24 +02:00
|
|
|
|
2010-05-02 22:32:37 +02:00
|
|
|
fixed CTerrain::GetExactGroundLevelFixed(fixed x, fixed z) const
|
2010-04-06 19:31:38 +02:00
|
|
|
{
|
|
|
|
// Clamp to size-2 so we can use the tiles (xi,zi)-(xi+1,zi+1)
|
2012-01-12 13:51:10 +01:00
|
|
|
const ssize_t xi = clamp((ssize_t)(x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
|
|
|
|
const ssize_t zi = clamp((ssize_t)(z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
|
2010-04-06 19:31:38 +02:00
|
|
|
|
2010-05-02 22:32:37 +02:00
|
|
|
const fixed one = fixed::FromInt(1);
|
2010-04-06 19:31:38 +02:00
|
|
|
|
2012-01-12 13:51:10 +01:00
|
|
|
const fixed xf = clamp((x / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(xi), fixed::Zero(), one);
|
|
|
|
const fixed zf = clamp((z / (int)TERRAIN_TILE_SIZE) - fixed::FromInt(zi), fixed::Zero(), one);
|
2010-04-06 19:31:38 +02:00
|
|
|
|
|
|
|
u16 h00 = m_Heightmap[zi*m_MapSize + xi];
|
|
|
|
u16 h01 = m_Heightmap[(zi+1)*m_MapSize + xi];
|
|
|
|
u16 h10 = m_Heightmap[zi*m_MapSize + (xi+1)];
|
|
|
|
u16 h11 = m_Heightmap[(zi+1)*m_MapSize + (xi+1)];
|
2010-05-02 22:32:37 +02:00
|
|
|
|
|
|
|
// Intermediate scaling of xf, so we don't overflow in the multiplications below
|
|
|
|
// (h00 <= 65535, xf <= 1, max fixed is < 32768; divide by 2 here so xf1*h00 <= 32767.5)
|
|
|
|
const fixed xf0 = xf / 2;
|
|
|
|
const fixed xf1 = (one - xf) / 2;
|
|
|
|
|
2010-04-06 19:31:38 +02:00
|
|
|
// Linearly interpolate
|
2010-05-02 22:32:37 +02:00
|
|
|
return ((one - zf).Multiply(xf1 * h00 + xf0 * h10)
|
|
|
|
+ zf.Multiply(xf1 * h01 + xf0 * h11)) / (int)(HEIGHT_UNITS_PER_METRE / 2);
|
2011-07-30 02:56:45 +02:00
|
|
|
|
|
|
|
// TODO: This should probably be more like GetExactGroundLevel()
|
|
|
|
// in handling triangulation properly
|
2010-04-06 19:31:38 +02:00
|
|
|
}
|
|
|
|
|
2011-03-13 20:10:38 +01:00
|
|
|
bool CTerrain::GetTriangulationDir(ssize_t i, ssize_t j) const
|
|
|
|
{
|
|
|
|
// Clamp to size-2 so we can use the tiles (i,j)-(i+1,j+1)
|
|
|
|
i = clamp(i, (ssize_t)0, m_MapSize-2);
|
|
|
|
j = clamp(j, (ssize_t)0, m_MapSize-2);
|
|
|
|
|
|
|
|
int h00 = m_Heightmap[j*m_MapSize + i];
|
|
|
|
int h01 = m_Heightmap[(j+1)*m_MapSize + i];
|
|
|
|
int h10 = m_Heightmap[j*m_MapSize + (i+1)];
|
|
|
|
int h11 = m_Heightmap[(j+1)*m_MapSize + (i+1)];
|
|
|
|
|
|
|
|
// Prefer triangulating in whichever direction means the midpoint of the diagonal
|
|
|
|
// will be the highest. (In particular this means a diagonal edge will be straight
|
|
|
|
// along the top, and jagged along the bottom, which makes sense for terrain.)
|
|
|
|
int mid1 = h00+h11;
|
|
|
|
int mid2 = h01+h10;
|
|
|
|
return (mid1 < mid2);
|
|
|
|
}
|
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Resize: resize this terrain to the given size (in patches per side)
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
void CTerrain::Resize(ssize_t size)
|
2004-05-29 22:56:24 +02:00
|
|
|
{
|
|
|
|
if (size==m_MapSizePatches) {
|
|
|
|
// inexplicable request to resize terrain to the same size .. ignore it
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_Heightmap) {
|
|
|
|
// not yet created a terrain; build a default terrain of the given size now
|
|
|
|
Initialize(size,0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate data for new terrain
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
ssize_t newMapSize=size*PATCH_SIZE+1;
|
2004-05-29 22:56:24 +02:00
|
|
|
u16* newHeightmap=new u16[newMapSize*newMapSize];
|
|
|
|
CPatch* newPatches=new CPatch[size*size];
|
|
|
|
|
|
|
|
if (size>m_MapSizePatches) {
|
|
|
|
// new map is bigger than old one - zero the heightmap so we don't get uninitialised
|
|
|
|
// height data along the expanded edges
|
2009-12-12 18:52:38 +01:00
|
|
|
memset(newHeightmap,0,newMapSize*newMapSize*sizeof(u16));
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// now copy over rows of data
|
2005-10-08 05:55:21 +02:00
|
|
|
u16* src=m_Heightmap;
|
2004-05-29 22:56:24 +02:00
|
|
|
u16* dst=newHeightmap;
|
2009-12-12 18:52:38 +01:00
|
|
|
ssize_t copysize=std::min(newMapSize, m_MapSize);
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (ssize_t j=0;j<copysize;j++) {
|
2010-11-01 12:09:03 +01:00
|
|
|
memcpy(dst,src,copysize*sizeof(u16));
|
2004-05-29 22:56:24 +02:00
|
|
|
dst+=copysize;
|
|
|
|
src+=m_MapSize;
|
|
|
|
if (newMapSize>m_MapSize) {
|
2009-11-08 17:49:52 +01:00
|
|
|
// extend the last height to the end of the row
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (size_t i=0;i<newMapSize-(size_t)m_MapSize;i++) {
|
2004-05-29 22:56:24 +02:00
|
|
|
*dst++=*(src-1);
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
}
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (newMapSize>m_MapSize) {
|
|
|
|
// copy over heights of the last row to any remaining rows
|
|
|
|
src=newHeightmap+((m_MapSize-1)*newMapSize);
|
|
|
|
dst=src+newMapSize;
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (ssize_t i=0;i<newMapSize-m_MapSize;i++) {
|
2010-11-01 12:09:03 +01:00
|
|
|
memcpy(dst,src,newMapSize*sizeof(u16));
|
2004-05-29 22:56:24 +02:00
|
|
|
dst+=newMapSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now build new patches
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (ssize_t j=0;j<size;j++) {
|
|
|
|
for (ssize_t i=0;i<size;i++) {
|
2004-05-29 22:56:24 +02:00
|
|
|
// copy over texture data from existing tiles, if possible
|
|
|
|
if (i<m_MapSizePatches && j<m_MapSizePatches) {
|
2010-11-01 12:09:03 +01:00
|
|
|
memcpy(newPatches[j*size+i].m_MiniPatches,m_Patches[j*m_MapSizePatches+i].m_MiniPatches,sizeof(CMiniPatch)*PATCH_SIZE*PATCH_SIZE);
|
2006-02-13 15:18:20 +01:00
|
|
|
}
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (j<m_MapSizePatches && size>m_MapSizePatches) {
|
|
|
|
// copy over the last tile from each column
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (ssize_t n=0;n<size-m_MapSizePatches;n++) {
|
|
|
|
for (ssize_t m=0;m<PATCH_SIZE;m++) {
|
2004-05-29 22:56:24 +02:00
|
|
|
CMiniPatch& src=m_Patches[j*m_MapSizePatches+m_MapSizePatches-1].m_MiniPatches[m][15];
|
2009-07-28 11:27:56 +02:00
|
|
|
for (ssize_t k=0;k<PATCH_SIZE;k++) {
|
2004-05-29 22:56:24 +02:00
|
|
|
CMiniPatch& dst=newPatches[j*size+m_MapSizePatches+n].m_MiniPatches[m][k];
|
2010-05-28 01:31:03 +02:00
|
|
|
dst = src;
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-02-13 15:18:20 +01:00
|
|
|
|
2004-05-29 22:56:24 +02:00
|
|
|
if (size>m_MapSizePatches) {
|
|
|
|
// copy over the last tile from each column
|
|
|
|
CPatch* srcpatch=&newPatches[(m_MapSizePatches-1)*size];
|
|
|
|
CPatch* dstpatch=srcpatch+size;
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
for (ssize_t p=0;p<(ssize_t)size-m_MapSizePatches;p++) {
|
|
|
|
for (ssize_t n=0;n<(ssize_t)size;n++) {
|
|
|
|
for (ssize_t m=0;m<PATCH_SIZE;m++) {
|
|
|
|
for (ssize_t k=0;k<PATCH_SIZE;k++) {
|
2004-05-29 22:56:24 +02:00
|
|
|
CMiniPatch& src=srcpatch->m_MiniPatches[15][k];
|
|
|
|
CMiniPatch& dst=dstpatch->m_MiniPatches[m][k];
|
2010-05-28 01:31:03 +02:00
|
|
|
dst = src;
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
srcpatch++;
|
|
|
|
dstpatch++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// release all the original data
|
|
|
|
ReleaseData();
|
|
|
|
|
|
|
|
// store new data
|
|
|
|
m_Heightmap=newHeightmap;
|
|
|
|
m_Patches=newPatches;
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
m_MapSize=(ssize_t)newMapSize;
|
|
|
|
m_MapSizePatches=(ssize_t)size;
|
2004-05-29 22:56:24 +02:00
|
|
|
|
|
|
|
// initialise all the new patches
|
|
|
|
InitialisePatches();
|
2012-04-18 22:39:00 +02:00
|
|
|
|
|
|
|
// initialise mipmap
|
|
|
|
m_HeightMipmap.Initialize(m_MapSize,m_Heightmap);
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// InitialisePatches: initialise patch data
|
|
|
|
void CTerrain::InitialisePatches()
|
|
|
|
{
|
2012-04-18 00:35:34 +02:00
|
|
|
for (ssize_t j = 0; j < m_MapSizePatches; j++)
|
|
|
|
{
|
|
|
|
for (ssize_t i = 0; i < m_MapSizePatches; i++)
|
|
|
|
{
|
|
|
|
CPatch* patch = GetPatch(i, j); // can't fail
|
|
|
|
patch->Initialize(this, i, j);
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2006-02-13 15:18:20 +01:00
|
|
|
// SetHeightMap: set up a new heightmap from 16-bit source data;
|
2004-05-29 22:56:24 +02:00
|
|
|
// assumes heightmap matches current terrain size
|
|
|
|
void CTerrain::SetHeightMap(u16* heightmap)
|
|
|
|
{
|
|
|
|
// keep a copy of the given heightmap
|
2012-04-18 00:35:34 +02:00
|
|
|
memcpy(m_Heightmap, heightmap, m_MapSize*m_MapSize*sizeof(u16));
|
2004-05-29 22:56:24 +02:00
|
|
|
|
|
|
|
// recalculate patch bounds, invalidate vertices
|
2012-04-18 00:35:34 +02:00
|
|
|
for (ssize_t j = 0; j < m_MapSizePatches; j++)
|
|
|
|
{
|
|
|
|
for (ssize_t i = 0; i < m_MapSizePatches; i++)
|
|
|
|
{
|
|
|
|
CPatch* patch = GetPatch(i, j); // can't fail
|
2005-02-10 00:19:48 +01:00
|
|
|
patch->InvalidateBounds();
|
2004-05-29 22:56:24 +02:00
|
|
|
patch->SetDirty(RENDERDATA_UPDATE_VERTICES);
|
|
|
|
}
|
|
|
|
}
|
2012-04-18 22:39:00 +02:00
|
|
|
|
|
|
|
// update mipmap
|
|
|
|
m_HeightMipmap.Update(m_Heightmap);
|
2004-05-29 22:56:24 +02:00
|
|
|
}
|
2004-10-06 20:45:59 +02:00
|
|
|
|
|
|
|
|
2005-07-03 18:25:48 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
had to remove uint and ulong from lib/types.h due to conflict with other library.
this snowballed into a massive search+destroy of the hodgepodge of
mostly equivalent types we had in use (int, uint, unsigned, unsigned
int, i32, u32, ulong, uintN).
it is more efficient to use 64-bit types in 64-bit mode, so the
preferred default is size_t (for anything remotely resembling a size or
index). tile coordinates are ssize_t to allow more efficient conversion
to/from floating point. flags are int because we almost never need more
than 15 distinct bits, bit test/set is not slower and int is fastest to
type. finally, some data that is pretty much directly passed to OpenGL
is now typed accordingly.
after several hours, the code now requires fewer casts and less
guesswork.
other changes:
- unit and player IDs now have an "invalid id" constant in the
respective class to avoid casting and -1
- fix some endian/64-bit bugs in the map (un)packing. added a
convenience function to write/read a size_t.
- ia32: change CPUID interface to allow passing in ecx (required for
cache topology detection, which I need at work). remove some unneeded
functions from asm, replace with intrinsics where possible.
This was SVN commit r5942.
2008-05-11 20:48:32 +02:00
|
|
|
void CTerrain::MakeDirty(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1, int dirtyFlags)
|
2005-07-03 18:25:48 +02:00
|
|
|
{
|
2012-04-18 00:35:34 +02:00
|
|
|
// Finds the inclusive limits of the patches that include the specified range of tiles
|
|
|
|
ssize_t pi0 = clamp( i0 /PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);
|
|
|
|
ssize_t pi1 = clamp((i1-1)/PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);
|
|
|
|
ssize_t pj0 = clamp( j0 /PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);
|
|
|
|
ssize_t pj1 = clamp((j1-1)/PATCH_SIZE, (ssize_t)0, m_MapSizePatches-1);
|
|
|
|
|
|
|
|
for (ssize_t j = pj0; j <= pj1; j++)
|
|
|
|
{
|
|
|
|
for (ssize_t i = pi0; i <= pi1; i++)
|
|
|
|
{
|
|
|
|
CPatch* patch = GetPatch(i, j); // can't fail (i,j were clamped)
|
2005-11-06 06:05:07 +01:00
|
|
|
if (dirtyFlags & RENDERDATA_UPDATE_VERTICES)
|
|
|
|
patch->CalcBounds();
|
|
|
|
patch->SetDirty(dirtyFlags);
|
2005-07-03 18:25:48 +02:00
|
|
|
}
|
|
|
|
}
|
2012-04-18 22:39:00 +02:00
|
|
|
|
|
|
|
if (m_Heightmap)
|
2012-04-21 15:07:47 +02:00
|
|
|
{
|
|
|
|
m_HeightMipmap.Update(m_Heightmap,
|
|
|
|
clamp(i0, (ssize_t)0, m_MapSize-1),
|
|
|
|
clamp(j0, (ssize_t)0, m_MapSize-1),
|
|
|
|
clamp(i1, (ssize_t)1, m_MapSize),
|
|
|
|
clamp(j1, (ssize_t)1, m_MapSize)
|
|
|
|
);
|
|
|
|
}
|
2005-07-03 18:25:48 +02:00
|
|
|
}
|
2005-11-05 05:59:54 +01:00
|
|
|
|
2005-11-06 06:05:07 +01:00
|
|
|
void CTerrain::MakeDirty(int dirtyFlags)
|
2005-11-05 05:59:54 +01:00
|
|
|
{
|
2012-04-18 00:35:34 +02:00
|
|
|
for (ssize_t j = 0; j < m_MapSizePatches; j++)
|
|
|
|
{
|
|
|
|
for (ssize_t i = 0; i < m_MapSizePatches; i++)
|
|
|
|
{
|
|
|
|
CPatch* patch = GetPatch(i, j); // can't fail
|
2005-11-06 06:05:07 +01:00
|
|
|
if (dirtyFlags & RENDERDATA_UPDATE_VERTICES)
|
|
|
|
patch->CalcBounds();
|
|
|
|
patch->SetDirty(dirtyFlags);
|
2005-11-05 05:59:54 +01:00
|
|
|
}
|
|
|
|
}
|
2012-04-18 22:39:00 +02:00
|
|
|
|
|
|
|
if (m_Heightmap)
|
|
|
|
m_HeightMipmap.Update(m_Heightmap);
|
2005-11-05 05:59:54 +01:00
|
|
|
}
|
2011-03-13 20:22:05 +01:00
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
CBoundingBoxAligned CTerrain::GetVertexesBound(ssize_t i0, ssize_t j0, ssize_t i1, ssize_t j1)
|
2011-03-13 20:22:05 +01:00
|
|
|
{
|
|
|
|
i0 = clamp(i0, (ssize_t)0, m_MapSize-1);
|
|
|
|
j0 = clamp(j0, (ssize_t)0, m_MapSize-1);
|
|
|
|
i1 = clamp(i1, (ssize_t)0, m_MapSize-1);
|
|
|
|
j1 = clamp(j1, (ssize_t)0, m_MapSize-1);
|
|
|
|
|
|
|
|
u16 minH = 65535;
|
|
|
|
u16 maxH = 0;
|
|
|
|
|
|
|
|
for (ssize_t j = j0; j <= j1; ++j)
|
|
|
|
{
|
|
|
|
for (ssize_t i = i0; i <= i1; ++i)
|
|
|
|
{
|
|
|
|
minH = std::min(minH, m_Heightmap[j*m_MapSize + i]);
|
|
|
|
maxH = std::max(maxH, m_Heightmap[j*m_MapSize + i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-25 07:36:13 +01:00
|
|
|
CBoundingBoxAligned bound;
|
2012-01-12 13:51:10 +01:00
|
|
|
bound[0].X = (float)(i0*TERRAIN_TILE_SIZE);
|
2011-03-13 20:22:05 +01:00
|
|
|
bound[0].Y = (float)(minH*HEIGHT_SCALE);
|
2012-01-12 13:51:10 +01:00
|
|
|
bound[0].Z = (float)(j0*TERRAIN_TILE_SIZE);
|
|
|
|
bound[1].X = (float)(i1*TERRAIN_TILE_SIZE);
|
2011-03-13 20:22:05 +01:00
|
|
|
bound[1].Y = (float)(maxH*HEIGHT_SCALE);
|
2012-01-12 13:51:10 +01:00
|
|
|
bound[1].Z = (float)(j1*TERRAIN_TILE_SIZE);
|
2011-03-13 20:22:05 +01:00
|
|
|
return bound;
|
|
|
|
}
|