Ben,
Here is my first pass at rendering buildings with holes. I have not checked
it in as there is a major hack in
FelkelIntersection::ApplyNonconvexIntersection. The hack disables a safety
check that is only valid for single polygon buildings but is definitely
needed. The code should not be a regression for single polygon buildings but
some multiple polygon buildings could fail or loop. It is one of those
problems that are extremely difficult to track down until you come across
some simple data that can reliably reproduce it. I have not got any and have
only tested this code against a simple handcrafted test case.
I have copied this to the list in the hope that someone else with a younger
brain and better math than me will try it out and review the code. I have
made this plea a number of times in the past and got no takers! So come on
someone, please step up to the plate.
I have not modified the code in Building3d.cpp that only invokes felkel for
buildings with more than 4 edges, so if you want felkel invoked for those
cases you will need to change that.
Roger
----------
//
// FelkelStraightSkeleton.cpp: implementation of the vtStraightSkeleton class.
//
// Copyright (c) 2003-2006 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//
// Straight skeleton algorithm and original implementation
// courtesy of Petr Felkel and Stepan Obdrzalek (petr.felkel@...)
// Re-implemented for the Virtual Terrain Project (vterrain.org)
// by Roger James (www.beardandsandals.co.uk)
//
#include "FelkelStraightSkeleton.h"
#include "vtdata/vtString.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
vtStraightSkeleton::vtStraightSkeleton()
{
}
vtStraightSkeleton::~vtStraightSkeleton()
{
}
CSkeleton& vtStraightSkeleton::MakeSkeleton(ContourVector &contours)
{
try
{
while (m_iq.size ())
m_iq.pop ();
m_vl.erase(m_vl.begin(), m_vl.end());
m_skeleton.erase(m_skeleton.begin(), m_skeleton.end());
m_boundaryedges.erase(m_boundaryedges.begin(), m_boundaryedges.end());
for (size_t ci = 0; ci < contours.size(); ci++)
{
Contour &points = contours[ci];
Contour::iterator first = points.begin();
if (first == points.end())
break;
Contour::iterator next = first;
while (++next != points.end ())
{
if (*first == *next)
points.erase (next);
else
first = next;
next = first;
}
int s = points.size();
CVertexList::iterator start = m_vl.end();
CVertexList::iterator from = start;
CVertexList::iterator to = start;
for (int f = 0; f <= s; f++)
{
if (0 == f)
{
m_vl.push_back(CVertex(points[0].m_Point, points[s - 1].m_Point, points[s -
1].m_Slope, points[1].m_Point, points[0].m_Slope));
to = m_vl.end();
to--;
start = to;
}
else if (f == s)
{
from = to;
to = start;
m_boundaryedges.push_front(CSkeletonLine(*from, *to));
}
else
{
from = to;
m_vl.push_back(CVertex(points[f].m_Point, points[f - 1].m_Point, points[f -
1].m_Slope, points[(f + 1) % s].m_Point, points[f].m_Slope));
to = m_vl.end();
to--;
m_boundaryedges.push_front(CSkeletonLine(*from, *to));
}
}
}
m_NumberOfBoundaryVertices = m_vl.size();
m_NumberOfBoundaryEdges = m_boundaryedges.size();
if (m_vl.size() < 3)
{
vtString *str = new vtString;
str->Format("%s (%d): Eave Polygon too small\n", __FILE__, __LINE__);
throw str;
}
CVertexList::iterator i;
size_t vn = 0, cn = 0;
CVertexList::iterator contourBegin;
for (i = m_vl.begin (); i != m_vl.end (); i++)
{
(*i).m_prevVertex = &*m_vl.prev(i);
(*i).m_nextVertex = &*m_vl.next(i);
(*i).m_leftVertex = &*i;
(*i).m_rightVertex = &*i;
if (vn == 0)
contourBegin = i;
if (vn == contours [cn].size () - 1)
{
(*i).m_nextVertex = &*contourBegin;
(*contourBegin).m_prevVertex = &*i;
vn = 0;
cn ++;
}
else
vn ++;
}
#ifdef FELKELDEBUG
VTLOG("Building initial intersection queue\n");
#endif
for (i = m_vl.begin(); i != m_vl.end (); i++)
{
if (!(*i).m_done)
{
CIntersection is(m_vl, *i);
if (is.m_height != CN_INFINITY)
m_iq.push(is);
}
}
#ifdef FELKELDEBUG
VTLOG("Processing intersection queue\n");
#endif
while (m_iq.size ())
{
CIntersection i = m_iq.top ();
m_iq.pop ();
#ifdef FELKELDEBUG
VTLOG("Processing %d %d left done %d right done %d\n",
i.m_leftVertex->m_ID, i.m_rightVertex->m_ID, i.m_leftVertex->m_done,
i.m_rightVertex->m_done);
#endif
if ((NULL == i.m_leftVertex) || (NULL == i.m_rightVertex))
{
vtString *str = new vtString;
str->Format("%s (%d): Invalid intersection queue entry\n", __FILE__,
__LINE__);
throw str;
}
if (i.m_leftVertex->m_done && i.m_rightVertex->m_done)
continue;
if (i.m_leftVertex->m_done || i.m_rightVertex->m_done)
{
if (!i.m_leftVertex->m_done)
m_iq.push(CIntersection (m_vl, *i.m_leftVertex));
if (!i.m_rightVertex->m_done)
m_iq.push(CIntersection (m_vl, *i.m_rightVertex));
continue;
}
#ifdef FELKELDEBUG
if (!(i.m_leftVertex->m_prevVertex != i.m_rightVertex))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
if (!(i.m_rightVertex->m_nextVertex != i.m_leftVertex))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
if (i.m_type == CIntersection::CONVEX)
if (i.m_leftVertex->m_prevVertex->m_prevVertex == i.m_rightVertex ||
i.m_rightVertex->m_nextVertex->m_nextVertex == i.m_leftVertex)
i.ApplyLast3(m_skeleton, m_vl);
else
i.ApplyConvexIntersection(m_skeleton, m_vl, m_iq);
if (i.m_type == CIntersection :: NONCONVEX)
i.ApplyNonconvexIntersection(m_skeleton, m_vl, m_iq, cn == 1);
}
#ifdef FELKELDEBUG
Dump();
#endif
FixSkeleton();
#ifdef FELKELDEBUG
Dump();
#endif
}
catch (vtString *str)
{
m_skeleton.erase(m_skeleton.begin(), m_skeleton.end());
VTLOG(*str);
delete str;
}
return m_skeleton;
}
CSkeleton& vtStraightSkeleton::MakeSkeleton(Contour &points)
{
ContourVector vv;
vv.push_back (points);
return MakeSkeleton(vv);
}
CSkeleton vtStraightSkeleton::CompleteWingedEdgeStructure(ContourVector
&contours)
{
// Save current skeleton
int iOldSize = m_skeleton.size();
int i;
CSkeleton::iterator si;
for (size_t ci = 0; ci < contours.size(); ci++)
{
Contour& points = contours[ci];
for (size_t pi = 0; pi < points.size(); pi++)
{
C3DPoint& LowerPoint = points[pi].m_Point;
C3DPoint& HigherPoint = points[(pi+1)%points.size()].m_Point;
// Find a matching empty lower left
for (i = 0, si = m_skeleton.begin(); i < iOldSize; i++, si++)
{
CSkeletonLine& Line = *si;
if ((Line.m_lower.m_vertex->m_point == LowerPoint) && (Line.m_lower.LeftID()
== -1))
break;
}
if (i == iOldSize)
{
VTLOG("CompleteWingedEdgeStructure - Failed to find matching empty lower
left\n");
return CSkeleton();
}
CSkeletonLine& OldLowerLeft = *si;
// Find a matching empty lower right
for (i = 0, si = m_skeleton.begin(); i < iOldSize; i++, si++)
{
CSkeletonLine& Line = *si;
if ((Line.m_lower.m_vertex->m_point == HigherPoint) &&
(Line.m_lower.RightID() == -1))
break;
}
if (i == iOldSize)
{
VTLOG("CompleteWingedEdgeStructure - Failed to find matching empty lower
right\n");
return CSkeleton();
}
CSkeletonLine& OldLowerRight = *si;
m_skeleton.push_back(CSkeletonLine(*OldLowerLeft.m_lower.m_vertex,
*OldLowerRight.m_lower.m_vertex));
CSkeletonLine& NewEdge = m_skeleton.back();
NewEdge.m_lower.m_right = &OldLowerLeft;
OldLowerLeft.m_lower.m_left = &NewEdge;
NewEdge.m_higher.m_left = &OldLowerRight;
OldLowerRight.m_lower.m_right = &NewEdge;
}
}
#ifdef FELKELDEBUG
Dump();
#endif
return m_skeleton;
}
void vtStraightSkeleton::FixSkeleton()
{
// Search the skeleton list for consecutive pairs of incorrectly linked lines
CSkeleton::iterator s1 = m_skeleton.begin();
for (unsigned int i = 0; i < m_skeleton.size() - 2; i++, s1++)
{
CSkeletonLine& Lower = *s1++;
CSkeletonLine& Higher1 = *s1++;
CSkeletonLine& Higher2 = *s1;
if ((Higher1.m_higher.RightID() == -1) &&
(Higher1.m_lower.LeftID() == -1) &&
(Higher2.m_higher.LeftID() == -1) &&
(Higher2.m_lower.RightID() == -1) &&
(Higher1.m_higher.VertexID() == Higher2.m_lower.VertexID()) &&
(Higher1.m_lower.VertexID() == Higher2.m_higher.VertexID())) // I don't think
I cam make this test much tighter !!!
{
CSkeletonLine* pLeft = Lower.m_higher.m_left;
CSkeletonLine* pRight = Lower.m_higher.m_right;
const CVertex* pVertex = Lower.m_higher.m_vertex;
if ((NULL == pLeft) || (NULL == pRight) || (NULL == pVertex))
{
vtString *str = new vtString;
str->Format("%s (%d): Problem fixing skeleton\n", __FILE__, __LINE__);
throw str;
}
// Fix up the left side
if ((pLeft->m_lower.VertexID() == pVertex->m_ID) ||
(pLeft->m_lower.VertexID() == pVertex->m_ID + 1))
{
// Fix up lower end
pLeft->m_lower.m_vertex = pVertex;
pLeft->m_lower.m_left = pRight;
if (pLeft->m_lower.RightID() != Lower.m_ID)
{
vtString *str = new vtString;
str->Format("%s (%d): Left Lower Right ID != Lower ID\n", __FILE__,
__LINE__);
throw str;
}
}
else if ((pLeft->m_higher.VertexID() == pVertex->m_ID) ||
(pLeft->m_higher.VertexID() == pVertex->m_ID + 1))
{
// Fix up upper end
pLeft->m_higher.m_vertex = pVertex;
pLeft->m_higher.m_left = pRight;
if (pLeft->m_higher.RightID() != Lower.m_ID)
{
vtString *str = new vtString;
str->Format("%s (%d): Left Higher Right ID != Lower ID\n", __FILE__,
__LINE__);
throw str;
}
}
else
{
vtString *str = new vtString;
str->Format("%s (%d): Problem fixing left side\n", __FILE__, __LINE__);
throw str;
}
// Fix up the right side
if ((pRight->m_lower.VertexID() == pVertex->m_ID) ||
(pRight->m_lower.VertexID() == pVertex->m_ID + 1))
{
// Fix up lower end
pRight->m_lower.m_vertex = pVertex;
pRight->m_lower.m_right = pLeft;
if (pRight->m_lower.LeftID() != Lower.m_ID)
{
vtString *str = new vtString;
str->Format("%s (%d): Right Lower Left ID != Lower ID\n", __FILE__,
__LINE__);
throw str;
}
}
else if ((pRight->m_higher.VertexID() == pVertex->m_ID) ||
(pRight->m_higher.VertexID() == pVertex->m_ID + 1))
{
// Fix up upper end
pRight->m_higher.m_vertex = pVertex;
pRight->m_higher.m_right = pLeft;
if (pRight->m_higher.LeftID() != Lower.m_ID)
{
vtString *str = new vtString;
str->Format("%s (%d): Right Higher Left ID != Lower ID\n", __FILE__,
__LINE__);
throw str;
}
}
else
{
vtString *str = new vtString;
str->Format("%s (%d): FixSkeleton - Problem fixing right side\n", __FILE__,
__LINE__);
throw str;
}
}
s1--;
s1--;
}
}
#ifdef FELKELDEBUG
void vtStraightSkeleton::Dump()
{
int i;
VTLOG("Skeleton:\n");
i = 0;
for (CSkeleton::iterator s1 = m_skeleton.begin(); s1 != m_skeleton.end(); s1++)
{
CSkeletonLine& db = (*s1);
VTLOG("ID: %d lower leftID %d rightID %d vertexID %d (%f %f %f)\nhigher leftID
%d rightID %d vertexID %d (%f %f %f)\n",
db.m_ID,
db.m_lower.LeftID(),
db.m_lower.RightID(),
db.m_lower.VertexID(), db.m_lower.m_vertex->m_point.m_x,
db.m_lower.m_vertex->m_point.m_y, db.m_lower.m_vertex->m_point.m_z,
db.m_higher.LeftID(),
db.m_higher.RightID(),
db.m_higher.VertexID(), db.m_higher.m_vertex->m_point.m_x,
db.m_higher.m_vertex->m_point.m_y, db.m_higher.m_vertex->m_point.m_z);
}
}
#endif
----------
//
// Building3d.cpp
//
// The vtBuilding3d class extends vtBuilding with the ability to procedurally
// create 3D geometry of the buildings.
//
// Copyright (c) 2001-2008 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//
#include "vtlib/vtlib.h"
#include "vtdata/HeightField.h"
#include "vtdata/Triangulate.h"
#include "vtdata/PolyChecker.h"
#include "Light.h"
#include "Terrain.h"
#include "Building3d.h"
#include "FelkelStraightSkeleton.h"
/////////////////////////////////////////////////////////////////////////////
vtBuilding3d::vtBuilding3d() : vtBuilding()
{
m_pContainer = NULL;
m_pGeom = NULL;
m_pHighlight = NULL;
}
vtBuilding3d::~vtBuilding3d()
{
// meshes will be automatically deleted by the geometry they're in
}
vtBuilding3d &vtBuilding3d::operator=(const vtBuilding &v)
{
// just call the copy method of the parent class
*((vtBuilding*)this) = v;
return *this;
}
//
// Convert the building's reference point into world coordinates.
//
void vtBuilding3d::UpdateWorldLocation(vtHeightField3d *pHeightField)
{
// Embed the building in the ground such that the lowest corner of its
// lowest level is at ground level.
float base_level = CalculateBaseElevation(pHeightField);
// Find the center of the building in world coordinates (the origin of
// the building's local coordinate system)
DPoint2 center;
GetBaseLevelCenter(center);
pHeightField->ConvertEarthToSurfacePoint(center, m_center);
m_center.y = base_level;
}
float vtBuilding3d::GetHeightOfStories()
{
float height = 0.0f;
int levs = m_Levels.GetSize();
for (int i = 0; i < levs; i++)
height += m_Levels[i]->m_iStories * m_Levels[i]->m_fStoryHeight;
return height;
}
void vtBuilding3d::DestroyGeometry()
{
if (!m_pGeom) // safety check
return;
m_pContainer->RemoveChild(m_pGeom);
m_pGeom->Release();
m_pGeom = NULL;
m_Mesh.Empty();
}
void vtBuilding3d::AdjustHeight(vtHeightField3d *pHeightField)
{
UpdateWorldLocation(pHeightField);
m_pContainer->SetTrans(m_center);
}
void vtBuilding3d::CreateUpperPolygon(vtLevel *lev, FPolygon3 &polygon,
FPolygon3 &polygon2)
{
int i, prev, next;
int rings = polygon.size();
polygon2 = polygon;
int base_edge = 0;
for (int ring = 0; ring < rings; ring++)
{
FLine3 &poly = polygon[ring];
FLine3 &poly2 = polygon2[ring];
int edges = poly.GetSize();
for (i = 0; i < edges; i++)
{
prev = (i-1 < 0) ? edges-1 : i-1;
next = (i+1 == edges) ? 0 : i+1;
FPoint3 p = poly[i];
int islope1 = lev->GetEdge(base_edge + prev)->m_iSlope;
int islope2 = lev->GetEdge(base_edge + i)->m_iSlope;
if (islope1 == 90 && islope2 == 90)
{
// easy case
p.y += lev->m_fStoryHeight;
}
else
{
float slope1 = (islope1 / 180.0f * PIf);
float slope2 = (islope2 / 180.0f * PIf);
// get edge vectors
FPoint3 vec1 = poly[prev] - poly[i];
FPoint3 vec2 = poly[next] - poly[i];
vec1.Normalize();
vec2.Normalize();
// get perpendicular (upward pointing) vectors
FPoint3 perp1, perp2;
perp1.Set(0, 1, 0);
perp2.Set(0, 1, 0);
// create rotation matrices to rotate them upward
FMatrix4 mat1, mat2;
mat1.Identity();
mat1.AxisAngle(vec1, -slope1);
mat2.Identity();
mat2.AxisAngle(vec2, slope2);
// create normals
FPoint3 norm1, norm2;
mat1.TransformVector(perp1, norm1);
mat2.TransformVector(perp2, norm2);
// vector of plane intersection is cross product of their normals
FPoint3 inter = norm1.Cross(norm2);
// Test that intersection vector is pointing into the polygon
// need a better test if we are going to handle downward sloping roofs
if (inter.y < 0)
inter = -inter; // Reverse vector to point upward
inter.Normalize();
inter *= (lev->m_fStoryHeight / inter.y);
p += inter;
}
poly2[i] = p;
}
base_edge += edges;
}
}
bool vtBuilding3d::CreateGeometry(vtHeightField3d *pHeightField)
{
PolyChecker PolyChecker;
int i;
unsigned int j, k;
UpdateWorldLocation(pHeightField);
// TEMP: we can handle complex polys now - i think
//if (!PolyChecker.IsSimplePolygon(GetLocalFootprint(0)))
// return false;
// create the edges (walls and roof)
float fHeight = 0.0f;
int iLevels = GetNumLevels();
int level_show = -1, edge_show = -1;
GetValueInt("level", level_show);
GetValueInt("edge", edge_show);
for (i = 0; i < iLevels; i++)
{
vtLevel *lev = m_Levels[i];
const FPolygon3 &foot = GetLocalFootprint(i);
unsigned int edges = lev->NumEdges();
// safety check
if (foot[0].GetSize() < 3)
return false;
if (lev->IsHorizontal())
{
// make flat roof
AddFlatRoof(foot, lev);
}
else if (lev->IsUniform())
{
int iHighlightEdge = level_show == i ? edge_show : -1;
CreateUniformLevel(i, fHeight, iHighlightEdge);
fHeight += lev->m_iStories * lev->m_fStoryHeight;
}
else if (lev->HasSlopedEdges() && edges > 4)
{
// For complicated roofs with sloped edges which meet at a
// roofline of uneven height, we need a sophisticated
// straight-skeleton solution like Petr Felkel's
float fRoofHeight = MakeFelkelRoof(foot, lev);
if (fRoofHeight < 0.0)
{
VTLOG("Failed to make Felkel roof - reverting to flat roof\n");
AddFlatRoof(foot, lev);
}
fHeight += fRoofHeight;
}
else
{
// Build a 'flat roof' for the floor
AddFlatRoof(foot, lev);
FPolygon3 poly = foot;
FPolygon3 poly2;
// Build a set of walls for each storey of the level
for (j = 0; j < lev->m_iStories; j++)
{
for (unsigned int r = 0; r < poly.size(); r++)
{
for (k = 0; k < poly[r].GetSize(); k++)
{
poly[r][k].y = fHeight;
}
}
CreateUpperPolygon(lev, poly, poly2);
int edge_start = 0;
for (unsigned int r = 0; r < poly.size(); r++)
{
for (k = edge_start; k < edge_start + poly[r].GetSize(); k++)
{
bool bShowEdge = (level_show == i && edge_show == k);
CreateEdgeGeometry(lev, poly, poly2, k, bShowEdge);
}
edge_start += poly[r].GetSize();
}
fHeight += lev->m_fStoryHeight;
}
}
}
#if 0 // testing
const FLine3 &roof = GetLocalFootprint(iLevels-1); // roof: top level
vtLevel *roof_lev = m_Levels[iLevels-1];
float roof_height = (roof_lev->m_fStoryHeight * roof_lev->m_iStories);
#endif
// wrap in a shape and set materials
m_pGeom = new vtGeom;
m_pGeom->SetName2("building-geom");
vtMaterialArray *pShared = GetSharedMaterialArray();
m_pGeom->SetMaterials(pShared);
for (j = 0; j < m_Mesh.GetSize(); j++)
{
vtMesh *mesh = m_Mesh[j].m_pMesh;
int index = m_Mesh[j].m_iMatIdx;
m_pGeom->AddMesh(mesh, index);
mesh->Release(); // pass ownership
}
// resize bounding box
if (m_pHighlight)
{
bool bEnabled = m_pHighlight->GetEnabled();
m_pContainer->RemoveChild(m_pHighlight);
m_pHighlight->Release();
FSphere sphere;
m_pGeom->GetBoundSphere(sphere);
m_pHighlight = CreateBoundSphereGeom(sphere);
m_pContainer->AddChild(m_pHighlight);
m_pHighlight->SetEnabled(bEnabled);
}
return true;
}
////////////////////////////////////////////////////////////////////////////
//
// Since each set of primitives with a specific material requires its own
// mesh, this method looks up or creates the mesh as needed.
//
vtMesh *vtBuilding3d::FindMatMesh(const vtString &Material,
const RGBi &color, vtMesh::PrimType ePrimType)
{
int mi;
int VertType;
RGBf fcolor = color;
// wireframe is a special case, used for highlight materials
if (ePrimType == vtMesh::LINE_STRIP)
{
mi = FindMatIndex(BMAT_NAME_HIGHLIGHT, fcolor);
VertType = 0;
}
else
{
// otherwise, find normal stored material
if (&Material == NULL)
mi = FindMatIndex(BMAT_NAME_PLAIN, fcolor);
else
mi = FindMatIndex(Material, fcolor);
VertType = VT_Normals | VT_TexCoords;
}
int i, size = m_Mesh.GetSize();
for (i = 0; i < size; i++)
{
if (m_Mesh[i].m_iMatIdx == mi && m_Mesh[i].m_ePrimType == ePrimType)
return m_Mesh[i].m_pMesh;
}
// didn't find it, so we need to make it
MatMesh mm;
mm.m_iMatIdx = mi;
mm.m_ePrimType = ePrimType;
// Potential Optimization: should calculate how many vertices the building
// will take. Even the simplest building will use 20 vertices, for now
// just use 40 as a reasonable starting point for each mesh.
mm.m_pMesh = new vtMesh(ePrimType, VertType, 40);
m_Mesh.Append(mm);
return mm.m_pMesh;
}
//
// Edges are created from a series of features ("panels", "sections")
//
void vtBuilding3d::CreateEdgeGeometry(vtLevel *pLev, const FPolygon3 &polygon1,
const FPolygon3 &polygon2, int iEdge, bool bShowEdge)
{
// Get edge from complete list
vtEdge *pEdge = pLev->GetEdge(iEdge);
// Then determine which ring its on
int ring = polygon1.WhichRing(iEdge);
const FLine3 &poly1 = polygon1[ring];
const FLine3 &poly2 = polygon2[ring];
// number of edges in this ring
int num_edges = poly1.GetSize();
int i = iEdge, j = (i+1)%num_edges;
FLine3 quad(4);
// start with the whole wall section
quad[0] = poly1[i];
quad[1] = poly1[j];
quad[2] = poly2[i];
quad[3] = poly2[j];
// length of the edge
FPoint3 dir1 = quad[1] - quad[0];
FPoint3 dir2 = quad[3] - quad[2];
float total_length1 = dir1.Length();
float total_length2 = dir2.Length();
if (total_length1 > 0.0f)
dir1.Normalize();
if (total_length2 > 0.0f)
dir2.Normalize();
if (bShowEdge)
{
AddHighlightSection(pEdge, quad);
}
// How wide should each feature be?
// Determine how much space we have for the proportional features after
// accounting for the fixed-width features
float fixed_width = pEdge->FixedFeaturesWidth();
float total_prop = pEdge->ProportionTotal();
float dyn_width = total_length1 - fixed_width;
if (pEdge->m_Facade != "")
{
// If we can successfully construct the facade, we don't need to
// use the edge features.
if (MakeFacade(pEdge, quad, 1))
return;
}
// build the edge features.
// point[0] is the first starting point of a panel.
for (i = 0; i < pEdge->NumFeatures(); i++)
{
vtEdgeFeature &feat = pEdge->m_Features[i];
// determine real width
float meter_width = 0.0f;
if (feat.m_width >= 0)
meter_width = feat.m_width;
else
meter_width = (feat.m_width / total_prop) * dyn_width;
quad[1] = quad[0] + dir1 * meter_width;
quad[3] = quad[2] + dir2 * (meter_width * total_length2 / total_length1);
if (feat.m_code == WFC_WALL)
{
AddWallNormal(pEdge, &feat, quad);
}
if (feat.m_code == WFC_GAP)
{
// do nothing
}
if (feat.m_code == WFC_POST)
{
// TODO
}
if (feat.m_code == WFC_WINDOW)
{
AddWindowSection(pEdge, &feat, quad);
}
if (feat.m_code == WFC_DOOR)
{
AddDoorSection(pEdge, &feat, quad);
}
quad[0] = quad[1];
quad[2] = quad[3];
}
}
/**
* Creates geometry for a highlighted area (an edge).
*/
void vtBuilding3d::AddHighlightSection(vtEdge *pEdge,
const FLine3 &quad)
{
// determine 4 points at corners of wall section
FPoint3 p0 = quad[0];
FPoint3 p1 = quad[1];
FPoint3 p3 = quad[2];
FPoint3 p2 = quad[3];
vtMesh *mesh = FindMatMesh(BMAT_NAME_PLAIN, RGBi(255,255,255),
vtMesh::LINE_STRIP);
// determine normal (not used for shading)
FPoint3 norm = Normal(p0,p1,p2);
int start =
mesh->AddVertex(p0 + norm);
mesh->AddVertex(p1 + norm);
mesh->AddVertex(p2 + norm);
mesh->AddVertex(p3 + norm);
mesh->AddVertex(p0 + norm);
mesh->AddFan(start, start+1, start+2, start+3, start+4);
start = mesh->AddVertex(p0);
mesh->AddVertex(p0 + norm);
mesh->AddFan(start, start+1);
start = mesh->AddVertex(p1);
mesh->AddVertex(p1 + norm);
mesh->AddFan(start, start+1);
start = mesh->AddVertex(p2);
mesh->AddVertex(p2 + norm);
mesh->AddFan(start, start+1);
start = mesh->AddVertex(p3);
mesh->AddVertex(p3 + norm);
mesh->AddFan(start, start+1);
norm *= 0.95f;
mesh = FindMatMesh(BMAT_NAME_PLAIN, RGBi(255,0,0), vtMesh::LINE_STRIP);
start =
mesh->AddVertex(p0 + norm);
mesh->AddVertex(p1 + norm);
mesh->AddVertex(p2 + norm);
mesh->AddVertex(p3 + norm);
mesh->AddVertex(p0 + norm);
mesh->AddFan(start, start+1, start+2, start+3, start+4);
}
/**
* Builds a wall, given material index, starting and end points, height, and
* starting height.
*/
void vtBuilding3d::AddWallSection(vtEdge *pEdge, bool bUniform,
const FLine3 &quad, float vf1, float vf2, float hf1)
{
// determine 4 points at corners of wall section
FPoint3 up1 = (quad[2] - quad[0]);
FPoint3 up2 = (quad[3] - quad[1]);
FPoint3 p0 = quad[0] + (up1 * vf1);
FPoint3 p1 = quad[1] + (up2 * vf1);
FPoint3 p3 = quad[0] + (up1 * vf2);
FPoint3 p2 = quad[1] + (up2 * vf2);
vtMesh *mesh;
if (bUniform)
mesh = FindMatMesh(BMAT_NAME_WINDOWWALL, pEdge->m_Color,
vtMesh::TRIANGLE_FAN);
else
mesh = FindMatMesh(*pEdge->m_pMaterial, pEdge->m_Color, vtMesh::TRIANGLE_FAN);
// determine normal and primary axes of the face
FPoint3 norm = Normal(p0, p1, p2);
FPoint3 axis0, axis1;
axis0 = p1 - p0;
axis0.Normalize();
axis1 = norm.Cross(axis0);
// determine UVs - special case for window-wall texture
FPoint2 uv0, uv1, uv2, uv3;
if (bUniform)
{
uv0.Set(0, 0);
uv1.Set(hf1, 0);
uv2.Set(hf1, vf2);
uv3.Set(0, vf2);
}
else
{
float u1 = (p1 - p0).Dot(axis0);
float u2 = (p2 - p0).Dot(axis0);
float u3 = (p3 - p0).Dot(axis0);
float v2 = (p2 - p0).Dot(axis1);
vtMaterialDescriptor *md =
s_MaterialDescriptors.FindMaterialDescriptor(*pEdge->m_pMaterial,
pEdge->m_Color);
uv0.Set(0, 0);
uv1.Set(u1, 0);
uv2.Set(u2, v2);
uv3.Set(u3, v2);
if (md != NULL)
{
// divide meters by [meters/uv] to get uv
FPoint2 UVScale = md->GetUVScale();
uv0.Div(UVScale);
uv1.Div(UVScale);
uv2.Div(UVScale);
uv3.Div(UVScale);
}
}
int start =
mesh->AddVertexNUV(p0, norm, uv0);
mesh->AddVertexNUV(p1, norm, uv1);
mesh->AddVertexNUV(p2, norm, uv2);
mesh->AddVertexNUV(p3, norm, uv3);
mesh->AddFan(start, start+1, start+2, start+3);
}
void vtBuilding3d::AddWallNormal(vtEdge *pEdge, vtEdgeFeature *pFeat,
const FLine3 &quad)
{
float vf1 = pFeat->m_vf1;
float vf2 = pFeat->m_vf2;
AddWallSection(pEdge, false, quad, vf1, vf2);
}
/**
* Builds a door section. will also build the wall above the door to ceiling
* height.
*/
void vtBuilding3d::AddDoorSection(vtEdge *pEdge, vtEdgeFeature *pFeat,
const FLine3 &quad)
{
float vf1 = 0;
float vf2 = pFeat->m_vf2;
// determine 4 points at corners of section
FPoint3 up1 = (quad[2] - quad[0]);
FPoint3 up2 = (quad[3] - quad[1]);
FPoint3 p0 = quad[0] + (up1 * vf1);
FPoint3 p1 = quad[1] + (up2 * vf1);
FPoint3 p3 = quad[0] + (up1 * vf2);
FPoint3 p2 = quad[1] + (up2 * vf2);
vtMesh *mesh = FindMatMesh(BMAT_NAME_DOOR, pEdge->m_Color,
vtMesh::TRIANGLE_FAN);
// determine normal (flat shading, all vertices have the same normal)
FPoint3 norm = Normal(p0, p1, p2);
int start =
mesh->AddVertexNUV(p0, norm, FPoint2(0.0f, 0.0f));
mesh->AddVertexNUV(p1, norm, FPoint2(1.0f, 0.0f));
mesh->AddVertexNUV(p2, norm, FPoint2(1.0f, 1.0f));
mesh->AddVertexNUV(p3, norm, FPoint2(0.0f, 1.0f));
mesh->AddFan(start, start+1, start+2, start+3);
//add wall above door
AddWallSection(pEdge, false, quad, vf2, 1.0f);
}
//builds a window section. builds the wall below and above a window too.
void vtBuilding3d::AddWindowSection(vtEdge *pEdge, vtEdgeFeature *pFeat,
const FLine3 &quad)
{
float vf1 = pFeat->m_vf1;
float vf2 = pFeat->m_vf2;
// build wall to base of window.
AddWallSection(pEdge, false, quad, 0, vf1);
// build wall above window
AddWallSection(pEdge, false, quad, vf2, 1.0f);
// determine 4 points at corners of section
FPoint3 up1 = (quad[2] - quad[0]);
FPoint3 up2 = (quad[3] - quad[1]);
FPoint3 p0 = quad[0] + (up1 * vf1);
FPoint3 p1 = quad[1] + (up2 * vf1);
FPoint3 p3 = quad[0] + (up1 * vf2);
FPoint3 p2 = quad[1] + (up2 * vf2);
vtMesh *mesh = FindMatMesh(BMAT_NAME_WINDOW, pEdge->m_Color,
vtMesh::TRIANGLE_FAN);
// determine normal (flat shading, all vertices have the same normal)
FPoint3 norm = Normal(p0,p1,p2);
int start =
mesh->AddVertexNUV(p0, norm, FPoint2(0.0f, 0.0f));
mesh->AddVertexNUV(p1, norm, FPoint2(1.0f, 0.0f));
mesh->AddVertexNUV(p2, norm, FPoint2(1.0f, 1.0f));
mesh->AddVertexNUV(p3, norm, FPoint2(0.0f, 1.0f));
mesh->AddFan(start, start+1, start+2, start+3);
}
void vtBuilding3d::AddFlatRoof(const FPolygon3 &pp, vtLevel *pLev)
{
FPoint3 up(0.0f, 1.0f, 0.0f); // vector pointing up
int rings = pp.size();
int outer_corners = pp[0].GetSize();
int i, j;
FPoint2 uv;
vtEdge *pEdge = pLev->GetEdge(0);
const vtString& Material = *pEdge->m_pMaterial;
vtMesh *mesh = FindMatMesh(Material, pEdge->m_Color, vtMesh::TRIANGLES);
vtMaterialDescriptor *md =
s_MaterialDescriptors.FindMaterialDescriptor(Material, pEdge->m_Color);
if (outer_corners > 4 || rings > 1)
{
// roof consists of a polygon which must be split into triangles
// Invoke the triangulator to triangulate this polygon.
#if 1
// Use 'Triangle'
const FLine3 &outer = pp[0];
float roof_y = outer[0].y;
DPolygon2 foot2d;
ProjectionXZ(pp, foot2d);
// Triangle has been known to behave poorly with redundant vertices
// We are in meters now, so we can use a centimeter epsilon.
int removed = foot2d.RemoveDegeneratePoints(0.08);
if (removed)
VTLOG("Skipped %d redundant vertices.\n", removed);
// a polyline to hold the answer in sets of three points
DLine2 result2d;
CallTriangle(foot2d, result2d);
FLine3 result;
ProjectionXZ(result2d, roof_y, result);
#else
// Use older, simpler triangulator
FLine3 outer = pp[0];
FLine2 roof;
roof.SetMaxSize(outer_corners);
for (i = 0; i < outer_corners; i++)
roof.Append(FPoint2(outer[i].x, outer[i].z));
float roof_y = outer[0].y;
FLine2 result;
Triangulate_f::Process(roof, result);
#endif
// use the results.
int tcount = result.GetSize()/3;
int ind[3];
FPoint2 gp;
FPoint3 p;
for (i=0; i<tcount; i++)
{
for (j = 0; j < 3; j++)
{
p = result[i*3+j];
uv.Set(p.x, p.z);
if (md)
uv.Div(md->GetUVScale()); // divide meters by [meters/uv] to get uv
ind[j] = mesh->AddVertexNUV(p, up, uv);
}
mesh->AddTri(ind[0], ind[1], ind[2]);
}
}
else
{
int idx[MAX_WALLS];
for (i = 0; i < outer_corners; i++)
{
FPoint3 p = pp[0][i];
uv.Set(p.x, p.z);
if (md)
uv.Div(md->GetUVScale()); // divide meters by [meters/uv] to get uv
idx[i] = mesh->AddVertexNUV(p, up, uv);
}
if (outer_corners > 2)
mesh->AddTri(idx[0], idx[1], idx[2]);
if (outer_corners > 3)
mesh->AddTri(idx[2], idx[3], idx[0]);
}
}
float vtBuilding3d::MakeFelkelRoof(const FPolygon3 &EavePolygons, vtLevel *pLev)
{
vtStraightSkeleton StraightSkeleton;
CSkeleton Skeleton;
float fMaxHeight = 0.0;
ContourVector RoofEaves(EavePolygons.size());
int i;
CSkeletonLine *pStartEdge;
CSkeletonLine *pEdge;
CSkeletonLine *pNextEdge;
bool bEdgeReversed;
float EaveY = EavePolygons[0][0].y;
#ifdef FELKELDEBUG
float DebugX;
float DebugY;
float DebugZ;
#endif
// Make a roof using felkels straight skeleton algorithm
// First of all build the eave footprint.
ContourVector::iterator itV = RoofEaves.begin();
for (FPolygon3::const_iterator itP = EavePolygons.begin(); itP !=
EavePolygons.end(); itP++, itV++)
{
int iVertices = (*itP).GetSize();
for (i = 0; i < iVertices; i++)
{
FPoint3 CurrentPoint = (*itP)[i];
FPoint3 NextPoint = (*itP)[(i+1)%iVertices];
FPoint3 PreviousPoint = (*itP)[(iVertices+i-1)%iVertices];
int iSlope = pLev->GetEdge(i)->m_iSlope;
if (iSlope > 89)
iSlope = 90;
else if (iSlope < 1)
iSlope = 0;
int iPrevSlope = pLev->GetEdge((iVertices+i-1)%iVertices)->m_iSlope;
if (iPrevSlope > 89)
iPrevSlope = 90;
else if (iPrevSlope < 1)
iPrevSlope = 0;
// If edges are in line and slopes are different then
if ((iPrevSlope != iSlope)
&& Collinear2d(PreviousPoint, CurrentPoint, NextPoint))
{
#ifdef FELKELDEBUG
VTLOG("Adding dummy eave segment at %d\n", i);
#endif
// Duplicate the current edge vector
FPoint3 OldEdge = NextPoint - CurrentPoint;
FPoint3 NewEdge;
int iNewSlope;
if (iSlope > iPrevSlope)
{
// Rotate new vertex inwards (clockwise)
NewEdge.x = OldEdge.z;
NewEdge.z = -OldEdge.x;
iNewSlope = iPrevSlope;
}
else
{
// Rotate new vertext outwards (anticlockwise)
NewEdge.x = -OldEdge.z;
NewEdge.z = OldEdge.x;
iNewSlope = iSlope;
}
// Scale to .01 of a co-ord unit
NewEdge.Normalize();
NewEdge = NewEdge/100.0f;
NewEdge += CurrentPoint;
(*itV).push_back(CEdge(NewEdge.x, 0, NewEdge.z,
iNewSlope / 180.0f * PIf, pLev->GetEdge(i)->m_pMaterial,
pLev->GetEdge(i)->m_Color));
}
(*itV).push_back(CEdge(CurrentPoint.x, 0, CurrentPoint.z,
iSlope / 180.0f * PIf, pLev->GetEdge(i)->m_pMaterial,
pLev->GetEdge(i)->m_Color));
}
}
// Now build the skeleton
StraightSkeleton.MakeSkeleton(RoofEaves);
if (0 == StraightSkeleton.m_skeleton.size())
return -1.0;
// Merge the original eaves back into the skeleton
Skeleton = StraightSkeleton.CompleteWingedEdgeStructure(RoofEaves);
if (0 == Skeleton.size())
return -1.0;
#ifdef FELKELDEBUG
VTLOG("Building Geometry\n");
#endif
// TODO - texture co-ordinates
// Build the geometry
for (size_t ci = 0; ci < RoofEaves.size(); ci++)
{
Contour& points = RoofEaves[ci];
for (size_t pi = 0; pi < points.size(); pi++)
{
// For each boundary edge zip round the polygon anticlockwise
// and build the vertex array
const vtString bmat = *points[pi].m_pMaterial;
vtMesh *pMesh = FindMatMesh(bmat, points[pi].m_Color, vtMesh::TRIANGLES);
vtMaterialDescriptor *pMd =
s_MaterialDescriptors.FindMaterialDescriptor(bmat, points[pi].m_Color);
FPoint2 UVScale;
if (NULL != pMd)
UVScale = pMd->GetUVScale();
else
UVScale = FPoint2(1.0, 1.0);
FLine3 RoofSection3D;
FLine3 TriangulatedRoofSection3D;
int iTriangleCount = 0;
FPoint3 PanelNormal;
FPoint3 UAxis;
FPoint3 VAxis;
FPoint3 TextureOrigin;
int i, j;
vtArray<int> iaVertices;
C3DPoint& p1 = points[pi].m_Point;
C3DPoint& p2 = points[(pi+1)%points.size()].m_Point;
// Find the starting edge
CSkeleton::iterator s1;
for (s1 = Skeleton.begin(); s1 != Skeleton.end(); s1++)
{
if (((*s1).m_lower.m_vertex->m_point == p1) &&
((*s1).m_higher.m_vertex->m_point == p2))
break;
}
if (s1 == Skeleton.end())
break;
pStartEdge = &(*s1);
pEdge = pStartEdge;
bEdgeReversed = false;
#ifdef FELKELDEBUG
VTLOG("Building panel\n");
#endif
unsigned int iNumberofPoints = 0;
do
{
if (iNumberofPoints++ > Skeleton.size())
{
VTLOG("MakeFelkelRoof - Roof geometry too complex - giving up\n");
return -1.0;
}
if (bEdgeReversed)
{
#ifdef FELKELDEBUG
DebugX = pEdge->m_higher.m_vertex->m_point.m_x;
DebugY = pEdge->m_higher.m_vertex->m_point.m_y;
DebugZ = pEdge->m_higher.m_vertex->m_point.m_z;
#endif
if (pEdge->m_higher.m_vertex->m_point.m_z > (double)fMaxHeight)
fMaxHeight = (float) pEdge->m_higher.m_vertex->m_point.m_z;
RoofSection3D.Append(FPoint3(pEdge->m_higher.m_vertex->m_point.m_x,
pEdge->m_higher.m_vertex->m_point.m_y + EaveY,
pEdge->m_higher.m_vertex->m_point.m_z));
pNextEdge = pEdge->m_higher.m_right;
// if (pEdge->m_higher.m_vertex->m_point !=
pNextEdge->m_higher.m_vertex->m_point)
if (pEdge->m_higher.VertexID() != pNextEdge->m_higher.VertexID())
bEdgeReversed = true;
else
bEdgeReversed = false;
}
else
{
#ifdef FELKELDEBUG
DebugX = pEdge->m_lower.m_vertex->m_point.m_x;
DebugY = pEdge->m_lower.m_vertex->m_point.m_y;
DebugZ = pEdge->m_lower.m_vertex->m_point.m_z;
#endif
if (pEdge->m_lower.m_vertex->m_point.m_z > (double)fMaxHeight)
fMaxHeight = (float) pEdge->m_lower.m_vertex->m_point.m_z;
RoofSection3D.Append(FPoint3(pEdge->m_lower.m_vertex->m_point.m_x,
pEdge->m_lower.m_vertex->m_point.m_y + EaveY,
pEdge->m_lower.m_vertex->m_point.m_z));
pNextEdge = pEdge->m_lower.m_right;
// if (pEdge->m_lower.m_vertex->m_point !=
pNextEdge->m_higher.m_vertex->m_point)
if (pEdge->m_lower.VertexID() != pNextEdge->m_higher.VertexID())
bEdgeReversed = true;
else
bEdgeReversed = false;
}
#ifdef FELKELDEBUG
VTLOG("Adding point (ID %d) x %e y %e z %e\n", pEdge->m_ID, DebugX, DebugY,
DebugZ);
#endif
pEdge = pNextEdge;
}
// For some reason the pointers dont end up quite the same
// I will work it out someday
while (pEdge->m_ID != pStartEdge->m_ID);
// Remove any vertices that are the same
for (i = 0; i < (int)RoofSection3D.GetSize(); i++)
{
FPoint3& Point = RoofSection3D[i];
for (j = i + 1; j < (int)RoofSection3D.GetSize(); j++)
{
FPoint3& NextPoint = RoofSection3D[j];
if (NextPoint == Point)
{
RoofSection3D.RemoveAt(j);
j--;
}
}
}
// determine normal and primary axes of the face
j = RoofSection3D.GetSize();
PanelNormal = Normal(RoofSection3D[1], RoofSection3D[0], RoofSection3D[j-1]);
UAxis = FPoint3(RoofSection3D[j-1] - RoofSection3D[0]).Normalize();
VAxis = PanelNormal.Cross(UAxis);
TextureOrigin = RoofSection3D[0];
#ifdef FELKELDEBUG
VTLOG("Panel normal x %e y %e z %e\n", PanelNormal.x, PanelNormal.y,
PanelNormal.z);
#endif
// Build transform to rotate plane parallel to the xz plane.
// N.B. this only work with angles from the plane normal to the y axis
// in the rangle 0 to pi/2 (this is ok for roofs). If you want
// it to work over a greater range you will have to mess with the sign of the
cosine
// of this angle.
float fHypot = sqrtf(PanelNormal.x * PanelNormal.x + PanelNormal.z *
PanelNormal.z);
FMatrix3 Transform;
Transform.SetRow(0, PanelNormal.x * PanelNormal.y / fHypot, PanelNormal.x,
-PanelNormal.z / fHypot);
Transform.SetRow(1, -fHypot, PanelNormal.y, 0);
Transform.SetRow(2, PanelNormal.z * PanelNormal.y / fHypot, PanelNormal.z,
PanelNormal.x / fHypot);
// Build vertex list
for (i = 0; i < j; i++)
{
FPoint3 Vertex = RoofSection3D[i];
FPoint2 UV = FPoint2((Vertex - TextureOrigin).Dot(UAxis), (Vertex -
TextureOrigin).Dot(VAxis));
UV.Div(UVScale);
iaVertices.Append(pMesh->AddVertexNUV(Vertex, PanelNormal, UV));
}
for (i = 0; i < j; i++)
{
// Source and dest cannot be the same
FPoint3 Temp = RoofSection3D[i];
Transform.Transform(Temp, RoofSection3D[i]);
}
Triangulate_f::Process(RoofSection3D, TriangulatedRoofSection3D);
iTriangleCount = TriangulatedRoofSection3D.GetSize() / 3;
for (i = 0; i < iTriangleCount; i++)
{
int iaIndex[3];
for (j = 0; j < 3; j++)
{
FPoint3 Point = TriangulatedRoofSection3D[i * 3 + j];
if (-1 == (iaIndex[j] = FindVertex(Point, RoofSection3D, iaVertices)))
return -1.0;
}
pMesh->AddTri(iaIndex[0], iaIndex[2], iaIndex[1]);
#ifdef FELKELDEBUG
VTLOG("AddTri1 %d %d %d\n", iaIndex[0], iaIndex[2], iaIndex[1]);
#endif
}
}
}
return fMaxHeight;
}
bool vtBuilding3d::Collinear2d(const FPoint3& Previous, const FPoint3& Current,
const FPoint3& Next)
{
FPoint3 l1 = Previous - Current;
FPoint3 l2 = Next - Current;
l1.y = 0;
l2.y = 0;
l1.Normalize();
l2.Normalize();
float CosTheta = l1.Dot(l2);
if (CosTheta < -1.0)
CosTheta = -1.0;
else if (CosTheta > 1.0)
CosTheta = 1.0;
float fTheta = acosf(CosTheta) / PIf * 180;
if (fabs(fTheta - 180.0) < 1.0)
return true;
else
return false;
}
int vtBuilding3d::FindVertex(FPoint3 Point, FLine3 &RoofSection3D,
vtArray<int> &iaVertices)
{
int iSize = RoofSection3D.GetSize();
int i;
for (i = 0; i < iSize; i++)
{
if ((Point.x == RoofSection3D[i].x) &&
(Point.y == RoofSection3D[i].y) &&
(Point.z == RoofSection3D[i].z))
break;
}
if (i < iSize)
return iaVertices[i];
else
{
VTLOG("FindVertex - vertex not found\n");
return -1;
}
}
//
// Walls which consist of regularly spaced windows and 'siding' material
// can be modelled far more efficiently. This is very useful for rendering
// speed for large scenes in which the user doesn't have or doesn't care
// about the exact material/windows of the buildings. We create
// optimized geometry in which each whole wall is a single quad.
//
void vtBuilding3d::CreateUniformLevel(int iLevel, float fHeight,
int iHighlightEdge)
{
vtLevel *pLev = m_Levels[iLevel];
const FPolygon3 &polygon1 = GetLocalFootprint(iLevel);
int i;
int base_edge = 0;
for (unsigned int ring = 0; ring < polygon1.size(); ring++)
{
FLine3 poly1 = polygon1[ring];
FLine3 poly2;
int edges = poly1.GetSize();
for (i = 0; i < edges; i++)
poly1[i].y = fHeight;
poly2 = poly1;
for (i = 0; i < edges; i++)
poly2[i].y += pLev->m_fStoryHeight;
for (i = 0; i < edges; i++)
{
int a = i, b = (a+1)%edges;
FLine3 quad(4);
vtEdge *pEdge = pLev->GetEdge(base_edge+i);
// do the whole wall section
quad[0] = poly1[a];
quad[1] = poly1[b];
quad[2] = poly2[a];
quad[3] = poly2[b];
if (pEdge->m_Facade != "")
{
float extraheight = pLev->m_fStoryHeight * (pLev->m_iStories-1);
quad[2].y += extraheight;
quad[3].y += extraheight;
// If we can successfully construct the facade, we don't need to
// use the edge features.
if (MakeFacade(pEdge, quad, pLev->m_iStories))
continue;
}
quad[2] = poly2[a];
quad[3] = poly2[b];
float h1 = 0.0f;
float h2 = (float) pLev->m_iStories;
float hf1 = (float) pEdge->NumFeaturesOfCode(WFC_WINDOW);
AddWallSection(pEdge, true, quad, h1, h2, hf1);
if (base_edge+i == iHighlightEdge)
{
for (unsigned int j = 0; j < pLev->m_iStories; j++)
{
AddHighlightSection(pEdge, quad);
quad[0].y += pLev->m_fStoryHeight;
quad[1].y += pLev->m_fStoryHeight;
quad[2].y += pLev->m_fStoryHeight;
quad[3].y += pLev->m_fStoryHeight;
}
}
}
base_edge += edges;
}
}
bool vtBuilding3d::MakeFacade(vtEdge *pEdge, FLine3 &quad, int stories)
{
// Paint a facade on this edge
// Add the facade image to the materials array
// Assume quad is ordered 0,1,3,2
MatMesh mm;
FPoint3 norm = Normal(quad[0],quad[1],quad[3]);
vtString fname = "Facade/";
fname += pEdge->m_Facade;
fname = FindFileOnPaths(vtGetDataPath(), (pcchar)fname);
if (fname == "")
{
// Older files may have their facades in 'BuildingModels'
fname = "BuildingModels/";
fname += pEdge->m_Facade;
fname = FindFileOnPaths(vtGetDataPath(), (pcchar)fname);
}
if (fname == "")
{
VTLOG(" Couldn't find facade texture '%s'\n", (const char*)pEdge->m_Facade);
return false;
}
mm.m_iMatIdx = GetSharedMaterialArray()->AddTextureMaterial2(fname,
true, true, false, false,
TERRAIN_AMBIENT,
TERRAIN_DIFFUSE,
1.0f, // alpha
TERRAIN_EMISSIVE);
// Create a mesh for the new material and add this to the mesh array
mm.m_pMesh = new vtMesh(vtMesh::TRIANGLE_FAN, VT_Normals | VT_TexCoords, 6);
m_Mesh.Append(mm);
// Calculate the vertices and add them to the mesh
float v = (float) stories;
int start = mm.m_pMesh->AddVertexNUV(quad[0], norm, FPoint2(0.0f, 0.0f));
mm.m_pMesh->AddVertexNUV(quad[1], norm, FPoint2(1.0f, 0.0f));
mm.m_pMesh->AddVertexNUV(quad[3], norm, FPoint2(1.0f, v));
mm.m_pMesh->AddVertexNUV(quad[2], norm, FPoint2(0.0f, v));
mm.m_pMesh->AddFan(start, start+1, start+2, start+3);
return true;
}
FPoint3 vtBuilding3d::Normal(const FPoint3 &p0, const FPoint3 &p1, const FPoint3
&p2)
{
FPoint3 a = p0 - p1;
FPoint3 b = p2 - p1;
FPoint3 norm = b.Cross(a);
norm.Normalize();
return norm;
}
//
// Randomize buildings characteristics
//
void vtBuilding3d::Randomize(int iStories)
{
RGBi color;
color = GetColor(BLD_BASIC);
if (color.r == -1 && color.g == -1 && color.b == -1)
{
// unset color
// random pastel color
unsigned char r, g, b;
r = (unsigned char) (128 + random(127));
g = (unsigned char) (128 + random(127));
b = (unsigned char) (128 + random(127));
SetColor(BLD_BASIC, RGBi(r, g, b));
}
color = GetColor(BLD_ROOF);
if (color.r == -1 && color.g == -1 && color.b == -1)
{
// unset color
// random roof color
int r = rand() %5;
switch (r) {
case 0: color.Set(255, 255, 250); break; //off-white
case 1: color.Set(153, 51, 51); break; //reddish
case 2: color.Set(153, 153, 255); break; //blue-ish
case 3: color.Set(153, 255, 153); break; //green-ish
case 4: color.Set(178, 102, 51); break; //brown
}
SetColor(BLD_ROOF, color);
}
}
/**
* Creates the geometry for the building.
* Capable of several levels of detail (defaults to full detail).
* If the geometry was already built previously, it is destroyed and re-created.
*
* \param pTerr The terrain on which to plant the building.
*/
bool vtBuilding3d::CreateNode(vtTerrain *pTerr)
{
if (m_pContainer)
{
// was build before; re-build geometry
DestroyGeometry();
}
else
{
// constructing for the first time
m_pContainer = new vtTransform;
m_pContainer->SetName2("building container");
}
if (!CreateGeometry(pTerr->GetHeightField()))
return false;
m_pContainer->AddChild(m_pGeom);
m_pContainer->SetTrans(m_center);
return true;
}
bool vtBuilding3d::IsCreated()
{
return (m_pContainer != NULL);
}
void vtBuilding3d::DeleteNode()
{
if (m_pContainer)
{
DestroyGeometry();
m_pContainer->Release();
m_pContainer = NULL;
}
}
/**
* Display some bounding wires around the object to highlight it.
*/
void vtBuilding3d::ShowBounds(bool bShow)
{
if (bShow)
{
if (!m_pHighlight)
{
// the highlight geometry doesn't exist, so create it
// get bounding sphere
FSphere sphere;
m_pGeom->GetBoundSphere(sphere);
m_pHighlight = CreateBoundSphereGeom(sphere);
m_pContainer->AddChild(m_pHighlight);
}
m_pHighlight->SetEnabled(true);
}
else
{
if (m_pHighlight)
m_pHighlight->SetEnabled(false);
}
}
----------
//
// FelkelIntersection.cpp: implementation of the CIntersection class.
//
// Copyright (c) 2003-2006 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//
// Straight skeleton algorithm and original implementation
// courtesy of Petr Felkel and Stepan Obdrzalek (petr.felkel@...)
// Re-implemented for the Virtual Terrain Project (vterrain.org)
// by Roger James (www.beardandsandals.co.uk)
//
#include "FelkelIntersection.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CIntersection :: CIntersection (CVertexList &vl, CVertex &v)
{
#if VTDEBUG
if (!(v.m_prevVertex == NULL || v.m_leftLine.FacingTowards (v.m_prevVertex ->
m_rightLine)))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
if (!(v.m_nextVertex == NULL || v.m_rightLine.FacingTowards (v.m_nextVertex ->
m_leftLine)))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
CVertex &l = *v.m_prevVertex;
CVertex &r = *v.m_nextVertex;
#if VTDEBUG
if (!(v.m_leftLine.m_Angle == v.m_leftVertex -> m_leftLine.m_Angle))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
if (!(v.m_rightLine.m_Angle == v.m_rightVertex -> m_rightLine.m_Angle))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
CNumber al = v.m_axis.m_Angle - l.m_axis.m_Angle;
al.NormalizeAngle();
CNumber ar = v.m_axis.m_Angle - r.m_axis.m_Angle;
ar.NormalizeAngle();
#ifdef FELKELDEBUG
VTLOG("New Intersection i1\n");
#endif
C3DPoint i1 = v.m_axis.FacingTowards(l.m_axis) ? C3DPoint(CN_INFINITY,
CN_INFINITY, CN_INFINITY) : v.m_axis.Intersection(l.m_axis);
i1.m_y = v.m_leftLine.Dist(i1) * fabs(tan(v.m_leftLine.m_Slope));
#ifdef FELKELDEBUG
VTLOG("New Intersection i1\nm_Origin(x %e y %e z %e) m_Angle %e m_Slope
%e\na.m_Origin(x %e y %e z %e) a.m_Angle %e a.m_Slope %e\n",
v.m_axis.m_Origin.m_x, v.m_axis.m_Origin.m_y, v.m_axis.m_Origin.m_z,
v.m_axis.m_Angle, v.m_axis.m_Slope,
l.m_axis.m_Origin.m_x, l.m_axis.m_Origin.m_y, l.m_axis.m_Origin.m_z,
l.m_axis.m_Angle, l.m_axis.m_Slope);
#endif
#ifdef FELKELDEBUG
VTLOG("New Intersection i2\n");
#endif
C3DPoint i2 = v.m_axis.FacingTowards (r.m_axis) ? C3DPoint(CN_INFINITY,
CN_INFINITY, CN_INFINITY) : v.m_axis.Intersection(r.m_axis);
i2.m_y = v.m_rightLine.Dist(i2) * fabs(tan(v.m_rightLine.m_Slope));
#ifdef FELKELDEBUG
VTLOG("m_Origin(x %e y %e z %e) m_Angle %e m_Slope %e\na.m_Origin(x %e y %e z
%e) a.m_Angle %e a.m_Slope %e\n",
v.m_axis.m_Origin.m_x, v.m_axis.m_Origin.m_y, v.m_axis.m_Origin.m_z,
v.m_axis.m_Angle, v.m_axis.m_Slope,
r.m_axis.m_Origin.m_x, r.m_axis.m_Origin.m_y, r.m_axis.m_Origin.m_z,
r.m_axis.m_Angle, r.m_axis.m_Slope);
#endif
#if VTDEBUG
CNumber Oldi1y = i1.m_y;
CNumber Oldi2y = i2.m_y;
#endif
// I need to check why this code is here !!!!!!!!!
// I must of put it here bu I cannot remember why
// Getting a slope of exactly PI/2 must be rare
// but could arise from many different edge slopes and vertex angles
if (SIMILAR(v.m_axis.m_Slope, CN_PI/2))
{
i1.m_y = C3DPoint(i1 - l.m_point).LengthXZ() * fabs(tan(l.m_axis.m_Slope)) +
l.m_point.m_y;
i2.m_y = C3DPoint(i2 - r.m_point).LengthXZ() * fabs(tan(r.m_axis.m_Slope)) +
r.m_point.m_y;
}
else
{
i1.m_y = C3DPoint(i1 - v.m_point).LengthXZ() * fabs(tan(v.m_axis.m_Slope)) +
v.m_point.m_y;
i2.m_y = C3DPoint(i2 - v.m_point).LengthXZ() * fabs(tan(v.m_axis.m_Slope)) +
v.m_point.m_y;
}
// assert ((Oldi1y == i1.m_y) && (Oldi2y == i2.m_y));
CNumber d1 = v.m_point.DistXZ(i1);
CNumber d2 = v.m_point.DistXZ(i2);
// CNumber d1 = i1.m_y;
// CNumber d2 = i2.m_y;
CVertex *leftPointer, *rightPointer;
C3DPoint p;
CNumber d3 = CN_INFINITY;
CNumber av = v.m_leftLine.m_Angle - v.m_rightLine.m_Angle;
av.NormalizeAngle();
if ((av >= 0.0 || av == - CN_PI) && (v.m_leftLine.Intersection(v.m_rightLine)
== v.m_point || v.m_leftLine.Intersection(v.m_rightLine) ==
C3DPoint(CN_INFINITY, CN_INFINITY, CN_INFINITY)))
d3 = v.NearestIntersection(vl, &leftPointer, &rightPointer, p);
// d3 = p.m_y;
#ifdef FELKELDEBUG
VTLOG("New Intersection i1\nm_Origin(x %e y %e z %e) m_Angle %e m_Slope
%e\na.m_Origin(x %e y %e z %e) a.m_Angle %e a.m_Slope %e\n",
v.m_axis.m_Origin.m_x, v.m_axis.m_Origin.m_y, v.m_axis.m_Origin.m_z,
v.m_axis.m_Angle, v.m_axis.m_Slope,
l.m_axis.m_Origin.m_x, l.m_axis.m_Origin.m_y, l.m_axis.m_Origin.m_z,
l.m_axis.m_Angle, l.m_axis.m_Slope);
VTLOG("New Intersection i2\nm_Origin(x %e y %e z %e) m_Angle %e m_Slope
%e\na.m_Origin(x %e y %e z %e) a.m_Angle %e a.m_Slope %e\n",
v.m_axis.m_Origin.m_x, v.m_axis.m_Origin.m_y, v.m_axis.m_Origin.m_z,
v.m_axis.m_Angle, v.m_axis.m_Slope,
r.m_axis.m_Origin.m_x, r.m_axis.m_Origin.m_y, r.m_axis.m_Origin.m_z,
r.m_axis.m_Angle, r.m_axis.m_Slope);
VTLOG("New Intersection\n al %e ar %e\ni1.x %e i1.y %e i1.z %e\ni2.x %e i2.y %e
i2.z %e\np.m_x %e p.m_y %e p.m_z %e\nd1 %e d2 %e d3 %e\n",
al, ar, i1.m_x, i1.m_y, i1.m_z, i2.m_x, i2.m_y, i2.m_z, p.m_x, p.m_y, p.m_z,
d1, d2, d3);
#endif
if (d3 <= d1 && d3 <= d2)
{
m_poi = p;
m_leftVertex = m_rightVertex = &v;
m_type = NONCONVEX;
if (v.InvalidIntersection (vl, *this))
{
d3 = CN_INFINITY;
m_poi == C3DPoint (CN_INFINITY, CN_INFINITY, CN_INFINITY);
}
}
if (d1 <= d2 && d1 <= d3)
{
m_leftVertex = &l;
m_rightVertex = &v;
m_poi = i1;
m_type = CONVEX;
}
else if (d2 <= d1 && d2 <= d3)
{
m_leftVertex = &v;
m_rightVertex = &r;
m_poi = i2;
m_type = CONVEX;
}
if (m_poi == C3DPoint (CN_INFINITY, CN_INFINITY, CN_INFINITY))
m_height = CN_INFINITY;
else
m_height = m_poi.m_y;
#ifdef FELKELDEBUG
VTLOG("New %s Intersection %d %d x %e y %e z %e height %e\n",
m_type == CONVEX ? "CONVEX" : "NONCONVEX",
m_leftVertex->m_ID, m_rightVertex->m_ID, m_poi.m_x, m_poi.m_y, m_poi.m_z,
m_height);
#endif
}
void CIntersection::ApplyNonconvexIntersection(CSkeleton &skeleton, CVertexList
&vl, IntersectionQueue &iq, bool bCheckVertexinCurrentContour)
{
#ifdef FELKELDEBUG
VTLOG("ApplyNonconvexIntersection\n");
#endif
#if VTDEBUG
// Left and right vertices must always be the same point
if (!(m_leftVertex == m_rightVertex))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
// Check to see of they are the same data structure RFJ !!!
if (!(m_leftVertex->m_ID == m_rightVertex->m_ID))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
CVertex *leftPointer, *rightPointer;
C3DPoint p;
CNumber d3 = CN_INFINITY;
d3 = m_leftVertex->NearestIntersection(vl, &leftPointer, &rightPointer, p);
if (d3 == CN_INFINITY)
return;
if (p != m_poi)
return;
if (!m_leftVertex->VertexInCurrentContour(*leftPointer))
{
if (bCheckVertexinCurrentContour) // Temporary hack to disable checking in
multiple contour buildings !!!! NEEDS FIXING
return;
else
VTLOG("Vertex in current contour check failed - needs to be fixed - this
check is not valid if one (or both?) of the contours is clockwise\n");
}
// Left and right vertex are actually the same in this case
if (!m_rightVertex->VertexInCurrentContour(*rightPointer))
{
VTLOG("Vertex in current contour check failed - needs to be fixed - this check
is not valid if one (or both?) of the contours is clockwise\n");
if (bCheckVertexinCurrentContour) // Temporary hack to disable checking in
multiple contour buildings !!!! NEEDS FIXING
return;
else
VTLOG("Vertex in current contour check failed - needs to be fixed - this
check is not valid if one (or both?) of the contours is clockwise\n");
}
#ifdef FELKELDEBUG
VTLOG("left vertex %d left ptr %d right ptr %d right vertex %d\n",
m_leftVertex->m_ID,
leftPointer->m_ID,
rightPointer->m_ID,
m_rightVertex->m_ID);
#endif
// Treat as a split event
CVertex v1 (p, *rightPointer, *m_rightVertex);
CVertex v2 (p, *m_leftVertex, *leftPointer);
#if VTDEBUG
if (!(v1.m_point != C3DPoint(CN_INFINITY, CN_INFINITY, CN_INFINITY)))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
if (!(v2.m_point != C3DPoint(CN_INFINITY, CN_INFINITY, CN_INFINITY)))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
m_leftVertex->m_done = true;
// i.rightVertex -> done = true;
CVertex *newNext1 = m_rightVertex->m_nextVertex;
CVertex *newPrev1 = leftPointer->Highest();
v1.m_prevVertex = newPrev1;
v1.m_nextVertex = newNext1;
vl.push_back(v1);
CVertex *v1Pointer = &vl.back();
newPrev1->m_nextVertex = v1Pointer;
newNext1->m_prevVertex = v1Pointer;
m_rightVertex->m_higher = v1Pointer;
CVertex *newNext2 = rightPointer->Highest();
CVertex *newPrev2 = m_leftVertex->m_prevVertex;
v2.m_prevVertex = newPrev2;
v2.m_nextVertex = newNext2;
vl.push_back(v2);
CVertex *v2Pointer = &vl.back();
newPrev2->m_nextVertex = v2Pointer;
newNext2->m_prevVertex = v2Pointer;
m_leftVertex->m_higher = v2Pointer;
skeleton.push_back(CSkeletonLine(*m_rightVertex, *v1Pointer));
CSkeletonLine *linePtr = &skeleton.back();
skeleton.push_back(CSkeletonLine(*v1Pointer, *v2Pointer));
CSkeletonLine *auxLine1Ptr = &skeleton.back ();
skeleton.push_back(CSkeletonLine(*v2Pointer, *v1Pointer));
CSkeletonLine *auxLine2Ptr = &skeleton.back();
linePtr->m_lower.m_right = m_leftVertex->m_leftSkeletonLine;
linePtr->m_lower.m_left = m_leftVertex->m_rightSkeletonLine;
v1Pointer->m_rightSkeletonLine = v2Pointer->m_leftSkeletonLine = linePtr;
v1Pointer->m_leftSkeletonLine = auxLine1Ptr;
v2Pointer->m_rightSkeletonLine = auxLine2Ptr;
auxLine1Ptr->m_lower.m_right = auxLine2Ptr;
auxLine2Ptr->m_lower.m_left = auxLine1Ptr;
if (m_leftVertex->m_leftSkeletonLine)
m_leftVertex->m_leftSkeletonLine ->m_higher.m_left = linePtr;
if (m_leftVertex->m_rightSkeletonLine)
m_leftVertex->m_rightSkeletonLine->m_higher.m_right = linePtr;
m_leftVertex->m_advancingSkeletonLine = linePtr;
if (newNext1 == newPrev1)
{
v1Pointer->m_done = true;
newNext1->m_done = true;
skeleton.push_back(CSkeletonLine(*v1Pointer, *newNext1));
CSkeletonLine *linePtr = &skeleton.back();
linePtr->m_lower.m_right = v1Pointer->m_leftSkeletonLine;
linePtr->m_lower.m_left = v1Pointer->m_rightSkeletonLine;
linePtr->m_higher.m_right = newNext1->m_leftSkeletonLine;
linePtr->m_higher.m_left = newNext1->m_rightSkeletonLine;
if (v1Pointer->m_leftSkeletonLine)
v1Pointer->m_leftSkeletonLine->m_higher.m_left = linePtr;
if (v1Pointer->m_rightSkeletonLine)
v1Pointer->m_rightSkeletonLine->m_higher.m_right = linePtr;
if (newNext1->m_leftSkeletonLine)
newNext1->m_leftSkeletonLine->m_higher.m_left = linePtr;
if (newNext1->m_rightSkeletonLine)
newNext1->m_rightSkeletonLine->m_higher.m_right = linePtr;
}
else
{
CIntersection i1(vl, *v1Pointer);
if (i1.m_height != CN_INFINITY)
iq.push (i1);
}
if (newNext2 == newPrev2)
{
v2Pointer->m_done = true;
newNext2 ->m_done = true;
skeleton.push_back(CSkeletonLine (*v2Pointer, *newNext2));
CSkeletonLine *linePtr = &skeleton.back();
linePtr->m_lower.m_right = v2Pointer->m_leftSkeletonLine;
linePtr->m_lower.m_left = v2Pointer->m_rightSkeletonLine;
linePtr->m_higher.m_right = newNext2->m_leftSkeletonLine;
linePtr->m_higher.m_left = newNext2->m_rightSkeletonLine;
if (v2Pointer->m_leftSkeletonLine)
v2Pointer->m_leftSkeletonLine->m_higher.m_left = linePtr;
if (v2Pointer->m_rightSkeletonLine)
v2Pointer->m_rightSkeletonLine->m_higher.m_right = linePtr;
if (newNext2->m_leftSkeletonLine)
newNext2 ->m_leftSkeletonLine->m_higher.m_left = linePtr;
if (newNext2->m_rightSkeletonLine)
newNext2->m_rightSkeletonLine->m_higher.m_right = linePtr;
}
else
{
CIntersection i2 (vl, *v2Pointer);
if (i2.m_height != CN_INFINITY)
iq.push(i2);
}
}
void CIntersection::ApplyConvexIntersection(CSkeleton &skeleton, CVertexList
&vl, IntersectionQueue &iq)
{
#ifdef FELKELDEBUG
VTLOG("ApplyConvexIntersection\n");
#endif
// create new vertex and link into current contour
CVertex vtx (m_poi, *m_leftVertex, *m_rightVertex);
#if VTDEBUG
if(!(vtx.m_point != C3DPoint(CN_INFINITY, CN_INFINITY, CN_INFINITY)))
VTLOG("%s %d Assert failed\n", __FILE__, __LINE__);
#endif
// Link vertex into overall chain
CVertex *newNext = m_rightVertex->m_nextVertex;
CVertex *newPrev = m_leftVertex->m_prevVertex;
vtx.m_prevVertex = newPrev;
vtx.m_nextVertex = newNext;
vl.push_back (vtx);
CVertex *vtxPointer = &vl.back();
newPrev->m_nextVertex = vtxPointer;
newNext->m_prevVertex = vtxPointer;
// Set this vertex as the higher skeleton point for the vertices which have
been
// removed from the active contour
m_leftVertex->m_higher = vtxPointer;
m_rightVertex->m_higher = vtxPointer;
// mark vertices as inactive
m_leftVertex->m_done = true;
m_rightVertex->m_done = true;
CIntersection newI(vl, *vtxPointer);
if (newI.m_height != CN_INFINITY)
iq.push(newI);
skeleton.push_back(CSkeletonLine(*m_leftVertex, *vtxPointer));
CSkeletonLine *lLinePtr = &skeleton.back();
skeleton.push_back(CSkeletonLine (*m_rightVertex, *vtxPointer));
CSkeletonLine *rLinePtr = &skeleton.back();
lLinePtr->m_lower.m_right = m_leftVertex->m_leftSkeletonLine;
lLinePtr->m_lower.m_left = m_leftVertex->m_rightSkeletonLine;
lLinePtr->m_higher.m_right = rLinePtr;
rLinePtr->m_lower.m_right = m_rightVertex->m_leftSkeletonLine;
rLinePtr->m_lower.m_left = m_rightVertex->m_rightSkeletonLine;
rLinePtr->m_higher.m_left = lLinePtr;
if (m_leftVertex->m_leftSkeletonLine)
m_leftVertex->m_leftSkeletonLine->m_higher.m_left = lLinePtr;
if (m_leftVertex->m_rightSkeletonLine)
m_leftVertex->m_rightSkeletonLine->m_higher.m_right = lLinePtr;
if (m_rightVertex->m_leftSkeletonLine)
m_rightVertex->m_leftSkeletonLine->m_higher.m_left = rLinePtr;
if (m_rightVertex->m_rightSkeletonLine)
m_rightVertex->m_rightSkeletonLine->m_higher.m_right = rLinePtr;
vtxPointer->m_leftSkeletonLine = lLinePtr;
vtxPointer->m_rightSkeletonLine = rLinePtr;
m_leftVertex->m_advancingSkeletonLine = lLinePtr;
m_rightVertex->m_advancingSkeletonLine = rLinePtr;
}
void CIntersection::ApplyLast3(CSkeleton &skeleton, CVertexList &vl)
{
#ifdef FELKELDEBUG
VTLOG("ApplyLast3\n");
#endif
CVertex &v1 = *m_leftVertex;
CVertex &v2 = *m_rightVertex;
CVertex &v3 = *m_leftVertex->m_prevVertex;
v1.m_done = true;
v2.m_done = true;
v3.m_done = true;
C3DPoint is = m_poi;
CVertex v(is);
v.m_done = true;
vl.push_back(v);
CVertex *vtxPointer = &vl.back();
skeleton.push_back (CSkeletonLine(v1, *vtxPointer));
CSkeletonLine *line1Ptr = &skeleton.back();
skeleton.push_back (CSkeletonLine(v2, *vtxPointer));
CSkeletonLine *line2Ptr = &skeleton.back();
skeleton.push_back (CSkeletonLine(v3, *vtxPointer));
CSkeletonLine *line3Ptr = &skeleton.back ();
line1Ptr->m_higher.m_right = line2Ptr; // zapojeni okridlenych hran
line2Ptr->m_higher.m_right = line3Ptr;
line3Ptr->m_higher.m_right = line1Ptr;
line1Ptr->m_higher.m_left = line3Ptr;
line2Ptr ->m_higher.m_left = line1Ptr;
line3Ptr->m_higher.m_left = line2Ptr;
line1Ptr->m_lower.m_left = v1.m_rightSkeletonLine;
line1Ptr->m_lower.m_right = v1.m_leftSkeletonLine;
line2Ptr->m_lower.m_left = v2.m_rightSkeletonLine;
line2Ptr->m_lower.m_right = v2.m_leftSkeletonLine;
line3Ptr->m_lower.m_left = v3.m_rightSkeletonLine;
line3Ptr->m_lower.m_right = v3.m_leftSkeletonLine;
if (v1.m_leftSkeletonLine)
v1.m_leftSkeletonLine->m_higher.m_left = line1Ptr;
if (v1.m_rightSkeletonLine)
v1.m_rightSkeletonLine->m_higher.m_right = line1Ptr;
if (v2.m_leftSkeletonLine)
v2.m_leftSkeletonLine->m_higher.m_left = line2Ptr;
if (v2.m_rightSkeletonLine)
v2.m_rightSkeletonLine->m_higher.m_right = line2Ptr;
if (v3.m_leftSkeletonLine)
v3.m_leftSkeletonLine->m_higher.m_left = line3Ptr;
if (v3.m_rightSkeletonLine)
v3.m_rightSkeletonLine->m_higher.m_right = line3Ptr;
v1.m_advancingSkeletonLine = line1Ptr;
v2.m_advancingSkeletonLine = line2Ptr;
v3.m_advancingSkeletonLine = line3Ptr;
}
#endif // DOXYGEN_SHOULD_SKIP_THIS
----------
//
// FelkelIntersection.h: interface for the CIntersection class.
//
// Copyright (c) 2003-2006 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//
// Straight skeleton algorithm and original implementation
// courtesy of Petr Felkel and Stepan Obdrzalek (petr.felkel@...)
// Re-implemented for the Virtual Terrain Project (vterrain.org)
// by Roger James (www.beardandsandals.co.uk)
//
#ifndef FELKELINTERSECTIONH
#define FELKELINTERSECTIONH
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#include <queue>
#include "FelkelComponents.h"
class CSkeleton;
typedef priority_queue <CIntersection, deque <CIntersection>, greater
<CIntersection> > IntersectionQueue;
class CIntersection
{
public:
CIntersection (void) { };
CIntersection (CVertexList &vl, CVertex &v);
void ApplyNonconvexIntersection(CSkeleton &skeleton, CVertexList &vl,
IntersectionQueue &iq, bool bCheckVertexinCurrentContour);
void ApplyConvexIntersection(CSkeleton &skeleton, CVertexList &vl,
IntersectionQueue &iq);
void ApplyLast3(CSkeleton &skeleton, CVertexList &vl);
C3DPoint m_poi;
CVertex *m_leftVertex, *m_rightVertex;
CNumber m_height;
enum Type { CONVEX, NONCONVEX } m_type;
bool operator > (const CIntersection &i) const
{
// Do exact comparison for intersection queue
// Using CNumber will also test for != which is implemented as !SIMILAR
double d1 = m_height;
double d2 = i.m_height;
return d1 > d2;
}
bool operator == (const CIntersection &i) const
{
return m_poi == i.m_poi;
}
};
#endif // DOXYGEN_SHOULD_SKIP_THIS
#endif // FELKELINTERSECTIONH
[Non-text portions of this message have been removed]
|