Split the GeoSphere stuff into GeoPatch and GeoPatchContext files.

Lots of tweaks and changes but still massive problems with Release mode :(
master
Andrew Copland 2013-03-10 19:59:08 +00:00
parent 08907fa67c
commit 4e6a5605f7
11 changed files with 1522 additions and 1303 deletions

727
src/GeoPatch.cpp Normal file
View File

@ -0,0 +1,727 @@
// Copyright © 2008-2013 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "libs.h"
#include "GeoPatchContext.h"
#include "GeoPatch.h"
#include "GeoSphere.h"
#include "perlin.h"
#include "Pi.h"
#include "RefCounted.h"
#include "graphics/Material.h"
#include "graphics/Renderer.h"
#include "graphics/Frustum.h"
#include "graphics/Graphics.h"
#include "graphics/VertexArray.h"
#include "graphics/gl2/GeoSphereMaterial.h"
#include "vcacheopt/vcacheopt.h"
#include <deque>
#include <algorithm>
// tri edge lengths
#define GEOPATCH_SUBDIVIDE_AT_CAMDIST 5.0
#define GEOPATCH_MAX_DEPTH 2 //15 + (2*Pi::detail.fracmult) //15
static const int GEOPATCH_MAX_EDGELEN = 55;
void GeoPatch::PatchJob::job_onFinish(void * userData, int userId) // runs in primary thread of the context
{
mData->pGeoSphere->AddSplitResult(mpResults);
PureJob::job_onFinish(userData, userId);
}
void GeoPatch::PatchJob::job_process(void * userData,int /* userId */) // RUNS IN ANOTHER THREAD!! MUST BE THREAD SAFE!
{
const SSplitRequestDescription &srd = (*mData.Get());
const vector3d v01 = (srd.v0+srd.v1).Normalized();
const vector3d v12 = (srd.v1+srd.v2).Normalized();
const vector3d v23 = (srd.v2+srd.v3).Normalized();
const vector3d v30 = (srd.v3+srd.v0).Normalized();
const vector3d cn = (srd.centroid).Normalized();
//
const vector3d vecs[4][4] = {
{srd.v0, v01, cn, v30},
{v01, srd.v1, v12, cn},
{cn, v12, srd.v2, v23},
{v30, cn, v23, srd.v3}
};
SSplitResult *sr = new SSplitResult(srd.patchID.GetPatchFaceIdx(), srd.depth);
for (int i=0; i<4; i++)
{
// fill out the data
GenerateMesh(srd.vertices[i], srd.normals[i], srd.colors[i],
vecs[i][0], vecs[i][1], vecs[i][2], vecs[i][3],
srd.edgeLen, srd.fracStep);
// add this patches data
sr->addResult(srd.vertices[i], srd.normals[i], srd.colors[i],
vecs[i][0], vecs[i][1], vecs[i][2], vecs[i][3],
srd.patchID.NextPatchID(srd.depth+1, i));
}
mpResults = sr;
}
// Generates full-detail vertices, and also non-edge normals and colors
void GeoPatch::PatchJob::GenerateMesh(
vector3d *vertices, vector3d *normals, vector3d *colors,
const vector3d &v0,
const vector3d &v1,
const vector3d &v2,
const vector3d &v3,
const int edgeLen,
const double fracStep) const
{
const SSplitRequestDescription &srd = (*mData.Get());
vector3d *vts = vertices;
vector3d *col = colors;
double xfrac;
double yfrac = 0;
for (int y=0; y<edgeLen; y++) {
xfrac = 0;
for (int x=0; x<edgeLen; x++) {
const vector3d p = GetSpherePoint(v0, v1, v2, v3, xfrac, yfrac);
const double height = srd.pTerrain->GetHeight(p);
*(vts++) = p * (height + 1.0);
// remember this -- we will need it later
(col++)->x = height;
xfrac += fracStep;
}
yfrac += fracStep;
}
assert(vts == &vertices[srd.NUMVERTICES()]);
// Generate normals & colors for non-edge vertices since they never change
for (int y=1; y<edgeLen-1; y++) {
for (int x=1; x<edgeLen-1; x++) {
// normal
const vector3d &x1 = vertices[x-1 + y*edgeLen];
const vector3d &x2 = vertices[x+1 + y*edgeLen];
const vector3d &y1 = vertices[x + (y-1)*edgeLen];
const vector3d &y2 = vertices[x + (y+1)*edgeLen];
const vector3d n = ((x2-x1).Cross(y2-y1)).Normalized();
normals[x + y*edgeLen] = n;
// color
const vector3d p = GetSpherePoint(v0, v1, v2, v3, x*fracStep, y*fracStep);
const double height = colors[x + y*edgeLen].x;
vector3d &col_r = colors[x + y*edgeLen];
col_r = srd.pTerrain->GetColor(p, height, n);
}
}
}
#pragma optimize( "", off )
GeoPatch::GeoPatch(const RefCountedPtr<GeoPatchContext> &ctx_, GeoSphere *gs,
const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_,
const int depth, const GeoPatchID &ID_)
: ctx(ctx_), v0(v0_), v1(v1_), v2(v2_), v3(v3_),
vertices(NULL), normals(NULL), colors(NULL),
m_vbo(0), parent(NULL), geosphere(gs),
m_depth(depth), mPatchID(ID_),
mHasSplitRequest(false), mCanMergeChildren(0x0F)
{
for (int i=0; i<NUM_KIDS; ++i) {
edgeFriend[i] = NULL;
kids[i] = NULL;
}
m_kidsLock = SDL_CreateMutex();
clipCentroid = (v0+v1+v2+v3) * 0.25;
centroid = clipCentroid.Normalized();
clipRadius = 0.0;
clipRadius = std::max(clipRadius, (v0-clipCentroid).Length());
clipRadius = std::max(clipRadius, (v1-clipCentroid).Length());
clipRadius = std::max(clipRadius, (v2-clipCentroid).Length());
clipRadius = std::max(clipRadius, (v3-clipCentroid).Length());
if (geosphere->m_sbody->type < SystemBody::TYPE_PLANET_ASTEROID) {
m_distMult = 10 / Clamp(depth, 1, 10);
} else {
m_distMult = 5 / Clamp(depth, 1, 5);
}
m_roughLength = GEOPATCH_SUBDIVIDE_AT_CAMDIST / pow(2.0, depth) * m_distMult;
m_needUpdateVBOs = false;
geosphere->IncrementCurrentNumPatches();
}
GeoPatch::~GeoPatch() {
assert(!mHasSplitRequest);
geosphere->DecrementCurrentNumPatches();
const int numVerts = ctx->NUMVERTICES();
const uint64_t memAlloced = (sizeof(vector3d) * numVerts) * 3 * 4;
geosphere->DelMemAllocatedToPatches(memAlloced);
SDL_DestroyMutex(m_kidsLock);
for (int i=0; i<NUM_KIDS; i++) {
if (edgeFriend[i]) edgeFriend[i]->NotifyEdgeFriendDeleted(this);
}
for (int i=0; i<NUM_KIDS; i++) if (kids[i]) delete kids[i];
delete [] vertices;
delete [] normals;
delete [] colors;
glDeleteBuffersARB(1, &m_vbo);
}
void GeoPatch::_UpdateVBOs() {
if (m_needUpdateVBOs) {
m_needUpdateVBOs = false;
if (!m_vbo) glGenBuffersARB(1, &m_vbo);
glBindBufferARB(GL_ARRAY_BUFFER, m_vbo);
glBufferDataARB(GL_ARRAY_BUFFER, sizeof(GeoPatchContext::VBOVertex)*ctx->NUMVERTICES(), 0, GL_DYNAMIC_DRAW);
for (int i=0; i<ctx->NUMVERTICES(); i++)
{
clipRadius = std::max(clipRadius, (vertices[i]-clipCentroid).Length());
GeoPatchContext::VBOVertex *pData = ctx->vbotemp + i;
pData->x = float(vertices[i].x - clipCentroid.x);
pData->y = float(vertices[i].y - clipCentroid.y);
pData->z = float(vertices[i].z - clipCentroid.z);
pData->nx = float(normals[i].x);
pData->ny = float(normals[i].y);
pData->nz = float(normals[i].z);
pData->col[0] = static_cast<unsigned char>(Clamp(colors[i].x*255.0, 0.0, 255.0));
pData->col[1] = static_cast<unsigned char>(Clamp(colors[i].y*255.0, 0.0, 255.0));
pData->col[2] = static_cast<unsigned char>(Clamp(colors[i].z*255.0, 0.0, 255.0));
pData->col[3] = 255;
}
glBufferDataARB(GL_ARRAY_BUFFER, sizeof(GeoPatchContext::VBOVertex)*ctx->NUMVERTICES(), ctx->vbotemp, GL_DYNAMIC_DRAW);
glBindBufferARB(GL_ARRAY_BUFFER, 0);
}
}
/* not quite edge, since we share edge vertices so that would be
* fucking pointless. one position inwards. used to make edge normals
* for adjacent tiles */
void GeoPatch::GetEdgeMinusOneVerticesFlipped(const int edge, vector3d *ev) const {
if (edge == 0) {
for (int x=0; x<ctx->edgeLen; x++) ev[ctx->edgeLen-1-x] = vertices[x + ctx->edgeLen];
} else if (edge == 1) {
const int x = ctx->edgeLen-2;
for (int y=0; y<ctx->edgeLen; y++) ev[ctx->edgeLen-1-y] = vertices[x + y*ctx->edgeLen];
} else if (edge == 2) {
const int y = ctx->edgeLen-2;
for (int x=0; x<ctx->edgeLen; x++) ev[ctx->edgeLen-1-x] = vertices[(ctx->edgeLen-1)-x + y*ctx->edgeLen];
} else {
for (int y=0; y<ctx->edgeLen; y++) ev[ctx->edgeLen-1-y] = vertices[1 + ((ctx->edgeLen-1)-y)*ctx->edgeLen];
}
}
void GeoPatch::FixEdgeNormals(const int edge, const vector3d *ev) {
int x, y;
switch (edge) {
case 0:
for (x=1; x<ctx->edgeLen-1; x++) {
const vector3d &x1 = vertices[x-1];
const vector3d &x2 = vertices[x+1];
const vector3d &y1 = ev[x];
const vector3d &y2 = vertices[x + ctx->edgeLen];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[x] = norm;
// make color
const vector3d p = GetSpherePoint(x*ctx->frac, 0);
const double height = colors[x].x;
colors[x] = geosphere->GetColor(p, height, norm);
}
break;
case 1:
x = ctx->edgeLen-1;
for (y=1; y<ctx->edgeLen-1; y++) {
const vector3d &x1 = vertices[(x-1) + y*ctx->edgeLen];
const vector3d &x2 = ev[y];
const vector3d &y1 = vertices[x + (y-1)*ctx->edgeLen];
const vector3d &y2 = vertices[x + (y+1)*ctx->edgeLen];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[x + y*ctx->edgeLen] = norm;
// make color
const vector3d p = GetSpherePoint(x*ctx->frac, y*ctx->frac);
const double height = colors[x + y*ctx->edgeLen].x;
colors[x + y*ctx->edgeLen] = geosphere->GetColor(p, height, norm);
// colors[x+y*ctx->edgeLen] = vector3d(1,0,0);
}
break;
case 2:
y = ctx->edgeLen-1;
for (x=1; x<ctx->edgeLen-1; x++) {
const vector3d &x1 = vertices[x-1 + y*ctx->edgeLen];
const vector3d &x2 = vertices[x+1 + y*ctx->edgeLen];
const vector3d &y1 = vertices[x + (y-1)*ctx->edgeLen];
const vector3d &y2 = ev[ctx->edgeLen-1-x];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[x + y*ctx->edgeLen] = norm;
// make color
const vector3d p = GetSpherePoint(x*ctx->frac, y*ctx->frac);
const double height = colors[x + y*ctx->edgeLen].x;
colors[x + y*ctx->edgeLen] = geosphere->GetColor(p, height, norm);
}
break;
case 3:
for (y=1; y<ctx->edgeLen-1; y++) {
const vector3d &x1 = ev[ctx->edgeLen-1-y];
const vector3d &x2 = vertices[1 + y*ctx->edgeLen];
const vector3d &y1 = vertices[(y-1)*ctx->edgeLen];
const vector3d &y2 = vertices[(y+1)*ctx->edgeLen];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[y*ctx->edgeLen] = norm;
// make color
const vector3d p = GetSpherePoint(0, y*ctx->frac);
const double height = colors[y*ctx->edgeLen].x;
colors[y*ctx->edgeLen] = geosphere->GetColor(p, height, norm);
// colors[y*ctx->edgeLen] = vector3d(0,1,0);
}
break;
}
}
void GeoPatch::FixEdgeFromParentInterpolated(const int edge) {
// noticeable artefacts from not doing so...
vector3d ev[GEOPATCH_MAX_EDGELEN];
vector3d en[GEOPATCH_MAX_EDGELEN];
vector3d ec[GEOPATCH_MAX_EDGELEN];
vector3d ev2[GEOPATCH_MAX_EDGELEN];
vector3d en2[GEOPATCH_MAX_EDGELEN];
vector3d ec2[GEOPATCH_MAX_EDGELEN];
ctx->GetEdge(parent->vertices, edge, ev);
ctx->GetEdge(parent->normals, edge, en);
ctx->GetEdge(parent->colors, edge, ec);
int kid_idx = parent->GetChildIdx(this);
if (edge == kid_idx) {
// use first half of edge
for (int i=0; i<=ctx->edgeLen/2; i++) {
ev2[i<<1] = ev[i];
en2[i<<1] = en[i];
ec2[i<<1] = ec[i];
}
} else {
// use 2nd half of edge
for (int i=ctx->edgeLen/2; i<ctx->edgeLen; i++) {
ev2[(i-(ctx->edgeLen/2))<<1] = ev[i];
en2[(i-(ctx->edgeLen/2))<<1] = en[i];
ec2[(i-(ctx->edgeLen/2))<<1] = ec[i];
}
}
// interpolate!!
for (int i=1; i<ctx->edgeLen; i+=2) {
ev2[i] = (ev2[i-1]+ev2[i+1]) * 0.5;
en2[i] = (en2[i-1]+en2[i+1]).Normalized();
ec2[i] = (ec2[i-1]+ec2[i+1]) * 0.5;
}
ctx->SetEdge(this->vertices, edge, ev2);
ctx->SetEdge(this->normals, edge, en2);
ctx->SetEdge(this->colors, edge, ec2);
}
void GeoPatch::FixCornerNormalsByEdge(const int edge, const vector3d *ev) {
vector3d ev2[GEOPATCH_MAX_EDGELEN];
/* XXX All these 'if's have an unfinished else, when a neighbour
* of our size doesn't exist and instead we must look at a bigger tile.
* But let's just leave it for the mo because it is a pain.
* See comment in OnEdgeFriendChanged() */
switch (edge) {
case 0:
if (edgeFriend[3]) {
const int we_are = edgeFriend[3]->GetEdgeIdxOf(this);
edgeFriend[3]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<0>(ev2, ev);
}
if (edgeFriend[1]) {
const int we_are = edgeFriend[1]->GetEdgeIdxOf(this);
edgeFriend[1]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<1>(ev, ev2);
}
break;
case 1:
if (edgeFriend[0]) {
const int we_are = edgeFriend[0]->GetEdgeIdxOf(this);
edgeFriend[0]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<1>(ev2, ev);
}
if (edgeFriend[2]) {
const int we_are = edgeFriend[2]->GetEdgeIdxOf(this);
edgeFriend[2]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<2>(ev, ev2);
}
break;
case 2:
if (edgeFriend[1]) {
const int we_are = edgeFriend[1]->GetEdgeIdxOf(this);
edgeFriend[1]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<2>(ev2, ev);
}
if (edgeFriend[3]) {
const int we_are = edgeFriend[3]->GetEdgeIdxOf(this);
edgeFriend[3]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<3>(ev, ev2);
}
break;
case 3:
if (edgeFriend[2]) {
const int we_are = edgeFriend[2]->GetEdgeIdxOf(this);
edgeFriend[2]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<3>(ev2, ev);
}
if (edgeFriend[0]) {
const int we_are = edgeFriend[0]->GetEdgeIdxOf(this);
edgeFriend[0]->GetEdgeMinusOneVerticesFlipped(we_are, ev2);
MakeCornerNormal<0>(ev, ev2);
}
break;
}
}
void GeoPatch::GenerateEdgeNormalsAndColors() {
vector3d ev[NUM_EDGES][GEOPATCH_MAX_EDGELEN];
bool doneEdge[NUM_EDGES];
memset(doneEdge, 0, sizeof(doneEdge));
for (int i=0; i<NUM_EDGES; i++) {
GeoPatch *e = edgeFriend[i];
if (e) {
int we_are = e->GetEdgeIdxOf(this);
e->GetEdgeMinusOneVerticesFlipped(we_are, ev[i]);
} else if (parent && parent->edgeFriend[i]) {
assert(parent->edgeFriend[i]);
doneEdge[i] = true;
// parent has valid edge, so take our
// bit of that, interpolated.
FixEdgeFromParentInterpolated(i);
// XXX needed for corners... probably not
// correct
ctx->GetEdge(vertices, i, ev[i]);
}
}
MakeCornerNormal<0>(ev[3], ev[0]);
MakeCornerNormal<1>(ev[0], ev[1]);
MakeCornerNormal<2>(ev[1], ev[2]);
MakeCornerNormal<3>(ev[2], ev[3]);
for (int i=0; i<NUM_EDGES; i++) if(!doneEdge[i]) FixEdgeNormals(i, ev[i]);
}
// Generates full-detail vertices, and also non-edge normals and colors
void GeoPatch::GenerateMesh() {
centroid = clipCentroid.Normalized();
centroid = (1.0 + geosphere->GetHeight(centroid)) * centroid;
vector3d *vts = vertices;
vector3d *col = colors;
double xfrac;
double yfrac = 0;
for (int y=0; y<ctx->edgeLen; y++) {
xfrac = 0;
for (int x=0; x<ctx->edgeLen; x++) {
vector3d p = GetSpherePoint(xfrac, yfrac);
double height = geosphere->GetHeight(p);
*(vts++) = p * (height + 1.0);
// remember this -- we will need it later
(col++)->x = height;
xfrac += ctx->frac;
}
yfrac += ctx->frac;
}
assert(vts == &vertices[ctx->NUMVERTICES()]);
// Generate normals & colors for non-edge vertices since they never change
for (int y=1; y<ctx->edgeLen-1; y++) {
for (int x=1; x<ctx->edgeLen-1; x++) {
// normal
const vector3d &x1 = vertices[x-1 + y*ctx->edgeLen];
const vector3d &x2 = vertices[x+1 + y*ctx->edgeLen];
const vector3d &y1 = vertices[x + (y-1)*ctx->edgeLen];
const vector3d &y2 = vertices[x + (y+1)*ctx->edgeLen];
const vector3d n = (x2-x1).Cross(y2-y1);
normals[x + y*ctx->edgeLen] = n.Normalized();
// color
const vector3d p = GetSpherePoint(x*ctx->frac, y*ctx->frac);
vector3d &col_r = colors[x + y*ctx->edgeLen];
const double height = col_r.x;
const vector3d &norm = normals[x + y*ctx->edgeLen];
col_r = geosphere->GetColor(p, height, norm);
}
}
}
void GeoPatch::OnEdgeFriendChanged(const int edge, GeoPatch *e) {
edgeFriend[edge] = e;
vector3d ev[GEOPATCH_MAX_EDGELEN];
const int we_are = e->GetEdgeIdxOf(this);
e->GetEdgeMinusOneVerticesFlipped(we_are, ev);
/* now we have a valid edge, fix the edge vertices */
if (edge == 0) {
for (int x=0; x<ctx->edgeLen; x++) {
const vector3d p = GetSpherePoint(x * ctx->frac, 0);
const double height = geosphere->GetHeight(p);
vertices[x] = p * (height + 1.0);
// XXX These bounds checks in each edge case are
// only necessary while the "All these 'if's"
// comment in FixCornerNormalsByEdge stands
if ((x>0) && (x<ctx->edgeLen-1)) {
colors[x].x = height;
}
}
} else if (edge == 1) {
for (int y=0; y<ctx->edgeLen; y++) {
const vector3d p = GetSpherePoint(1.0, y * ctx->frac);
const double height = geosphere->GetHeight(p);
const int pos = (ctx->edgeLen-1) + y*ctx->edgeLen;
vertices[pos] = p * (height + 1.0);
if ((y>0) && (y<ctx->edgeLen-1)) {
colors[pos].x = height;
}
}
} else if (edge == 2) {
for (int x=0; x<ctx->edgeLen; x++) {
const vector3d p = GetSpherePoint(x * ctx->frac, 1.0);
const double height = geosphere->GetHeight(p);
const int pos = x + (ctx->edgeLen-1)*ctx->edgeLen;
vertices[pos] = p * (height + 1.0);
if ((x>0) && (x<ctx->edgeLen-1)) {
colors[pos].x = height;
}
}
} else {
for (int y=0; y<ctx->edgeLen; y++) {
const vector3d p = GetSpherePoint(0, y * ctx->frac);
const double height = geosphere->GetHeight(p);
const int pos = y * ctx->edgeLen;
vertices[pos] = p * (height + 1.0);
if ((y>0) && (y<ctx->edgeLen-1)) {
colors[pos].x = height;
}
}
}
FixEdgeNormals(edge, ev);
FixCornerNormalsByEdge(edge, ev);
UpdateVBOs();
if (kids[0]) {
if (edge == 0) {
kids[0]->FixEdgeFromParentInterpolated(0);
kids[0]->UpdateVBOs();
kids[1]->FixEdgeFromParentInterpolated(0);
kids[1]->UpdateVBOs();
} else if (edge == 1) {
kids[1]->FixEdgeFromParentInterpolated(1);
kids[1]->UpdateVBOs();
kids[2]->FixEdgeFromParentInterpolated(1);
kids[2]->UpdateVBOs();
} else if (edge == 2) {
kids[2]->FixEdgeFromParentInterpolated(2);
kids[2]->UpdateVBOs();
kids[3]->FixEdgeFromParentInterpolated(2);
kids[3]->UpdateVBOs();
} else {
kids[3]->FixEdgeFromParentInterpolated(3);
kids[3]->UpdateVBOs();
kids[0]->FixEdgeFromParentInterpolated(3);
kids[0]->UpdateVBOs();
}
}
}
void GeoPatch::NotifyEdgeFriendSplit(GeoPatch *e) {
if (!kids[0]) {return;}
const int idx = GetEdgeIdxOf(e);
const int we_are = e->GetEdgeIdxOf(this);
// match e's new kids to our own... :/
kids[idx]->OnEdgeFriendChanged(idx, e->kids[(we_are+1)%NUM_KIDS]);
kids[(idx+1)%NUM_KIDS]->OnEdgeFriendChanged(idx, e->kids[we_are]);
}
void GeoPatch::NotifyEdgeFriendDeleted(const GeoPatch *e) {
const int idx = GetEdgeIdxOf(e);
assert(idx>=0 && idx<NUM_EDGES);
edgeFriend[idx] = NULL;
if (!parent) return;
if (parent->edgeFriend[idx]) {
FixEdgeFromParentInterpolated(idx);
UpdateVBOs();
} else {
// XXX TODO XXX
// Bad. not fixing up edges in this case!!!
}
}
GeoPatch *GeoPatch::GetEdgeFriendForKid(const int kid, const int edge) const {
const GeoPatch *e = edgeFriend[edge];
if (!e) return NULL;
//assert (e);// && (e->m_depth >= m_depth));
const int we_are = e->GetEdgeIdxOf(this);
// neighbour patch has not split yet (is at depth of this patch), so kids of this patch do
// not have same detail level neighbours yet
if (edge == kid) return e->kids[(we_are+1)%NUM_KIDS];
else return e->kids[we_are];
}
void GeoPatch::Render(vector3d &campos, const Graphics::Frustum &frustum) {
PiVerify(SDL_mutexP(m_kidsLock)==0);
if (kids[0]) {
for (int i=0; i<NUM_KIDS; i++) kids[i]->Render(campos, frustum);
SDL_mutexV(m_kidsLock);
} else if (NULL!=vertices) {
SDL_mutexV(m_kidsLock);
_UpdateVBOs();
if (!frustum.TestPoint(clipCentroid, clipRadius))
return;
vector3d relpos = clipCentroid - campos;
glPushMatrix();
glTranslated(relpos.x, relpos.y, relpos.z);
Pi::statSceneTris += 2*(ctx->edgeLen-1)*(ctx->edgeLen-1);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
// update the indices used for rendering
ctx->updateIndexBufferId(determineIndexbuffer());
glBindBufferARB(GL_ARRAY_BUFFER, m_vbo);
glVertexPointer(3, GL_FLOAT, sizeof(GeoPatchContext::VBOVertex), 0);
glNormalPointer(GL_FLOAT, sizeof(GeoPatchContext::VBOVertex), reinterpret_cast<void *>(3*sizeof(float)));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GeoPatchContext::VBOVertex), reinterpret_cast<void *>(6*sizeof(float)));
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, ctx->indices_vbo);
glDrawElements(GL_TRIANGLES, ctx->indices_tri_count*3, GL_UNSIGNED_SHORT, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopMatrix();
}
}
#pragma optimize( "", off )
void GeoPatch::LODUpdate(const vector3d &campos) {
// there should be no LODUpdate'ing when we have active split requests
if(mHasSplitRequest)
return;
bool canSplit = true;
bool canMerge = (NULL!=kids[0]) && (0==mCanMergeChildren);
if (canMerge) {
for (int i=0; i<NUM_KIDS; i++) {
canMerge &= kids[i]->canBeMerged();
}
}
// always split at first level
if (parent) {
for (int i=0; i<NUM_EDGES; i++) {
if (!edgeFriend[i]) {
canSplit = false;
break;
} else if (edgeFriend[i]->m_depth < m_depth) {
canSplit = false;
break;
}
}
const float centroidDist = (campos - centroid).Length();
const bool errorSplit = (centroidDist < m_roughLength);
if( !(canSplit && (m_depth < GEOPATCH_MAX_DEPTH) && errorSplit) )
{
canSplit = false;
}
}
if (canSplit) {
if (!kids[0]) {
// don't do anything if we can't handle anymore jobs
if( !Pi::jobs.canAddJob() ) {
return;
}
mHasSplitRequest = true;
if(parent) {
// set the bit flag preventing merging
parent->mCanMergeChildren |= 1<<mPatchID.GetPatchIdx(m_depth);
}
const int numVerts = ctx->NUMVERTICES();
const uint64_t memAlloced = (sizeof(vector3d) * numVerts) * 3 * 4;
geosphere->AddMemAllocatedToPatches(memAlloced);
SSplitRequestDescription *ssrd = new SSplitRequestDescription(v0, v1, v2, v3, centroid.Normalized(), m_depth,
geosphere->m_sbody->path, mPatchID, ctx->edgeLen,
ctx->frac, geosphere->m_terrain, geosphere);
assert(!mCurrentJob.Valid());
mCurrentJob.Reset(new PatchJob(ssrd));
Pi::jobs.addJob(mCurrentJob.Get(), NULL);
} else {
for (int i=0; i<NUM_KIDS; i++) {
kids[i]->LODUpdate(campos);
}
}
} else if (canMerge) {
PiVerify(SDL_mutexP(m_kidsLock)==0);
for (int i=0; i<NUM_KIDS; i++) {
delete kids[i];
kids[i] = NULL;
}
PiVerify(SDL_mutexV(m_kidsLock)!=-1);
}
}
#pragma optimize( "", off )
void GeoPatch::ReceiveHeightmaps(const SSplitResult *psr)
{
if (m_depth<psr->depth) {
// this should work because each depth should have a common history
const uint32_t kidIdx = psr->data[0].patchID.GetPatchIdx(m_depth+1);
kids[kidIdx]->ReceiveHeightmaps(psr);
} else {
const int nD = m_depth+1;
for (int i=0; i<NUM_KIDS; i++)
{
assert(NULL==kids[i]);
kids[i] = new GeoPatch(ctx, geosphere,
psr->data[i].v0, psr->data[i].v1, psr->data[i].v2, psr->data[i].v3,
nD, psr->data[i].patchID);
}
// hm.. edges. Not right to pass this
// edgeFriend...
kids[0]->edgeFriend[0] = GetEdgeFriendForKid(0, 0);
kids[0]->edgeFriend[1] = kids[1];
kids[0]->edgeFriend[2] = kids[3];
kids[0]->edgeFriend[3] = GetEdgeFriendForKid(0, 3);
kids[1]->edgeFriend[0] = GetEdgeFriendForKid(1, 0);
kids[1]->edgeFriend[1] = GetEdgeFriendForKid(1, 1);
kids[1]->edgeFriend[2] = kids[2];
kids[1]->edgeFriend[3] = kids[0];
kids[2]->edgeFriend[0] = kids[1];
kids[2]->edgeFriend[1] = GetEdgeFriendForKid(2, 1);
kids[2]->edgeFriend[2] = GetEdgeFriendForKid(2, 2);
kids[2]->edgeFriend[3] = kids[3];
kids[3]->edgeFriend[0] = kids[0];
kids[3]->edgeFriend[1] = kids[2];
kids[3]->edgeFriend[2] = GetEdgeFriendForKid(3, 2);
kids[3]->edgeFriend[3] = GetEdgeFriendForKid(3, 3);
kids[0]->parent = kids[1]->parent = kids[2]->parent = kids[3]->parent = this;
for (int i=0; i<NUM_KIDS; i++)
{
kids[i]->vertices = psr->data[i].vertices;
kids[i]->normals = psr->data[i].normals;
kids[i]->colors = psr->data[i].colors;
}
PiVerify(SDL_mutexP(m_kidsLock)==0);
for (int i=0; i<NUM_EDGES; i++) { if(edgeFriend[i]) edgeFriend[i]->NotifyEdgeFriendSplit(this); }
for (int i=0; i<NUM_KIDS; i++) {
kids[i]->GenerateEdgeNormalsAndColors();
kids[i]->UpdateVBOs();
}
PiVerify(SDL_mutexV(m_kidsLock)!=-1);
assert(mCurrentJob.Valid());
mCurrentJob.Reset();
mHasSplitRequest = false;
if(parent) {
// remove the bit flag
parent->mCanMergeChildren &= ~(1<<mPatchID.GetPatchIdx(m_depth));
}
}
}

351
src/GeoPatch.h Normal file
View File

@ -0,0 +1,351 @@
// Copyright © 2008-2013 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#ifndef _GEOPATCH_H
#define _GEOPATCH_H
#include <SDL_stdinc.h>
#include "vector3.h"
#include "Random.h"
#include "galaxy/StarSystem.h"
#include "graphics/Material.h"
#include "terrain/Terrain.h"
#include "GeoPatchID.h"
#include "jobswarm/JobManager.h"
#include <deque>
namespace Graphics { class Renderer; class Frustum; }
class SystemBody;
class GeoPatch;
class GeoPatchContext;
class GeoSphere;
class SSplitRequestDescription {
public:
SSplitRequestDescription(const vector3d &v0_,
const vector3d &v1_,
const vector3d &v2_,
const vector3d &v3_,
const vector3d &cn,
const uint32_t depth_,
const SystemPath &sysPath_,
const GeoPatchID &patchID_,
const int edgeLen_,
const double fracStep_,
Terrain *pTerrain_,
GeoSphere *pGeoSphere_)
: v0(v0_), v1(v1_), v2(v2_), v3(v3_), centroid(cn), depth(depth_),
sysPath(sysPath_), patchID(patchID_), edgeLen(edgeLen_), fracStep(fracStep_),
pTerrain(pTerrain_),
pGeoSphere(pGeoSphere_)
{
const int numVerts = NUMVERTICES(edgeLen_);
for( int i=0 ; i<4 ; ++i )
{
vertices[i] = new vector3d[numVerts];
normals[i] = new vector3d[numVerts];
colors[i] = new vector3d[numVerts];
}
}
inline int NUMVERTICES() const { return edgeLen*edgeLen; }
inline int NUMVERTICES(const int el) const { return el*el; }
const vector3d v0;
const vector3d v1;
const vector3d v2;
const vector3d v3;
const vector3d centroid;
const uint32_t depth;
const SystemPath sysPath;
const GeoPatchID patchID;
const int edgeLen;
const double fracStep;
Terrain *pTerrain;
// quick hack, do not have in the final version!
GeoSphere *pGeoSphere;
vector3d *vertices[4];
vector3d *normals[4];
vector3d *colors[4];
private:
// deliberately prevent copy constructor access
SSplitRequestDescription(const SSplitRequestDescription &r) : v0(r.v0), v1(r.v1), v2(r.v2), v3(r.v3), centroid(r.centroid), depth(r.depth),
sysPath(r.sysPath), patchID(r.patchID), edgeLen(r.edgeLen), fracStep(r.fracStep),
pTerrain(r.pTerrain),
pGeoSphere(r.pGeoSphere)
{
assert(false);
for( int i=0 ; i<4 ; ++i )
{
vertices[i] = r.vertices[i];
normals[i] = r.normals[i];
colors[i] = r.colors[i];
}
}
};
class SSplitResult {
public:
struct SSplitResultData {
SSplitResultData(vector3d *v_, vector3d *n_, vector3d *c_, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, const GeoPatchID &patchID_) :
vertices(v_), normals(n_), colors(c_), v0(v0_), v1(v1_), v2(v2_), v3(v3_), patchID(patchID_)
{
}
SSplitResultData(const SSplitResultData &r) :
vertices(r.vertices), normals(r.normals), colors(r.colors), v0(r.v0), v1(r.v1), v2(r.v2), v3(r.v3), patchID(r.patchID)
{}
vector3d *vertices;
vector3d *normals;
vector3d *colors;
const vector3d v0;
const vector3d v1;
const vector3d v2;
const vector3d v3;
const GeoPatchID patchID;
};
SSplitResult(const int32_t face_, const int32_t depth_) : face(face_), depth(depth_)
{
}
void addResult(vector3d *v_, vector3d *n_, vector3d *c_, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, const GeoPatchID &patchID_)
{
data.push_back(SSplitResultData(v_, n_, c_, v0_, v1_, v2_, v3_, patchID_));
assert(data.size()<=4);
}
const int32_t face;
const int32_t depth;
std::deque<SSplitResultData> data;
private:
// deliberately prevent copy constructor access
SSplitResult(const SSplitResult &r) : face(0), depth(0) {}
};
class GeoPatch {
private:
//********************************************************************************
// Overloaded PureJob class to handle generating the mesh for each patch
//********************************************************************************
class PatchJob : public PureJob
{
public:
PatchJob(SSplitRequestDescription *data) : mData(data)
{
}
virtual ~PatchJob()
{
}
virtual void init(unsigned int *counter)
{
PureJob::init( counter );
}
virtual void job_process(void * userData,int /* userId */); // RUNS IN ANOTHER THREAD!! MUST BE THREAD SAFE!
virtual void job_onFinish(void * userData, int userId); // runs in primary thread of the context
virtual void job_onCancel(void * userData, int userId) // runs in primary thread of the context
{
PureJob::job_onCancel(userData, userId);
}
private:
ScopedPtr<SSplitRequestDescription> mData;
SSplitResult *mpResults;
/* in patch surface coords, [0,1] */
inline vector3d GetSpherePoint(const vector3d &v0, const vector3d &v1, const vector3d &v2, const vector3d &v3, const double x, const double y) const {
return (v0 + x*(1.0-y)*(v1-v0) + x*y*(v2-v0) + (1.0-x)*y*(v3-v0)).Normalized();
}
// Generates full-detail vertices, and also non-edge normals and colors
void GenerateMesh(
vector3d *vertices, vector3d *normals, vector3d *colors,
const vector3d &v0,
const vector3d &v1,
const vector3d &v2,
const vector3d &v3,
const int edgeLen,
const double fracStep) const;
};
ScopedPtr<PatchJob> mCurrentJob;
public:
static const uint32_t NUM_EDGES = 4;
static const uint32_t NUM_KIDS = NUM_EDGES;
RefCountedPtr<GeoPatchContext> ctx;
const vector3d v0, v1, v2, v3;
vector3d *vertices;
vector3d *normals;
vector3d *colors;
GLuint m_vbo;
GeoPatch *kids[NUM_KIDS];
GeoPatch *parent;
GeoPatch *edgeFriend[NUM_EDGES]; // [0]=v01, [1]=v12, [2]=v20
GeoSphere *geosphere;
double m_roughLength;
vector3d clipCentroid, centroid;
double clipRadius;
int m_depth;
SDL_mutex *m_kidsLock;
bool m_needUpdateVBOs;
double m_distMult;
const GeoPatchID mPatchID;
bool mHasSplitRequest;
uint8_t mCanMergeChildren;
GeoPatch(const RefCountedPtr<GeoPatchContext> &_ctx, GeoSphere *gs,
const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_,
const int depth, const GeoPatchID &ID_);
~GeoPatch();
inline void UpdateVBOs() {
m_needUpdateVBOs = (NULL!=vertices);
}
void _UpdateVBOs();
/* not quite edge, since we share edge vertices so that would be
* fucking pointless. one position inwards. used to make edge normals
* for adjacent tiles */
void GetEdgeMinusOneVerticesFlipped(const int edge, vector3d *ev) const;
inline int GetEdgeIdxOf(const GeoPatch *e) const {
for (int i=0; i<NUM_KIDS; i++) {if (edgeFriend[i] == e) {return i;}}
abort();
return -1;
}
void FixEdgeNormals(const int edge, const vector3d *ev);
int GetChildIdx(const GeoPatch *child) const {
for (int i=0; i<NUM_KIDS; i++) {
if (kids[i] == child) return i;
}
abort();
return -1;
}
void FixEdgeFromParentInterpolated(const int edge);
template <int corner>
void MakeCornerNormal(const vector3d *ev, const vector3d *ev2) {
switch (corner) {
case 0: {
const vector3d &x1 = ev[ctx->edgeLen-1];
const vector3d &x2 = vertices[1];
const vector3d &y1 = ev2[0];
const vector3d &y2 = vertices[ctx->edgeLen];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[0] = norm;
// make color
const vector3d pt = GetSpherePoint(0, 0);
// const double height = colors[0].x;
const double height = geosphere->GetHeight(pt);
colors[0] = geosphere->GetColor(pt, height, norm);
}
break;
case 1: {
const int p = ctx->edgeLen-1;
const vector3d &x1 = vertices[p-1];
const vector3d &x2 = ev2[0];
const vector3d &y1 = ev[ctx->edgeLen-1];
const vector3d &y2 = vertices[p + ctx->edgeLen];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[p] = norm;
// make color
const vector3d pt = GetSpherePoint(p*ctx->frac, 0);
// const double height = colors[p].x;
const double height = geosphere->GetHeight(pt);
colors[p] = geosphere->GetColor(pt, height, norm);
}
break;
case 2: {
const int p = ctx->edgeLen-1;
const vector3d &x1 = vertices[(p-1) + p*ctx->edgeLen];
const vector3d &x2 = ev[ctx->edgeLen-1];
const vector3d &y1 = vertices[p + (p-1)*ctx->edgeLen];
const vector3d &y2 = ev2[0];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[p + p*ctx->edgeLen] = norm;
// make color
const vector3d pt = GetSpherePoint(p*ctx->frac, p*ctx->frac);
// const double height = colors[p + p*ctx->edgeLen].x;
const double height = geosphere->GetHeight(pt);
colors[p + p*ctx->edgeLen] = geosphere->GetColor(pt, height, norm);
}
break;
case 3: {
const int p = ctx->edgeLen-1;
const vector3d &x1 = ev2[0];
const vector3d &x2 = vertices[1 + p*ctx->edgeLen];
const vector3d &y1 = vertices[(p-1)*ctx->edgeLen];
const vector3d &y2 = ev[ctx->edgeLen-1];
const vector3d norm = (x2-x1).Cross(y2-y1).Normalized();
normals[p*ctx->edgeLen] = norm;
// make color
const vector3d pt = GetSpherePoint(0, p*ctx->frac);
// const double height = colors[p*ctx->edgeLen].x;
const double height = geosphere->GetHeight(pt);
colors[p*ctx->edgeLen] = geosphere->GetColor(pt, height, norm);
}
break;
}
}
void FixCornerNormalsByEdge(const int edge, const vector3d *ev);
void GenerateEdgeNormalsAndColors();
// in patch surface coords, [0,1]
inline vector3d GetSpherePoint(const double x, const double y) const {
return (v0 + x*(1.0-y)*(v1-v0) + x*y*(v2-v0) + (1.0-x)*y*(v3-v0)).Normalized();
}
// Generates full-detail vertices, and also non-edge normals and colors
void GenerateMesh();
void OnEdgeFriendChanged(const int edge, GeoPatch *e);
void NotifyEdgeFriendSplit(GeoPatch *e);
void NotifyEdgeFriendDeleted(const GeoPatch *e);
GeoPatch *GetEdgeFriendForKid(const int kid, const int edge) const;
inline GLuint determineIndexbuffer() const {
return // index buffers are ordered by edge resolution flags
(edgeFriend[0] ? 1u : 0u) |
(edgeFriend[1] ? 2u : 0u) |
(edgeFriend[2] ? 4u : 0u) |
(edgeFriend[3] ? 8u : 0u);
}
void Render(vector3d &campos, const Graphics::Frustum &frustum);
inline bool canBeMerged() const {
bool merge = true;
if (kids[0]) {
for (int i=0; i<NUM_KIDS; i++) {
merge &= kids[i]->canBeMerged();
}
}
merge &= !(mHasSplitRequest);
return merge;
}
void LODUpdate(const vector3d &campos);
void ReceiveHeightmaps(const SSplitResult *psr);
};
#endif /* _GEOPATCH_H */

292
src/GeoPatchContext.cpp Normal file
View File

@ -0,0 +1,292 @@
// Copyright © 2008-2013 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#include "libs.h"
#include "GeoPatchContext.h"
#include "perlin.h"
#include "Pi.h"
#include "RefCounted.h"
#include "graphics/Material.h"
#include "graphics/Renderer.h"
#include "graphics/Frustum.h"
#include "graphics/Graphics.h"
#include "graphics/VertexArray.h"
#include "graphics/gl2/GeoSphereMaterial.h"
#include "vcacheopt/vcacheopt.h"
#include <deque>
#include <algorithm>
void GeoPatchContext::Cleanup() {
midIndices.Reset();
for (int i=0; i<4; i++) {
loEdgeIndices[i].Reset();
hiEdgeIndices[i].Reset();
}
if (indices_vbo) {
indices_vbo = 0;
}
for (int i=0; i<NUM_INDEX_LISTS; i++) {
if (indices_list[i]) {
glDeleteBuffersARB(1, &indices_list[i]);
}
}
delete [] vbotemp;
}
void GeoPatchContext::updateIndexBufferId(const GLuint edge_hi_flags) {
assert(edge_hi_flags < GLuint(NUM_INDEX_LISTS));
indices_vbo = indices_list[edge_hi_flags];
indices_tri_count = indices_tri_counts[edge_hi_flags];
}
int GeoPatchContext::getIndices(std::vector<unsigned short> &pl, const unsigned int edge_hi_flags)
{
// calculate how many tri's there are
int tri_count = (VBO_COUNT_MID_IDX() / 3);
for( int i=0; i<4; ++i ) {
if( edge_hi_flags & (1 << i) ) {
tri_count += (VBO_COUNT_HI_EDGE() / 3);
} else {
tri_count += (VBO_COUNT_LO_EDGE() / 3);
}
}
// pre-allocate enough space
pl.reserve(tri_count);
// add all of the middle indices
for(int i=0; i<VBO_COUNT_MID_IDX(); ++i) {
pl.push_back(midIndices[i]);
}
// selectively add the HI or LO detail indices
for (int i=0; i<4; i++) {
if( edge_hi_flags & (1 << i) ) {
for(int j=0; j<VBO_COUNT_HI_EDGE(); ++j) {
pl.push_back(hiEdgeIndices[i][j]);
}
} else {
for(int j=0; j<VBO_COUNT_LO_EDGE(); ++j) {
pl.push_back(loEdgeIndices[i][j]);
}
}
}
return tri_count;
}
void GeoPatchContext::Init() {
frac = 1.0 / double(edgeLen-1);
vbotemp = new VBOVertex[NUMVERTICES()];
unsigned short *idx;
midIndices.Reset(new unsigned short[VBO_COUNT_MID_IDX()]);
for (int i=0; i<4; i++) {
loEdgeIndices[i].Reset(new unsigned short[VBO_COUNT_LO_EDGE()]);
hiEdgeIndices[i].Reset(new unsigned short[VBO_COUNT_HI_EDGE()]);
}
/* also want vtx indices for tris not touching edge of patch */
idx = midIndices.Get();
for (int x=1; x<edgeLen-2; x++) {
for (int y=1; y<edgeLen-2; y++) {
idx[0] = x + edgeLen*y;
idx[1] = x+1 + edgeLen*y;
idx[2] = x + edgeLen*(y+1);
idx+=3;
idx[0] = x+1 + edgeLen*y;
idx[1] = x+1 + edgeLen*(y+1);
idx[2] = x + edgeLen*(y+1);
idx+=3;
}
}
{
for (int x=1; x<edgeLen-3; x+=2) {
// razor teeth near edge 0
idx[0] = x + edgeLen;
idx[1] = x+1;
idx[2] = x+1 + edgeLen;
idx+=3;
idx[0] = x+1;
idx[1] = x+2 + edgeLen;
idx[2] = x+1 + edgeLen;
idx+=3;
}
for (int x=1; x<edgeLen-3; x+=2) {
// near edge 2
idx[0] = x + edgeLen*(edgeLen-2);
idx[1] = x+1 + edgeLen*(edgeLen-2);
idx[2] = x+1 + edgeLen*(edgeLen-1);
idx+=3;
idx[0] = x+1 + edgeLen*(edgeLen-2);
idx[1] = x+2 + edgeLen*(edgeLen-2);
idx[2] = x+1 + edgeLen*(edgeLen-1);
idx+=3;
}
for (int y=1; y<edgeLen-3; y+=2) {
// near edge 1
idx[0] = edgeLen-2 + y*edgeLen;
idx[1] = edgeLen-1 + (y+1)*edgeLen;
idx[2] = edgeLen-2 + (y+1)*edgeLen;
idx+=3;
idx[0] = edgeLen-2 + (y+1)*edgeLen;
idx[1] = edgeLen-1 + (y+1)*edgeLen;
idx[2] = edgeLen-2 + (y+2)*edgeLen;
idx+=3;
}
for (int y=1; y<edgeLen-3; y+=2) {
// near edge 3
idx[0] = 1 + y*edgeLen;
idx[1] = 1 + (y+1)*edgeLen;
idx[2] = (y+1)*edgeLen;
idx+=3;
idx[0] = 1 + (y+1)*edgeLen;
idx[1] = 1 + (y+2)*edgeLen;
idx[2] = (y+1)*edgeLen;
idx+=3;
}
}
// full detail edge triangles
{
idx = hiEdgeIndices[0].Get();
for (int x=0; x<edgeLen-1; x+=2) {
idx[0] = x; idx[1] = x+1; idx[2] = x+1 + edgeLen;
idx+=3;
idx[0] = x+1; idx[1] = x+2; idx[2] = x+1 + edgeLen;
idx+=3;
}
idx = hiEdgeIndices[1].Get();
for (int y=0; y<edgeLen-1; y+=2) {
idx[0] = edgeLen-1 + y*edgeLen;
idx[1] = edgeLen-1 + (y+1)*edgeLen;
idx[2] = edgeLen-2 + (y+1)*edgeLen;
idx+=3;
idx[0] = edgeLen-1 + (y+1)*edgeLen;
idx[1] = edgeLen-1 + (y+2)*edgeLen;
idx[2] = edgeLen-2 + (y+1)*edgeLen;
idx+=3;
}
idx = hiEdgeIndices[2].Get();
for (int x=0; x<edgeLen-1; x+=2) {
idx[0] = x + (edgeLen-1)*edgeLen;
idx[1] = x+1 + (edgeLen-2)*edgeLen;
idx[2] = x+1 + (edgeLen-1)*edgeLen;
idx+=3;
idx[0] = x+1 + (edgeLen-2)*edgeLen;
idx[1] = x+2 + (edgeLen-1)*edgeLen;
idx[2] = x+1 + (edgeLen-1)*edgeLen;
idx+=3;
}
idx = hiEdgeIndices[3].Get();
for (int y=0; y<edgeLen-1; y+=2) {
idx[0] = y*edgeLen;
idx[1] = 1 + (y+1)*edgeLen;
idx[2] = (y+1)*edgeLen;
idx+=3;
idx[0] = (y+1)*edgeLen;
idx[1] = 1 + (y+1)*edgeLen;
idx[2] = (y+2)*edgeLen;
idx+=3;
}
}
// these edge indices are for patches with no
// neighbour of equal or greater detail -- they reduce
// their edge complexity by 1 division
{
idx = loEdgeIndices[0].Get();
for (int x=0; x<edgeLen-2; x+=2) {
idx[0] = x;
idx[1] = x+2;
idx[2] = x+1+edgeLen;
idx += 3;
}
idx = loEdgeIndices[1].Get();
for (int y=0; y<edgeLen-2; y+=2) {
idx[0] = (edgeLen-1) + y*edgeLen;
idx[1] = (edgeLen-1) + (y+2)*edgeLen;
idx[2] = (edgeLen-2) + (y+1)*edgeLen;
idx += 3;
}
idx = loEdgeIndices[2].Get();
for (int x=0; x<edgeLen-2; x+=2) {
idx[0] = x+edgeLen*(edgeLen-1);
idx[2] = x+2+edgeLen*(edgeLen-1);
idx[1] = x+1+edgeLen*(edgeLen-2);
idx += 3;
}
idx = loEdgeIndices[3].Get();
for (int y=0; y<edgeLen-2; y+=2) {
idx[0] = y*edgeLen;
idx[2] = (y+2)*edgeLen;
idx[1] = 1 + (y+1)*edgeLen;
idx += 3;
}
}
// these will hold the optimised indices
std::vector<unsigned short> pl_short[NUM_INDEX_LISTS];
// populate the N indices lists from the arrays built during InitTerrainIndices()
for( int i=0; i<NUM_INDEX_LISTS; ++i ) {
const unsigned int edge_hi_flags = i;
indices_tri_counts[i] = getIndices(pl_short[i], edge_hi_flags);
}
// iterate over each index list and optimize it
for( int i=0; i<NUM_INDEX_LISTS; ++i ) {
int tri_count = indices_tri_counts[i];
VertexCacheOptimizerUShort vco;
VertexCacheOptimizerUShort::Result res = vco.Optimize(&pl_short[i][0], tri_count);
assert(0 == res);
}
// everything should be hunky-dory for setting up as OpenGL index buffers now.
for( int i=0; i<NUM_INDEX_LISTS; ++i ) {
glGenBuffersARB(1, &indices_list[i]);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, indices_list[i]);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*indices_tri_counts[i]*3, &(pl_short[i][0]), GL_STATIC_DRAW);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, 0);
}
// default it to the last entry which uses the hi-res borders
indices_vbo = indices_list[NUM_INDEX_LISTS-1];
indices_tri_count = indices_tri_counts[NUM_INDEX_LISTS-1];
if (midIndices) {
midIndices.Reset();
for (int i=0; i<4; i++) {
loEdgeIndices[i].Reset();
hiEdgeIndices[i].Reset();
}
}
}
void GeoPatchContext::GetEdge(vector3d *array, int edge, vector3d *ev) {
if (edge == 0) {
for (int x=0; x<edgeLen; x++) ev[x] = array[x];
} else if (edge == 1) {
const int x = edgeLen-1;
for (int y=0; y<edgeLen; y++) ev[y] = array[x + y*edgeLen];
} else if (edge == 2) {
const int y = edgeLen-1;
for (int x=0; x<edgeLen; x++) ev[x] = array[(edgeLen-1)-x + y*edgeLen];
} else {
for (int y=0; y<edgeLen; y++) ev[y] = array[0 + ((edgeLen-1)-y)*edgeLen];
}
}
void GeoPatchContext::SetEdge(vector3d *array, int edge, const vector3d *ev) {
if (edge == 0) {
for (int x=0; x<edgeLen; x++) array[x] = ev[x];
} else if (edge == 1) {
const int x = edgeLen-1;
for (int y=0; y<edgeLen; y++) array[x + y*edgeLen] = ev[y];
} else if (edge == 2) {
const int y = edgeLen-1;
for (int x=0; x<edgeLen; x++) array[(edgeLen-1)-x + y*edgeLen] = ev[x];
} else {
for (int y=0; y<edgeLen; y++) array[0 + ((edgeLen-1)-y)*edgeLen] = ev[y];
}
}

90
src/GeoPatchContext.h Normal file
View File

@ -0,0 +1,90 @@
// Copyright © 2008-2013 Pioneer Developers. See AUTHORS.txt for details
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
#ifndef _GEOPATCHCONTEXT_H
#define _GEOPATCHCONTEXT_H
#include <SDL_stdinc.h>
#include "vector3.h"
#include "Random.h"
#include "galaxy/StarSystem.h"
#include "graphics/Material.h"
#include "terrain/Terrain.h"
#include "GeoPatchID.h"
#include <deque>
// hold the 16 possible terrain edge connections
const int NUM_INDEX_LISTS = 16;
namespace Graphics { class Renderer; }
class SystemBody;
class GeoPatch;
class GeoPatchContext;
class GeoSphere;
class GeoPatchContext : public RefCounted {
public:
#pragma pack(4)
struct VBOVertex
{
float x,y,z;
float nx,ny,nz;
unsigned char col[4];
float padding;
};
#pragma pack()
int edgeLen;
inline int VBO_COUNT_LO_EDGE() const { return 3*(edgeLen/2); }
inline int VBO_COUNT_HI_EDGE() const { return 3*(edgeLen-1); }
inline int VBO_COUNT_MID_IDX() const { return (4*3*(edgeLen-3)) + 2*(edgeLen-3)*(edgeLen-3)*3; }
// ^^ serrated teeth bit ^^^ square inner bit
inline int IDX_VBO_LO_OFFSET(int i) const { return i*sizeof(unsigned short)*3*(edgeLen/2); }
inline int IDX_VBO_HI_OFFSET(int i) const { return (i*sizeof(unsigned short)*VBO_COUNT_HI_EDGE())+IDX_VBO_LO_OFFSET(4); }
inline int IDX_VBO_MAIN_OFFSET() const { return IDX_VBO_HI_OFFSET(4); }
inline int IDX_VBO_COUNT_ALL_IDX() const { return ((edgeLen-1)*(edgeLen-1))*2*3; }
inline int NUMVERTICES() const { return edgeLen*edgeLen; }
double frac;
ScopedArray<unsigned short> midIndices;
ScopedArray<unsigned short> loEdgeIndices[4];
ScopedArray<unsigned short> hiEdgeIndices[4];
GLuint indices_vbo;
GLuint indices_list[NUM_INDEX_LISTS];
GLuint indices_tri_count;
GLuint indices_tri_counts[NUM_INDEX_LISTS];
VBOVertex *vbotemp;
GeoPatchContext(int _edgeLen) : edgeLen(_edgeLen) {
Init();
}
~GeoPatchContext() {
Cleanup();
}
void Refresh() {
Cleanup();
Init();
}
void Cleanup();
void updateIndexBufferId(const GLuint edge_hi_flags);
int getIndices(std::vector<unsigned short> &pl, const unsigned int edge_hi_flags);
void Init();
void GetEdge(vector3d *array, int edge, vector3d *ev);
void SetEdge(vector3d *array, int edge, const vector3d *ev);
};
#endif /* _GEOPATCHCONTEXT_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,101 +20,10 @@ class SystemBody;
class GeoPatch;
class GeoPatchContext;
class GeoSphere;
class SSplitRequestDescription;
class SSplitResult;
struct SSplitRequestDescription {
SSplitRequestDescription(const vector3d &v0_,
const vector3d &v1_,
const vector3d &v2_,
const vector3d &v3_,
const vector3d &cn,
const uint32_t depth_,
const SystemPath &sysPath_,
const GeoPatchID &patchID_,
const int edgeLen_,
const double fracStep_,
Terrain *pTerrain_,
GeoSphere *pGeoSphere_)
: v0(v0_), v1(v1_), v2(v2_), v3(v3_), centroid(cn), depth(depth_),
sysPath(sysPath_), patchID(patchID_), edgeLen(edgeLen_), fracStep(fracStep_),
pTerrain(pTerrain_),
pGeoSphere(pGeoSphere_)
{
for( int i=0 ; i<4 ; ++i )
{
vertices[i] = new vector3d[NUMVERTICES(edgeLen_)];
normals[i] = new vector3d[NUMVERTICES(edgeLen_)];
colors[i] = new vector3d[NUMVERTICES(edgeLen_)];
}
}
SSplitRequestDescription(const SSplitRequestDescription &r) : v0(r.v0), v1(r.v1), v2(r.v2), v3(r.v3), centroid(r.centroid), depth(r.depth),
sysPath(r.sysPath), patchID(r.patchID), edgeLen(r.edgeLen), fracStep(r.fracStep),
pTerrain(r.pTerrain),
pGeoSphere(r.pGeoSphere)
{
for( int i=0 ; i<4 ; ++i )
{
vertices[i] = r.vertices[i];
normals[i] = r.normals[i];
colors[i] = r.colors[i];
}
}
inline int NUMVERTICES() const { return edgeLen*edgeLen; }
inline int NUMVERTICES(const int el) const { return el*el; }
const vector3d v0;
const vector3d v1;
const vector3d v2;
const vector3d v3;
const vector3d centroid;
const uint32_t depth;
const SystemPath sysPath;
const GeoPatchID patchID;
const int edgeLen;
const double fracStep;
Terrain *pTerrain;
// quick hack, do not have in the final version!
GeoSphere *pGeoSphere;
vector3d *vertices[4];
vector3d *normals[4];
vector3d *colors[4];
};
struct SSplitResult {
struct SSplitResultData {
SSplitResultData(vector3d *v_, vector3d *n_, vector3d *c_, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, const GeoPatchID &patchID_) :
vertices(v_), normals(n_), colors(c_), v0(v0_), v1(v1_), v2(v2_), v3(v3_), patchID(patchID_)
{
}
SSplitResultData(const SSplitResultData &r) :
vertices(r.vertices), normals(r.normals), colors(r.colors), v0(r.v0), v1(r.v1), v2(r.v2), v3(r.v3), patchID(r.patchID)
{}
vector3d *vertices;
vector3d *normals;
vector3d *colors;
const vector3d v0;
const vector3d v1;
const vector3d v2;
const vector3d v3;
const GeoPatchID patchID;
};
SSplitResult(const int32_t face_, const uint32_t depth_) : face(face_), depth(depth_)
{
}
void addResult(vector3d *v_, vector3d *n_, vector3d *c_, const vector3d &v0_, const vector3d &v1_, const vector3d &v2_, const vector3d &v3_, const GeoPatchID &patchID_)
{
data.push_back(SSplitResultData(v_, n_, c_, v0_, v1_, v2_, v3_, patchID_));
assert(data.size()<=4);
}
const int32_t face;
const uint32_t depth;
std::deque<SSplitResultData> data;
};
#define NUM_PATCHES 6
class GeoSphere {
public:
@ -151,6 +60,12 @@ public:
bool AddSplitResult(SSplitResult *res);
void ProcessSplitResults();
void IncrementCurrentNumPatches() {++mCurrentNumPatches;}
void DecrementCurrentNumPatches() {--mCurrentNumPatches;}
void AddMemAllocatedToPatches(const uint64_t amt) {mCurrentMemAllocatedToPatches+=amt;}
void DelMemAllocatedToPatches(const uint64_t amt) {mCurrentMemAllocatedToPatches-=amt;}
private:
void BuildFirstPatches();
GeoPatch *m_patches[6];
@ -162,10 +77,12 @@ private:
static const uint32_t MAX_SPLIT_OPERATIONS = 128;
std::deque<SSplitRequestDescription*> mSplitRequestDescriptions;
std::deque<SSplitResult*> mSplitResult;
SDL_mutex *m_splitResultLock;
vector3d m_tempCampos;
uint32_t mCurrentNumPatches;
uint64_t mCurrentMemAllocatedToPatches;
inline vector3d GetColor(const vector3d &p, double height, const vector3d &norm) const {
return m_terrain->GetColor(p, height, norm);
}

View File

@ -16,7 +16,7 @@
#define DEF_THREADS 8
JobManager::JobManager(const int iNumThreads)
: mpContext(NULL), mPrevTasksRemaining(0), mTasksRemaining(0), mCurrentTaskID(0), mMaxNumTasks(0), mIncomingMutex(0)
: mpContext(NULL), mPrevTasksRemaining(0), mTasksRemaining(0), mCurrentTaskID(0), mMaxNumTasks(0), mNumTasksSoFar(0), mIncomingMutex(0)
{
// check and limit number of threads
int32_t numCoresToUse = std::min( std::max( getNumCores()-2, MIN_THREADS ), MAX_THREADS );
@ -64,11 +64,13 @@ void JobManager::update()
{
if (mIncomingJobs.size() > 0) {
mIncomingMutex->lock();
std::deque<TIncomingJobData>::iterator iter = mIncomingJobs.begin();
while(mIncomingJobs.end() != iter) {
std::deque<TIncomingJobData>::const_iterator iter = mIncomingJobs.begin();
std::deque<TIncomingJobData>::const_iterator itEnd = mIncomingJobs.end();
while(itEnd != iter) {
addIncomingJob((*iter).first, (*iter).second);
++iter;
}
mNumTasksSoFar += mIncomingJobs.size();
mIncomingJobs.clear();
mIncomingMutex->unlock();
}

View File

@ -64,7 +64,7 @@ class JobManager
public:
// terrain always adds jobs in blocks of 4 so we'll actually go over this value sometimes then settle back down to it.
// this is not a flaw or a problem it's just a way of limiting the number of jobs which was occassionally... insane.
static const unsigned int MAX_NUMBER_JOBS = (2048-3);
static const unsigned int MAX_NUMBER_JOBS = 1024;
static const unsigned int INVALID_JOB_HANDLE = UINT_MAX;
typedef unsigned int JobHandle;
@ -87,6 +87,7 @@ private:
JobHandle mTasksRemaining;
JobHandle mCurrentTaskID;
JobHandle mMaxNumTasks;
JobHandle mNumTasksSoFar;
typedef std::pair<PureJob*, unsigned char*> TIncomingJobData;
std::deque<TIncomingJobData> mIncomingJobs;

View File

@ -21,6 +21,8 @@
#include "fixed.h"
#include "Frame.h"
#include "gameconsts.h"
#include "GeoPatchContext.h"
#include "GeoPatch.h"
#include "GeoSphere.h"
//#include "glfreetype.h"
#include "gui/Gui.h"

View File

@ -35,7 +35,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v110</PlatformToolset>
</PropertyGroup>
@ -155,6 +155,8 @@
<ClCompile Include="..\..\src\Game.cpp" />
<ClCompile Include="..\..\src\GameConfig.cpp" />
<ClCompile Include="..\..\src\GameMenuView.cpp" />
<ClCompile Include="..\..\src\GeoPatch.cpp" />
<ClCompile Include="..\..\src\GeoPatchContext.cpp" />
<ClCompile Include="..\..\src\GeoPatchID.cpp" />
<ClCompile Include="..\..\src\GeoSphere.cpp" />
<ClCompile Include="..\..\src\HyperspaceCloud.cpp" />
@ -314,6 +316,8 @@
<ClInclude Include="..\..\src\GameConfig.h" />
<ClInclude Include="..\..\src\gameconsts.h" />
<ClInclude Include="..\..\src\GameMenuView.h" />
<ClInclude Include="..\..\src\GeoPatch.h" />
<ClInclude Include="..\..\src\GeoPatchContext.h" />
<ClInclude Include="..\..\src\GeoPatchID.h" />
<ClInclude Include="..\..\src\GeoSphere.h" />
<ClInclude Include="..\..\src\HyperspaceCloud.h" />

View File

@ -420,6 +420,12 @@
<ClCompile Include="..\..\src\GeoPatchID.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\src\GeoPatch.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\src\GeoPatchContext.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\Aabb.h">
@ -860,6 +866,12 @@
<ClInclude Include="..\..\src\GeoPatchID.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\..\src\GeoPatch.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\..\src\GeoPatchContext.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\..\src\Makefile.am">