fucxk
This commit is contained in:
4
build
4
build
@@ -10,6 +10,8 @@ else
|
|||||||
time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE
|
time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE
|
||||||
fi
|
fi
|
||||||
echo [Target built]
|
echo [Target built]
|
||||||
|
|
||||||
|
if [ "$1" == "run" ]; then
|
||||||
./target/somaesque
|
./target/somaesque
|
||||||
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ void backtrackSolve(Arena *arena, Solver *solver, uint64 working_solution, size_
|
|||||||
VoxelSpaceReprList last_soln_copy = PushList(arena, VoxelSpaceReprList, last_soln.length);
|
VoxelSpaceReprList last_soln_copy = PushList(arena, VoxelSpaceReprList, last_soln.length);
|
||||||
last_soln_copy.length = last_soln.length;
|
last_soln_copy.length = last_soln.length;
|
||||||
memcpy(last_soln_copy.data, last_soln.data, last_soln.length * ListElementSize(VoxelSpaceReprList));
|
memcpy(last_soln_copy.data, last_soln.data, last_soln.length * ListElementSize(VoxelSpaceReprList));
|
||||||
AppendList(solutions, last_soln_copy);
|
ListAppend(*solutions, last_soln_copy);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
backtrackSolve(arena, solver, new_working_solution, curr_piece + 1);
|
backtrackSolve(arena, solver, new_working_solution, curr_piece + 1);
|
||||||
@@ -165,7 +165,7 @@ SomaSolutionList getSolutionRotations(Arena *arena, SomaSolution *solution, int
|
|||||||
};
|
};
|
||||||
VoxelSpaceList pieceRotations = getAllRotations(arena, &space);
|
VoxelSpaceList pieceRotations = getAllRotations(arena, &space);
|
||||||
for (EachIn(pieceRotations, rot_i)) {
|
for (EachIn(pieceRotations, rot_i)) {
|
||||||
AppendList(&result.data[rot_i], pieceRotations.data[rot_i].space);
|
ListAppend(result.data[rot_i], pieceRotations.data[rot_i].space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -204,7 +204,7 @@ SomaSolutionList filterUnique(Arena *arena, SomaSolutionList *solutions, int dim
|
|||||||
solutionCopy.capacity = solution->length;
|
solutionCopy.capacity = solution->length;
|
||||||
solutionCopy.length = solution->length;
|
solutionCopy.length = solution->length;
|
||||||
memcpy(solutionCopy.data, solution->data, ListElementSize(SomaSolutionList) * solution->length);
|
memcpy(solutionCopy.data, solution->data, ListElementSize(SomaSolutionList) * solution->length);
|
||||||
AppendList(&uniqueSolns, solutionCopy);
|
ListAppend(uniqueSolns, solutionCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return uniqueSolns;
|
return uniqueSolns;
|
||||||
@@ -233,7 +233,7 @@ SomaSolutionList solveSoma(Arena *solutionsArena, VoxelSpaceReprList reprsInput,
|
|||||||
dims[2],
|
dims[2],
|
||||||
};
|
};
|
||||||
|
|
||||||
AppendList(&offsets, 0);
|
ListAppend(offsets, 0);
|
||||||
|
|
||||||
uint64 possibleCombos = 0;
|
uint64 possibleCombos = 0;
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ SomaSolutionList solveSoma(Arena *solutionsArena, VoxelSpaceReprList reprsInput,
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 1; i < reprsInput.length; i++) {
|
for (size_t i = 1; i < reprsInput.length; i++) {
|
||||||
AppendList(&offsets, polycubes.capacity);
|
ListAppend(offsets, polycubes.capacity);
|
||||||
VoxelSpace space = emptyVoxelSpace;
|
VoxelSpace space = emptyVoxelSpace;
|
||||||
space.space = reprsInput.data[i];
|
space.space = reprsInput.data[i];
|
||||||
cullEmptySpace(&space);
|
cullEmptySpace(&space);
|
||||||
@@ -262,10 +262,10 @@ SomaSolutionList solveSoma(Arena *solutionsArena, VoxelSpaceReprList reprsInput,
|
|||||||
memcpy(insertion, perms.data, perms.capacity * ListElementSize(VoxelSpaceReprList));
|
memcpy(insertion, perms.data, perms.capacity * ListElementSize(VoxelSpaceReprList));
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendList(&offsets, polycubes.length);
|
ListAppend(offsets, polycubes.length);
|
||||||
|
|
||||||
SomaSolutionList solutions = PushList(permsArena, SomaSolutionList, (size_t)floor(sqrt(possibleCombos)));
|
SomaSolutionList solutions = PushList(permsArena, SomaSolutionList, (size_t)floor(sqrt(possibleCombos)));
|
||||||
AppendList(&solutions, PushFullList(permsArena, SomaSolution, reprsInput.length));
|
ListAppend(solutions, PushFullList(permsArena, SomaSolution, reprsInput.length));
|
||||||
|
|
||||||
Solver solver = {
|
Solver solver = {
|
||||||
&polycubes,
|
&polycubes,
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ void pushNewUniqueSpins(VoxelSpaceList *existingSpaces, VoxelSpace* spaceToSpin)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!matchFound) {
|
if (!matchFound) {
|
||||||
AppendList(existingSpaces, spins[i]);
|
ListAppend(*existingSpaces, spins[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@ void pushXAxisSpins(Arena *arena, VoxelSpaceList *existingSpaces, VoxelSpace* sp
|
|||||||
VoxelSpace refSpace = *spaceToSpin;
|
VoxelSpace refSpace = *spaceToSpin;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
rotate90X(&refSpace);
|
rotate90X(&refSpace);
|
||||||
AppendList(existingSpaces, refSpace);
|
ListAppend(*existingSpaces, refSpace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule src/lib/djstdlib updated: 9b772e2046...3f3ef5351f
159
src/main.c
159
src/main.c
@@ -217,7 +217,7 @@ struct Soma {
|
|||||||
SomaSolutionList solutions;
|
SomaSolutionList solutions;
|
||||||
SolveTaskCtx solveTaskCtx;
|
SolveTaskCtx solveTaskCtx;
|
||||||
uint32 puzzleDims[3];
|
uint32 puzzleDims[3];
|
||||||
HandleList solutionEntities;
|
int32 solutionNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *executeSolve(void *ctx) {
|
void *executeSolve(void *ctx) {
|
||||||
@@ -232,7 +232,7 @@ void *executeSolve(void *ctx) {
|
|||||||
void scheduleSolve(Soma *soma) {
|
void scheduleSolve(Soma *soma) {
|
||||||
VoxelSpaceReprList mappedInputs = PushList(soma->solveTaskCtx.arena, VoxelSpaceReprList, soma->polycubeInput.length);
|
VoxelSpaceReprList mappedInputs = PushList(soma->solveTaskCtx.arena, VoxelSpaceReprList, soma->polycubeInput.length);
|
||||||
for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) {
|
for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) {
|
||||||
AppendList(&mappedInputs, polycubeInput->repr.space);
|
ListAppend(mappedInputs, polycubeInput->repr.space);
|
||||||
}
|
}
|
||||||
soma->solveTaskCtx.input = mappedInputs;
|
soma->solveTaskCtx.input = mappedInputs;
|
||||||
soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving;
|
soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving;
|
||||||
@@ -248,9 +248,10 @@ void show(Scene *s, uint32 graphNodeHandle) {
|
|||||||
if (node->entityHandle) {
|
if (node->entityHandle) {
|
||||||
getEntity(s, node->entityHandle)->flags |= EntityFlags_Visible;
|
getEntity(s, node->entityHandle)->flags |= EntityFlags_Visible;
|
||||||
}
|
}
|
||||||
for (EachIn(node->children, i)) {
|
int32 next = node->firstChild;
|
||||||
uint32 child = node->children.data[i];
|
while (next) {
|
||||||
show(s, child);
|
show(s, next);
|
||||||
|
next = getSceneGraphNode(s, next)->nextSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,20 +260,24 @@ void hide(Scene *s, uint32 graphNodeHandle) {
|
|||||||
if (node->entityHandle) {
|
if (node->entityHandle) {
|
||||||
getEntity(s, node->entityHandle)->flags &= ~EntityFlags_Visible;
|
getEntity(s, node->entityHandle)->flags &= ~EntityFlags_Visible;
|
||||||
}
|
}
|
||||||
for (EachIn(node->children, i)) {
|
int32 next = node->firstChild;
|
||||||
uint32 child = node->children.data[i];
|
while (next) {
|
||||||
hide(s, child);
|
hide(s, next);
|
||||||
|
next = getSceneGraphNode(s, next)->nextSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RLVector3 centreFromPolycube(Scene *scene, uint32 p) {
|
RLVector3 centreFromPolycube(Scene *scene, uint32 p) {
|
||||||
RLVector3 centre = (RLVector3){0,0,0};
|
RLVector3 centre = (RLVector3){0,0,0};
|
||||||
HandleList *children = &getSceneGraphNode(scene, p)->children;
|
int32 childCount = 0;
|
||||||
for (EachIn(*children, i)) {
|
int32 nextChild = getSceneGraphNode(scene, p)->firstChild;
|
||||||
uint32 child = children->data[i];
|
while (nextChild) {
|
||||||
centre = Vector3Add(centre, getSceneGraphNode(scene, child)->translation);
|
SceneGraphNode *node = getSceneGraphNode(scene, nextChild);
|
||||||
|
centre = Vector3Add(centre, node->translation);
|
||||||
|
nextChild = node->nextSibling;
|
||||||
|
childCount++;
|
||||||
}
|
}
|
||||||
centre = Vector3Scale(centre, 1.0f/(getSceneGraphNode(scene, p)->children.length));
|
centre = Vector3Scale(centre, 1.0f/childCount);
|
||||||
return centre;
|
return centre;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +416,7 @@ void processInput(Soma *soma, UI_Context *ui) {
|
|||||||
soma->state.displayedSolution += 1;
|
soma->state.displayedSolution += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (soma->state.displayedPolycube == 6) {
|
if (soma->state.displayedPolycube == soma->polycubeInput.length - 1) {
|
||||||
soma->state.displayedPolycube = 0;
|
soma->state.displayedPolycube = 0;
|
||||||
} else {
|
} else {
|
||||||
soma->state.displayedPolycube += 1;
|
soma->state.displayedPolycube += 1;
|
||||||
@@ -422,14 +427,15 @@ void processInput(Soma *soma, UI_Context *ui) {
|
|||||||
if (input->keyboard.enter && !prevInput->keyboard.enter) {
|
if (input->keyboard.enter && !prevInput->keyboard.enter) {
|
||||||
if (soma->state.displayingSolutions) {
|
if (soma->state.displayingSolutions) {
|
||||||
soma->state.displayingSolutions = false;
|
soma->state.displayingSolutions = false;
|
||||||
} else {
|
soma->state.displayedSolution = -1;
|
||||||
|
} else if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Ready) {
|
||||||
scheduleSolve(soma);
|
scheduleSolve(soma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input->mouse.btnLeft && ui->hotNode == 0) {
|
if (input->mouse.btnLeft && ui->hotNode == 0) {
|
||||||
uint32 currentObject = soma->state.displayingSolutions
|
uint32 currentObject = soma->state.displayingSolutions
|
||||||
? soma->solutionEntities.data[soma->state.displayedSolution]
|
? soma->solutionNode
|
||||||
: soma->polycubes.data[soma->state.displayedPolycube];
|
: soma->polycubes.data[soma->state.displayedPolycube];
|
||||||
SceneGraphNode *objectGraphNode = getSceneGraphNode(soma->scene, currentObject);
|
SceneGraphNode *objectGraphNode = getSceneGraphNode(soma->scene, currentObject);
|
||||||
|
|
||||||
@@ -456,7 +462,7 @@ uint32 createPolycubeFromRepr(Scene *s, VoxelSpace *repr, RLVector4 color) {
|
|||||||
polycubeSegment->color = color;
|
polycubeSegment->color = color;
|
||||||
polycubeSegment->mesh = &cubeMesh;
|
polycubeSegment->mesh = &cubeMesh;
|
||||||
polycubeSegment->tex = &wallTex;
|
polycubeSegment->tex = &wallTex;
|
||||||
SceneGraphNode *graphNode = getSceneGraphNode(s, polycubeSegment->graphNodeHandle);
|
SceneGraphNode *graphNode = getSceneGraphNodeForEntity(s, segmentEntityHandle);
|
||||||
graphNode->translation = (RLVector3){
|
graphNode->translation = (RLVector3){
|
||||||
-((repr->dim_z - 1)/2.0f) + z,
|
-((repr->dim_z - 1)/2.0f) + z,
|
||||||
-((repr->dim_x - 1)/2.0f) + x,
|
-((repr->dim_x - 1)/2.0f) + x,
|
||||||
@@ -557,7 +563,6 @@ void updateRectangleObjectBuffers(Renderer *r) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Renderer createRenderer(Arena *arena, Scene *scene) {
|
Renderer createRenderer(Arena *arena, Scene *scene) {
|
||||||
scene->sceneRoot = createSceneGraphNode(scene);
|
|
||||||
return (Renderer){
|
return (Renderer){
|
||||||
.scene = scene,
|
.scene = scene,
|
||||||
.rects = createRectangleObjects(arena, 1024),
|
.rects = createRectangleObjects(arena, 1024),
|
||||||
@@ -615,12 +620,12 @@ void renderEnd(Soma *soma, Renderer *renderer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) {
|
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) {
|
||||||
AppendList(&r->rects.p0, ((RLVector2){ x, y }));
|
ListAppend(r->rects.p0, ((RLVector2){ x, y }));
|
||||||
AppendList(&r->rects.p1, ((RLVector2){ x + width, y + height }));
|
ListAppend(r->rects.p1, ((RLVector2){ x + width, y + height }));
|
||||||
AppendList(&r->rects.color, color);
|
ListAppend(r->rects.color, color);
|
||||||
AppendList(&r->rects.borderRadius, borderRadius);
|
ListAppend(r->rects.borderRadius, borderRadius);
|
||||||
AppendList(&r->rects.borderThickness, borderThickness);
|
ListAppend(r->rects.borderThickness, borderThickness);
|
||||||
AppendList(&r->rects.edgeSoftness, 0.0f);
|
ListAppend(r->rects.edgeSoftness, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pointInRect(RLVector2 point, UI_Rect rect) {
|
bool pointInRect(RLVector2 point, UI_Rect rect) {
|
||||||
@@ -729,76 +734,70 @@ void uiPass(Soma *soma, UI_Context *ui) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updatePolycubeDisplay(Arena *arena, Soma *soma) {
|
void updatePolycubeDisplay(Arena *arena, Soma *soma) {
|
||||||
if (!soma->prevState.displayingSolutions && soma->state.displayingSolutions) {
|
Scene *s = soma->scene;
|
||||||
if (soma->prevState.displayedPolycube < soma->polycubes.length) {
|
|
||||||
hide(soma->scene, soma->polycubes.data[soma->state.displayedPolycube]);
|
if (soma->state.displayingSolutions && soma->state.displayedSolution != soma->prevState.displayedSolution) {
|
||||||
|
removeSceneGraphNode(s, soma->solutionNode);
|
||||||
|
if (soma->state.displayedSolution >= 0 && soma->state.displayedSolution < soma->solutions.length) {
|
||||||
|
SomaSolution soln = soma->solutions.data[soma->state.displayedSolution];
|
||||||
|
soma->solutionNode = createSceneGraphNode(s);
|
||||||
|
for (EachIn(soln, i)) {
|
||||||
|
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(
|
||||||
|
s,
|
||||||
|
&(VoxelSpace){ soln.data[i], 3, 3, 3 },
|
||||||
|
colorFromIndex(i)
|
||||||
|
);
|
||||||
|
sceneNodeAddNode(s, soma->solutionNode, polycubeGraphNodeHandle);
|
||||||
}
|
}
|
||||||
} else if (soma->prevState.displayingSolutions && !soma->state.displayingSolutions) {
|
sceneNodeAddNode(s, s->sceneRoot, soma->solutionNode);
|
||||||
if (soma->prevState.displayedSolution < soma->solutionEntities.length) {
|
|
||||||
hide(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (soma->state.displayingSolutions) {
|
if (soma->state.displayingSolutions) {
|
||||||
if (soma->solutions.length > 0) {
|
show(s, soma->solutionNode);
|
||||||
show(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]);
|
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
|
||||||
if (soma->state.displayedSolution != soma->prevState.displayedSolution) {
|
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
|
||||||
hide(soma->scene, soma->solutionEntities.data[soma->prevState.displayedSolution]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (soma->polycubes.length > 0) {
|
hide(s, soma->solutionNode);
|
||||||
show(soma->scene, soma->polycubes.data[soma->state.displayedPolycube]);
|
if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) {
|
||||||
|
show(s, soma->polycubes.data[soma->state.displayedPolycube]);
|
||||||
|
}
|
||||||
if (soma->state.displayedPolycube != soma->prevState.displayedPolycube) {
|
if (soma->state.displayedPolycube != soma->prevState.displayedPolycube) {
|
||||||
hide(soma->scene, soma->polycubes.data[soma->prevState.displayedPolycube]);
|
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
|
||||||
|
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (soma->state.polycubeDirty) {
|
if (soma->state.polycubeDirty) {
|
||||||
if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) {
|
if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) {
|
||||||
uint32 currentPolycubeHandle = soma->polycubes.data[soma->state.displayedPolycube];
|
uint32 oldHandle = soma->polycubes.data[soma->state.displayedPolycube];
|
||||||
PolycubeInput *pinput = &soma->polycubeInput.data[soma->state.displayedPolycube];
|
Quaternion rot = getSceneGraphNode(s, oldHandle)->rotation;
|
||||||
VoxelSpace culledRepr = pinput->repr;
|
removeSceneGraphNode(s, oldHandle);
|
||||||
cullEmptySpace(&culledRepr);
|
|
||||||
uint32 newPolycubeHandle = createPolycubeFromRepr(soma->scene, &culledRepr, pinput->color);
|
|
||||||
SceneGraphNode *newPolycubeGraphNode = getSceneGraphNode(soma->scene, newPolycubeHandle);
|
|
||||||
newPolycubeGraphNode->rotation = getSceneGraphNode(soma->scene, currentPolycubeHandle)->rotation;
|
|
||||||
removeEntity(soma->scene, currentPolycubeHandle);
|
|
||||||
soma->polycubes.data[soma->state.displayedPolycube] = newPolycubeHandle;
|
|
||||||
sceneNodeAddNode(soma->scene, soma->scene->sceneRoot, newPolycubeHandle);
|
|
||||||
soma->state.polycubeDirty = false;
|
|
||||||
hide(soma->scene, currentPolycubeHandle);
|
|
||||||
show(soma->scene, newPolycubeHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void createSolutionEntities(Arena *arena, Soma *soma) {
|
PolycubeInput *newInput = &soma->polycubeInput.data[soma->state.displayedPolycube];
|
||||||
if (soma->solutions.length > 0) {
|
VoxelSpace newInputCulled = newInput->repr;
|
||||||
soma->solutionEntities = PushList(arena, HandleList, soma->solutions.length);
|
cullEmptySpace(&newInputCulled);
|
||||||
for (EachEl(soma->solutions, SomaSolution, solution)) {
|
|
||||||
uint32 solutionGraphNodeHandle = createSceneGraphNode(soma->scene);
|
uint32 newHandle = createPolycubeFromRepr(s, &newInputCulled, newInput->color);
|
||||||
AppendList(&soma->solutionEntities, solutionGraphNodeHandle);
|
getSceneGraphNode(s, newHandle)->rotation = rot;
|
||||||
sceneNodeAddNode(soma->renderer->scene, soma->renderer->scene->sceneRoot, solutionGraphNodeHandle);
|
|
||||||
for (EachIn(*solution, soln_i)) {
|
soma->polycubes.data[soma->state.displayedPolycube] = newHandle;
|
||||||
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(
|
sceneNodeAddNode(s, s->sceneRoot, newHandle);
|
||||||
soma->scene,
|
|
||||||
&(VoxelSpace){ solution->data[soln_i], 3, 3, 3 },
|
soma->state.polycubeDirty = false;
|
||||||
colorFromIndex(soln_i)
|
show(s, newHandle);
|
||||||
);
|
|
||||||
sceneNodeAddNode(soma->renderer->scene, solutionGraphNodeHandle, polycubeGraphNodeHandle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mainGfx() {
|
int mainGfx() {
|
||||||
Arena *arena = arenaAlloc(Megabytes(128));
|
Arena *arena = arenaAlloc(Megabytes(128));
|
||||||
Arena *solutionsArena = arenaAlloc(Megabytes(128));
|
Arena *solutionsArena = arenaAlloc(Megabytes(128));
|
||||||
|
|
||||||
int winWidth = 800, winHeight = 600;
|
int winWidth = 800;
|
||||||
|
int winHeight = 600;
|
||||||
GLFWwindow *windowHandle = initWindowAndGL(winWidth, winHeight);
|
GLFWwindow *windowHandle = initWindowAndGL(winWidth, winHeight);
|
||||||
if (!windowHandle) {
|
if (!windowHandle) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -840,9 +839,9 @@ int mainGfx() {
|
|||||||
.taskStatus = SolveTaskStatus_Ready,
|
.taskStatus = SolveTaskStatus_Ready,
|
||||||
},
|
},
|
||||||
.state = {
|
.state = {
|
||||||
.displayingSolutions = false,
|
|
||||||
.displayedPolycube = 0,
|
.displayedPolycube = 0,
|
||||||
.displayedSolution = 0,
|
.displayingSolutions = false,
|
||||||
|
.displayedSolution = -1,
|
||||||
.isInitialState = true,
|
.isInitialState = true,
|
||||||
.light = createEntity(&mainScene),
|
.light = createEntity(&mainScene),
|
||||||
.camera = mainFrame.cam,
|
.camera = mainFrame.cam,
|
||||||
@@ -871,10 +870,10 @@ int mainGfx() {
|
|||||||
VoxelSpace voxelSpace = { stdSoma.data[i], 3, 3, 3 };
|
VoxelSpace voxelSpace = { stdSoma.data[i], 3, 3, 3 };
|
||||||
RLVector4 color = colorFromIndex(i);
|
RLVector4 color = colorFromIndex(i);
|
||||||
PolycubeInput input = (PolycubeInput){ voxelSpace, color };
|
PolycubeInput input = (PolycubeInput){ voxelSpace, color };
|
||||||
AppendList(&soma.polycubeInput, input);
|
ListAppend(soma.polycubeInput, input);
|
||||||
cullEmptySpace(&voxelSpace);
|
cullEmptySpace(&voxelSpace);
|
||||||
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(soma.scene, &voxelSpace, color);
|
uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(soma.scene, &voxelSpace, color);
|
||||||
AppendList(&soma.polycubes, polycubeGraphNodeHandle);
|
ListAppend(soma.polycubes, polycubeGraphNodeHandle);
|
||||||
sceneNodeAddNode(renderer.scene, renderer.scene->sceneRoot, polycubeGraphNodeHandle);
|
sceneNodeAddNode(renderer.scene, renderer.scene->sceneRoot, polycubeGraphNodeHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -903,15 +902,13 @@ int mainGfx() {
|
|||||||
processInput(&soma, &ui);
|
processInput(&soma, &ui);
|
||||||
|
|
||||||
if (soma.solveTaskCtx.taskStatus == SolveTaskStatus_Complete) {
|
if (soma.solveTaskCtx.taskStatus == SolveTaskStatus_Complete) {
|
||||||
soma.solutions = PushFullList(solutionsArena, SomaSolutionList, soma.solveTaskCtx.solutions.length);
|
soma.solutions = PushList(solutionsArena, SomaSolutionList, soma.solveTaskCtx.solutions.length);
|
||||||
memcpy(soma.solutions.data, soma.solveTaskCtx.solutions.data, soma.solutions.length*sizeof(soma.solutions.data[0]));
|
for (EachIn(soma.solveTaskCtx.solutions, i)) {
|
||||||
for (EachIn(soma.solutions, i)) {
|
ListAppend(soma.solutions, PushListCopy(solutionsArena, SomaSolution, soma.solveTaskCtx.solutions.data[i]));
|
||||||
soma.solutions.data[i] = PushFullList(solutionsArena, SomaSolution, soma.solveTaskCtx.input.length);
|
|
||||||
memcpy(&soma.solutions.data[i], &soma.solveTaskCtx.solutions.data[i], soma.solveTaskCtx.input.length*sizeof(uint64));
|
|
||||||
}
|
}
|
||||||
createSolutionEntities(arena, &soma);
|
|
||||||
soma.solveTaskCtx.taskStatus = SolveTaskStatus_Ready;
|
soma.solveTaskCtx.taskStatus = SolveTaskStatus_Ready;
|
||||||
soma.state.displayingSolutions = true;
|
soma.state.displayingSolutions = true;
|
||||||
|
soma.state.displayedSolution = 0;
|
||||||
arenaFreeFrom(soma.solveTaskCtx.arena, 0);
|
arenaFreeFrom(soma.solveTaskCtx.arena, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -289,15 +289,15 @@ void test() {
|
|||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (positions1.data[i] != expected_results1[i]) {
|
if (positions1.data[i] != expected_results1[i]) {
|
||||||
MismatchData data = { i, positions1.data[i], expected_results1[i] };
|
MismatchData data = { i, positions1.data[i], expected_results1[i] };
|
||||||
AppendList(&mismatches1, data);
|
ListAppend(mismatches1, data);
|
||||||
}
|
}
|
||||||
if (positions2.data[i] != expected_results2[i]) {
|
if (positions2.data[i] != expected_results2[i]) {
|
||||||
MismatchData data = { i, positions2.data[i], expected_results2[i] };
|
MismatchData data = { i, positions2.data[i], expected_results2[i] };
|
||||||
AppendList(&mismatches2, data);
|
ListAppend(mismatches2, data);
|
||||||
}
|
}
|
||||||
if (positions3.data[i] != expected_results3[i]) {
|
if (positions3.data[i] != expected_results3[i]) {
|
||||||
MismatchData data = { i, positions3.data[i], expected_results3[i] };
|
MismatchData data = { i, positions3.data[i], expected_results3[i] };
|
||||||
AppendList(&mismatches3, data);
|
ListAppend(mismatches3, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert(mismatches1.length == 0);
|
Assert(mismatches1.length == 0);
|
||||||
|
|||||||
@@ -1,44 +1,60 @@
|
|||||||
#include "string.h"
|
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
Entity *getEntity(Scene *s, uint32 entityHandle) {
|
Entity *getEntity(Scene *s, int32 entityHandle) {
|
||||||
if (entityHandle) {
|
return &s->entities.data[entityHandle];
|
||||||
return &s->entities.data[entityHandle - 1];
|
}
|
||||||
|
|
||||||
|
SceneGraphNode *getSceneGraphNode(Scene *s, int32 sceneGraphNodeHandle) {
|
||||||
|
return &s->graphNodes.data[sceneGraphNodeHandle];
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle) {
|
||||||
|
return getSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 createSceneGraphNode(Scene *s) {
|
||||||
|
SceneGraphNode *newNode;
|
||||||
|
int32 newNodeHandle;
|
||||||
|
if (s->nextFreeNode) {
|
||||||
|
newNodeHandle = s->nextFreeNode;
|
||||||
|
newNode = getSceneGraphNode(s, newNodeHandle);
|
||||||
|
if (newNode->next) {
|
||||||
|
s->nextFreeNode = newNode->next;
|
||||||
|
} else {
|
||||||
|
s->nextFreeNode = 0;
|
||||||
}
|
}
|
||||||
return NULL;
|
newNode->next = 0;
|
||||||
}
|
} else {
|
||||||
|
ListAppend(s->graphNodes, (SceneGraphNode){0});
|
||||||
SceneGraphNode *getSceneGraphNode(Scene *s, uint32 sceneGraphNodeHandle) {
|
newNodeHandle = (int32)s->graphNodes.length - 1;
|
||||||
if (sceneGraphNodeHandle) {
|
newNode = getSceneGraphNode(s, newNodeHandle);
|
||||||
return &s->graphNodes.data[sceneGraphNodeHandle - 1];
|
newNode->nextSibling = 0;
|
||||||
}
|
}
|
||||||
return NULL;
|
initGraphNode(newNode);
|
||||||
|
return newNodeHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, uint32 entityHandle) {
|
int32 createEntity(Scene *s) {
|
||||||
Entity *e = getEntity(s, entityHandle);
|
Entity *newEntity;
|
||||||
if (e) {
|
int32 newEntityHandle;
|
||||||
return getSceneGraphNode(s, e->graphNodeHandle);
|
println("%d", s->nextFreeEntity);
|
||||||
|
if (s->nextFreeEntity) {
|
||||||
|
newEntityHandle = s->nextFreeEntity;
|
||||||
|
newEntity = getEntity(s, newEntityHandle);
|
||||||
|
if (newEntity->next) {
|
||||||
|
s->nextFreeEntity = newEntity->next;
|
||||||
|
} else {
|
||||||
|
s->nextFreeEntity = 0;
|
||||||
}
|
}
|
||||||
return NULL;
|
newEntity->next = 0;
|
||||||
}
|
} else {
|
||||||
|
ListAppend(s->entities, (Entity){0});
|
||||||
uint32 createSceneGraphNode(Scene *s) {
|
newEntityHandle = (int32)s->entities.length - 1;
|
||||||
AppendList(&s->graphNodes, (SceneGraphNode){0});
|
newEntity = getEntity(s, newEntityHandle);
|
||||||
SceneGraphNode *node = &s->graphNodes.data[s->graphNodes.length - 1];
|
}
|
||||||
node->children = PushList(s->arena, HandleList, 1000);
|
newEntity->graphNodeHandle = createSceneGraphNode(s);
|
||||||
initGraphNode(node);
|
getSceneGraphNodeForEntity(s, newEntityHandle)->entityHandle = newEntityHandle;
|
||||||
return (uint32)s->graphNodes.length;
|
return newEntityHandle;
|
||||||
}
|
|
||||||
|
|
||||||
uint32 createEntity(Scene *s) {
|
|
||||||
AppendList(&s->entities, (Entity){0});
|
|
||||||
uint32 graphNodeId = createSceneGraphNode(s);
|
|
||||||
s->entities.data[s->entities.length - 1].graphNodeHandle = graphNodeId;
|
|
||||||
getSceneGraphNode(s, graphNodeId)->entityHandle = (uint32)s->entities.length;
|
|
||||||
uint32 handle = (uint32)s->entities.length;
|
|
||||||
uint32 graphNodeHandle = (uint32)s->graphNodes.length;
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initGraphNode(SceneGraphNode *n) {
|
void initGraphNode(SceneGraphNode *n) {
|
||||||
@@ -49,32 +65,32 @@ void initGraphNode(SceneGraphNode *n) {
|
|||||||
n->world = n->local;
|
n->world = n->local;
|
||||||
}
|
}
|
||||||
|
|
||||||
void recalcGraphNode(SceneGraphNode *n) {
|
|
||||||
n->local = MatrixCompose(n->translation, n->rotation, n->scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
Scene createScene(Arena *arena) {
|
Scene createScene(Arena *arena) {
|
||||||
Scene result = {
|
Scene result = {
|
||||||
.arena = arena,
|
.entities = PushListZero(arena, EntityList, 100000),
|
||||||
.entities = PushList(arena, EntityList, 100000),
|
.nextFreeEntity = 0,
|
||||||
.graphNodes = PushList(arena, SceneGraphNodeList, 100000),
|
.graphNodes = PushListZero(arena, SceneGraphNodeList, 100000),
|
||||||
|
.nextFreeNode = 0,
|
||||||
};
|
};
|
||||||
|
int32 handle = createEntity(&result); // Intialise the "zero" nodes
|
||||||
|
getEntity(&result, handle)->flags = EntityFlags_None | EntityFlags_Dead;
|
||||||
|
|
||||||
result.sceneRoot = createSceneGraphNode(&result);
|
result.sceneRoot = createSceneGraphNode(&result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void recalcSceneGraphNode(Scene *s, uint32 parentHandle) {
|
void recalcSceneGraphNode(Scene *s, int32 parentHandle) {
|
||||||
SceneGraphNode *node = getSceneGraphNode(s, parentHandle);
|
if (!parentHandle) return;
|
||||||
if (node->entityHandle) {
|
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
|
||||||
getEntity(s, node->entityHandle)->flags |= EntityFlags_Render;
|
getEntity(s, parentNode->entityHandle)->flags |= EntityFlags_Render;
|
||||||
}
|
int32 nextChild = parentNode->firstChild;
|
||||||
for (EachIn(node->children, i)) {
|
while (nextChild) {
|
||||||
uint32 nodeId = node->children.data[i];
|
SceneGraphNode *childNode = getSceneGraphNode(s, nextChild);
|
||||||
SceneGraphNode *graphNode = getSceneGraphNode(s, nodeId);
|
childNode->parentHandle = parentHandle;
|
||||||
graphNode->parentHandle = parentHandle;
|
childNode->local = MatrixCompose(childNode->translation, childNode->rotation, childNode->scale);
|
||||||
recalcGraphNode(graphNode);
|
childNode->world = MatrixMultiply(childNode->local, parentNode->world);
|
||||||
graphNode->world = MatrixMultiply(graphNode->local, node->world);
|
recalcSceneGraphNode(s, nextChild);
|
||||||
recalcSceneGraphNode(s, nodeId);
|
nextChild = childNode->nextSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,31 +98,73 @@ void recalcScene(Scene *s) {
|
|||||||
recalcSceneGraphNode(s, s->sceneRoot);
|
recalcSceneGraphNode(s, s->sceneRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeEntity(Scene *s, uint32 entityHandle) {
|
function void removeSceneGraphNodeRecursive(Scene *s, int32 graphNodeHandle, bool deletingParent) {
|
||||||
Entity *entity = getEntity(s, entityHandle);
|
if (!graphNodeHandle) return;
|
||||||
entity->flags |= EntityFlags_Dead;
|
SceneGraphNode *graphNode = getSceneGraphNode(s, graphNodeHandle);
|
||||||
SceneGraphNode *graphNode = getSceneGraphNode(s, entity->graphNodeHandle);
|
|
||||||
if (graphNode != NULL && graphNode->parentHandle) {
|
int32 nextChild = graphNode->firstChild;
|
||||||
|
while (nextChild) {
|
||||||
|
int32 sibling = getSceneGraphNode(s, nextChild)->nextSibling;
|
||||||
|
removeSceneGraphNodeRecursive(s, nextChild, true);
|
||||||
|
nextChild = sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (graphNode->entityHandle) {
|
||||||
|
Entity *entity = getEntity(s, graphNode->entityHandle);
|
||||||
|
*entity = (Entity){0};
|
||||||
|
if (s->nextFreeEntity) {
|
||||||
|
entity->next = s->nextFreeEntity;
|
||||||
|
}
|
||||||
|
s->nextFreeEntity = graphNode->entityHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->nextFreeNode) {
|
||||||
|
graphNode->next = s->nextFreeNode;
|
||||||
|
}
|
||||||
|
s->nextFreeNode = graphNodeHandle;
|
||||||
|
|
||||||
|
if (!deletingParent && graphNode->parentHandle) {
|
||||||
SceneGraphNode *parentNode = getSceneGraphNode(s, graphNode->parentHandle);
|
SceneGraphNode *parentNode = getSceneGraphNode(s, graphNode->parentHandle);
|
||||||
if (parentNode != NULL) {
|
if (parentNode->firstChild == graphNodeHandle) {
|
||||||
for (EachIn(parentNode->children, i)) {
|
if (graphNode->nextSibling) {
|
||||||
if (parentNode->children.data[i] == entity->graphNodeHandle) {
|
parentNode->firstChild = graphNode->nextSibling;
|
||||||
memcpy(parentNode->children.data + i, parentNode->children.data + i + 1, parentNode->children.length - i - 1);
|
} else {
|
||||||
//ListRemove(&parentNode->children, i);
|
parentNode->firstChild = 0;
|
||||||
graphNode->parentHandle = 0;
|
}
|
||||||
|
} else {
|
||||||
|
int32 prevSibling = parentNode->firstChild;
|
||||||
|
int32 nextSibling = getSceneGraphNode(s, parentNode->firstChild)->nextSibling;
|
||||||
|
while (nextSibling) {
|
||||||
|
SceneGraphNode *siblingNode = getSceneGraphNode(s, nextSibling);
|
||||||
|
if (nextSibling == graphNodeHandle) {
|
||||||
|
SceneGraphNode *prevSiblingNode = getSceneGraphNode(s, prevSibling);
|
||||||
|
prevSiblingNode->nextSibling = graphNode->nextSibling;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
nextSibling = siblingNode->nextSibling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graphNode->firstChild = 0;
|
||||||
|
graphNode->parentHandle = 0;
|
||||||
|
graphNode->entityHandle = 0;
|
||||||
|
graphNode->nextSibling = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneNodeAddEntity(Scene *s, uint32 graphNodeHandle, uint32 entityHandle) {
|
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle) {
|
||||||
HandleList *childList = &getSceneGraphNode(s, graphNodeHandle)->children;
|
removeSceneGraphNodeRecursive(s, graphNodeHandle, false);
|
||||||
AppendList(childList, getEntity(s, entityHandle)->graphNodeHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneNodeAddNode(Scene *s, uint32 recipientNodeHandle, uint32 graphNodeHandle) {
|
void removeEntity(Scene *s, int32 entityHandle) {
|
||||||
HandleList *childList = &getSceneGraphNode(s, recipientNodeHandle)->children;
|
if (!entityHandle) return;
|
||||||
AppendList(childList, graphNodeHandle);
|
removeSceneGraphNode(s, getEntity(s, entityHandle)->graphNodeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sceneNodeAddNode(Scene *s, int32 parentHandle, int32 childHandle) {
|
||||||
|
SceneGraphNode *parentNode = getSceneGraphNode(s, parentHandle);
|
||||||
|
SceneGraphNode *childNode = getSceneGraphNode(s, childHandle);
|
||||||
|
childNode->nextSibling = parentNode->firstChild;
|
||||||
|
parentNode->firstChild = childHandle;
|
||||||
|
childNode->parentHandle = parentHandle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#include "../gfx/gfx.h"
|
#include "../gfx/gfx.h"
|
||||||
#include "../lib/raymath.h"
|
#include "../lib/raymath.h"
|
||||||
|
|
||||||
DefineList(uint32, Handle);
|
DefineList(int32, Handle);
|
||||||
|
|
||||||
enum EntityFlags {
|
enum EntityFlags {
|
||||||
|
EntityFlags_None=0,
|
||||||
EntityFlags_Visible=1<<0,
|
EntityFlags_Visible=1<<0,
|
||||||
EntityFlags_Dead=1<<1,
|
EntityFlags_Dead=1<<1,
|
||||||
EntityFlags_Render=1<<2,
|
EntityFlags_Render=1<<2,
|
||||||
@@ -11,11 +12,14 @@ enum EntityFlags {
|
|||||||
|
|
||||||
typedef struct Entity Entity;
|
typedef struct Entity Entity;
|
||||||
struct Entity {
|
struct Entity {
|
||||||
uint32 graphNodeHandle;
|
int32 graphNodeHandle;
|
||||||
uint64 flags;
|
uint64 flags;
|
||||||
RLVector4 color;
|
RLVector4 color;
|
||||||
Mesh *mesh;
|
Mesh *mesh;
|
||||||
Texture *tex;
|
Texture *tex;
|
||||||
|
|
||||||
|
// Free list
|
||||||
|
int32 next;
|
||||||
};
|
};
|
||||||
DefineList(Entity, Entity);
|
DefineList(Entity, Entity);
|
||||||
|
|
||||||
@@ -26,30 +30,36 @@ struct SceneGraphNode {
|
|||||||
RLVector3 translation;
|
RLVector3 translation;
|
||||||
Quaternion rotation;
|
Quaternion rotation;
|
||||||
RLVector3 scale;
|
RLVector3 scale;
|
||||||
HandleList children;
|
int32 entityHandle;
|
||||||
uint32 entityHandle;
|
int32 parentHandle;
|
||||||
uint32 parentHandle;
|
|
||||||
|
// Free list
|
||||||
|
int32 next;
|
||||||
|
|
||||||
|
// Children
|
||||||
|
int32 nextSibling;
|
||||||
|
int32 firstChild;
|
||||||
};
|
};
|
||||||
DefineList(SceneGraphNode, SceneGraphNode);
|
DefineList(SceneGraphNode, SceneGraphNode);
|
||||||
|
|
||||||
typedef struct Scene Scene;
|
typedef struct Scene Scene;
|
||||||
struct Scene {
|
struct Scene {
|
||||||
uint32 sceneRoot;
|
int32 sceneRoot;
|
||||||
EntityList entities;
|
EntityList entities;
|
||||||
|
int32 nextFreeEntity;
|
||||||
SceneGraphNodeList graphNodes;
|
SceneGraphNodeList graphNodes;
|
||||||
Arena *arena;
|
int32 nextFreeNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 createEntity(Scene *s);
|
int32 createEntity(Scene *s);
|
||||||
Entity *getEntity(Scene *s, uint32 id);
|
Entity *getEntity(Scene *s, int32 id);
|
||||||
SceneGraphNode *getSceneGraphNode(Scene *s, uint32 id);
|
SceneGraphNode *getSceneGraphNode(Scene *s, int32 id);
|
||||||
uint32 createSceneGraphNode(Scene *s);
|
int32 createSceneGraphNode(Scene *s);
|
||||||
Scene createScene(Arena *arena);
|
Scene createScene(Arena *arena);
|
||||||
void initGraphNode(SceneGraphNode *n);
|
void initGraphNode(SceneGraphNode *n);
|
||||||
void recalcGraphNode(SceneGraphNode *n);
|
void recalcSceneGraphNode(Scene *s, int32 parentHandle);
|
||||||
void recalcSceneGraphNode(Scene *s, uint32 parentHandle);
|
|
||||||
void recalcScene(Scene *s);
|
void recalcScene(Scene *s);
|
||||||
void removeEntity(Scene *s, uint32 entityHandle);
|
void removeEntity(Scene *s, int32 entityHandle);
|
||||||
void sceneNodeAddNode(Scene *s, uint32 recipientNodeHandle, uint32 graphNodeHandle);
|
void removeSceneGraphNode(Scene *s, int32 graphNodeHandle);
|
||||||
void sceneNodeAddEntity(Scene *s, uint32 graphNodeHandle, uint32 entityHandle);
|
void sceneNodeAddNode(Scene *s, int32 recipientNodeHandle, int32 graphNodeHandle);
|
||||||
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, uint32 entityHandle);
|
SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, int32 entityHandle);
|
||||||
|
|||||||
Reference in New Issue
Block a user