aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNguyễn Gia Phong <cnx@loang.net>2023-11-18 21:40:15 +0900
committerNguyễn Gia Phong <cnx@loang.net>2023-11-18 21:40:15 +0900
commitae0810b2d4cdd31cd05f5746c6411da9d458eead (patch)
treea827fbe10c12a4a4665b98144f32e767ba5a3553 /src
parent371906f5fb958691a8bfce85c28eb4dfaf63559c (diff)
downloadblackshades-ae0810b2d4cdd31cd05f5746c6411da9d458eead.tar.gz
Convert model geometry to Zig
Diffstat (limited to 'src')
-rw-r--r--src/Decals.cpp146
-rw-r--r--src/Decals.h3
-rw-r--r--src/GameDraw.cpp117
-rw-r--r--src/GameTick.cpp296
-rw-r--r--src/Models.cpp46
-rw-r--r--src/Models.h25
-rw-r--r--src/Person.cpp13
-rw-r--r--src/Skeleton.cpp9
-rw-r--r--src/decal.zig146
-rw-r--r--src/geom.zig22
-rw-r--r--src/main.zig1
-rw-r--r--src/model.zig90
12 files changed, 459 insertions, 455 deletions
diff --git a/src/Decals.cpp b/src/Decals.cpp
deleted file mode 100644
index 510a557..0000000
--- a/src/Decals.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include <algorithm>
-#include <cmath>
-
-#include "Decals.h"
-
-#define NORMAL_OFFSET 0.02f
-
-enum corner { SW, SE, NE, NW };
-
-void bind(struct Decals *d, XYZ location, float size, XYZ normal,
- int poly, Model *model, XYZ move, float rotation,
- XYZ right, XYZ up, enum corner direction)
-{
- float x, y;
- switch (direction) {
- case SW:
- x = -1.0f;
- y = -1.0f;
- break;
- case NW:
- x = 1.0f;
- y = -1.0f;
- break;
- case NE:
- x = 1.0f;
- y = 1.0f;
- break;
- case SE:
- x = -1.0f;
- y = 1.0f;
- break;
- }
-
- size_t i = d->len * 8 + d->numpoints[d->len];
- d->points[i] = location + right * x + up * y;
- d->texcoordsx[i] = x * 0.5f + 0.5f;
- d->texcoordsy[i] = y * 0.5f + 0.5f;
-
- XYZ temp;
- if ((move.x == 0 && move.y == 0 && move.z == 0 && rotation == 0)
- || segCrossTrigon(d->points[i] + normal / 25,
- d->points[i] - normal / 25,
- model->vertex + model->Triangles[poly].vertex[0],
- model->vertex + model->Triangles[poly].vertex[1],
- model->vertex + model->Triangles[poly].vertex[2],
- &normal, &temp)) {
- d->numpoints[d->len]++;
- return;
- }
-
- const auto n = normal / 25.0f;
- const float countinc = std::max(0.01f, std::min(1.0f / size, 0.2f));
- int good = -1;
- float count = 1.0f - countinc;
- while (good == -1 && count > -1.0f) {
- d->texcoordsx[i] = x * 0.5f + 0.5f;
- d->texcoordsy[i] = y * count * 0.5f + 0.5f;
- d->points[i] = location + right * x + up * (y * count);
- count -= countinc;
- good = model->LineCheck2(d->points[i] + n,
- d->points[i] - n,
- &temp, move, rotation);
- }
- if (good > -1) {
- d->numpoints[d->len]++;
- i++;
- }
-
- good = -1;
- count = 1.0f - countinc;
- while (good == -1 && count > -1.0f) {
- d->texcoordsx[i] = x * count * 0.5f + 0.5f;
- d->texcoordsy[i] = y * 0.5f + 0.5f;
- d->points[i] = location + right * (x * count) + up * y;
- count -= countinc;
- good = model->LineCheck2(d->points[i] + n,
- d->points[i] - n,
- &temp, move, rotation);
- }
- if (good > -1) {
- d->numpoints[d->len]++;
- return;
- }
-
- float count2 = 1.0f - countinc;
- while (good == -1 && count2 > -1.0f){
- count = 1.0f - countinc;
- while (good == -1 && count > -1.0f) {
- d->texcoordsx[i] = x * count2 * 0.5f + 0.5f;
- d->texcoordsy[i] = y * count * 0.5f + 0.5f;
- d->points[i] = location + right * (x * count2) + up * (y * count);
- count -= countinc;
- good = model->LineCheck2(d->points[i] + n,
- d->points[i] - n,
- &temp, move, rotation);
- }
- count2 -= countinc;
- }
- if (good > -1)
- d->numpoints[d->len]++;
-}
-
-void addDecal(struct Decals *d, enum decal kind, XYZ location, float size,
- XYZ normal, int poly, Model *model, XYZ move, float rotation)
-{
- if (d->len >= MAX_DECALS)
- return;
- d->kind[d->len] = kind;
- d->alive[d->len] = 0;
-
- float normalv[] = {abs(normal.x), abs(normal.y), abs(normal.z)};
- unsigned char major = 0;
- if (normalv[1] > normalv[major])
- major = 1;
- if (normalv[2] > normalv[major])
- major = 2;
-
- XYZ right = {0.0f};
- if (normalv[0] == 1.0f || normalv[1] == 1.0f || normalv[2] == 1.0f) {
- if ((major == 0 && normal.x > 0) || major == 1)
- right.z = -1.0f;
- else if (major == 0)
- right.z = 1.0f;
- else
- right.x = normal.z;
- } else {
- XYZ axis = {0.0f};
- ((float *) &axis)[major] = 1.0f;
- right = crossProduct(axis, normal);
- }
-
- d->numpoints[d->len] = 0;
- XYZ up = normalize(crossProduct(normal, right)) * (size / 3);
- right = normalize(right) * (size / 3);
- bind(d, location, size, normal, poly, model,
- move, rotation, right, up, SW);
- bind(d, location, size, normal, poly, model,
- move, rotation, right, up, NW);
- bind(d, location, size, normal, poly, model,
- move, rotation, right, up, NE);
- bind(d, location, size, normal, poly, model,
- move, rotation, right, up, SE);
- for (int i = 0; i < d->numpoints[d->len]; ++i)
- d->points[d->len * 8 + i] += normal * NORMAL_OFFSET;
- d->len++;
-}
diff --git a/src/Decals.h b/src/Decals.h
index cfe7a0e..4eb6cf7 100644
--- a/src/Decals.h
+++ b/src/Decals.h
@@ -46,7 +46,8 @@ struct Decals {
extern "C" {
#endif // __cplusplus
void addDecal(struct Decals *d, enum decal kind, XYZ location, float size,
- XYZ normal, int poly, Model *model, XYZ move, float rotation);
+ XYZ normal, int poly, const struct Model *model,
+ XYZ move, float rotation);
void updateDecals(struct Decals *d);
void drawDecals(struct Decals *d);
void destroyDecals(struct Decals *d);
diff --git a/src/GameDraw.cpp b/src/GameDraw.cpp
index 846a7fa..42382cc 100644
--- a/src/GameDraw.cpp
+++ b/src/GameDraw.cpp
@@ -485,7 +485,6 @@ void Game::DrawGLScene(void)
if(endz>num_blocks-1)endz=num_blocks-1;
bool draw;
- int whichtri;
XYZ collpoint;
for(int i=beginx;i<=endx;i++){
for(int j=beginz;j<=endz;j++){
@@ -502,8 +501,13 @@ void Game::DrawGLScene(void)
if(distsquared>(viewdistance*viewdistance+block_spacing*block_spacing ))draw=0;
if(draw&&citytype[i][j]!=3&&!cubeInFrustum(frustum, (i)*block_spacing,0,(j)*block_spacing,block_spacing))draw=0;
- if(draw&&citytype[i][j]!=3&&!sphereInFrustum(frustum, blocks[citytype[i][j]].boundingspherecenter.x+(i)*block_spacing,blocks[citytype[i][j]].boundingspherecenter.y,blocks[citytype[i][j]].boundingspherecenter.z+(j)*block_spacing,blocks[citytype[i][j]].boundingsphereradius))draw=0;
-
+ if (draw && citytype[i][j] != 3
+ && !sphereInFrustum(frustum,
+ blocks[citytype[i][j]].center.x + i * block_spacing,
+ blocks[citytype[i][j]].center.y,
+ blocks[citytype[i][j]].center.z + j * block_spacing,
+ blocks[citytype[i][j]].radius))
+ draw = false;
if(draw){
glPushMatrix();
glTranslatef(i*block_spacing,0,j*block_spacing);
@@ -552,72 +556,47 @@ void Game::DrawGLScene(void)
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_BLEND);
for(int i=0;i<numpeople;i++){
- draw=1;
- if(person[i].skeleton.free<1){
- if(person[i].whichblockx>=0&&person[i].whichblockx<num_blocks&&person[i].whichblocky>=0&&person[i].whichblocky<num_blocks){
- if(!drawn[person[i].whichblockx][person[i].whichblocky])draw=0;
- }else draw=0;
- if(draw)
- if(!cubeInFrustum(frustum, person[i].playercoords.x,person[i].playercoords.y,person[i].playercoords.z,5))draw=0;
- if (draw && sqrlen(person[i].playercoords - camera.position) > 1000000)
- draw = 0;
- if(draw)
- for(int j=beginx;j<=endx;j++){
- for(int k=beginz;k<=endz;k++){
- if(draw){
- move.y=0;
- move.x=j*block_spacing;
- move.z=k*block_spacing;
- if (sqrlen(move - camera.position) < 100000) {
- whichtri=blockocclude.LineCheck2(camera.position,person[i].playercoords,&collpoint,move,0);
- if(whichtri!=-1)draw=0;
- }
- }
- }
- }
-
- if(draw){
- move.y=0;
- move.x=person[i].whichblockx*block_spacing;
- move.z=person[i].whichblocky*block_spacing;
- whichtri=blockocclude.LineCheck2(camera.position,person[i].playercoords,&collpoint,move,0);
- if(whichtri!=-1)draw=0;
- }
- if(i==0)draw=1;
- }
-
- if(person[i].skeleton.free==1){
- if(draw)
- if(!person[i].skeleton.broken&&!cubeInFrustum(frustum, person[i].averageloc.x,person[i].averageloc.y,person[i].averageloc.z,5))draw=0;
- if (draw && sqrlen(person[i].averageloc - camera.position) > 1000000)
- draw = 0;
- if(draw)
- if(person[i].skeleton.joints[0].position.y<-2)draw=0;
-
- for(int j=beginx;j<=endx;j++){
- for(int k=beginz;k<=endz;k++){
- if(draw){
- move.y=0;
- move.x=j*block_spacing;
- move.z=k*block_spacing;
- if (sqrlen(move - camera.position) < 100000) {
- whichtri=blockocclude.LineCheck2(camera.position,person[i].averageloc,&collpoint,move,0);
- if(whichtri!=-1)draw=0;
- }
- }
- }
- }
- if(draw){
- move.y=0;
- move.x=person[i].whichblockx*block_spacing;
- move.z=person[i].whichblocky*block_spacing;
- whichtri=blockocclude.LineCheck2(camera.position,person[i].averageloc,&collpoint,move,0);
- if(whichtri!=-1)draw=0;
- }
- if(i==0)draw=1;
- }
-
- if(draw&&person[i].existing==1){
+ draw = true;
+ if (((!person[i].skeleton.free
+ || !person[i].skeleton.broken)
+ && !cubeInFrustum(frustum,
+ person[i].playercoords.x,
+ person[i].playercoords.y,
+ person[i].playercoords.z, 5))
+ || (person[i].skeleton.free
+ && (person[i].whichblockx < 0
+ || person[i].whichblockx >= num_blocks
+ || person[i].whichblocky < 0
+ || person[i].whichblocky >= num_blocks
+ || !drawn[person[i].whichblockx][person[i].whichblocky]))
+ || (person[i].skeleton.free
+ && person[i].skeleton.joints[0].position.y < -2)
+ || (sqrlen(person[i].playercoords - camera.position)
+ > 1000000))
+ draw = false;
+ for (auto j = beginx; j <= endx; ++j)
+ for(auto k = beginz; k <= endz; ++k)
+ if (draw && sqrlen(move - camera.position) < 100000
+ && (segCrossModelTrans(camera.position, person[i].playercoords, &blockocclude,
+ {(float) j * block_spacing,
+ 0.0f,
+ (float) k * block_spacing},
+ 0.0f,
+ &collpoint)
+ > -1))
+ draw = false;
+ if (draw && (segCrossModelTrans(camera.position, person[i].playercoords, &blockocclude,
+ {(float) person[i].whichblockx * block_spacing,
+ 0.0f,
+ (float) person[i].whichblocky * block_spacing},
+ 0.0f,
+ &collpoint)
+ > -1))
+ draw = false;
+ if (i == 0)
+ draw = true;
+
+ if (draw && person[i].existing) {
if ((sqrlen(person[i].playercoords - camera.position) < 100000 + this->zoom * 3000000
&& !person[i].skeleton.free)
|| (sqrlen(person[i].averageloc - camera.position) < 100000 + this->zoom * 3000000
diff --git a/src/GameTick.cpp b/src/GameTick.cpp
index b628637..2bfdda6 100644
--- a/src/GameTick.cpp
+++ b/src/GameTick.cpp
@@ -5,7 +5,7 @@
// Copyright (C) 2003 Steven Fuller
// Copyright (C) 2003 Zachary Jack Slater
// Copyright (C) 2003 Toby Haynes
-// Copyright (C) 2021-2022 Nguyễn Gia Phong
+// Copyright (C) 2021-2023 Nguyễn Gia Phong
//
// This file is part of Black Shades.
//
@@ -497,8 +497,10 @@ void checkPersonCollisions(Game* game, int k)
const XYZ move = {(float) i * block_spacing,
0.0f, (float) j * block_spacing};
XYZ collpoint;
- if (game->sidewalkcollide.LineCheck2(overpoint,
- underpoint, &collpoint, move, city_rot) == -1)
+ if (segCrossModelTrans(overpoint, underpoint,
+ &game->sidewalkcollide, move, city_rot,
+ &collpoint)
+ == -1)
continue;
if (person.playercoords.y <= collpoint.y
@@ -516,19 +518,13 @@ void checkPersonCollisions(Game* game, int k)
// Wall collision
const auto city_type = game->citytype[i][j];
for (auto& bound : game->boundingpoints) {
- const auto whichtri = game->blockwalls[city_type].LineCheck2(person.playercoords + bound,
- person.playercoords + bound,
- &collpoint, move, city_rot);
- if (whichtri == -1)
- continue;
- }
- for (auto& bound : game->boundingpoints) {
auto pointnum = k + 1;
if (pointnum > 3)
pointnum = 0;
- const auto whichtri = game->blockwalls[city_type].LineCheck2(person.playercoords + bound,
+ const auto whichtri = segCrossModelTrans(person.playercoords + bound,
person.playercoords + game->boundingpoints[pointnum],
- &collpoint, move, city_rot);
+ game->blockwalls + city_type,
+ move, city_rot, &collpoint);
if (whichtri == -1)
continue;
person.playercoords += rotate(game->blockwalls[city_type].normals[whichtri], 0, city_rot, 0);
@@ -752,20 +748,21 @@ void bleed(Game* game, size_t i)
overpoint.y += 3000;
auto underpoint = person.skeleton.joints[abdomen].position;
underpoint.y -= 3000;
- XYZ temp;
+ XYZ loc;
XYZ move {(float) x * block_spacing, 0.0f, (float) y * block_spacing, };
- auto whichtri = game->sidewalkcollide.LineCheck2(overpoint,
- underpoint, &temp, move, rot);
+ auto whichtri = segCrossModelTrans(overpoint, underpoint,
+ &game->sidewalkcollide, move, rot, &loc);
XYZ normish {0.0f, 1.0f, 0.0f};
if (whichtri >= 0) {
- addDecal(&decals, BLOOD_POOL, temp, 12, normish,
+ addDecal(&decals, BLOOD_POOL, loc, 12, normish,
whichtri, &game->sidewalkcollide, move, rot);
} else {
- temp = person.skeleton.joints[abdomen].position;
- temp.y = -0.5f;
- addDecal(&decals, BLOOD_POOL, temp, 12, normish,
- 0, &game->sidewalkcollide, {}, 0);
+ loc = person.skeleton.joints[abdomen].position;
+ loc.y = -0.5f;
+ move = {0.0f};
+ addDecal(&decals, BLOOD_POOL, loc, 12, normish,
+ 0, &game->sidewalkcollide, move, 0);
}
person.firstlongdead = true;
return;
@@ -953,14 +950,15 @@ void renderLaser(Game* game)
};
XYZ tmp {};
auto& block = game->blocks[game->citytype[i][j]];
- auto whichtri = block.LineCheck2(start, end,
- &tmp, move, game->cityrotation[i][j]*90);
- if (whichtri != -1)
+ if (segCrossModelTrans(start, end, &block,
+ move, game->cityrotation[i][j]*90, &tmp)
+ > -1)
end = tmp;
}
- XYZ tmp {camera.position.x, 0.0f, camera.position.z};
- auto whichtri = game->Bigstreet.LineCheck2(start, end, &tmp, tmp, 0);
- if (whichtri != -1)
+ XYZ tmp;
+ if (segCrossModelTrans(start, end, &game->Bigstreet,
+ {camera.position.x, 0.0f, camera.position.z}, 0, &tmp)
+ > -1)
end = tmp;
// People
@@ -1151,10 +1149,17 @@ void Game::Tick()
person[i].pathtarget.x+=l*block_spacing;
person[i].pathtarget.z+=m*block_spacing;
- if (sqrlen(person[i].playercoords - person[i].pathtarget) < leastdistance
- && sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords) > sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords)
- && j != 1 && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[person[i].whichblockx][person[i].whichblocky]) == -1
- && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[l][m]) == -1) {
+ if (j != 1
+ && (sqrlen(person[i].playercoords - person[i].pathtarget)
+ < leastdistance)
+ && (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
+ < sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords))
+ && (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
+ move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
+ == -1)
+ && (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
+ move, cityrotation[l][m], &blah)
+ == -1)) {
person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
closesttarget=j;
@@ -1226,28 +1231,14 @@ void Game::Tick()
person[i].pathtarget.x+=person[i].whichblockx*block_spacing;
person[i].pathtarget.z+=person[i].whichblocky*block_spacing;
- if (sqrlen(person[i].playercoords - person[i].pathtarget) < leastdistance
- && sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords) < person[i].lastdistancevictim
- && j != 1 && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[person[i].whichblockx][person[i].whichblocky]) == -1) {
- leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
- person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
- closesttarget=j;
- finaltarget=person[i].pathtarget;
- }
- }
-
- leastdistance=2000000;
- for(int j=0;j<path.vertexNum;j++){
- person[i].pathtarget.x=path.vertex[j].x;
- person[i].pathtarget.z=path.vertex[j].z;
- person[i].pathtarget.y=path.vertex[j].y;
- person[i].pathtarget*=person[i].pathsize;
- person[i].pathtarget.x+=person[i].whichblockx*block_spacing;
- person[i].pathtarget.z+=person[i].whichblocky*block_spacing;
-
- if (sqrlen(person[i].playercoords - person[i].pathtarget) < leastdistance
- && sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords) < person[i].lastdistancevictim
- && j != 1 && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[person[i].whichblockx][person[i].whichblocky]) == -1) {
+ if (j != 1
+ && (sqrlen(person[i].playercoords - person[i].pathtarget)
+ < leastdistance)
+ && (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
+ < person[i].lastdistancevictim)
+ && (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
+ move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
+ == -1)) {
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
person[i].lastdistancevictim = sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords);
closesttarget=j;
@@ -1279,10 +1270,17 @@ void Game::Tick()
person[i].pathtarget.x+=l*block_spacing;
person[i].pathtarget.z+=m*block_spacing;
- if (sqrlen(person[i].playercoords - person[i].pathtarget) < leastdistance
- && sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords) < sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords)
- && j != 1 && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[l][m]) == -1
- && blocksimple.LineCheck2(person[i].playercoords, person[i].pathtarget, &blah, move, cityrotation[person[i].whichblockx][person[i].whichblocky]) == -1) {
+ if (j != 1
+ && (sqrlen(person[i].playercoords - person[i].pathtarget)
+ < leastdistance)
+ && (sqrlen(person[i].pathtarget - person[person[i].killtarget].playercoords)
+ < sqrlen(person[i].playercoords - person[person[i].killtarget].playercoords))
+ && (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
+ move, cityrotation[person[i].whichblockx][person[i].whichblocky], &blah)
+ == -1)
+ && (segCrossModelTrans(person[i].playercoords, person[i].pathtarget, &blocksimple,
+ move, cityrotation[l][m], &blah)
+ == -1)) {
leastdistance = sqrlen(person[i].playercoords - person[i].pathtarget);
closesttarget=j;
finaltarget=person[i].pathtarget;
@@ -1315,29 +1313,27 @@ void Game::Tick()
person[i].killtargetvisible = 0;
if(person[i].killtargetvisible){
- beginx=person[i].whichblockx-2;
- if(beginx<0)beginx=0;
- beginz=person[i].whichblocky-2;
- if(beginz<0)beginz=0;
- endx=person[i].whichblockx+2;
- if(endx>num_blocks-1)endx=num_blocks-1;
- endz=person[i].whichblocky+2;
- if(endz>num_blocks-1)endz=num_blocks-1;
-
- for(int l=beginx;l<=endx;l++){
- for(int m=beginx;m<=endx;m++){
- move.x=l*block_spacing;
- move.z=m*block_spacing;
- move.y=-3;
- if(person[i].killtargetvisible){
- if(blocksimple.LineCheck2(person[i].playercoords,person[person[i].killtarget].playercoords,&blah,move,cityrotation[l][m])!=-1)
- {
- person[i].killtargetvisible=0;
- }
- }
- }
- }
- }
+ beginx = std::max(0,
+ person[i].whichblockx - 2);
+ beginz = std::max(0,
+ person[i].whichblocky - 2);
+ endx = std::min(num_blocks - 1,
+ person[i].whichblockx + 2);
+ endz = std::min(num_blocks - 1,
+ person[i].whichblocky + 2);
+
+ for (auto l = beginx; l <= endx; ++l)
+ for (auto m = beginx; m <= endx; ++m)
+ if (person[i].killtargetvisible
+ && (segCrossModelTrans(person[i].playercoords,
+ person[person[i].killtarget].playercoords,
+ &blocksimple,
+ {(float) l * block_spacing,
+ -3.0f,
+ (float) m * block_spacing},
+ cityrotation[l][m], &blah)
+ > -1))
+ person[i].killtargetvisible = 0;
if(person[i].type==eviltype){
if(!person[i].killtargetvisible&&person[i].targetanimation==idleanim){
@@ -1458,6 +1454,7 @@ void Game::Tick()
finaltarget=person[person[i].killtarget].playercoords;
}
}
+ }
if(person[i].killtargetvisible||realcheck)person[i].pathtarget=finaltarget;
if (realcheck)
@@ -1783,34 +1780,28 @@ void Game::Tick()
for(int i=beginx;i<=endx;i++)
for(int j=beginz;j<=endz;j++){
move = {(float) i * block_spacing, 0.0f, (float) j * block_spacing};
- whichtri=blocks[citytype[i][j]].LineCheck2(start,end,&wallhit,move,cityrotation[i][j]*90);
-
- if(whichtri!=-1){
-
- whichhit=-1;
-
- end=wallhit;
-
- finalwallhit=wallhit;
-
- hitnorm=rotate(blocks[citytype[i][j]].normals[whichtri],0,cityrotation[i][j]*90,0);
-
- hitmove=move;
-
- hitrotation=cityrotation[i][j]*90;
-
- hitpoly=whichtri;
-
- model=&blocks[citytype[i][j]];
-
- if(j==0&&blocks[citytype[i][j]].normals[whichtri].y>.9)bulletstrength=2;
+ whichtri = segCrossModelTrans(start, end,
+ blocks + citytype[i][j],
+ move, cityrotation[i][j] * 90,
+ &wallhit);
+ if (whichtri > -1) {
+ whichhit = -1;
+ end = finalwallhit = wallhit;
+ hitnorm = rotate(blocks[citytype[i][j]].normals[whichtri],
+ 0, cityrotation[i][j]*90, 0);
+ hitmove = move;
+ hitrotation = cityrotation[i][j]*90;
+ hitpoly = whichtri;
+ model = &blocks[citytype[i][j]];
+ if (j == 0
+ && blocks[citytype[i][j]].normals[whichtri].y > 0.9)
+ bulletstrength = 2;
}
}
- wallhit = {camera.position.x, 0.0f, camera.position.z};
-
- whichtri=Bigstreet.LineCheck2(start,end,&wallhit,wallhit,0);
-
- if(whichtri!=-1){
+ whichtri = segCrossModelTrans(start, end, &Bigstreet,
+ {camera.position.x, 0.0f, camera.position.z}, 0,
+ &wallhit);
+ if (whichtri > -1) {
end.y-=.5;
end=wallhit;
finalwallhit=wallhit;
@@ -2095,54 +2086,27 @@ void Game::Tick()
}
// with wall
- if(oldend==finalwallhit){
-
- addDecal(&decals, BULLET_HOLE, finalwallhit,.7,hitnorm, hitpoly, model, hitmove, hitrotation);
-
- XYZ velocity;
-
- velocity=aim*-4;
-
- velocity=hitnorm*3;
-
- if(person[j].whichgun==sniperrifle){
-
+ if (oldend == finalwallhit) {
+ addDecal(&decals, BULLET_HOLE, finalwallhit, 0.7f,
+ hitnorm, hitpoly, model, hitmove, hitrotation);
+ // FIXME: WTF?
+ XYZ velocity = aim * -4.0f;
+ velocity = hitnorm * 3;
+ switch (person[j].whichgun) {
+ case sniperrifle:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, finalwallhit, velocity, 10);
-
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, finalwallhit, velocity, 2);
-
- }
-
- if(person[j].whichgun==shotgun){
-
+ break;
+ case shotgun:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, finalwallhit, velocity, 5);
-
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, finalwallhit, velocity, .8);
-
- }
-
- if(person[j].whichgun==assaultrifle){
-
- sprites.MakeSprite(smokesprite, .4, 1, 1, 1, finalwallhit, velocity, 6);
-
- sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, finalwallhit, velocity, 1);
-
- }
-
- if(person[j].whichgun==handgun1){
-
- sprites.MakeSprite(smokesprite, .4, 1, 1, 1, finalwallhit, velocity, 6);
-
- sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, finalwallhit, velocity, 1);
-
- }
-
- if(person[j].whichgun==handgun2){
-
+ break;
+ case assaultrifle:
+ case handgun1:
+ case handgun2:
sprites.MakeSprite(smokesprite, .4, 1, 1, 1, finalwallhit, velocity, 6);
-
sprites.MakeSprite(muzzleflashsprite, 1, 1, 1, 1, finalwallhit, velocity, 1);
-
+ break;
}
auto soundpos = finalwallhit - camera.position;
@@ -2237,13 +2201,20 @@ void Game::Tick()
int wherex = sprites.location[i].x / block_spacing + 0.5f;
int wherey = sprites.location[i].z / block_spacing + 0.5f;
move = {(float) wherex * block_spacing, 0.0f, (float) wherey * block_spacing};
- whichtri=blocks[citytype[wherex][wherey]].LineCheck2(sprites.oldlocation[i],sprites.location[i],&wallhit,move,cityrotation[wherex][wherey]*90);
+ whichtri = segCrossModelTrans(sprites.oldlocation[i],
+ sprites.location[i],
+ blocks + citytype[wherex][wherey],
+ move, cityrotation[wherex][wherey] * 90,
+ &wallhit);
if (whichtri != -1) {
impact = true;
auto normalrotated = rotate(blocks[citytype[wherex][wherey]].normals[whichtri], 0, cityrotation[wherex][wherey] * 90, 0);
if (sprites.size[i] > 1)
- addDecal(&decals, CRATER, wallhit, 9, normalrotated, whichtri, &blocks[citytype[wherex][wherey]], move, cityrotation[wherex][wherey] * 90);
+ addDecal(&decals, CRATER, wallhit, 9.0f,
+ normalrotated, whichtri,
+ &blocks[citytype[wherex][wherey]],
+ move, cityrotation[wherex][wherey] * 90);
sprites.location[i] = wallhit + normalrotated * 0.02f;
reflect(&sprites.velocity[i], normalrotated);
sprites.velocity[i] *= 0.3f;
@@ -2276,7 +2247,8 @@ void Game::Tick()
move = {};
sprites.location[i].y=-.5;
XYZ normish = {0.0f, 1.0f, 0.0f};
- addDecal(&decals, CRATER, sprites.location[i],9,normish, 0, &blocks[citytype[wherex][wherey]], move, 0);
+ addDecal(&decals, CRATER, sprites.location[i], 9.0f,
+ normish, 0, blocks + citytype[wherex][wherey], move, 0);
}
auto soundpos = sprites.location[i] - camera.position;
@@ -2395,18 +2367,20 @@ void Game::Tick()
move = {(float) wherex * block_spacing, 0.0f, (float) wherey * block_spacing};
XYZ temp;
- whichtri=sidewalkcollide.LineCheck2(overpoint,underpoint,&temp,move,cityrotation[wherex][wherey]*90);
-
+ whichtri = segCrossModelTrans(overpoint, underpoint,
+ &sidewalkcollide,
+ move, cityrotation[wherex][wherey] * 90, &temp);
XYZ normish = {0.0f, 1.0f, 0.0f};
- if(whichtri>=0){
- addDecal(&decals, CRATER, sprites.location[i],9,normish, 0, &sidewalkcollide, move, cityrotation[wherex][wherey]*90);
- }
-
- if(whichtri==-1){
- temp=sprites.location[i];
- temp.y=-.5;
- move = {};
- addDecal(&decals, CRATER, sprites.location[i],9,normish, 0, &sidewalkcollide, move, 0);
+ if (whichtri > -1) {
+ addDecal(&decals, CRATER, sprites.location[i], 9.0f,
+ normish, 0, &sidewalkcollide, move,
+ cityrotation[wherex][wherey]*90);
+ } else {
+ temp = sprites.location[i];
+ temp.y = -0.5f;
+ move = {0.0f};
+ addDecal(&decals, CRATER, sprites.location[i], 9.0f,
+ normish, 0, &sidewalkcollide, move, 0);
}
for(int k=0;k<numpeople;k++){
diff --git a/src/Models.cpp b/src/Models.cpp
index 63cf26a..bc0b4d2 100644
--- a/src/Models.cpp
+++ b/src/Models.cpp
@@ -46,16 +46,16 @@ void Model::CalculateNormals()
vArray[i*27+26]=Triangles[i].b;
}
- boundingspherecenter = {};
+ this->center = {};
for (int i = 0; i < vertexNum; ++i)
- boundingspherecenter += vertex[i];
- boundingspherecenter /= vertexNum;
+ this->center += vertex[i];
+ this->center /= vertexNum;
- boundingsphereradius = 0;
+ this->radius = 0;
for (int i = 0; i < vertexNum; ++i)
- boundingsphereradius = std::max(boundingsphereradius,
- sqrlen(boundingspherecenter - vertex[i]));
- boundingsphereradius = sqrt(boundingsphereradius);
+ this->radius = std::max(this->radius,
+ sqrlen(this->center - vertex[i]));
+ this->radius = sqrt(this->radius);
}
void Model::load(const char* path)
@@ -106,35 +106,3 @@ void Model::draw(float r, float g, float b)
glColor4f(r, g, b, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, TriangleNum*3);
}
-
-int Model::LineCheck(XYZ p1, XYZ p2, XYZ *p)
-{
- int result = -1;
- if (segCrossSphere(p1, p2, boundingspherecenter, boundingsphereradius)) {
- float olddistance = 9999999.0;
- for (int j = 0; j < TriangleNum; ++j) {
- XYZ point;
- if (!segCrossTrigon(p1, p2,
- vertex + Triangles[j].vertex[0],
- vertex + Triangles[j].vertex[1],
- vertex + Triangles[j].vertex[2],
- normals + j, &point))
- continue;
- float distance = sqrlen(point - p1);
- if (distance < olddistance || result == -1) {
- olddistance = distance;
- result = j;
- *p = point;
- }
- }
- }
- return result;
-}
-
-int Model::LineCheck2(XYZ p1, XYZ p2, XYZ *p, XYZ move, float deg_y)
-{
- int result = this->LineCheck(rotate(p1 - move, 0, -deg_y, 0),
- rotate(p2 - move, 0, -deg_y, 0), p);
- *p = rotate(*p, 0, deg_y, 0) + move;
- return result;
-}
diff --git a/src/Models.h b/src/Models.h
index 7929887..8ab4f90 100644
--- a/src/Models.h
+++ b/src/Models.h
@@ -9,24 +9,22 @@
#define MAX_TEXTURED_TRIANGLES 400
struct TexturedTriangle {
- short vertex[3];
+ GLuint vertex[3];
float r,g,b;
};
struct Model {
- short vertexNum, TriangleNum;
-
XYZ vertex[MAX_TEXTURED_TRIANGLES * 3];
+ GLuint vertexNum;
XYZ normals[MAX_TEXTURED_TRIANGLES];
+
TexturedTriangle Triangles[MAX_TEXTURED_TRIANGLES];
- GLfloat vArray[MAX_TEXTURED_TRIANGLES * 27];
+ GLuint TriangleNum;
- XYZ boundingspherecenter;
- float boundingsphereradius;
- int LineCheck(XYZ, XYZ, XYZ*);
- int LineCheck2(XYZ, XYZ, XYZ*, XYZ, float);
+ XYZ center;
+ float radius;
+ GLfloat vArray[MAX_TEXTURED_TRIANGLES * 27];
- void UpdateVertexArray();
void load(const char*);
void save(const char*);
void CalculateNormals();
@@ -34,4 +32,13 @@ struct Model {
void draw(float r,float g,float b);
};
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+int segCrossModel(struct XYZ, struct XYZ, struct Model *, struct XYZ *);
+int segCrossModelTrans(struct XYZ, struct XYZ, struct Model *,
+ struct XYZ, float, struct XYZ *);
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
#endif
diff --git a/src/Person.cpp b/src/Person.cpp
index 65eb002..0b0a97c 100644
--- a/src/Person.cpp
+++ b/src/Person.cpp
@@ -105,9 +105,9 @@ HitStruct Person::BulletCollideWithPlayer(int who, XYZ start, XYZ end){
tempbulletloc[1].y=M[13];
tempbulletloc[1].z=M[14];
glPopMatrix();
- collide=skeletonmodels[joint.modelnum].LineCheck(tempbulletloc[0],tempbulletloc[1],&collisionpoint);
- if(collide!=-1)
- {
+ collide = segCrossModel(tempbulletloc[0], tempbulletloc[1],
+ skeletonmodels + joint.modelnum, &collisionpoint);
+ if (collide > -1) {
glPushMatrix();
glLoadIdentity();
glTranslatef((joint.position.x + joint.parent->position.x) / 2,
@@ -165,9 +165,10 @@ HitStruct Person::BulletCollideWithPlayer(int who, XYZ start, XYZ end){
tempbulletloc[1].y=M[13];
tempbulletloc[1].z=M[14];
glPopMatrix();
- collide=skeletonmodels[skeleton.muscles[j].parent1->modelnum].LineCheck(tempbulletloc[0],tempbulletloc[1],&collisionpoint);
- if(collide!=-1)
- {
+ collide = segCrossModel(tempbulletloc[0], tempbulletloc[1],
+ skeletonmodels + skeleton.muscles[j].parent1->modelnum,
+ &collisionpoint);
+ if (collide > -1) {
glPushMatrix();
glLoadIdentity();
glTranslatef( (skeleton.muscles[j].parent1->position.x+skeleton.muscles[j].parent2->position.x)/2,
diff --git a/src/Skeleton.cpp b/src/Skeleton.cpp
index b01e27d..b29ec53 100644
--- a/src/Skeleton.cpp
+++ b/src/Skeleton.cpp
@@ -83,13 +83,12 @@ void Skeleton::DoConstraints(Model *collide, XYZ *move, float rotation)
overpoint.y += 10;
XYZ underpoint = pos;
XYZ impact;
- int whichtri = collide->LineCheck2(overpoint, underpoint,
- &impact, *move, rotation);
+ int whichtri = segCrossModelTrans(overpoint, underpoint,
+ collide, *move, rotation, &impact);
if (whichtri == -1
|| collide->normals[whichtri].y <= 0.8)
- whichtri = collide->LineCheck2(
- joints[i].realoldposition, pos,
- &impact, *move, rotation);
+ whichtri = segCrossModelTrans(joints[i].realoldposition, pos,
+ collide, *move, rotation, &impact);
if (pos.y <= 0 || whichtri != -1) {
if (whichtri == -1
diff --git a/src/decal.zig b/src/decal.zig
index a89fc22..3292534 100644
--- a/src/decal.zig
+++ b/src/decal.zig
@@ -18,10 +18,19 @@
// You should have received a copy of the GNU General Public License
// along with Black Shades. If not, see <https://www.gnu.org/licenses/>.
-const XYZ = @import("geom.zig").XYZ;
+const Child = @import("std").meta.Child;
+const Model = model.Model;
+const XYZ = geom.XYZ;
const c = @import("cimport.zig");
+const geom = @import("geom.zig");
+const model = @import("model.zig");
+const norm = geom.norm;
+const segCrossModelTrans = model.segCrossModelTrans;
+const segCrossTrigon = geom.segCrossTrigon;
+const splat = geom.splat;
-const size = 120;
+const ones: @Vector(3, f32) = @splat(1.0);
+const max_len = 120;
const Kind = enum(c_int) { bullet_hole, crater, blood_pool };
@@ -30,14 +39,135 @@ const Decals = extern struct {
hole_textures: [2]u32,
blood_textures: [11]u32,
len: u32,
- kind: [size]Kind,
- points: [size * 8]XYZ,
- numpoints: [size]u32,
- texcoordsx: [size * 8]f32,
- texcoordsy: [size * 8]f32,
- alive: [size]f32,
+ kind: [max_len]Kind,
+ points: [max_len * 8]XYZ,
+ numpoints: [max_len]u32,
+ texcoordsx: [max_len * 8]f32,
+ texcoordsy: [max_len * 8]f32,
+ alive: [max_len]f32,
};
+fn cross(u: @Vector(3, f32), v: @Vector(3, f32)) @Vector(3, f32) {
+ return .{
+ u[1] * v[2] - u[2] * v[1],
+ u[2] * v[0] - u[0] * v[2],
+ u[0] * v[1] - u[1] * v[0],
+ };
+}
+
+fn normalize(vector: anytype, unit: Child(@TypeOf(vector))) @TypeOf(vector) {
+ const d = norm(vector) / unit;
+ if (d == 0) return vector;
+ return vector / @as(@TypeOf(vector), @splat(d));
+}
+
+fn pointTouchModel(p: @Vector(3, f32), n: @Vector(3, f32),
+ m: *const Model, move: XYZ, rot: f32) bool {
+ var temp: XYZ = undefined;
+ return segCrossModelTrans(@bitCast(p + n), @bitCast(p - n),
+ m, move, rot, &temp) > -1;
+}
+
+export fn addDecal(d: *Decals, kind: Kind, location: XYZ,
+ size: f32, normal: XYZ, poly: c_int,
+ m: *const Model, move: XYZ, rot: f32) void {
+ if (d.len >= max_len) return;
+ const n: @Vector(3, f32) = @bitCast(normal);
+ const abs_n = @fabs(n);
+ var major: u2 = 0;
+ if (abs_n[1] > abs_n[major])
+ major = 1;
+ if (abs_n[2] > abs_n[major])
+ major = 2;
+
+ const r: @Vector(3, f32) = if (@reduce(.And, abs_n != ones))
+ cross(switch (major) {
+ 0 => .{ 1.0, 0.0, 0.0 },
+ 1 => .{ 0.0, 1.0, 0.0 },
+ 2 => .{ 0.0, 0.0, 1.0 },
+ else => unreachable,
+ }, n)
+ else if (major == 0 and normal.x > 0 or major == 1)
+ .{ 0.0, 0.0, -1.0 }
+ else if (major == 0)
+ .{ 0.0, 0.0, 1.0 }
+ else
+ .{ normal.z, 0.0, 0.0 };
+ const up = normalize(cross(n, r), size / 3.0);
+ const right = normalize(r, size / 3.0);
+ const loc: @Vector(3, f32) = @bitCast(location);
+ const trigon = &m.trigons[@intCast(poly)].vertices;
+ const n_eps = n * splat(3, @as(f32, 0.02));
+ const n_eps2 = n * splat(3, @as(f32, 0.04));
+
+ d.kind[d.len] = kind;
+ d.numpoints[d.len] = 0;
+ d.alive[d.len] = 0;
+ for ([_]f32{ -1, 1, 1, -1 }, [_]f32{ -1, -1, 1, 1 }) |x, y| {
+ var p = loc + right * splat(3, x) + up * splat(3, y);
+ var i = d.len * 8 + d.numpoints[d.len];
+ var temp: XYZ = undefined;
+ if (move.x == 0 and move.y == 0 and move.z == 0 and rot == 0
+ or segCrossTrigon(@bitCast(p + n_eps2), @bitCast(p - n_eps2),
+ &m.vertices[trigon.*[0]],
+ &m.vertices[trigon.*[1]],
+ &m.vertices[trigon.*[2]],
+ &normal, &temp)) {
+ d.texcoordsx[i] = x * 0.5 + 0.5;
+ d.texcoordsy[i] = y * 0.5 + 0.5;
+ d.points[i] = @bitCast(p + n_eps);
+ d.numpoints[d.len] += 1;
+ continue;
+ }
+
+ const count_inc = @max(0.01, @min(1.0 / size, 0.2));
+ var good: bool = false;
+ var count = 1.0 - count_inc;
+ while (!good and count > -1.0) : (count -= count_inc) {
+ d.texcoordsx[i] = x * 0.5 + 0.5;
+ d.texcoordsy[i] = y * count * 0.5 + 0.5;
+ p = loc + right * splat(3, x) + up * splat(3, y * count);
+ good = pointTouchModel(p, n_eps2, m, move, rot);
+ }
+ if (good) {
+ d.points[i] = @bitCast(p + n_eps);
+ d.numpoints[d.len] += 1;
+ i += 1;
+ }
+
+ good = false;
+ count = 1.0 - count_inc;
+ while (!good and count > -1.0) : (count -= count_inc) {
+ d.texcoordsx[i] = x * count * 0.5 + 0.5;
+ d.texcoordsy[i] = y * 0.5 + 0.5;
+ p = loc + right * splat(3, x * count) + up * splat(3, y);
+ good = pointTouchModel(p, n_eps2, m, move, rot);
+ }
+ if (good) {
+ d.points[i] = @bitCast(p + n_eps);
+ d.numpoints[d.len] += 1;
+ continue;
+ }
+
+ var count2 = 1.0 - count_inc;
+ while (!good and count2 > -1.0) : (count2 -= count_inc) {
+ count = 1.0 - count_inc;
+ while (!good and count > -1.0) : (count -= count_inc) {
+ d.texcoordsx[i] = x * count2 * 0.5 + 0.5;
+ d.texcoordsy[i] = y * count * 0.5 + 0.5;
+ p = loc + right * splat(3, x * count2)
+ + up * splat(3, y * count);
+ good = pointTouchModel(p, n_eps2, m, move, rot);
+ }
+ }
+ if (good) {
+ d.points[i] = @bitCast(p + n_eps);
+ d.numpoints[d.len] += 1;
+ }
+ }
+ d.len += 1;
+}
+
export fn drawDecals(d: *const Decals) void {
c.glAlphaFunc(c.GL_GREATER, 0.01);
c.glDepthFunc(c.GL_LEQUAL);
diff --git a/src/geom.zig b/src/geom.zig
index 5727a39..42002ee 100644
--- a/src/geom.zig
+++ b/src/geom.zig
@@ -19,10 +19,10 @@
const Child = std.meta.Child;
const degreesToRadians = std.math.degreesToRadians;
-const f32Eps = std.math.floatEps(f32);
+const floatEps = std.math.floatEps;
const std = @import("std");
-fn sqr(x: anytype) @TypeOf(x) {
+pub fn sqr(x: anytype) @TypeOf(x) {
return x * x;
}
@@ -37,7 +37,7 @@ export fn sqrlen(v: XYZ) f32 {
return dot(u, u);
}
-fn norm(v: anytype) Child(@TypeOf(v)) {
+pub fn norm(v: anytype) Child(@TypeOf(v)) {
return @sqrt(dot(v, v));
}
@@ -54,7 +54,7 @@ export fn crossProduct(u: XYZ, v: XYZ) XYZ {
};
}
-inline fn splat(comptime n: comptime_int, x: anytype) @Vector(n, @TypeOf(x)) {
+pub fn splat(comptime n: comptime_int, x: anytype) @Vector(n, @TypeOf(x)) {
return @splat(x);
}
@@ -70,7 +70,7 @@ export fn reflect(v: XYZ, n: XYZ) XYZ {
return @bitCast(u - m * splat(3, dot(u, m) * 2));
}
-fn rotate2d(i: *f32, j: *f32, a: f32) void {
+pub fn rotate2d(i: *f32, j: *f32, a: f32) void {
if (a == 0) return;
const x = i.*;
const y = j.*;
@@ -87,7 +87,7 @@ export fn rotate(v: XYZ, deg_x: f32, deg_y: f32, deg_z: f32) XYZ {
return u;
}
-export fn segCrossSphere(a: XYZ, b: XYZ, i: XYZ, r: f32) bool {
+pub export fn segCrossSphere(a: XYZ, b: XYZ, i: XYZ, r: f32) bool {
const p: @Vector(3, f32) = @bitCast(a);
const q: @Vector(3, f32) = @bitCast(b);
const c: @Vector(3, f32) = @bitCast(i);
@@ -99,14 +99,14 @@ export fn segCrossSphere(a: XYZ, b: XYZ, i: XYZ, r: f32) bool {
return sqr(dot(u, (p - c))) >= @reduce(.Add, sqr(p - c)) - sqr(r);
}
-export fn segCrossTrigon(start: XYZ, end: XYZ,
- p_a: *const XYZ, p_b: *const XYZ, p_c: *const XYZ,
- normal: *const XYZ, intersection: *XYZ) bool {
+pub export fn segCrossTrigon(start: XYZ, end: XYZ,
+ p_a: *const XYZ, p_b: *const XYZ, p_c: *const XYZ,
+ normal: *const XYZ, intersection: *XYZ) bool {
const p: @Vector(3, f32) = @bitCast(start);
const q: @Vector(3, f32) = @bitCast(end);
const n: @Vector(3, f32) = @bitCast(normal.*);
const denom = dot(q - p, n);
- if (@fabs(denom) < f32Eps)
+ if (@fabs(denom) < floatEps(f32))
return false; // parallel segment and triangle
const a: @Vector(3, f32) = @bitCast(p_a.*);
@@ -132,7 +132,7 @@ export fn segCrossTrigon(start: XYZ, end: XYZ,
const v = @Vector(3, f32){ i[k[1]], b[k[1]], c[k[1]] } - splat(3, a[k[1]]);
intersection.* = @bitCast(i);
- if (@fabs(u[1]) < f32Eps) {
+ if (@fabs(u[1]) < floatEps(f32)) {
const s = u[0] / u[2];
if (s >= 0 and s <= 1) {
const t = (v[0] - s * v[2]) / v[1];
diff --git a/src/main.zig b/src/main.zig
index afffafd..dae76e9 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -39,6 +39,7 @@ var prng: DefaultPrng = undefined;
comptime {
_ = @import("decal.zig");
_ = @import("geom.zig");
+ _ = @import("model.zig");
} // export functions in C ABI
fn resizeWindow(window: gf.Window, width: c_int, height: c_int) void {
diff --git a/src/model.zig b/src/model.zig
new file mode 100644
index 0000000..274176d
--- /dev/null
+++ b/src/model.zig
@@ -0,0 +1,90 @@
+// 3D model
+// Copyright (C) 2002 David Rosen
+// Copyright (C) 2023 Nguyễn Gia Phong
+//
+// This file is part of Black Shades.
+//
+// Black Shades is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Black Shades is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Black Shades. If not, see <https://www.gnu.org/licenses/>.
+
+const XYZ = geom.XYZ;
+const degToRad = std.math.degreesToRadians;
+const floatMax = std.math.floatMax;
+const geom = @import("geom.zig");
+const rotate2d = geom.rotate2d;
+const segCrossSphere = geom.segCrossSphere;
+const segCrossTrigon = geom.segCrossTrigon;
+const sqr = geom.sqr;
+const std = @import("std");
+
+const max_trigons = 400;
+
+const Trigon = extern struct {
+ vertices: [3]u32,
+ r: f32,
+ g: f32,
+ b: f32,
+};
+
+pub const Model = extern struct {
+ vertices: [max_trigons * 3]XYZ,
+ vertices_len: u32,
+ normals: [max_trigons]XYZ,
+ trigons: [max_trigons]Trigon,
+ trigons_len: u32,
+ center: XYZ,
+ radius: f32,
+ vert_array: [max_trigons * 27]f32,
+};
+
+pub export fn segCrossModel(start: XYZ, end: XYZ, m: *const Model,
+ intersection: *XYZ) c_int {
+ if (!segCrossSphere(start, end, m.center, m.radius))
+ return -1;
+ const p: @Vector(3, f32) = @bitCast(start);
+ var result: c_int = -1;
+ var shortest = floatMax(f32);
+ for (0..m.trigons_len) |j| {
+ var v: @Vector(3, f32) = undefined;
+ if (!segCrossTrigon(start, end,
+ &m.vertices[m.trigons[j].vertices[0]],
+ &m.vertices[m.trigons[j].vertices[1]],
+ &m.vertices[m.trigons[j].vertices[2]],
+ &m.normals[j], @ptrCast(&v)))
+ continue;
+ const distance = @reduce(.Add, sqr(v - p));
+ if (distance < shortest or result == -1) {
+ shortest = distance;
+ result = @intCast(j);
+ intersection.* = @bitCast(v);
+ }
+ }
+ return result;
+}
+
+pub export fn segCrossModelTrans(start: XYZ, end: XYZ, m: *const Model,
+ move: XYZ, rot: f32,
+ intersection: *XYZ) c_int {
+ const t: @Vector(3, f32) = @bitCast(move);
+ var p = @as(@Vector(3, f32), @bitCast(start)) - t;
+ rotate2d(&p[2], &p[0], degToRad(f32, -rot));
+ var q = @as(@Vector(3, f32), @bitCast(end)) - t;
+ rotate2d(&q[2], &q[0], degToRad(f32, -rot));
+
+ defer {
+ rotate2d(&intersection.z, &intersection.x, degToRad(f32, rot));
+ intersection.* = @bitCast(@as(@Vector(3, f32),
+ @bitCast(intersection.*)) + t);
+ }
+ return segCrossModel(@bitCast(p), @bitCast(q), m, intersection);
+}