Work on portal surface hole cutting

This commit is contained in:
James Lambert 2022-06-26 18:57:00 -06:00
parent 92247567e0
commit 17ef47fe2c
9 changed files with 466 additions and 11 deletions

View file

@ -41,7 +41,8 @@ RUN apt install -y binutils-mips-n64 \
build-essential \
wget \
unzip \
sox
sox \
mpg123
COPY skelatool64/src skelatool64/src
COPY skelatool64/main.cpp skelatool64/main.cpp

View file

@ -215,17 +215,27 @@ build/src/levels/levels.o: build/assets/test_chambers/level_list.h build/assets/
SOUND_ATTRIBUTES = $(shell find assets/ -type f -name '*.sox')
MUSIC_ATTRIBUTES = $(shell find assets/sound/music/ -type f -name '*.msox')
INS_SOUNDS = $(shell find assets/ -type f -name '*.ins')
SOUND_CLIPS = $(SOUND_ATTRIBUTES:%.sox=build/%.aifc) $(INS_SOUNDS)
SOUND_CLIPS = $(SOUND_ATTRIBUTES:%.sox=build/%.aifc) $(INS_SOUNDS) $(MUSIC_ATTRIBUTES:%.msox=build/%.aifc)
$(INS_SOUNDS): portal_pak_dir
portal_pak_dir/sound/music/%.wav: portal_pak_dir/sound/music/%.mp3
build/%.aifc: %.sox portal_pak_dir
@mkdir -p $(@D)
sox $(<:assets/%.sox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav)
$(SFZ2N64) -o $@ $(@:%.aifc=%.wav)
build/%.aifc: %.msox portal_pak_dir
@mkdir -p $(@D)
mpg123 -w $(<:assets/%.msox=portal_pak_dir/%.wav) $(<:assets/%.msox=portal_pak_dir/%.mp3)
sox $(<:assets/%.msox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav)
$(SFZ2N64) -o $@ $(@:%.aifc=%.wav)
build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl: $(SOUND_CLIPS)
@mkdir -p $(@D)
$(SFZ2N64) -o $@ $^

View file

@ -22,7 +22,7 @@ Install `vtf2png`, `sfz2n64` and `skeletool64`
echo "deb [trusted=yes] https://lambertjamesd.github.io/apt/ ./" \
| tee /etc/apt/sources.list.d/lambertjamesd.list
sudo apt install vtf2png sfz2n64 skeletool64
sudo apt install vtf2png sfz2n64 skeletool64 mpg123
```
<br />
@ -92,3 +92,4 @@ Where `/home/james/Blender/blender-2.93.1-linux-x64` is the folder where Blender
- [ ] Cut holes in portal walls
- [ ] Cube dispenser
- [ ] NAN in overlap
- [ ] Get an optimized build working

View file

@ -0,0 +1 @@
-c 1 -r 22050

View file

@ -169,6 +169,8 @@ std::unique_ptr<StructureDataChunk> calculatePortalSingleSurface(CFileDefinition
portalSurface->AddPrimitive(sideCount);
// edgesCount
portalSurface->AddPrimitive(edgeOrder.size());
// vertexCount
portalSurface->AddPrimitive(mesh.mMesh->mNumVertices);
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeA)));
portalSurface->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(quad.edgeB)));

30
src/math/vector2s16.c Normal file
View file

@ -0,0 +1,30 @@
#include "vector2s16.h"
void vector2s16Add(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* output) {
output->x = a->x + b->x;
output->y = a->y + b->y;
}
void vector2s16Sub(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* output) {
output->x = a->x - b->x;
output->y = a->y - b->y;
}
int vector2s16Dot(struct Vector2s16* a, struct Vector2s16* b) {
return (int)a->x * b->x + (int)a->y * b->y;
}
int vector2s16Cross(struct Vector2s16* a, struct Vector2s16* b) {
return (int)a->x * b->y - (int)a->y * b->x;
}
int vector2s16MagSqr(struct Vector2s16* a) {
return (int)a->x * a->x + (int)a->y * a->y;
}
int vector2s16DistSqr(struct Vector2s16* a, struct Vector2s16* b) {
int x = (int)a->x - (int)b->x;
int y = (int)a->y - (int)b->y;
return x * x + y * y;
}

25
src/math/vector2s16.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef __MATH_VECTOR2S16_H__
#define __MATH_VECTOR2S16_H__
struct Vector2s16 {
union {
struct {
short x;
short y;
};
int equalTest;
};
};
void vector2s16Add(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* output);
void vector2s16Sub(struct Vector2s16* a, struct Vector2s16* b, struct Vector2s16* output);
int vector2s16Dot(struct Vector2s16* a, struct Vector2s16* b);
int vector2s16Cross(struct Vector2s16* a, struct Vector2s16* b);
int vector2s16MagSqr(struct Vector2s16* a);
int vector2s16DistSqr(struct Vector2s16* a, struct Vector2s16* b);
#endif

View file

@ -4,9 +4,387 @@
#include "math/mathf.h"
#include "math/vector2.h"
#include <math.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 MAX_SEARCH_ITERATIONS 20
#define ADDITIONAL_EDGE_CAPACITY 32
#define ADDITIONAL_VERTEX_CAPACITY 16
struct SurfaceEdgeWithSide {
int edgeIndex;
int isReverse;
};
struct PortalSurfaceBuilder {
struct PortalSurface* surface;
struct Vector2s16* additionalVertices;
struct SurfaceEdge* additionalEdges;
short currentVertex;
short currentEdge;
struct SurfaceEdgeWithSide edgeOnSearchLoop;
union {
// set when hasEdge is true
struct SurfaceEdgeWithSide cuttingEdge;
// set when hasEdge is false
struct Vector2s16* 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 nextEdgeIndex;
int currentIteration = 0;
while (currentIteration < MAX_SEARCH_ITERATIONS && (nextEdgeIndex = GET_NEXT_EDGE(currentEdge, isEdgeReverse)) != 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
);
if (vector2s16Cross(&edgeDir, &pointDir) < 0) {
// the point is on the opposite side of this edge
startEdgeIndex = surface->edges - currentEdge;
isEdgeReverse = !isEdgeReverse;
nextEdgeIndex = GET_NEXT_EDGE(currentEdge, isEdgeReverse);
if (nextEdgeIndex == 0xFF) {
// has no opposite edge
return 0;
}
}
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) {
if (edgeIndex < surfaceBuilder->surface->edgeCount) {
return &surfaceBuilder->surface->edges[edgeIndex];
} else {
return &surfaceBuilder->additionalEdges[edgeIndex - surfaceBuilder->surface->edgeCount];
}
}
struct Vector2s16* portalSurfaceGetPoint(struct PortalSurfaceBuilder* surfaceBuilder, int vertexIndex) {
if (vertexIndex < surfaceBuilder->surface->vertexCount) {
return &surfaceBuilder->surface->vertices[vertexIndex];
} else {
return &surfaceBuilder->additionalVertices[vertexIndex - surfaceBuilder->surface->vertexCount];
}
}
void portalSurfaceNextEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* nextEdge) {
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex);
nextEdge->edgeIndex = GET_NEXT_EDGE(edge, currentEdge->isReverse);
nextEdge->isReverse = portalSurfaceGetEdge(surfaceBuilder, nextEdge->edgeIndex)->prevEdgeReverse == currentEdge->edgeIndex;
}
void portalSurfacePrevEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* currentEdge, struct SurfaceEdgeWithSide* prevEdge) {
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge->edgeIndex);
prevEdge->edgeIndex = GET_PREV_EDGE(edge, currentEdge->isReverse);
prevEdge->isReverse = portalSurfaceGetEdge(surfaceBuilder, prevEdge->edgeIndex)->nextEdgeReverse == currentEdge->edgeIndex;
}
#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)vector2MagSqr(&edgeDir) * vector2MagSqr(&originOffset);
s64 dotProduct = vector2Dot(&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 pointLerp = vector2s16Cross(&originOffset, &edgeDir);
if (pointLerp <= 0) {
return IntersectionTypeNone;
}
int denominator = vector2s16Cross(pointDir, &edgeDir);
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 (vector2DistSqr(edgeB, pointA) >= vector2MagSqr(pointDir)) {
// edge ends first
*intersection = *edgeB;
} else {
// point ends first
vector2s16Add(pointA, pointDir, intersection);
}
} else {
if (vector2MagSqr(originOffset) >= vector2MagSqr(pointDir)) {
// edge ends first
*intersection = *edgeA;
} else {
// point ends first
vector2s16Add(pointA, pointDir, intersection);
}
}
// the lines are colinear
return IntersectionTypeColinear;
}
if (pointLerp > denominator || denominator == 0) {
return IntersectionTypeNone;
}
int edgeLerp = vector2s16Cross(pointDir, &originOffset);
if (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;
}
void portalSurfaceIntersectEdgeWithLoop(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* pointA, struct Vector2s16* pointDir) {
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->edgeOnSearchLoop;
for (int iteration = 0; iteration < MAX_INTERSECT_LOOPS; ++iteration) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge);
if (nextEdge.edgeIndex == surfaceBuilder->edgeOnSearchLoop.edgeIndex && nextEdge.isReverse == surfaceBuilder->edgeOnSearchLoop.isReverse) {
// finished searching loop
break;
}
struct SurfaceEdge* edge = portalSurfaceGetEdge(surfaceBuilder, currentEdge.edgeIndex);
struct Vector2s16* edgeA = portalSurfaceGetPoint(surfaceBuilder, GET_CURRENT_POINT(edge, currentEdge.isReverse));
struct Vector2s16* edgeB = portalSurfaceGetPoint(surfaceBuilder, GET_NEXT_POINT(edge, currentEdge.isReverse));
struct Vector2s16 intersectionPoint;
enum IntersectionType intersectType = portalSurfaceIntersect(pointA, pointDir, edgeA, edgeB, &intersectionPoint);
if (intersectType) {
}
currentEdge = nextEdge;
}
}
int portalSurfaceSplitEdge(struct PortalSurfaceBuilder* surfaceBuilder, struct SurfaceEdgeWithSide* edge, struct Vector2s16* point) {
if (surfaceBuilder->currentEdge == ADDITIONAL_EDGE_CAPACITY || surfaceBuilder->currentVertex == ADDITIONAL_VERTEX_CAPACITY) {
return 0;
}
surfaceBuilder->additionalVertices[surfaceBuilder->currentVertex] = *point;
int newVertexIndex = surfaceBuilder->currentVertex + surfaceBuilder->surface->vertexCount;
++surfaceBuilder->currentVertex;
struct SurfaceEdge* existingEdge = portalSurfaceGetEdge(surfaceBuilder, edge->edgeIndex);
struct SurfaceEdgeWithSide nextEdge;
struct SurfaceEdgeWithSide prevReverseEdge;
portalSurfaceNextEdge(surfaceBuilder, edge, &nextEdge);
portalSurfacePrevEdge(surfaceBuilder, edge, &prevReverseEdge);
struct SurfaceEdge previousEdgeCopy = *existingEdge;
struct SurfaceEdge* newEdge = &surfaceBuilder->additionalEdges[surfaceBuilder->currentEdge];
int newEdgeIndex = surfaceBuilder->currentEdge + surfaceBuilder->surface->edgeCount;
++surfaceBuilder->currentEdge;
newEdge->bIndex = existingEdge->bIndex;
newEdge->aIndex = newVertexIndex;
newEdge->nextEdge = existingEdge->nextEdge;
newEdge->prevEdge = edge->edgeIndex;
newEdge->nextEdgeReverse = edge->edgeIndex;
newEdge->prevEdgeReverse = existingEdge->prevEdgeReverse;
existingEdge->bIndex = newVertexIndex;
existingEdge->nextEdge = newEdgeIndex;
existingEdge->prevEdgeReverse = newVertexIndex;
struct SurfaceEdge* nextEdgePtr = portalSurfaceGetEdge(surfaceBuilder, nextEdge.edgeIndex);
if (nextEdge.isReverse) {
nextEdgePtr->prevEdgeReverse = newEdgeIndex;
} else {
nextEdgePtr->prevEdge = newEdgeIndex;
}
struct SurfaceEdge* prevEdgePtr = portalSurfaceGetEdge(surfaceBuilder, prevReverseEdge.edgeIndex);
if (prevReverseEdge.isReverse) {
prevEdgePtr->nextEdgeReverse = newEdgeIndex;
} else {
prevEdgePtr->nextEdge = newEdgeIndex;
}
return 1;
}
int portalSurfaceFindStartingPoint(struct PortalSurfaceBuilder* surfaceBuilder, struct Vector2s16* point) {
struct SurfaceEdgeWithSide currentEdge = surfaceBuilder->edgeOnSearchLoop;
struct Vector2s16* edgeA = portalSurfaceGetPoint(surfaceBuilder, GET_CURRENT_POINT(edge, currentEdge.isReverse));
for (int iteration = 0; iteration < MAX_INTERSECT_LOOPS; ++iteration) {
struct SurfaceEdgeWithSide nextEdge;
portalSurfaceNextEdge(surfaceBuilder, &currentEdge, &nextEdge);
if (nextEdge.edgeIndex == surfaceBuilder->edgeOnSearchLoop.edgeIndex && nextEdge.isReverse == surfaceBuilder->edgeOnSearchLoop.isReverse) {
// finished searching loop
break;
}
struct Vector2s16* edgeB = portalSurfaceGetPoint(surfaceBuilder, GET_NEXT_POINT(edge, 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)) {
return 0;
}
surfaceBuilder->cuttingEdge = currentEdge;
}
surfaceBuilder->edgeOnSearchLoop = surfaceBuilder->cuttingEdge;
return;
}
edgeA = edgeB;
currentEdge = nextEdge;
}
surfaceBuilder->hasEdge = 0;
surfaceBuilder->hasConnected = 0;
surfaceBuilder->startingPoint = point;
return 1;
}
struct PortalSurface* portalSurfaceCutHole(struct PortalSurface* surface, struct Vector2s16* loop, int loopSize) {
struct PortalSurfaceBuilder surfaceBuilder;
surfaceBuilder.additionalVertices = stackMalloc(sizeof(struct Vector2s16) * ADDITIONAL_VERTEX_CAPACITY);
surfaceBuilder.currentVertex = 0;
surfaceBuilder.additionalEdges = stackMalloc(sizeof(struct SurfaceEdge) * ADDITIONAL_EDGE_CAPACITY);
surfaceBuilder.currentEdge = 0;
if (!portalSurfaceFindEnclosingFace(surface, &loop[0], &surfaceBuilder.edgeOnSearchLoop)) {
return NULL;
}
if (!portalSurfaceFindStartingPoint(surfaceBuilder, &loop[0])) {
return NULL;
}
struct Vector2s16* prev = &loop[0];
for (int i = 1; i < loopSize; ++i) {
struct Vector2s16* next = &loop[0];
struct Vector2s16 dir;
vector2s16Sub(next, prev, &dir);
portalSurfaceIntersectEdgeWithLoop(surfaceBuilder, prev, &dir);
}
return NULL;
}
void portalSurface2DPoint(struct PortalSurface* surface, struct Vector3* at, struct Vector2s16* output) {
struct Vector3 offset;
vector3Sub(at, &surface->corner, &offset);
@ -61,7 +439,9 @@ int portalSurfaceIsInside(struct PortalSurface* surface, struct Transform* porta
return intersectionCount % 2;
}
void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform* portalAt, struct Vector2s16* output, struct Vector2s16* outlineLoopOutput) {
#define MAX_POS_ADJUST_ITERATIONS 3
int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform* portalAt, struct Vector2s16* output, struct Vector2s16* outlineLoopOutput) {
struct Vector2s16 minPortal;
struct Vector2s16 maxPortal;
@ -88,7 +468,9 @@ void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform
halfSize.x = (maxPortal.x - minPortal.x) >> 1;
halfSize.y = (maxPortal.y - minPortal.y) >> 1;
for (int interation = 0; interation < 2; ++interation) {
int iteration = 0;
for (interation = 0; interation < MAX_POS_ADJUST_ITERATIONS; ++interation) {
int minOverlap = NO_OVERLAP;
struct Vector2s16 minOverlapOffset;
@ -171,6 +553,10 @@ void portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform
output->x += minOverlapOffset.x;
output->y += minOverlapOffset.y;
}
// running out of iterations is a sign there isn't enough
// room for the portal
return iteration != MAX_POS_ADJUST_ITERATIONS;
}
int portalSurfaceGenerate(struct PortalSurface* surface, struct Transform* portalAt, Vtx* vertices, Gfx* triangles) {
@ -181,7 +567,9 @@ int portalSurfaceGenerate(struct PortalSurface* surface, struct Transform* porta
// find all portal edge intersections
struct Vector2s16 correctPosition;
struct Vector2s16 portalOutline[PORTAL_LOOP_SIZE];
portalSurfaceAdjustPosition(surface, portalAt, &correctPosition, portalOutline);
if (!portalSurfaceAdjustPosition(surface, portalAt, &correctPosition, portalOutline)) {
return 0;
}
portalSurfaceInverse(surface, &correctPosition, &portalAt->position);
// TODO

View file

@ -5,17 +5,13 @@
#include "math/transform.h"
#include "math/plane.h"
#include "math/vector2s16.h"
#define FIXED_POINT_PRECISION 8
#define FIXED_POINT_SCALAR (1 << FIXED_POINT_PRECISION)
#define VECTOR2s16_AS_ARRAY(vector) ((s16*)(vector))
struct Vector2s16 {
s16 x;
s16 y;
};
struct SurfaceEdge {
u8 aIndex;
u8 bIndex;
@ -32,6 +28,7 @@ struct PortalSurface {
u8 sideCount;
u8 edgeCount;
u8 vertexCount;
struct Vector3 right;
struct Vector3 up;