commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
468
include/engine/core/terrain.cpp
Normal file
468
include/engine/core/terrain.cpp
Normal file
@ -0,0 +1,468 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "core/terrain.h"
|
||||
#include "core/geometry.h"
|
||||
#include "core/resource_factories.h"
|
||||
#include "core/render_resource_factory.h"
|
||||
#include "picture/pict.h"
|
||||
#include "picture/pict_io.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::Core;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::RenderSetup(ResourceFactories *f)
|
||||
{
|
||||
if (LoadHeightmap(heightmap_path))
|
||||
{
|
||||
ComputeNormals();
|
||||
BuildQuadtree();
|
||||
}
|
||||
|
||||
ComputeNormals();
|
||||
BuildQuadtree();
|
||||
|
||||
if (render_data = new RenderData)
|
||||
if (f && f->render)
|
||||
{
|
||||
render_data->blendmap = f->render->LoadTexture(blendmap_path);
|
||||
render_data->material = f->render->LoadMaterial(material);
|
||||
|
||||
for (uint n = 0; n < 4; ++n)
|
||||
{
|
||||
render_data->layer[n].diffuse = f->render->LoadTexture(layer[n].diffuse);
|
||||
render_data->layer[n].specular = f->render->LoadTexture(layer[n].specular);
|
||||
render_data->layer[n].normal = f->render->LoadTexture(layer[n].normal);
|
||||
render_data->layer[n].self = f->render->LoadTexture(layer[n].self);
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::LocalToTexture(const Vector4 &p, float w, float h, float &u, float &v) const
|
||||
{
|
||||
float lx = p.x + width * 0.5f,
|
||||
lz = p.z + depth * 0.5f;
|
||||
|
||||
u = (lx / width) * w;
|
||||
v = (lz / depth) * h;
|
||||
|
||||
return asbool((u >= 0) && (u < w) && (v >= 0) && (v < h));
|
||||
}
|
||||
bool Terrain::LocalToHeightmap(const Vector4 &p, float &u, float &v) const
|
||||
{
|
||||
return LocalToTexture(p, (float)heightmap_w, (float)heightmap_h, u, v);
|
||||
}
|
||||
float Terrain::SampleHeight(float u, float v) const
|
||||
{
|
||||
// Fetch samples coordinates.
|
||||
int s_u[4], s_v[4];
|
||||
s_u[0] = Types::Clamp(int(u), 0, heightmap_w); s_v[0] = Types::Clamp(int(v), 0, heightmap_h);
|
||||
s_u[1] = Types::Clamp(s_u[0] + 1, 0, heightmap_w); s_v[1] = Types::Clamp(s_v[0] + 0, 0, heightmap_h);
|
||||
s_u[2] = Types::Clamp(s_u[0] + 0, 0, heightmap_w); s_v[2] = Types::Clamp(s_v[0] + 1, 0, heightmap_h);
|
||||
s_u[3] = Types::Clamp(s_u[0] + 1, 0, heightmap_w); s_v[3] = Types::Clamp(s_v[0] + 1, 0, heightmap_h);
|
||||
|
||||
// Fetch samples.
|
||||
float s[4];
|
||||
for (int n = 0; n < 4; ++n)
|
||||
s[n] = heightmap[s_v[n] * GetHeightmapPitch() + s_u[n]];
|
||||
|
||||
// Bilinear.
|
||||
float k_u = u - int(u), k_v = v - (int)v;
|
||||
return (s[0] * (1 - k_u) + s[1] * k_u) * (1 - k_v) + (s[2] * (1 - k_u) + s[3] * k_u) * k_v;
|
||||
}
|
||||
float Terrain::SampleHeight(const Vector4 &p) const
|
||||
{
|
||||
float u, v;
|
||||
if (!LocalToHeightmap(p, u, v))
|
||||
return 0;
|
||||
return SampleHeight(u, v);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ComputeRenderableMinMax(MinMax &mm)
|
||||
{
|
||||
if (root_node)
|
||||
{
|
||||
OBB obb(root_node->minmax);
|
||||
obb.Transform(GetMatrix());
|
||||
obb.ComputeMinMax(mm);
|
||||
}
|
||||
}
|
||||
void Terrain::CullRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context context, Patch *node)
|
||||
{
|
||||
MinMax &minmax = node->minmax;
|
||||
if (view.frustum.ClassifyMinMax(minmax, &GetMatrix()) == Frustum::Outside)
|
||||
return;
|
||||
|
||||
// Check error for the current level.
|
||||
Vector4 center = minmax.GetCenter() * GetMatrix();
|
||||
|
||||
float lod_d = (minmax.mx - minmax.mn).Len() * 1.5f,
|
||||
/*v_d = nVector::Dist(center, view.GetMatrix().GetRow(3)),*/
|
||||
d_d = Vector4::Dist(center, default_view.GetMatrix().GetRow(3));
|
||||
|
||||
float d = d_d;// Types::Min(v_d, d_d); // Ensure high-resolution close to the viewer and the light source.
|
||||
|
||||
if ((d > lod_d) || !node->children[0])
|
||||
list.Push(new Render::Primitive(node, this, 0));
|
||||
else
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (node->children[n])
|
||||
CullRenderablePrimitiveList(view, default_view, list, context, node->children[n]);
|
||||
}
|
||||
uint Terrain::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Context context, bool nUnused(cull))
|
||||
{
|
||||
if (root_node)
|
||||
CullRenderablePrimitiveList(view, default_view, list, context, root_node);
|
||||
return node_count;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Geometry *Terrain::CreateNodeGeometry(const Patch *node)
|
||||
{
|
||||
Geometry *geometry = new Geometry;
|
||||
|
||||
int patch_w = node->w / node->decimation,
|
||||
patch_h = node->h / node->decimation;
|
||||
|
||||
// Create vertices.
|
||||
geometry->vtx.Allocate((patch_w + 1) * (patch_h + 1));
|
||||
|
||||
float step_w = width / heightmap_w, step_h = depth / heightmap_h;
|
||||
|
||||
Vector4 p(node->u * step_w * GetUnit(), 0, -node->v * step_h * GetUnit()),
|
||||
*pv = geometry->vtx;
|
||||
|
||||
for (int _v = node->v; _v <= (node->v + node->h); _v += node->decimation)
|
||||
{
|
||||
float *ph = &heightmap[_v * heightmap_w + node->u];
|
||||
|
||||
for (int _u = node->u; _u <= (node->u + node->w); _u += node->decimation)
|
||||
{
|
||||
pv->Set(p.x, ph[0], p.z);
|
||||
pv++;
|
||||
|
||||
ph += node->decimation;
|
||||
p.x += step_w * node->decimation * GetUnit();
|
||||
}
|
||||
p.x = node->u * step_w * GetUnit();
|
||||
p.z += step_h * node->decimation * GetUnit();
|
||||
}
|
||||
|
||||
// Create polygons.
|
||||
geometry->AllocatePolygon(patch_w * patch_h);
|
||||
for (uint n = 0; n < geometry->pol.GetCount(); ++n)
|
||||
{
|
||||
geometry->pol[n].vtx_count = 4;
|
||||
geometry->pol[n].material = 0;
|
||||
}
|
||||
geometry->AllocatePolygonBinding();
|
||||
|
||||
Polygon *polygon = geometry->pol;
|
||||
for (int _v = 0; _v < patch_h; ++_v)
|
||||
for (int _u = 0; _u < patch_w; ++_u)
|
||||
{
|
||||
polygon->vtx_count = 4;
|
||||
polygon->material = 0;
|
||||
|
||||
polygon->binding[0] = _v * (patch_w + 1) + _u;
|
||||
polygon->binding[1] = _v * (patch_w + 1) + _u + 1;
|
||||
polygon->binding[2] = (_v + 1) * (patch_w + 1) + _u + 1;
|
||||
polygon->binding[3] = (_v + 1) * (patch_w + 1) + _u;
|
||||
|
||||
polygon++;
|
||||
}
|
||||
|
||||
geometry->ComputeVertexNormal();
|
||||
return geometry;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::UpdatePatchMinMax(Patch *node) const
|
||||
{
|
||||
float *ph = &heightmap[node->v * GetHeightmapPitch() + node->u];
|
||||
float min_height, max_height;
|
||||
|
||||
min_height = max_height = ph[0];
|
||||
for (int _v = 0; _v < node->h; _v += node->decimation)
|
||||
{
|
||||
for (int _u = 0; _u < node->w; _u += node->decimation)
|
||||
{
|
||||
if (ph[_u] < min_height)
|
||||
min_height = ph[_u];
|
||||
if (ph[_u] > max_height)
|
||||
max_height = ph[_u];
|
||||
}
|
||||
ph += GetHeightmapPitch() * node->decimation;
|
||||
}
|
||||
|
||||
node->minmax.mn.Set(node->u * GetUnit() - width * 0.5f, min_height, (node->v + node->h) * GetUnit() - depth * 0.5f);
|
||||
node->minmax.mx.Set((node->u + node->w) * GetUnit() - width * 0.5f, max_height, node->v * GetUnit() - depth * 0.5f);
|
||||
}
|
||||
Patch *Terrain::BuildTerrainStaticQuadtree(int u, int v, int w, int h, int decimation, int tree_depth)
|
||||
{
|
||||
if (!decimation || (tree_depth == 8))
|
||||
return NULL;
|
||||
if (!w || !h)
|
||||
return NULL;
|
||||
|
||||
// Create a new node.
|
||||
Patch *node = new Patch;
|
||||
|
||||
node->terrain = this;
|
||||
node->u = u; node->v = v;
|
||||
node->w = w; node->h = h;
|
||||
node->decimation = decimation;
|
||||
|
||||
// Create node geometry (helper function, should normally be done on the fly by the renderer).
|
||||
// CreateNodeGeometry(node);
|
||||
|
||||
// Update patch minmax.
|
||||
UpdatePatchMinMax(node);
|
||||
|
||||
// Split to create children.
|
||||
int hw = w / 2, hh = h / 2;
|
||||
|
||||
node->children[0] = BuildTerrainStaticQuadtree(node->u, node->v, hw, hh, decimation / 2, tree_depth + 1);
|
||||
node->children[1] = BuildTerrainStaticQuadtree(node->u + hw, node->v, node->w - hw, hh, decimation / 2, tree_depth + 1);
|
||||
node->children[2] = BuildTerrainStaticQuadtree(node->u, node->v + hh, hw, node->h - hh, decimation / 2, tree_depth + 1);
|
||||
node->children[3] = BuildTerrainStaticQuadtree(node->u + hw, node->v + hh, node->w - hw, node->h - hh, decimation / 2, tree_depth + 1);
|
||||
|
||||
node_count++;
|
||||
return node;
|
||||
}
|
||||
Patch *Terrain::BuildQuadtree()
|
||||
{
|
||||
node_count = 0;
|
||||
return root_node = BuildTerrainStaticQuadtree(0, 0, heightmap_w, heightmap_h, heightmap_w / 64, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::UpdateQuadtreePatch(Patch *patch, iRect &update_rect)
|
||||
{
|
||||
iRect patch_rect = patch->GetRect();
|
||||
|
||||
if (!patch_rect.Intersect(update_rect))
|
||||
return;
|
||||
|
||||
UpdatePatchMinMax(patch);
|
||||
for (int n = 0; n < 4; ++n)
|
||||
if (patch->children[n])
|
||||
UpdateQuadtreePatch(patch->children[n], update_rect);
|
||||
}
|
||||
void Terrain::UpdateQuadtree(int u, int v, int w, int h)
|
||||
{
|
||||
iRect update_rect(u, v, u + w, v + h);
|
||||
UpdateQuadtreePatch(root_node, update_rect);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ComputeNormals(int u, int v, int w, int h)
|
||||
{
|
||||
if (!attribmap)
|
||||
return;
|
||||
|
||||
if (!u && !v && !w && !h)
|
||||
{
|
||||
w = heightmap_w;
|
||||
h = heightmap_h;
|
||||
}
|
||||
|
||||
u = Types::Clamp(u, 0, heightmap_w);
|
||||
v = Types::Clamp(v, 0, heightmap_h);
|
||||
|
||||
if (u + w > heightmap_w) w = heightmap_w - u;
|
||||
if (v + h > heightmap_h) h = heightmap_h - v;
|
||||
|
||||
float *ph = &heightmap[v * GetHeightmapPitch() + u];
|
||||
Attrib *pv = &attribmap[v * GetHeightmapPitch() + u];
|
||||
|
||||
for (int _v = 0; _v < h; ++_v)
|
||||
{
|
||||
float *sh = ph;
|
||||
Attrib *sv = pv;
|
||||
for (int _u = 0; _u < w; ++_u)
|
||||
{
|
||||
Vector4 vu(unit, sh[1] - sh[0], 0),
|
||||
vv(0, sh[GetHeightmapPitch()] - sh[0], unit),
|
||||
n = vv.Normalized().Cross(vu.Normalized());
|
||||
|
||||
sv[0].nx = (char)(n.x * 127.f);
|
||||
sv[0].ny = (char)(n.y * 127.f);
|
||||
sv[0].nz = (char)(n.z * 127.f);
|
||||
++sh;
|
||||
++sv;
|
||||
}
|
||||
pv += GetHeightmapPitch();
|
||||
ph += GetHeightmapPitch();
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::Allocate(uint w_res, uint h_res, float _unit)
|
||||
{
|
||||
Free();
|
||||
|
||||
// Global heightmap.
|
||||
heightmap_w = w_res;
|
||||
heightmap_h = h_res;
|
||||
|
||||
if (!attribmap.Allocate((heightmap_w + 1) * (heightmap_h + 1)) || !heightmap.Allocate((heightmap_w + 1) * (heightmap_h + 1)))
|
||||
__ERR__(__LOG_E__ << "Failed to allocate terrain heightmap.\n", false)
|
||||
|
||||
Memory::Set(&attribmap[0], 0, (heightmap_w + 1) * (heightmap_h + 1) * sizeof(Attrib));
|
||||
Memory::Set(&heightmap[0], 0, sizeof(float) * (heightmap_w + 1) * (heightmap_h + 1));
|
||||
|
||||
width = w_res * _unit;
|
||||
depth = h_res * _unit;
|
||||
unit = _unit;
|
||||
return true;
|
||||
}
|
||||
void Terrain::Free()
|
||||
{
|
||||
width = 0;
|
||||
depth = 0;
|
||||
attribmap.Free();
|
||||
heightmap.Free();
|
||||
heightmap_w = 0;
|
||||
heightmap_h = 0;
|
||||
node_count = 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Terrain::Raytrace(const Vector4 &w_s, const Vector4 &w_d, TraceResult &trace, float len)
|
||||
{
|
||||
trace.has_hit = false;
|
||||
|
||||
// Move ray to terrain space.
|
||||
Vector4 s = w_s * GetInverseMatrix(),
|
||||
d = w_d * GetRotationMatrix().Transposed();
|
||||
|
||||
// Clip ray against the terrain bounding box.
|
||||
float tmin, tmax;
|
||||
if (!root_node->minmax.IntersectRay(s, d, tmin, tmax))
|
||||
return false;
|
||||
|
||||
tmin -= 0.1f; tmax += 0.1f;
|
||||
Vector4 e = s + d * tmax;
|
||||
if (tmin > 0)
|
||||
s += d * tmin;
|
||||
|
||||
// Walk along the ray, looking for an intersection point.
|
||||
bool side = SampleHeight(s) < s.y;
|
||||
|
||||
Vector4 dt = e - s;
|
||||
float max_dist = dt.Len();
|
||||
|
||||
dt /= max_dist;
|
||||
dt *= 1.f; // minimum step size is 1 meters (so as not to spend too much time stepping)
|
||||
float dt_len = dt.Len();
|
||||
|
||||
float dist = 0.f;
|
||||
for (Vector4 p = s + dt; dist < max_dist; p += dt)
|
||||
{
|
||||
if ((SampleHeight(p) < p.y) != side)
|
||||
{
|
||||
#if 1
|
||||
// [EJ] Extra precision at terrain crossing boundary.
|
||||
s = p - dt;
|
||||
for (int n = 0; n < 24; ++n)
|
||||
{
|
||||
Vector4 m = (s + p) * 0.5f;
|
||||
if ((SampleHeight(m) < m.y) != side)
|
||||
p = m;
|
||||
else s = m;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Final hit.
|
||||
trace.has_hit = true;
|
||||
trace.w_i = p * GetMatrix();
|
||||
LocalToHeightmap(p, trace.uv.x, trace.uv.y);
|
||||
return true;
|
||||
}
|
||||
dist += dt_len;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Terrain::ApplyBlur(int pass_count)
|
||||
{
|
||||
if (heightmap)
|
||||
for (int p = 0; p < pass_count; ++p)
|
||||
{
|
||||
int n = (heightmap_w + 1) + 1;
|
||||
for (int v = 1; v < heightmap_h; ++v)
|
||||
{
|
||||
for (int u = 1; u < heightmap_w; ++u)
|
||||
{
|
||||
heightmap[n] = (heightmap[n] + heightmap[n - 1] + heightmap[n - (heightmap_w + 1)] + heightmap[n + 1] + heightmap[n + (heightmap_w + 1)]) / 5.f;
|
||||
++n;
|
||||
}
|
||||
n += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool Terrain::FromPicture(const char *path, int blur_pass_count)
|
||||
{
|
||||
// Initial values.
|
||||
Picture pic;
|
||||
PictureIO::Get().Load(pic, path);
|
||||
|
||||
int n = 0;
|
||||
for (int v = 0; v < (heightmap_h + 1); ++v)
|
||||
for (int u = 0; u < (heightmap_w + 1); ++u)
|
||||
heightmap[n++] = pic.SampleColor((float)u / heightmap_w, (float)v / heightmap_h).x * 16.f;
|
||||
|
||||
ApplyBlur(blur_pass_count);
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Terrain::Attrib *Terrain::GetAttributesMapAt(int u, int v) const
|
||||
{ return &attribmap[(heightmap_w + 1) * v + u]; }
|
||||
Vector4 Terrain::GetNormalAt(int u, int v) const
|
||||
{
|
||||
Attrib *n = GetAttributesMapAt(u, v);
|
||||
return Vector4((float)n->nx / 127.f, (float)n->ny / 127.f, (float)n->nz / 127.f);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Terrain::Terrain()
|
||||
{
|
||||
width = 0;
|
||||
depth = 0;
|
||||
|
||||
heightmap_w = 0;
|
||||
heightmap_h = 0;
|
||||
node_count = 0;
|
||||
|
||||
for (uint n = 1; n < 4; ++n)
|
||||
layer[n].enabled = false;
|
||||
}
|
||||
Terrain::~Terrain()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user