mirror of
https://github.com/mwpenny/portal64-still-alive.git
synced 2024-10-20 10:37:37 -04:00
Implement hole cutting algorithm
This commit is contained in:
parent
c7734ca524
commit
cf9f53755e
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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, ¤t, &check);
|
||||
struct SurfaceEdge* checkPtr = portalSurfaceGetEdge(surfaceBuilder, check.edgeIndex);
|
||||
|
||||
if (checkPtr->prevEdge != i && checkPtr->prevEdgeReverse != i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
portalSurfacePrevEdge(surfaceBuilder, ¤t, &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, ¤tEdge, forPoint)) {
|
||||
struct SurfaceEdgeWithSide nextEdge;
|
||||
|
||||
if (!portalSurfaceNextLoop(surfaceBuilder, ¤tEdge, &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, ¤tEdge, ¤tEdge), ++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, ¤tEdge, &intersectionPoint);
|
||||
|
||||
if (newPointIndex == -1) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!portalSurfaceConnectToPoint(surfaceBuilder, newPointIndex, ¤tEdge)) {
|
||||
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, ¤tEdge, &surfaceBuilder->cuttingEdge);
|
||||
} else if (intersectionPoint.equalTest == edgeB->equalTest) {
|
||||
newPointIndex = GET_NEXT_POINT(edge, currentEdge.isReverse);
|
||||
surfaceBuilder->cuttingEdge = currentEdge;
|
||||
} else {
|
||||
newPointIndex = portalSurfaceSplitEdge(surfaceBuilder, ¤tEdge, &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, ¤tEdge, &surfaceBuilder->cuttingEdge);
|
||||
} else if (point->equalTest == edgeB->equalTest) {
|
||||
surfaceBuilder->cuttingEdge = currentEdge;
|
||||
} else {
|
||||
if (portalSurfaceSplitEdge(surfaceBuilder, ¤tEdge, point) == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
surfaceBuilder->cuttingEdge = currentEdge;
|
||||
}
|
||||
|
||||
surfaceBuilder->edgeOnSearchLoop = surfaceBuilder->cuttingEdge;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
edgeA = edgeB;
|
||||
struct SurfaceEdgeWithSide nextEdge;
|
||||
portalSurfaceNextEdge(surfaceBuilder, ¤tEdge, &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, ¤tEdge, &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, ¤tEdge, &nextEdge);
|
||||
|
||||
portalSurfaceSetFlag(surfaceBuilder, ¤tEdge, 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, ¤tEdge, ¤tEdge);
|
||||
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, ¤tEdge, &nextEdge);
|
||||
portalSurfacePrevEdge(surfaceBuilder, ¤tEdge, &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, ¤tEdge, 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, ¤tEdge, &nextCurrentEdge)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
portalSurfaceSetFlag(surfaceBuilder, &prevEdge, SurfaceEdgeFlagsTriangulated);
|
||||
portalSurfaceSetFlag(surfaceBuilder, ¤tEdge, SurfaceEdgeFlagsTriangulated);
|
||||
} else {
|
||||
if (!portalSurfaceConnectEdges(surfaceBuilder, &prevEdge, &nextEdge, &nextCurrentEdge)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
portalSurfaceSetFlag(surfaceBuilder, ¤tEdge, 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);
|
||||
}
|
|
@ -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
|
1004
src/scene/portal_surface_generator.c
Normal file
1004
src/scene/portal_surface_generator.c
Normal file
File diff suppressed because it is too large
Load diff
73
src/scene/portal_surface_generator.h
Normal file
73
src/scene/portal_surface_generator.h
Normal 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
|
233
src/scene/portal_surface_gfx.c
Normal file
233
src/scene/portal_surface_gfx.c
Normal 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;
|
||||
}
|
25
src/scene/portal_surface_gfx.h
Normal file
25
src/scene/portal_surface_gfx.h
Normal 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
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue