Implement hole cutting algorithm

This commit is contained in:
James Lambert 2022-07-01 22:12:09 -06:00
parent c7734ca524
commit cf9f53755e
14 changed files with 1431 additions and 943 deletions

View file

@ -48,16 +48,16 @@ uint8_t getEdgeIndex(std::map<int, EdgeIndices>& edges, int edgeKey) {
return result->second.edgeIndex;
}
std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition& fileDefinition, const CollisionQuad& quad, ExtendedMesh& mesh, float scale) {
std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition& fileDefinition, const CollisionQuad& quad, StaticMeshInfo& mesh, float scale) {
std::unique_ptr<StructureDataChunk> portalSurface(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> vertices(new StructureDataChunk());
std::string name(mesh.mMesh->mName.C_Str());
std::string name(mesh.staticMesh->mMesh->mName.C_Str());
for (unsigned i = 0; i < mesh.mMesh->mNumVertices; ++i) {
for (unsigned i = 0; i < mesh.staticMesh->mMesh->mNumVertices; ++i) {
short x, y;
quad.ToLocalCoords(mesh.mMesh->mVertices[i] * scale, x, y);
quad.ToLocalCoords(mesh.staticMesh->mMesh->mVertices[i] * scale, x, y);
std::unique_ptr<StructureDataChunk> vertexWrapperWrapper(new StructureDataChunk());
std::unique_ptr<StructureDataChunk> vertexWrapper(new StructureDataChunk());
@ -69,7 +69,7 @@ std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition
vertices->Add(std::move(vertexWrapperWrapper));
}
std::string meshName(mesh.mMesh->mName.C_Str());
std::string meshName(mesh.staticMesh->mMesh->mName.C_Str());
std::string verticesName = fileDefinition.GetUniqueName(meshName + "_portal_mesh");
fileDefinition.AddDefinition(std::unique_ptr<FileDefinition>(new DataFileDefinition("struct Vector2s16", verticesName, true, "_geo", std::move(vertices))));
@ -77,8 +77,8 @@ std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition
std::map<int, EdgeIndices> edgeDirection;
std::vector<int> edgeOrder;
for (unsigned faceIndex = 0; faceIndex < mesh.mMesh->mNumFaces; ++faceIndex) {
aiFace* face = &mesh.mMesh->mFaces[faceIndex];
for (unsigned faceIndex = 0; faceIndex < mesh.staticMesh->mMesh->mNumFaces; ++faceIndex) {
aiFace* face = &mesh.staticMesh->mMesh->mFaces[faceIndex];
std::vector<int> edgeKeys;
std::vector<bool> isReverseEdge;
@ -176,12 +176,23 @@ std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition
// edgesCount
portalSurface->AddPrimitive(edgeOrder.size());
// vertexCount
portalSurface->AddPrimitive(mesh.mMesh->mNumVertices);
portalSurface->AddPrimitive(mesh.staticMesh->mMesh->mNumVertices);
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeA)));
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeB)));
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.corner)));
std::string vertexBufferName = fileDefinition.GetVertexBuffer(
mesh.staticMesh,
Material::GetVertexType(mesh.material),
Material::TextureWidth(mesh.material),
Material::TextureHeight(mesh.material),
"_geo"
);
portalSurface->AddPrimitive(vertexBufferName);
portalSurface->AddPrimitive(mesh.gfxName);
return portalSurface;
}
@ -199,7 +210,7 @@ void generatePortalSurfacesDefinition(const aiScene* scene, CFileDefinition& fil
collisionWithPadding.mMax = collisionWithPadding.mMax + aiVector3D(0.1f, 0.1f, 0.1f);
for (auto mesh : staticOutput.staticMeshes) {
aiMaterial* material = scene->mMaterials[mesh->mMesh->mMaterialIndex];
aiMaterial* material = scene->mMaterials[mesh.staticMesh->mMesh->mMaterialIndex];
std::string materialName = ExtendedMesh::GetMaterialName(material);
@ -207,9 +218,9 @@ void generatePortalSurfacesDefinition(const aiScene* scene, CFileDefinition& fil
continue;
}
aiAABB meshBB(mesh->bbMin * settings.mModelScale, mesh->bbMax * settings.mModelScale);
aiAABB meshBB(mesh.staticMesh->bbMin * settings.mModelScale, mesh.staticMesh->bbMax * settings.mModelScale);
if (!collision.IsCoplanar(*mesh, settings.mModelScale)) {
if (!collision.IsCoplanar(*mesh.staticMesh, settings.mModelScale)) {
continue;
}
@ -217,7 +228,7 @@ void generatePortalSurfacesDefinition(const aiScene* scene, CFileDefinition& fil
continue;
}
portalSurfaces->Add(std::move(calculatePortalSingleSurface(fileDefinition, collision, *mesh, settings.mModelScale)));
portalSurfaces->Add(std::move(calculatePortalSingleSurface(fileDefinition, collision, mesh, settings.mModelScale)));
++surfaceCount;
}
@ -248,13 +259,13 @@ void generateBoundingBoxesDefinition(const aiScene* scene, CFileDefinition& file
for (auto& mesh : staticOutput.staticMeshes) {
std::unique_ptr<StructureDataChunk> sphere(new StructureDataChunk());
sphere->AddPrimitive((short)(mesh->bbMin.x * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMin.y * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMin.z * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMin.x * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMin.y * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMin.z * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.x * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.y * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh->bbMax.z * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMax.x * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMax.y * combinedScale + 0.5f));
sphere->AddPrimitive((short)(mesh.staticMesh->bbMax.z * combinedScale + 0.5f));
boundingBoxes->Add(std::move(sphere));
}

View file

@ -39,7 +39,12 @@ std::shared_ptr<StaticGeneratorOutput> generateStatic(const aiScene* scene, CFil
elements.push_back(element);
output->staticMeshes.push_back(chunk.mMesh);
StaticMeshInfo meshInfo;
meshInfo.staticMesh = chunk.mMesh;
meshInfo.gfxName = element.meshName;
meshInfo.material = chunk.mMaterial;
output->staticMeshes.push_back(meshInfo);
output->staticRooms.push_back(roomMapping.RoomForNode(nodeInfo.node));
}
}

View file

@ -4,15 +4,22 @@
#include "DefinitionGenerator.h"
#include "../DisplayListSettings.h"
#include "./RoomGenerator.h"
#include "../RenderChunk.h"
struct StaticContentElement {
std::string meshName;
std::string materialName;
};
struct StaticMeshInfo {
std::shared_ptr<ExtendedMesh> staticMesh;
std::string gfxName;
Material* material;
};
struct StaticGeneratorOutput {
std::string staticContentName;
std::vector<std::shared_ptr<ExtendedMesh>> staticMeshes;
std::vector<StaticMeshInfo> staticMeshes;
std::vector<int> staticRooms;
std::string roomMappingName;
};

View file

@ -39,4 +39,25 @@ int vector2s16FallsBetween(struct Vector2s16* from, struct Vector2s16* towards,
} else {
return vector2s16Cross(from, check) >= 0 || vector2s16Cross(check, towards) >= 0;
}
}
void vector2s16Barycentric(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* c, struct Vector2s16* point, struct Vector3* output) {
struct Vector2s16 v0;
struct Vector2s16 v1;
struct Vector2s16 v2;
vector2s16Sub(b, a, &v0);
vector2s16Sub(c, a, &v1);
vector2s16Sub(point, a, &v2);
float d00 = (float)vector2s16Dot(&v0, &v0);
float d01 = (float)vector2s16Dot(&v0, &v1);
float d11 = (float)vector2s16Dot(&v1, &v1);
float d20 = (float)vector2s16Dot(&v2, &v0);
float d21 = (float)vector2s16Dot(&v2, &v1);
float denom = 1.0f / (d00 * d11 - d01 * d01);
output->y = (d11 * d20 - d01 * d21) * denom;
output->z = (d00 * d21 - d01 * d20) * denom;
output->x = 1.0f - output->y - output->z;
}

View file

@ -1,6 +1,8 @@
#ifndef __MATH_VECTOR2S16_H__
#define __MATH_VECTOR2S16_H__
#include "vector3.h"
struct Vector2s16 {
union {
struct {
@ -23,4 +25,6 @@ int vector2s16DistSqr(struct Vector2s16* a, struct Vector2s16* b);
int vector2s16FallsBetween(struct Vector2s16* from, struct Vector2s16* towards, struct Vector2s16* check);
void vector2s16Barycentric(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* c, struct Vector2s16* point, struct Vector3* output);
#endif

View file

@ -142,4 +142,8 @@ void vector3ToVector3u8(struct Vector3* input, struct Vector3u8* output) {
output->x = floatTos8norm(input->x);
output->y = floatTos8norm(input->y);
output->z = floatTos8norm(input->z);
}
float vector3EvalBarycentric1D(struct Vector3* baryCoords, float a, float b, float c) {
return baryCoords->x * a + baryCoords->y * b + baryCoords->z * c;
}

View file

@ -41,4 +41,6 @@ int vector3IsZero(struct Vector3* vector);
void vector3ToVector3u8(struct Vector3* input, struct Vector3u8* output);
float vector3EvalBarycentric1D(struct Vector3* baryCoords, float a, float b, float c);
#endif

View file

@ -4,927 +4,9 @@
#include "math/mathf.h"
#include "math/vector2.h"
#include <math.h>
#include "portal_surface_generator.h"
#include "../util/memory.h"
#define NO_OVERLAP 0x10000
#define GET_NEXT_EDGE(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->nextEdgeReverse : (surfaceEdge)->nextEdge)
#define GET_PREV_EDGE(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->prevEdgeReverse : (surfaceEdge)->prevEdge)
#define GET_CURRENT_POINT(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->bIndex : (surfaceEdge->aIndex))
#define GET_NEXT_POINT(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->aIndex : (surfaceEdge->bIndex))
#define SET_NEXT_EDGE(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->nextEdgeReverse = (value); else (surfaceEdge)->nextEdge = (value); } while (0)
#define SET_PREV_EDGE(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->prevEdgeReverse = (value); else (surfaceEdge)->prevEdge = (value); } while (0)
#define SET_CURRENT_POINT(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->bIndex = (value); else (surfaceEdge)->aIndex = (value); } while (0)
#define SET_NEXT_POINT(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->aIndex = (value); else (surfaceEdge)->bIndex = (value); } while (0)
#define IS_ORIGINAL_VERTEX_INDEX(surfaceBuilder, vertexIndex) ((vertexIndex) < (surfaceBuilder)->original->vertexCount)
#define MAX_SEARCH_ITERATIONS 32
#define ADDITIONAL_EDGE_CAPACITY 64
#define ADDITIONAL_VERTEX_CAPACITY 32
#define VERIFY_INTEGRITY 0
struct SurfaceEdgeWithSide {
int edgeIndex;
int isReverse;
};
enum SurfaceEdgeFlags {
SurfaceEdgeFlagsUsed = (1 << 0),
SurfaceEdgeFlagsTriangulated = (1 << 1),
SurfaceEdgeFlagsFilled = (1 << 2),
};
struct PortalSurfaceBuilder {
struct PortalSurface* original;
struct Vector2s16* vertices;
struct SurfaceEdge* edges;
short currentVertex;
short currentEdge;
u8* isLoopEdge;
u8* edgeFlags;
struct SurfaceEdgeWithSide edgeOnSearchLoop;
union {
// set when hasEdge is true
struct SurfaceEdgeWithSide cuttingEdge;
// set when hasEdge is false
int startingPoint;
};
short hasEdge;
short hasConnected;
};
int portalSurfaceFindEnclosingFace(struct PortalSurface* surface, struct Vector2s16* aroundPoint, struct SurfaceEdgeWithSide* output) {
struct SurfaceEdge* currentEdge = &surface->edges[0];
int edgeDistanceSq = vector2s16DistSqr(&surface->vertices[surface->edges[0].aIndex], aroundPoint);
for (int i = 1; i < surface->sideCount; ++i) {
int dist = vector2s16DistSqr(&surface->vertices[surface->edges[i].aIndex], aroundPoint);
if (dist < edgeDistanceSq) {
edgeDistanceSq = dist;
currentEdge = &surface->edges[i];
}
}
int isEdgeReverse = 0;
int startEdgeIndex = currentEdge - surface->edges;
int currentIteration = 0;
while (currentIteration < MAX_SEARCH_ITERATIONS && (currentIteration == 0 || (currentEdge - surface->edges) != startEdgeIndex)) {
struct Vector2s16 edgeDir;
struct Vector2s16 pointDir;
int anchorPoint = GET_CURRENT_POINT(currentEdge, isEdgeReverse);
vector2s16Sub(
&surface->vertices[GET_NEXT_POINT(currentEdge, isEdgeReverse)],
&surface->vertices[anchorPoint],
&edgeDir
);
vector2s16Sub(
aroundPoint,
&surface->vertices[anchorPoint],
&pointDir
);
int nextEdgeIndex;
if (vector2s16Cross(&edgeDir, &pointDir) < 0) {
// the point is on the opposite side of this edge
startEdgeIndex = currentEdge - surface->edges;
isEdgeReverse = !isEdgeReverse;
nextEdgeIndex = GET_NEXT_EDGE(currentEdge, isEdgeReverse);
if (nextEdgeIndex == 0xFF) {
// has no opposite edge
return 0;
}
} else {
nextEdgeIndex = GET_NEXT_EDGE(currentEdge, isEdgeReverse);
}
int currentIndex = currentEdge - surface->edges;
currentEdge = &surface->edges[nextEdgeIndex];
isEdgeReverse = currentEdge->prevEdgeReverse == currentIndex;
++currentIteration;
}
if (currentIteration == MAX_SEARCH_ITERATIONS) {
return 0;
}
output->edgeIndex = currentEdge - surface->edges;
output->isReverse = isEdgeReverse;
return 1;
}
struct SurfaceEdge* portalSurfaceGetEdge(struct PortalSurfaceBuilder* surfaceBuilder, int edgeIndex) {
return &surfaceBuilder->edges[edgeIndex];
}
struct Vector2s16* portalSurfaceGetVertex(struct PortalSurfaceBuilder* surfaceBuilder, int vertexIndex) {
return &surfaceBuilder->vertices[vertexIndex];
}
void portalSurfaceNextEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* nextEdge) {
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex);
int edgeIndex = currentEdge->edgeIndex;
nextEdge->edgeIndex = GET_NEXT_EDGE(edge, currentEdge->isReverse);
nextEdge->isReverse = portalSurfaceGetEdge(surfaceBuilder, nextEdge->edgeIndex)->prevEdgeReverse == edgeIndex;
}
void portalSurfacePrevEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* prevEdge) {
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex);
int edgeIndex = currentEdge->edgeIndex;
prevEdge->edgeIndex = GET_PREV_EDGE(edge, currentEdge->isReverse);
prevEdge->isReverse = portalSurfaceGetEdge(surfaceBuilder, prevEdge->edgeIndex)->nextEdgeReverse == edgeIndex;
}
#if VERIFY_INTEGRITY
int portalSurfaceIsWellFormed(struct PortalSurfaceBuilder* surfaceBuilder) {
for (int i = 0; i < surfaceBuilder->currentEdge; ++i) {
struct SurfaceEdgeWithSide current;
current.edgeIndex = i;
for (current.isReverse = 0; current.isReverse < 2; ++current.isReverse) {
if (current.isReverse && portalSurfaceGetEdge(surfaceBuilder, i)->nextEdgeReverse == NO_EDGE_CONNECTION) {
break;
}
struct SurfaceEdgeWithSide check;
portalSurfaceNextEdge(surfaceBuilder, &current, &check);
struct SurfaceEdge* checkPtr = portalSurfaceGetEdge(surfaceBuilder, check.edgeIndex);
if (checkPtr->prevEdge != i && checkPtr->prevEdgeReverse != i) {
return 0;
}
portalSurfacePrevEdge(surfaceBuilder, &current, &check);
checkPtr = portalSurfaceGetEdge(surfaceBuilder, check.edgeIndex);
if (checkPtr->nextEdge != i && checkPtr->nextEdgeReverse != i) {
return 0;
}
}
}
return 1;
}
#endif
int portalSurfacePointInsideFace(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct Vector2s16* point) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, currentEdge, &nextEdge);
struct SurfaceEdge* edgePtr = portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex);
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
struct Vector2s16* corner = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(edgePtr, currentEdge->isReverse));
struct Vector2s16* prevPoint = portalSurfaceGetVertex(surfaceBuilder, GET_CURRENT_POINT(edgePtr, currentEdge->isReverse));
struct Vector2s16* nextPoint = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(nextEdgePtr, nextEdge.isReverse));
struct Vector2s16 nextDir;
struct Vector2s16 prevDir;
struct Vector2s16 pointDir;
vector2s16Sub(nextPoint, corner, &nextDir);
vector2s16Sub(prevPoint, corner, &prevDir);
vector2s16Sub(point, corner, &pointDir);
return vector2s16FallsBetween(&nextDir, &prevDir, &pointDir);
}
int portalSurfaceIsEdgeValid(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge) {
return GET_NEXT_EDGE(portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex), currentEdge->isReverse) != NO_EDGE_CONNECTION;
}
int portalSurfaceNextLoop(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* nextFace) {
portalSurfaceNextEdge(surfaceBuilder, currentEdge, nextFace);
nextFace->isReverse = !nextFace->isReverse;
if (portalSurfaceIsEdgeValid(surfaceBuilder, nextFace)) {
return 1;
}
*nextFace = *currentEdge;
nextFace->isReverse = !nextFace->isReverse;
int i = 0;
while (i < MAX_SEARCH_ITERATIONS && portalSurfaceIsEdgeValid(surfaceBuilder, nextFace)) {
struct SurfaceEdgeWithSide prevFace;
portalSurfacePrevEdge(surfaceBuilder, nextFace, &prevFace);
*nextFace = prevFace;
nextFace->isReverse = !nextFace->isReverse;
++i;
}
nextFace->isReverse = !nextFace->isReverse;
return i < MAX_SEARCH_ITERATIONS;
}
int portalSurfaceFindNextLoop(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* forPoint) {
if (!surfaceBuilder->hasConnected) {
return 1;
}
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->edgeOnSearchLoop;
int nextIndex = GET_NEXT_EDGE(portalSurfaceGetEdge(surfaceBuilder, surfaceBuilder->cuttingEdge.edgeIndex), surfaceBuilder->cuttingEdge.isReverse);
// the cutting edge is its own next edge
// this means edgeOnSearchLoop will already be correct
if (nextIndex == surfaceBuilder->cuttingEdge.edgeIndex) {
return 1;
}
int i = 0;
while (i < MAX_SEARCH_ITERATIONS && !portalSurfacePointInsideFace(surfaceBuilder, &currentEdge, forPoint)) {
struct SurfaceEdgeWithSide nextEdge;
if (!portalSurfaceNextLoop(surfaceBuilder, &currentEdge, &nextEdge)) {
return 0;
}
if (i != 0 && currentEdge.edgeIndex == surfaceBuilder->edgeOnSearchLoop.edgeIndex) {
// a full loop and no face found
return 0;
}
currentEdge = nextEdge;
++i;
}
surfaceBuilder->edgeOnSearchLoop = currentEdge;
surfaceBuilder->cuttingEdge = currentEdge;
return i < MAX_SEARCH_ITERATIONS;
}
#define MAX_INTERSECT_LOOPS 20
int portalSurfaceIsPointOnLine(struct Vector2s16* pointA, struct Vector2s16* edgeA, struct Vector2s16* edgeDir) {
struct Vector2s16 originOffset;
vector2s16Sub(edgeA, pointA, &originOffset);
s64 magProduct = (s64)vector2s16MagSqr(edgeDir) * vector2s16MagSqr(&originOffset);
s64 dotProduct = vector2s16Dot(edgeDir, &originOffset);
return magProduct == dotProduct * dotProduct;
}
enum IntersectionType {
IntersectionTypeNone,
IntersectionTypePoint,
IntersectionTypeColinear
};
enum IntersectionType portalSurfaceIntersect(struct Vector2s16* pointA, struct Vector2s16* pointDir, struct Vector2s16* edgeA, struct Vector2s16* edgeB, struct Vector2s16* intersection) {
struct Vector2s16 edgeDir;
vector2s16Sub(edgeB, edgeA, &edgeDir);
struct Vector2s16 originOffset;
vector2s16Sub(edgeA, pointA, &originOffset);
int denominator = vector2s16Cross(&edgeDir, pointDir);
if (denominator == 0) {
if (!portalSurfaceIsPointOnLine(pointA, edgeA, &edgeDir)) {
return IntersectionTypeNone;
}
int directionDot = vector2s16Dot(pointDir, &edgeDir);
// find the point furthest in the direction of pointDir
// that is on both line segments
if (directionDot > 0) {
// pointing towards b
if (vector2s16DistSqr(edgeB, pointA) >= vector2s16MagSqr(pointDir)) {
// edge ends first
*intersection = *edgeB;
} else {
// point ends first
vector2s16Add(pointA, pointDir, intersection);
}
} else {
if (vector2s16MagSqr(&originOffset) >= vector2s16MagSqr(pointDir)) {
// edge ends first
*intersection = *edgeA;
} else {
// point ends first
vector2s16Add(pointA, pointDir, intersection);
}
}
// the lines are colinear
return IntersectionTypeColinear;
}
int pointLerp = vector2s16Cross(&edgeDir, &originOffset);
if (denominator > 0 ? (pointLerp <= 0 || pointLerp > denominator) : (pointLerp >= 0 || pointLerp < denominator)) {
return IntersectionTypeNone;
}
int edgeLerp = vector2s16Cross(pointDir, &originOffset);
if (denominator > 0 ? (edgeLerp < 0 || edgeLerp > denominator) : (edgeLerp > 0 || edgeLerp < denominator)) {
return IntersectionTypeNone;
}
intersection->x = (short)((s64)pointDir->x * (s64)pointLerp / (s64)denominator) + pointA->x;
intersection->y = (short)((s64)pointDir->y * (s64)pointLerp / (s64)denominator) + pointA->y;
return IntersectionTypePoint;
}
int portalSurfaceNewVertex(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* point) {
if (surfaceBuilder->currentVertex == ADDITIONAL_VERTEX_CAPACITY) {
return -1;
}
int newVertexIndex = surfaceBuilder->currentVertex;
surfaceBuilder->vertices[newVertexIndex] = *point;
++surfaceBuilder->currentVertex;
return newVertexIndex;
}
int portalSurfaceNewEdge(struct PortalSurfaceBuilder* surfaceBuilder, int isLoopEdge) {
if (surfaceBuilder->currentEdge == ADDITIONAL_EDGE_CAPACITY) {
return -1;
}
int newEdgeIndex = surfaceBuilder->currentEdge;
surfaceBuilder->isLoopEdge[surfaceBuilder->currentEdge] = isLoopEdge;
++surfaceBuilder->currentEdge;
return newEdgeIndex;
}
int portalSurfaceSplitEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, struct Vector2s16* point) {
int newVertexIndex = portalSurfaceNewVertex(surfaceBuilder, point);
if (newVertexIndex == -1) {
return -1;
}
struct SurfaceEdge* existingEdge = portalSurfaceGetEdge(surfaceBuilder, edge->edgeIndex);
struct SurfaceEdgeWithSide nextEdge;
struct SurfaceEdgeWithSide prevReverseEdge;
portalSurfaceNextEdge(surfaceBuilder, edge, &nextEdge);
prevReverseEdge = *edge;
prevReverseEdge.isReverse = !prevReverseEdge.isReverse;
portalSurfacePrevEdge(surfaceBuilder, &prevReverseEdge, &prevReverseEdge);
int newEdgeIndex = portalSurfaceNewEdge(surfaceBuilder, 0);
if (newEdgeIndex == -1) {
return -1;
}
struct SurfaceEdge* newEdge = portalSurfaceGetEdge(surfaceBuilder, newEdgeIndex);
SET_NEXT_POINT(newEdge, edge->isReverse, GET_NEXT_POINT(existingEdge, edge->isReverse));
SET_CURRENT_POINT(newEdge, edge->isReverse, newVertexIndex);
SET_NEXT_EDGE(newEdge, edge->isReverse, GET_NEXT_EDGE(existingEdge, edge->isReverse));
SET_PREV_EDGE(newEdge, edge->isReverse, edge->edgeIndex);
SET_NEXT_EDGE(newEdge, !edge->isReverse, edge->edgeIndex);
SET_PREV_EDGE(newEdge, !edge->isReverse, GET_PREV_EDGE(existingEdge, !edge->isReverse));
SET_NEXT_POINT(existingEdge, edge->isReverse, newVertexIndex);
SET_NEXT_EDGE(existingEdge, edge->isReverse, newEdgeIndex);
SET_PREV_EDGE(existingEdge, !edge->isReverse, newEdgeIndex);
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
SET_PREV_EDGE(nextEdgePtr, nextEdge.isReverse, newEdgeIndex);
struct SurfaceEdge* prevEdgePtr = portalSurfaceGetEdge(surfaceBuilder, prevReverseEdge.edgeIndex);
SET_NEXT_EDGE(prevEdgePtr, prevReverseEdge.isReverse, newEdgeIndex);
#if VERIFY_INTEGRITY
if (!portalSurfaceIsWellFormed(surfaceBuilder)) {
return -1;
}
#endif
return newVertexIndex;
}
int portalSurfaceConnectToPoint(struct PortalSurfaceBuilder* surfaceBuilder, int pointIndex, struct SurfaceEdgeWithSide* edge) {
int newEdge = portalSurfaceNewEdge(surfaceBuilder, 1);
if (newEdge == -1) {
return 0;
}
struct SurfaceEdge* newEdgePtr = portalSurfaceGetEdge(surfaceBuilder, newEdge);
if (surfaceBuilder->hasEdge) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, &surfaceBuilder->cuttingEdge, &nextEdge);
struct SurfaceEdge* cuttingEdgePtr = portalSurfaceGetEdge(surfaceBuilder, surfaceBuilder->cuttingEdge.edgeIndex);
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
newEdgePtr->aIndex = GET_NEXT_POINT(cuttingEdgePtr, surfaceBuilder->cuttingEdge.isReverse);
newEdgePtr->prevEdge = surfaceBuilder->cuttingEdge.edgeIndex;
newEdgePtr->nextEdgeReverse = nextEdge.edgeIndex;
SET_NEXT_EDGE(cuttingEdgePtr, surfaceBuilder->cuttingEdge.isReverse, newEdge);
SET_PREV_EDGE(nextEdgePtr, nextEdge.isReverse, newEdge);
} else {
newEdgePtr->prevEdge = newEdge;
newEdgePtr->nextEdgeReverse = newEdge;
newEdgePtr->aIndex = surfaceBuilder->startingPoint;
}
newEdgePtr->bIndex = pointIndex;
if (edge) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, edge, &nextEdge);
struct SurfaceEdge* edgePtr = portalSurfaceGetEdge(surfaceBuilder, edge->edgeIndex);
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
newEdgePtr->nextEdge = nextEdge.edgeIndex;
newEdgePtr->prevEdgeReverse = edge->edgeIndex;
SET_NEXT_EDGE(edgePtr, edge->isReverse, newEdge);
SET_PREV_EDGE(nextEdgePtr, nextEdge.isReverse, newEdge);
} else {
newEdgePtr->nextEdge = newEdge;
newEdgePtr->prevEdgeReverse = newEdge;
}
surfaceBuilder->hasEdge = 1;
surfaceBuilder->cuttingEdge.edgeIndex = newEdge;
surfaceBuilder->cuttingEdge.isReverse = 0;
#if VERIFY_INTEGRITY
if (!portalSurfaceIsWellFormed(surfaceBuilder)) {
return -1;
}
#endif
return 1;
}
struct Vector2s16* portalSurfaceIntersectEdgeWithLoop(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* pointA, struct Vector2s16* pointDir) {
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->edgeOnSearchLoop;
int iteration;
for (iteration = 0;
iteration < MAX_INTERSECT_LOOPS && (
iteration == 0 ||
currentEdge.edgeIndex != surfaceBuilder->edgeOnSearchLoop.edgeIndex ||
currentEdge.isReverse != surfaceBuilder->edgeOnSearchLoop.isReverse
); portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &currentEdge), ++iteration) {
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex);
struct Vector2s16* edgeA = portalSurfaceGetVertex(surfaceBuilder, GET_CURRENT_POINT(edge, currentEdge.isReverse));
struct Vector2s16* edgeB = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(edge, currentEdge.isReverse));
struct Vector2s16 intersectionPoint;
enum IntersectionType intersectType = portalSurfaceIntersect(pointA, pointDir, edgeA, edgeB, &intersectionPoint);
if (intersectionPoint.equalTest == pointA->equalTest) {
continue;
}
if (intersectType == IntersectionTypePoint) {
int newPointIndex;
if (intersectionPoint.equalTest == edgeA->equalTest) {
newPointIndex = GET_CURRENT_POINT(edge, currentEdge.isReverse);
} else if (intersectionPoint.equalTest == edgeB->equalTest) {
newPointIndex = GET_NEXT_POINT(edge, currentEdge.isReverse);
} else {
newPointIndex = portalSurfaceSplitEdge(surfaceBuilder, &currentEdge, &intersectionPoint);
if (newPointIndex == -1) {
return NULL;
}
}
if (!portalSurfaceConnectToPoint(surfaceBuilder, newPointIndex, &currentEdge)) {
return NULL;
}
surfaceBuilder->hasConnected = 1;
surfaceBuilder->edgeOnSearchLoop = surfaceBuilder->cuttingEdge;
return portalSurfaceGetVertex(surfaceBuilder, newPointIndex);
} else if (intersectType == IntersectionTypeColinear) {
int newPointIndex;
if (intersectionPoint.equalTest == edgeA->equalTest) {
newPointIndex = GET_CURRENT_POINT(edge, currentEdge.isReverse);
portalSurfacePrevEdge(surfaceBuilder, &currentEdge, &surfaceBuilder->cuttingEdge);
} else if (intersectionPoint.equalTest == edgeB->equalTest) {
newPointIndex = GET_NEXT_POINT(edge, currentEdge.isReverse);
surfaceBuilder->cuttingEdge = currentEdge;
} else {
newPointIndex = portalSurfaceSplitEdge(surfaceBuilder, &currentEdge, &intersectionPoint);
if (newPointIndex == -1) {
return NULL;
}
surfaceBuilder->cuttingEdge = currentEdge;
}
surfaceBuilder->hasEdge = 1;
surfaceBuilder->hasConnected = 1;
return portalSurfaceGetVertex(surfaceBuilder, newPointIndex);
}
}
struct Vector2s16 pointB;
vector2s16Add(pointA, pointDir, &pointB);
int newPointIndex = portalSurfaceNewVertex(surfaceBuilder, &pointB);
if (!portalSurfaceConnectToPoint(surfaceBuilder, newPointIndex, NULL)) {
return NULL;
}
return portalSurfaceGetVertex(surfaceBuilder, newPointIndex);
}
int portalSurfaceFindStartingPoint(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* point) {
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->edgeOnSearchLoop;
struct Vector2s16* edgeA = portalSurfaceGetVertex(surfaceBuilder, GET_CURRENT_POINT(portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex), currentEdge.isReverse));
for (int iteration = 0; iteration < MAX_INTERSECT_LOOPS; ++iteration) {
if (iteration > 0 && currentEdge.edgeIndex == surfaceBuilder->edgeOnSearchLoop.edgeIndex && currentEdge.isReverse == surfaceBuilder->edgeOnSearchLoop.isReverse) {
// finished searching loop
break;
}
struct Vector2s16* edgeB = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex), currentEdge.isReverse));
struct Vector2s16 edgeDir;
vector2s16Sub(edgeB, edgeA, &edgeDir);
if (portalSurfaceIsPointOnLine(point, edgeA, &edgeDir)) {
surfaceBuilder->hasEdge = 1;
surfaceBuilder->hasConnected = 1;
if (point->equalTest == edgeA->equalTest) {
portalSurfacePrevEdge(surfaceBuilder, &currentEdge, &surfaceBuilder->cuttingEdge);
} else if (point->equalTest == edgeB->equalTest) {
surfaceBuilder->cuttingEdge = currentEdge;
} else {
if (portalSurfaceSplitEdge(surfaceBuilder, &currentEdge, point) == -1) {
return 0;
}
surfaceBuilder->cuttingEdge = currentEdge;
}
surfaceBuilder->edgeOnSearchLoop = surfaceBuilder->cuttingEdge;
return 1;
}
edgeA = edgeB;
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge);
currentEdge = nextEdge;
}
surfaceBuilder->hasEdge = 0;
surfaceBuilder->hasConnected = 0;
surfaceBuilder->startingPoint = portalSurfaceNewVertex(surfaceBuilder, point);
return 1;
}
int portalSurfaceJoinInnerLoopToOuterLoop(struct PortalSurfaceBuilder* surfaceBuilder) {
struct SurfaceEdge* outerLoopEdge = portalSurfaceGetEdge(surfaceBuilder, surfaceBuilder->edgeOnSearchLoop.edgeIndex);
struct Vector2s16* outerLoopPoint = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(outerLoopEdge, surfaceBuilder->edgeOnSearchLoop.isReverse));
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->cuttingEdge;
struct SurfaceEdgeWithSide nextEdge;
struct SurfaceEdgeWithSide closestEdge;
int closestDistance = 0x7FFFFFFF;
while (portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge), nextEdge.edgeIndex != surfaceBuilder->cuttingEdge.edgeIndex) {
struct Vector2s16* edgePoint = portalSurfaceGetVertex(
surfaceBuilder,
GET_NEXT_POINT(portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex), currentEdge.isReverse)
);
int edgeDistance = vector2s16DistSqr(outerLoopPoint, edgePoint);
if (edgeDistance < closestDistance) {
closestDistance = edgeDistance;
closestEdge = currentEdge;
}
currentEdge = nextEdge;
}
surfaceBuilder->cuttingEdge = closestEdge;
return portalSurfaceConnectToPoint(surfaceBuilder, GET_NEXT_POINT(outerLoopEdge, surfaceBuilder->edgeOnSearchLoop.isReverse), &surfaceBuilder->edgeOnSearchLoop);
}
int portalSurfaceHasFlag(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, enum SurfaceEdgeFlags value) {
return ((surfaceBuilder->edgeFlags[edge->edgeIndex] & (edge->isReverse ? value << 4 : value))) != 0;
}
void portalSurfaceSetFlag(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, enum SurfaceEdgeFlags value) {
surfaceBuilder->edgeFlags[edge->edgeIndex] |= edge->isReverse ? value << 4 : value;
}
void portalSurfaceMarkLoopAsUsed(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edgeOnLoop) {
struct SurfaceEdgeWithSide currentEdge = *edgeOnLoop;
int iteration;
for (iteration = 0; iteration < MAX_SEARCH_ITERATIONS && (iteration == 0 || currentEdge.edgeIndex != edgeOnLoop->edgeIndex || currentEdge.isReverse != edgeOnLoop->isReverse); ++iteration) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge);
portalSurfaceSetFlag(surfaceBuilder, &currentEdge, SurfaceEdgeFlagsUsed);
if (currentEdge.isReverse) {
surfaceBuilder->edges[currentEdge.edgeIndex].nextEdgeReverse = NO_EDGE_CONNECTION;
surfaceBuilder->edges[currentEdge.edgeIndex].prevEdgeReverse = NO_EDGE_CONNECTION;
} else {
surfaceBuilder->edges[currentEdge.edgeIndex].nextEdge = NO_EDGE_CONNECTION;
surfaceBuilder->edges[currentEdge.edgeIndex].prevEdge = NO_EDGE_CONNECTION;
}
struct SurfaceEdge* surfaceEdge = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
// since this function clears out nextEdge and prevEdge
// portalSurfaceNextEdge will fail after the loop as been
// marked. If this check fails the loop is finished
if (surfaceEdge->prevEdge != currentEdge.edgeIndex && surfaceEdge->prevEdgeReverse != currentEdge.edgeIndex) {
return;
}
currentEdge = nextEdge;
}
}
void portalSurfaceMarkHoleAsUsed(struct PortalSurfaceBuilder* surfaceBuilder) {
for (int i = 0; i < surfaceBuilder->currentEdge; ++i) {
if (surfaceBuilder->isLoopEdge[i]) {
struct SurfaceEdgeWithSide edgeOnLoop;
edgeOnLoop.edgeIndex = i;
edgeOnLoop.isReverse = 1;
if (!portalSurfaceHasFlag(surfaceBuilder, &edgeOnLoop, SurfaceEdgeFlagsUsed)) {
portalSurfaceMarkLoopAsUsed(surfaceBuilder, &edgeOnLoop);
}
}
}
}
int portalSurfaceConnectEdges(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* from, struct SurfaceEdgeWithSide* to, struct SurfaceEdgeWithSide* newEdge) {
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, to->edgeIndex);
surfaceBuilder->cuttingEdge = *from;
surfaceBuilder->hasEdge = 1;
if (!portalSurfaceConnectToPoint(surfaceBuilder, GET_NEXT_POINT(nextEdgePtr, to->isReverse), to)) {
return 0;
}
*newEdge = surfaceBuilder->cuttingEdge;
return 1;
}
int portalSurfaceTriangulateLoop(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edgeOnLoop) {
if (portalSurfaceHasFlag(surfaceBuilder, edgeOnLoop, SurfaceEdgeFlagsTriangulated)) {
return 1;
}
struct SurfaceEdgeWithSide currentEdge = *edgeOnLoop;
int iteration;
struct SurfaceEdge* currentEdgePtr = portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex);
// Find the starting point
while (!IS_ORIGINAL_VERTEX_INDEX(surfaceBuilder, GET_CURRENT_POINT(currentEdgePtr, currentEdge.isReverse)) || IS_ORIGINAL_VERTEX_INDEX(surfaceBuilder, GET_NEXT_POINT(currentEdgePtr, currentEdge.isReverse))) {
++iteration;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &currentEdge);
currentEdgePtr = portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex);
if (iteration == MAX_SEARCH_ITERATIONS || (currentEdge.edgeIndex == edgeOnLoop->edgeIndex && currentEdge.isReverse == edgeOnLoop->isReverse)) {
return 0;
}
}
for (iteration = 0; iteration < MAX_SEARCH_ITERATIONS; ++iteration) {
currentEdgePtr = portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex);
struct Vector2s16* edgePointA = portalSurfaceGetVertex(surfaceBuilder, GET_CURRENT_POINT(currentEdgePtr, currentEdge.isReverse));
struct Vector2s16* edgePointB = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(currentEdgePtr, currentEdge.isReverse));
struct SurfaceEdgeWithSide nextEdge;
struct SurfaceEdgeWithSide prevEdge;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge);
portalSurfacePrevEdge(surfaceBuilder, &currentEdge, &prevEdge);
int prevPointIndex = GET_CURRENT_POINT(portalSurfaceGetEdge(surfaceBuilder, prevEdge.edgeIndex), prevEdge.isReverse);
struct Vector2s16* nextPoint = portalSurfaceGetVertex(surfaceBuilder, GET_NEXT_POINT(portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex), nextEdge.isReverse));
struct Vector2s16* prevPoint = portalSurfaceGetVertex(surfaceBuilder, prevPointIndex);
// check if finished
if (nextPoint == prevPoint) {
portalSurfaceSetFlag(surfaceBuilder, &currentEdge, SurfaceEdgeFlagsTriangulated);
portalSurfaceSetFlag(surfaceBuilder, &nextEdge, SurfaceEdgeFlagsTriangulated);
portalSurfaceSetFlag(surfaceBuilder, &prevEdge, SurfaceEdgeFlagsTriangulated);
return 1;
}
struct Vector2s16 edgeDir;
struct Vector2s16 nextEdgeDir;
struct Vector2s16 prevEdgeDir;
vector2s16Sub(edgePointB, edgePointA, &edgeDir);
vector2s16Sub(nextPoint, edgePointB, &nextEdgeDir);
vector2s16Sub(edgePointB, edgePointA, &edgeDir);
struct SurfaceEdgeWithSide nextCurrentEdge;
if (IS_ORIGINAL_VERTEX_INDEX(surfaceBuilder, prevPointIndex) && vector2s16Cross(&prevEdgeDir, &nextEdgeDir) > vector2s16Cross(&edgeDir, &nextEdgeDir)) {
struct SurfaceEdgeWithSide prevPrevEdge;
portalSurfacePrevEdge(surfaceBuilder, &prevEdge, &prevPrevEdge);
if (!portalSurfaceConnectEdges(surfaceBuilder, &prevPrevEdge, &currentEdge, &nextCurrentEdge)) {
return 0;
}
portalSurfaceSetFlag(surfaceBuilder, &prevEdge, SurfaceEdgeFlagsTriangulated);
portalSurfaceSetFlag(surfaceBuilder, &currentEdge, SurfaceEdgeFlagsTriangulated);
} else {
if (!portalSurfaceConnectEdges(surfaceBuilder, &prevEdge, &nextEdge, &nextCurrentEdge)) {
return 0;
}
portalSurfaceSetFlag(surfaceBuilder, &currentEdge, SurfaceEdgeFlagsTriangulated);
portalSurfaceSetFlag(surfaceBuilder, &nextEdge, SurfaceEdgeFlagsTriangulated);
}
currentEdge = nextCurrentEdge;
nextCurrentEdge.isReverse = !nextCurrentEdge.isReverse;
portalSurfaceSetFlag(surfaceBuilder, &nextCurrentEdge, SurfaceEdgeFlagsTriangulated);
}
return 0;
}
int portalSurfaceTriangulate(struct PortalSurfaceBuilder* surfaceBuilder) {
struct SurfaceEdgeWithSide edge;
for (int i = 0; i < surfaceBuilder->currentEdge; ++i) {
edge.edgeIndex = i;
for (edge.isReverse = 0; edge.isReverse < 2; ++edge.isReverse) {
if (GET_NEXT_EDGE(portalSurfaceGetEdge(surfaceBuilder, edge.edgeIndex), edge.isReverse) == NO_EDGE_CONNECTION) {
continue;
}
if (!portalSurfaceTriangulateLoop(surfaceBuilder, &edge)) {
return 0;
}
}
}
return 1;
}
struct PortalSurface* portalSurfaceCutHole(struct PortalSurface* surface, struct Vector2s16* loop) {
struct PortalSurfaceBuilder surfaceBuilder;
int currentEdge = surface->edgeCount + ADDITIONAL_EDGE_CAPACITY;
surfaceBuilder.original = surface;
surfaceBuilder.vertices = stackMalloc(sizeof(struct Vector2s16) * (surface->vertexCount + ADDITIONAL_VERTEX_CAPACITY));
memCopy(surfaceBuilder.vertices, surface->vertices, sizeof(struct Vector2s16) * surface->vertexCount);
surfaceBuilder.currentVertex = surface->vertexCount;
surfaceBuilder.edges = stackMalloc(sizeof(struct SurfaceEdge) * currentEdge);
memCopy(surfaceBuilder.edges, surface->edges, sizeof(struct SurfaceEdge) * surface->edgeCount);
surfaceBuilder.isLoopEdge = stackMalloc(sizeof(u8) * currentEdge);
surfaceBuilder.currentEdge = surface->edgeCount;
surfaceBuilder.edgeFlags = stackMalloc(sizeof(u8) *currentEdge);
zeroMemory(surfaceBuilder.edgeFlags, surface->edgeCount + currentEdge);
zeroMemory(surfaceBuilder.isLoopEdge, surface->edgeCount + currentEdge);
struct Vector2s16* prev = &loop[0];
if (!portalSurfaceFindEnclosingFace(surface, prev, &surfaceBuilder.edgeOnSearchLoop)) {
goto error;
}
if (!portalSurfaceFindStartingPoint(&surfaceBuilder, prev)) {
goto error;
}
for (int index = 1; index <= PORTAL_LOOP_SIZE;) {
struct Vector2s16* next = &loop[index == PORTAL_LOOP_SIZE ? 0 : index];
struct Vector2s16 dir;
if (!portalSurfaceFindNextLoop(&surfaceBuilder, next)) {
goto error;
}
vector2s16Sub(next, prev, &dir);
struct Vector2s16* newPoint = portalSurfaceIntersectEdgeWithLoop(&surfaceBuilder, prev, &dir);
if (!newPoint) {
goto error;
}
// check if the portal loop ever intersected an edge
if (index == PORTAL_LOOP_SIZE && !surfaceBuilder.hasConnected) {
// the portal loop is fully contained
int firstEdgeIndex = surface->edgeCount;
int lastEdgeIndex = surfaceBuilder.currentEdge - 1;
struct SurfaceEdge* firstEdge = portalSurfaceGetEdge(&surfaceBuilder, firstEdgeIndex);
struct SurfaceEdge* lastEdge = portalSurfaceGetEdge(&surfaceBuilder, lastEdgeIndex);
firstEdge->prevEdge = lastEdgeIndex;
firstEdge->nextEdgeReverse = lastEdgeIndex;
lastEdge->nextEdge = firstEdgeIndex;
lastEdge->prevEdgeReverse = firstEdgeIndex;
--surfaceBuilder.currentVertex;
if (!portalSurfaceJoinInnerLoopToOuterLoop(&surfaceBuilder)) {
return NULL;
}
}
if (newPoint->equalTest == next->equalTest) {
// only increment i if the next point was reached
++index;
}
prev = newPoint;
}
portalSurfaceMarkHoleAsUsed(&surfaceBuilder);
if (!portalSurfaceTriangulate(&surfaceBuilder)) {
goto error;
}
stackMallocFree(surfaceBuilder.edgeFlags);
stackMallocFree(surfaceBuilder.isLoopEdge);
stackMallocFree(surfaceBuilder.edges);
stackMallocFree(surfaceBuilder.vertices);
return NULL;
error:
stackMallocFree(surfaceBuilder.edgeFlags);
stackMallocFree(surfaceBuilder.isLoopEdge);
stackMallocFree(surfaceBuilder.edges);
stackMallocFree(surfaceBuilder.vertices);
return NULL;
}
void portalSurface2DPoint(struct PortalSurface* surface, struct Vector3* at, struct Vector2s16* output) {
struct Vector3 offset;
vector3Sub(at, &surface->corner, &offset);
@ -1018,7 +100,7 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
int iteration = 0;
for (iteration = 0; iteration < MAX_POS_ADJUST_ITERATIONS; ++iteration) {
int minOverlap = NO_OVERLAP;
int minOverlap = PORTAL_SURFACE_OVERLAP;
struct Vector2s16 minOverlapOffset;
for (int i = 0; i < surface->sideCount; ++i) {
@ -1093,7 +175,7 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
}
}
if (minOverlap == NO_OVERLAP) {
if (minOverlap == PORTAL_SURFACE_OVERLAP) {
break;
}
@ -1118,7 +200,11 @@ int portalSurfaceGenerate(struct PortalSurface* surface, struct Transform* porta
return 0;
}
portalSurfaceCutHole(surface, portalOutline);
struct PortalSurface* newSurface = newPortalSurfaceWithHole(surface, portalOutline);
if (newSurface) {
deletePortalSurface(newSurface);
}
portalSurfaceInverse(surface, &correctPosition, &portalAt->position);
// TODO
@ -1127,4 +213,12 @@ int portalSurfaceGenerate(struct PortalSurface* surface, struct Transform* porta
// if triangle intersects portal then retrianglute face
return 1;
}
void deletePortalSurface(struct PortalSurface* portalSurface) {
free(portalSurface->vertices);
free(portalSurface->edges);
free(portalSurface->gfxVertices);
free(portalSurface->triangles);
free(portalSurface);
}

View file

@ -33,6 +33,9 @@ struct PortalSurface {
struct Vector3 right;
struct Vector3 up;
struct Vector3 corner;
Vtx* gfxVertices;
Gfx* triangles;
};
struct PortalSurfaceMapping {
@ -44,4 +47,6 @@ int portalSurfaceIsInside(struct PortalSurface* surface, struct Transform* porta
int portalSurfaceGenerate(struct PortalSurface* surface, struct Transform* portalAt, Vtx* vertices, Gfx* triangles);
void deletePortalSurface(struct PortalSurface* portalSurface);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,73 @@
#ifndef __PORTAL_SURFACE_GENERATOR_H__
#define __PORTAL_SURFACE_GENERATOR_H__
#include "portal_surface.h"
#define PORTAL_SURFACE_OVERLAP 0x10000
#define SB_GET_NEXT_EDGE(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->nextEdgeReverse : (surfaceEdge)->nextEdge)
#define SB_GET_PREV_EDGE(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->prevEdgeReverse : (surfaceEdge)->prevEdge)
#define SB_GET_CURRENT_POINT(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->bIndex : (surfaceEdge->aIndex))
#define SB_GET_NEXT_POINT(surfaceEdge, isReverse) ((isReverse) ? (surfaceEdge)->aIndex : (surfaceEdge->bIndex))
#define SB_SET_NEXT_EDGE(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->nextEdgeReverse = (value); else (surfaceEdge)->nextEdge = (value); } while (0)
#define SB_SET_PREV_EDGE(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->prevEdgeReverse = (value); else (surfaceEdge)->prevEdge = (value); } while (0)
#define SB_SET_CURRENT_POINT(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->bIndex = (value); else (surfaceEdge)->aIndex = (value); } while (0)
#define SB_SET_NEXT_POINT(surfaceEdge, isReverse, value) do { if (isReverse) (surfaceEdge)->aIndex = (value); else (surfaceEdge)->bIndex = (value); } while (0)
struct SurfaceEdgeWithSide {
int edgeIndex;
int isReverse;
};
#define SB_ORIGINAL_EDGE_TO_EDGE_WITH_SIDE(originalEdge, edge) do { (edge)->edgeIndex = originalEdge & 0x7F; (edge)->isReverse = (originalEdge & 0x80) != 0; } while (0)
#define SB_PACK_ORIGINALEDGE(edge) (((edge)->edgeIndex & 0x7F) | ((edge)->isReverse) ? 0x80 : 0x00)
struct OriginalEdgeMapping {
u8 originalEdge;
u8 originalEdgeReverse;
};
enum SurfaceEdgeFlags {
SurfaceEdgeFlagsUsed = (1 << 0),
SurfaceEdgeFlagsTriangulated = (1 << 1),
SurfaceEdgeFlagsFilled = (1 << 2),
};
struct PortalSurfaceBuilder {
struct PortalSurface* original;
struct Vector2s16* vertices;
struct SurfaceEdge* edges;
short currentVertex;
short currentEdge;
u8* isLoopEdge;
u8* edgeFlags;
struct OriginalEdgeMapping* originalEdgeIndex;
struct SurfaceEdgeWithSide edgeOnSearchLoop;
union {
// set when hasEdge is true
struct SurfaceEdgeWithSide cuttingEdge;
// set when hasEdge is false
int startingPoint;
};
short hasEdge;
short hasConnected;
Vtx* gfxVertices;
};
struct PortalSurface* newPortalSurfaceWithHole(struct PortalSurface* surface, struct Vector2s16* loop);
int portalSurfaceHasFlag(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, enum SurfaceEdgeFlags value);
void portalSurfaceSetFlag(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, enum SurfaceEdgeFlags value);
struct SurfaceEdge* portalSurfaceGetEdge(struct PortalSurfaceBuilder* surfaceBuilder, int edgeIndex);
void portalSurfaceNextEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* nextEdge);
void portalSurfacePrevEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* prevEdge);
#endif

View file

@ -0,0 +1,233 @@
#include "portal_surface_gfx.h"
#include "../util/memory.h"
#define GFX_VERTEX_CACHE_SIZE 32
void gfxBuilderFillTriangle(struct GfxBuilderState* gfxBuilder, struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge) {
struct SurfaceEdgeWithSide nextEdge;
struct SurfaceEdgeWithSide prevEdge;
portalSurfaceNextEdge(surfaceBuilder, edge, &nextEdge);
portalSurfacePrevEdge(surfaceBuilder, edge, &prevEdge);
struct SurfaceEdge* edgePtr = portalSurfaceGetEdge(surfaceBuilder, edge->edgeIndex);
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
struct SurfaceEdge* prevEdgePtr = portalSurfaceGetEdge(surfaceBuilder, prevEdge.edgeIndex);
portalSurfaceSetFlag(surfaceBuilder, edge, SurfaceEdgeFlagsFilled);
portalSurfaceSetFlag(surfaceBuilder, &nextEdge, SurfaceEdgeFlagsFilled);
portalSurfaceSetFlag(surfaceBuilder, &prevEdge, SurfaceEdgeFlagsFilled);
struct GfxTraingleIndices* triangle = &gfxBuilder->triangles[gfxBuilder->triangleCount];
triangle->indices[0] = SB_GET_CURRENT_POINT(edgePtr, edge->isReverse);
triangle->indices[1] = SB_GET_CURRENT_POINT(nextEdgePtr, nextEdge.isReverse);
triangle->indices[2] = SB_GET_CURRENT_POINT(prevEdgePtr, prevEdge.isReverse);
++gfxBuilder->triangleCount;
}
void gfxBuilderCollectTriangles(struct GfxBuilderState* gfxBuilder, struct PortalSurfaceBuilder* surfaceBuilder) {
struct SurfaceEdgeWithSide edge;
for (edge.edgeIndex = 0; edge.edgeIndex < surfaceBuilder->currentEdge; ++edge.edgeIndex) {
struct SurfaceEdge* edgePtr = portalSurfaceGetEdge(surfaceBuilder, edge.edgeIndex);
for (edge.isReverse = 0; edge.isReverse <= 2; ++edge.isReverse) {
if (SB_GET_NEXT_EDGE(edgePtr, edge.isReverse) == NO_EDGE_CONNECTION) {
continue;
}
if (!portalSurfaceHasFlag(surfaceBuilder, &edge, SurfaceEdgeFlagsFilled)) {
gfxBuilderFillTriangle(gfxBuilder, surfaceBuilder, &edge);
}
}
}
}
#define NO_VERTEX 0xFF
void sortU8Array(u8* input, u8* tmpBuffer, int min, int max) {
int count = max - min;
if (count <= 1) {
return;
}
if (count == 2) {
if (input[min] > input[max - 1]) {
int tmp = input[min];
input[min] = input[max - 1];
input[max - 1] = tmp;
}
return;
}
int midpoint = (min + max) >> 1;
sortU8Array(input, tmpBuffer, min, midpoint);
sortU8Array(input, tmpBuffer, midpoint, max);
int aHead = min;
int bHead = midpoint;
int writeHead = min;
while (aHead < midpoint && bHead < max) {
if (input[aHead] < input[bHead]) {
tmpBuffer[writeHead] = input[aHead];
++aHead;
++writeHead;
} else {
tmpBuffer[writeHead] = input[bHead];
++bHead;
++writeHead;
}
}
while (aHead < midpoint) {
tmpBuffer[writeHead] = input[aHead];
++aHead;
++writeHead;
}
while (bHead < max) {
tmpBuffer[writeHead] = input[bHead];
++bHead;
++writeHead;
}
for (int i = min; i < max; ++i) {
input[i] = tmpBuffer[i];
}
}
void gfxBuilderFlushCache(struct GfxBuilderState* gfxBuilder, u8* vertexCacheContents, u8* vertexToCacheIndex, int startTriangle, int endTriangle, int usedSlotsCount) {
u8 tmp[GFX_VERTEX_CACHE_SIZE];
sortU8Array(vertexCacheContents, tmp, 0, usedSlotsCount);
int lastStart = 0;
for (int i = 0; i < usedSlotsCount; ++i) {
// update the mapping
vertexToCacheIndex[vertexCacheContents[i]] = i;
// check if the vertex needs to updated
if (i > 0 && vertexCacheContents[i - 1] + 1 != vertexCacheContents[i]) {
gSPVertex(gfxBuilder->gfx++, &gfxBuilder->vtxCopy[vertexCacheContents[lastStart]], i - lastStart, lastStart);
lastStart = i;
}
}
gSPVertex(gfxBuilder->gfx++, &gfxBuilder->vtxCopy[vertexCacheContents[lastStart]], usedSlotsCount - lastStart, lastStart);
for (int triangleIndex = startTriangle; triangleIndex < endTriangle; triangleIndex += 2) {
struct GfxTraingleIndices* triangle = &gfxBuilder->triangles[triangleIndex];
if (triangleIndex + 1 < endTriangle) {
struct GfxTraingleIndices* nextTriangle = &gfxBuilder->triangles[triangleIndex + 1];
gSP2Triangles(gfxBuilder->gfx++,
triangle->indices[0], triangle->indices[1], triangle->indices[2], 0,
nextTriangle->indices[0], nextTriangle->indices[1], nextTriangle->indices[2], 0
);
} else {
gSP1Triangle(gfxBuilder->gfx++,
triangle->indices[0], triangle->indices[1], triangle->indices[2], 0
);
}
}
}
void gfxBuilderBuildGfx(struct GfxBuilderState* gfxBuilder, struct PortalSurfaceBuilder* surfaceBuilder) {
u8 vertexCacheContents[GFX_VERTEX_CACHE_SIZE];
u8* vertexToCacheIndex = stackMalloc(surfaceBuilder->currentVertex);
int usedSlotsCount = 0;
int triangleStart = 0;
for (int i = 0; i < GFX_VERTEX_CACHE_SIZE; ++i) {
vertexCacheContents[i] = NO_VERTEX;
}
for (int i = 0; i < surfaceBuilder->currentVertex; ++i) {
vertexToCacheIndex[i] = NO_VERTEX;
}
for (int triangleIndex = 0; triangleIndex < gfxBuilder->triangleCount; ++triangleIndex) {
struct GfxTraingleIndices* triangle = &gfxBuilder->triangles[triangleIndex];
int neededSlots = 0;
for (int index = 0; index < 3; ++index) {
if (vertexToCacheIndex[triangle->indices[index]] == NO_VERTEX) {
++neededSlots;
}
}
if (neededSlots + usedSlotsCount > GFX_VERTEX_CACHE_SIZE) {
gfxBuilderFlushCache(gfxBuilder, vertexCacheContents, vertexToCacheIndex, triangleStart, triangleIndex, usedSlotsCount);
neededSlots = 3;
usedSlotsCount = 0;
triangleStart = triangleIndex;
for (int i = 0; i < surfaceBuilder->currentVertex; ++i) {
vertexToCacheIndex[i] = NO_VERTEX;
}
}
for (int index = 0; index < 3; ++index) {
int vertex = triangle->indices[index];
if (vertexToCacheIndex[vertex] == NO_VERTEX) {
vertexToCacheIndex[vertex] = usedSlotsCount;
vertexCacheContents[usedSlotsCount] = vertex;
++usedSlotsCount;
}
}
}
gfxBuilderFlushCache(gfxBuilder, vertexCacheContents, vertexToCacheIndex, triangleStart, gfxBuilder->triangleCount, usedSlotsCount);
gSPEndDisplayList(gfxBuilder->gfx++);
stackMallocFree(vertexToCacheIndex);
}
struct DisplayListResult newGfxFromSurfaceBuilder(struct PortalSurfaceBuilder* surfaceBuilder) {
struct GfxBuilderState builderState;
int possibleTriangleCount = (
// double sided edge count
(surfaceBuilder->currentEdge - surfaceBuilder->original->sideCount) * 2
// single sided edge count
+ surfaceBuilder->original->sideCount
// round up just in case
+ 2
) / 3;
builderState.triangles = stackMalloc(sizeof(struct GfxTraingleIndices) * possibleTriangleCount);
builderState.triangleCount = 0;
builderState.vtxCopy = malloc(sizeof(Vtx) * surfaceBuilder->currentVertex);
memCopy(builderState.vtxCopy, surfaceBuilder->gfxVertices, sizeof(Vtx) * surfaceBuilder->currentVertex);
gfxBuilderCollectTriangles(&builderState, surfaceBuilder);
Gfx* tmpResult = stackMalloc(builderState.triangleCount * sizeof(Gfx));
builderState.gfx = tmpResult;
gfxBuilderBuildGfx(&builderState, surfaceBuilder);
int finalGfxSize = sizeof(Gfx) * (builderState.gfx - tmpResult);
struct DisplayListResult result;
result.gfx = malloc(finalGfxSize);
result.vtx = builderState.vtxCopy;
memCopy(result.gfx, tmpResult, finalGfxSize);
stackMallocFree(tmpResult);
stackMallocFree(builderState.triangles);
return result;
}

View file

@ -0,0 +1,25 @@
#ifndef __PORTAL_SURFACE_GFX_H__
#define __PORTAL_SURFACE_GFX_H__
#include "portal_surface_generator.h"
struct GfxTraingleIndices {
u8 indices[3];
};
struct GfxBuilderState {
short triangleCount;
struct GfxTraingleIndices* triangles;
Vtx* vtxCopy;
Gfx* gfx;
};
struct DisplayListResult {
Vtx* vtx;
Gfx* gfx;
};
struct DisplayListResult newGfxFromSurfaceBuilder(struct PortalSurfaceBuilder* surfaceBuilder);
#endif

View file

@ -340,7 +340,7 @@ void memCopy(void* target, const void* src, int size)
}
}
#define STACK_MALLOC_SIZE_BYTES 4096
#define STACK_MALLOC_SIZE_BYTES (8 * 1024)
#define STACK_MALLOC_SIZE_WORDS (STACK_MALLOC_SIZE_BYTES >> 3)
int gStackMallocAt;