You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
799 lines
28 KiB
799 lines
28 KiB
#define OLC_PGE_APPLICATION
|
|
#include "pixelGameEngine.h"
|
|
#include "geometry2d.h"
|
|
#include <strstream>
|
|
#include <algorithm>
|
|
#include "main.h"
|
|
|
|
using namespace olc;
|
|
|
|
FaceBall* game;
|
|
|
|
void FaceBall::InitializeEnemyData() {
|
|
enemyData[EnemyID::NONE] = { "VOID",BLACK };
|
|
enemyData[EXIT] = { "EXIT",GREEN };
|
|
enemyData[START] = { "SPAWN POSITION",{128,64,0} };
|
|
enemyData[SHOOTME] = { "SHOOTME",YELLOW,1,1,PI / 8,2,1,3 };
|
|
enemyData[COIN] = { "Coin",BLUE };
|
|
enemyData[POWERUP_ARMOR] = { "Armor",{96,0,96} };
|
|
enemyData[POWERUP_SPEED] = { "Speed",{96,0,96} };
|
|
enemyData[POWERUP_SHOT] = { "Shot",{96,0,96} };
|
|
enemyData[SPECIAL_CAMO] = { "Camouflage",DARK_RED };
|
|
enemyData[SPECIAL_STOP] = { "Stop",DARK_RED };
|
|
enemyData[SPECIAL_SHIELD] = { "Shield",DARK_RED };
|
|
enemyData[AREA_MAP] = { "Map",GREEN };
|
|
}
|
|
|
|
void FaceBall::LoadLevel(int level)
|
|
{
|
|
this->level = level;
|
|
std::vector<std::vector<Tile>>mapData = editor.LoadLevel(level);
|
|
MAP_SIZE = { (int)mapData[0].size(),(int)mapData.size() };
|
|
map.clear();
|
|
mapMesh.tris.clear();
|
|
objects.clear();
|
|
for (int y = 0; y < MAP_SIZE.y; y++) {
|
|
std::vector<MapSquare>row;
|
|
for (int x = 0; x < MAP_SIZE.x; x++) {
|
|
row.push_back({});
|
|
mapMesh.tris.push_back({ {{(float)x,0,(float)y},{(float)x,0,(float)y + 1},{(float)x + 1,0,(float)y}},{{0,0},{0,1},{1,0}},BLUE });
|
|
mapMesh.tris.push_back({ {{(float)x + 1,0,(float)y},{(float)x,0,(float)y + 1},{(float)x + 1,0,(float)y + 1}},{{1,0},{0,1},{1,1}},BLUE });
|
|
}
|
|
map.push_back(row);
|
|
}
|
|
for (int y = 0; y < MAP_SIZE.y; y++) {
|
|
for (int x = 0; x < MAP_SIZE.x; x++) {
|
|
int wallData = 0;
|
|
if (mapData[y][x].wallN) {
|
|
wallData |= NORTH;
|
|
}
|
|
if (mapData[y][x].wallE) {
|
|
wallData |= EAST;
|
|
}
|
|
if (mapData[y][x].wallS) {
|
|
wallData |= SOUTH;
|
|
}
|
|
if (mapData[y][x].wallW) {
|
|
wallData |= WEST;
|
|
}
|
|
AddWall(wallData, { x,y });
|
|
if (map[y][x].wallN != NULL) {
|
|
mapMesh.tris.push_back({ {{(float)x,1,(float)y},{(float)x,0,(float)y},{(float)x + 1,1,(float)y}},{{0,0},{0,1},{1,0}},YELLOW });
|
|
mapMesh.tris.push_back({ {{(float)x,0,(float)y},{(float)x + 1,0,(float)y},{(float)x + 1,1,(float)y}},{{0,1},{1,1},{1,0}},YELLOW });
|
|
}
|
|
if (map[y][x].wallS != NULL) {
|
|
mapMesh.tris.push_back({ {{(float)x + 1,1,(float)y + 1},{(float)x,0,(float)y + 1},{(float)x,1,(float)y + 1}},{{0,0},{1,1},{1,0}},DARK_RED });
|
|
mapMesh.tris.push_back({ {{(float)x + 1,1,(float)y + 1},{(float)x + 1,0,(float)y + 1},{(float)x,0,(float)y + 1}},{{0,0},{0,1},{1,1}},DARK_RED });
|
|
}
|
|
if (map[y][x].wallW != NULL) {
|
|
mapMesh.tris.push_back({ {{(float)x,1,(float)y},{(float)x,1,(float)y + 1}, {(float)x,0,(float)y + 1}},{{1,0},{0,0},{0,1}},MAGENTA });
|
|
mapMesh.tris.push_back({ {{(float)x,0,(float)y},{(float)x,1,(float)y}, {(float)x,0,(float)y + 1}}, {{1,1},{1,0},{0,1}},MAGENTA });
|
|
}
|
|
if (map[y][x].wallE != NULL) {
|
|
mapMesh.tris.push_back({ {{(float)x + 1,0,(float)y + 1},{(float)x + 1,1,(float)y + 1},{(float)x + 1,1,(float)y}},{{1,1},{1,0},{0,0}},CYAN });
|
|
mapMesh.tris.push_back({ {{(float)x + 1,0,(float)y + 1} ,{(float)x + 1,1,(float)y},{(float)x + 1,0,(float)y}},{{1,1},{0,0},{0,1}},CYAN });
|
|
}
|
|
}
|
|
}
|
|
objects.push_back({ mapMesh });
|
|
}
|
|
|
|
bool FaceBall::CheckCollision(vec3d movementVector,vf2d pos,float radius){
|
|
utils::geom2d::circle<float>newPos{ {pos.x + movementVector.x,pos.y + movementVector.z},radius };
|
|
bool collisionOccured = false;
|
|
for (int y = -1; y <= 1; y++) {
|
|
for (int x = -1; x <= 1; x++) {
|
|
int offsetX = (int)pos.x + x;
|
|
int offsetY = (int)pos.y + y;
|
|
if (offsetX >= 0 && offsetX < MAP_SIZE.x && offsetY >= 0 && offsetY < MAP_SIZE.y) {
|
|
MapSquare tile = map[offsetY][offsetX];
|
|
if (tile.wallN != NULL) {
|
|
utils::geom2d::line<float>wall{ {(float)offsetX,(float)offsetY},{(float)offsetX+1,(float)offsetY} };
|
|
if (((int)(newPos.pos.x-radius)==offsetX||
|
|
(int)(newPos.pos.x + radius) == offsetX)
|
|
&&
|
|
newPos.pos.y > wall.start.y && wall.start.y > newPos.pos.y - radius) {
|
|
collisionOccured = true;
|
|
goto breakOutOfCollisionLoop;
|
|
}
|
|
}
|
|
if (tile.wallE != NULL) {
|
|
utils::geom2d::line<float>wall{ {(float)offsetX+1,(float)offsetY},{(float)offsetX + 1,(float)offsetY+1} };
|
|
if (((int)(newPos.pos.y - radius) == offsetY ||
|
|
(int)(newPos.pos.y + radius) == offsetY)
|
|
&& newPos.pos.x<wall.start.x && wall.start.x < newPos.pos.x + radius) {
|
|
collisionOccured = true;
|
|
goto breakOutOfCollisionLoop;
|
|
}
|
|
}
|
|
if (tile.wallS != NULL) {
|
|
utils::geom2d::line<float>wall{ {(float)offsetX,(float)offsetY+1},{(float)offsetX + 1,(float)offsetY+1} };
|
|
if (((int)(newPos.pos.x - radius) == offsetX ||
|
|
(int)(newPos.pos.x + radius) == offsetX)
|
|
&& newPos.pos.y < wall.start.y && wall.start.y < newPos.pos.y + radius) {
|
|
collisionOccured = true;
|
|
goto breakOutOfCollisionLoop;
|
|
}
|
|
}
|
|
if (tile.wallW != NULL) {
|
|
utils::geom2d::line<float>wall{ {(float)offsetX,(float)offsetY},{(float)offsetX,(float)offsetY + 1} };
|
|
if (((int)(newPos.pos.y - radius) == offsetY ||
|
|
(int)(newPos.pos.y + radius) == offsetY)
|
|
&& newPos.pos.x > wall.start.x && wall.start.x > newPos.pos.x - radius) {
|
|
collisionOccured = true;
|
|
goto breakOutOfCollisionLoop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
breakOutOfCollisionLoop:
|
|
return collisionOccured;
|
|
}
|
|
|
|
EnemyData FaceBall::GetData(EnemyID id)
|
|
{
|
|
return enemyData[id];
|
|
}
|
|
|
|
vec3d
|
|
FaceBall::Matrix_MultiplyVector(mat4x4& m, vec3d& i)
|
|
{
|
|
vec3d v;
|
|
v.x = i.x * m.m[0][0] + i.y * m.m[1][0] + i.z * m.m[2][0] + i.w * m.m[3][0];
|
|
v.y = i.x * m.m[0][1] + i.y * m.m[1][1] + i.z * m.m[2][1] + i.w * m.m[3][1];
|
|
v.z = i.x * m.m[0][2] + i.y * m.m[1][2] + i.z * m.m[2][2] + i.w * m.m[3][2];
|
|
v.w = i.x * m.m[0][3] + i.y * m.m[1][3] + i.z * m.m[2][3] + i.w * m.m[3][3];
|
|
return v;
|
|
};
|
|
|
|
mat4x4 FaceBall::Matrix_MakeIdentity()
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = 1.0f;
|
|
matrix.m[1][1] = 1.0f;
|
|
matrix.m[2][2] = 1.0f;
|
|
matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
};
|
|
|
|
mat4x4 FaceBall::Matrix_MakeRotationX(float fAngleRad)
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = 1.0f;
|
|
matrix.m[1][1] = cosf(fAngleRad);
|
|
matrix.m[1][2] = sinf(fAngleRad);
|
|
matrix.m[2][1] = -sinf(fAngleRad);
|
|
matrix.m[2][2] = cosf(fAngleRad);
|
|
matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_MakeRotationY(float fAngleRad)
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = cosf(fAngleRad);
|
|
matrix.m[0][2] = sinf(fAngleRad);
|
|
matrix.m[2][0] = -sinf(fAngleRad);
|
|
matrix.m[1][1] = 1.0f;
|
|
matrix.m[2][2] = cosf(fAngleRad);
|
|
matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_MakeRotationZ(float fAngleRad)
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = cosf(fAngleRad);
|
|
matrix.m[0][1] = sinf(fAngleRad);
|
|
matrix.m[1][0] = -sinf(fAngleRad);
|
|
matrix.m[1][1] = cosf(fAngleRad);
|
|
matrix.m[2][2] = 1.0f;
|
|
matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_MakeTranslation(float x, float y, float z)
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = 1.0f;
|
|
matrix.m[1][1] = 1.0f;
|
|
matrix.m[2][2] = 1.0f;
|
|
matrix.m[3][3] = 1.0f;
|
|
matrix.m[3][0] = x;
|
|
matrix.m[3][1] = y;
|
|
matrix.m[3][2] = z;
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_MakeProjection(float fFovDegrees, float fAspectRatio, float fNear, float fFar)
|
|
{
|
|
float fFovRad = 1.0f / tanf(fFovDegrees * 0.5f / 180.0f * 3.14159f);
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = fAspectRatio * fFovRad;
|
|
matrix.m[1][1] = fFovRad;
|
|
matrix.m[2][2] = fFar / (fFar - fNear);
|
|
matrix.m[3][2] = (-fFar * fNear) / (fFar - fNear);
|
|
matrix.m[2][3] = 1.0f;
|
|
matrix.m[3][3] = 0.0f;
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_MultiplyMatrix(mat4x4& m1, mat4x4& m2)
|
|
{
|
|
mat4x4 matrix;
|
|
for (int c = 0; c < 4; c++)
|
|
for (int r = 0; r < 4; r++)
|
|
matrix.m[r][c] = m1.m[r][0] * m2.m[0][c] + m1.m[r][1] * m2.m[1][c] + m1.m[r][2] * m2.m[2][c] + m1.m[r][3] * m2.m[3][c];
|
|
return matrix;
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_PointAt(vec3d& pos, vec3d& target, vec3d& up)
|
|
{
|
|
// Calculate new forward direction
|
|
vec3d newForward = Vector_Sub(target, pos);
|
|
newForward = Vector_Normalise(newForward);
|
|
|
|
// Calculate new Up direction
|
|
vec3d a = Vector_Mul(newForward, Vector_DotProduct(up, newForward));
|
|
vec3d newUp = Vector_Sub(up, a);
|
|
newUp = Vector_Normalise(newUp);
|
|
|
|
// New Right direction is easy, its just cross product
|
|
vec3d newRight = Vector_CrossProduct(newUp, newForward);
|
|
|
|
// Construct Dimensioning and Translation Matrix
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = newRight.x; matrix.m[0][1] = newRight.y; matrix.m[0][2] = newRight.z; matrix.m[0][3] = 0.0f;
|
|
matrix.m[1][0] = newUp.x; matrix.m[1][1] = newUp.y; matrix.m[1][2] = newUp.z; matrix.m[1][3] = 0.0f;
|
|
matrix.m[2][0] = newForward.x; matrix.m[2][1] = newForward.y; matrix.m[2][2] = newForward.z; matrix.m[2][3] = 0.0f;
|
|
matrix.m[3][0] = pos.x; matrix.m[3][1] = pos.y; matrix.m[3][2] = pos.z; matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
|
|
}
|
|
|
|
mat4x4 FaceBall::Matrix_QuickInverse(mat4x4& m) // Only for Rotation/Translation Matrices
|
|
{
|
|
mat4x4 matrix;
|
|
matrix.m[0][0] = m.m[0][0]; matrix.m[0][1] = m.m[1][0]; matrix.m[0][2] = m.m[2][0]; matrix.m[0][3] = 0.0f;
|
|
matrix.m[1][0] = m.m[0][1]; matrix.m[1][1] = m.m[1][1]; matrix.m[1][2] = m.m[2][1]; matrix.m[1][3] = 0.0f;
|
|
matrix.m[2][0] = m.m[0][2]; matrix.m[2][1] = m.m[1][2]; matrix.m[2][2] = m.m[2][2]; matrix.m[2][3] = 0.0f;
|
|
matrix.m[3][0] = -(m.m[3][0] * matrix.m[0][0] + m.m[3][1] * matrix.m[1][0] + m.m[3][2] * matrix.m[2][0]);
|
|
matrix.m[3][1] = -(m.m[3][0] * matrix.m[0][1] + m.m[3][1] * matrix.m[1][1] + m.m[3][2] * matrix.m[2][1]);
|
|
matrix.m[3][2] = -(m.m[3][0] * matrix.m[0][2] + m.m[3][1] * matrix.m[1][2] + m.m[3][2] * matrix.m[2][2]);
|
|
matrix.m[3][3] = 1.0f;
|
|
return matrix;
|
|
}
|
|
|
|
vec3d FaceBall::Vector_Add(vec3d& v1, vec3d& v2)
|
|
{
|
|
return { v1.x + v2.x, v1.y + v2.y, v1.z + v2.z };
|
|
}
|
|
|
|
vec3d FaceBall::Vector_Sub(vec3d& v1, vec3d& v2)
|
|
{
|
|
return { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
|
|
}
|
|
|
|
vec3d FaceBall::Vector_Mul(vec3d& v1, float k)
|
|
{
|
|
return { v1.x * k, v1.y * k, v1.z * k };
|
|
}
|
|
|
|
vec3d FaceBall::Vector_Div(vec3d& v1, float k)
|
|
{
|
|
return { v1.x / k, v1.y / k, v1.z / k };
|
|
}
|
|
|
|
float FaceBall::Vector_DotProduct(vec3d& v1, vec3d& v2)
|
|
{
|
|
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
|
}
|
|
|
|
float FaceBall::Vector_Length(vec3d& v)
|
|
{
|
|
return sqrtf(Vector_DotProduct(v, v));
|
|
}
|
|
|
|
vec3d FaceBall::Vector_Normalise(vec3d& v)
|
|
{
|
|
float l = Vector_Length(v);
|
|
return { v.x / l, v.y / l, v.z / l };
|
|
}
|
|
|
|
vec3d FaceBall::Vector_CrossProduct(vec3d& v1, vec3d& v2)
|
|
{
|
|
vec3d v;
|
|
v.x = v1.y * v2.z - v1.z * v2.y;
|
|
v.y = v1.z * v2.x - v1.x * v2.z;
|
|
v.z = v1.x * v2.y - v1.y * v2.x;
|
|
return v;
|
|
}
|
|
|
|
vec3d FaceBall::Vector_IntersectPlane(vec3d& plane_p, vec3d& plane_n, vec3d& lineStart, vec3d& lineEnd, float& t)
|
|
{
|
|
plane_n = Vector_Normalise(plane_n);
|
|
float plane_d = -Vector_DotProduct(plane_n, plane_p);
|
|
float ad = Vector_DotProduct(lineStart, plane_n);
|
|
float bd = Vector_DotProduct(lineEnd, plane_n);
|
|
t = (-plane_d - ad) / (bd - ad);
|
|
vec3d lineStartToEnd = Vector_Sub(lineEnd, lineStart);
|
|
vec3d lineToIntersect = Vector_Mul(lineStartToEnd, t);
|
|
return Vector_Add(lineStart, lineToIntersect);
|
|
}
|
|
|
|
|
|
int FaceBall::Triangle_ClipAgainstPlane(vec3d plane_p, vec3d plane_n, Triangle& in_tri, Triangle& out_tri1, Triangle& out_tri2)
|
|
{
|
|
// Make sure plane normal is indeed normal
|
|
plane_n = Vector_Normalise(plane_n);
|
|
|
|
// Return signed shortest distance from point to plane, plane normal must be normalised
|
|
auto dist = [&](vec3d& p)
|
|
{
|
|
vec3d n = Vector_Normalise(p);
|
|
return (plane_n.x * p.x + plane_n.y * p.y + plane_n.z * p.z - Vector_DotProduct(plane_n, plane_p));
|
|
};
|
|
|
|
// Create two temporary storage arrays to classify points either side of plane
|
|
// If distance sign is positive, point lies on "inside" of plane
|
|
vec3d* inside_points[3]; int nInsidePointCount = 0;
|
|
vec3d* outside_points[3]; int nOutsidePointCount = 0;
|
|
vec2d* inside_tex[3]; int nInsideTexCount = 0;
|
|
vec2d* outside_tex[3]; int nOutsideTexCount = 0;
|
|
|
|
|
|
// Get signed distance of each point in triangle to plane
|
|
float d0 = dist(in_tri.p[0]);
|
|
float d1 = dist(in_tri.p[1]);
|
|
float d2 = dist(in_tri.p[2]);
|
|
|
|
if (d0 >= 0) { inside_points[nInsidePointCount++] = &in_tri.p[0]; inside_tex[nInsideTexCount++] = &in_tri.uv[0]; }
|
|
else {
|
|
outside_points[nOutsidePointCount++] = &in_tri.p[0]; outside_tex[nOutsideTexCount++] = &in_tri.uv[0];
|
|
}
|
|
if (d1 >= 0) {
|
|
inside_points[nInsidePointCount++] = &in_tri.p[1]; inside_tex[nInsideTexCount++] = &in_tri.uv[1];
|
|
}
|
|
else {
|
|
outside_points[nOutsidePointCount++] = &in_tri.p[1]; outside_tex[nOutsideTexCount++] = &in_tri.uv[1];
|
|
}
|
|
if (d2 >= 0) {
|
|
inside_points[nInsidePointCount++] = &in_tri.p[2]; inside_tex[nInsideTexCount++] = &in_tri.uv[2];
|
|
}
|
|
else {
|
|
outside_points[nOutsidePointCount++] = &in_tri.p[2]; outside_tex[nOutsideTexCount++] = &in_tri.uv[2];
|
|
}
|
|
|
|
// Now classify triangle points, and break the input triangle into
|
|
// smaller output triangles if required. There are four possible
|
|
// outcomes...
|
|
|
|
if (nInsidePointCount == 0)
|
|
{
|
|
// All points lie on the outside of plane, so clip whole triangle
|
|
// It ceases to exist
|
|
|
|
return 0; // No returned triangles are valid
|
|
}
|
|
|
|
if (nInsidePointCount == 3)
|
|
{
|
|
// All points lie on the inside of plane, so do nothing
|
|
// and allow the triangle to simply pass through
|
|
out_tri1 = in_tri;
|
|
|
|
return 1; // Just the one returned original triangle is valid
|
|
}
|
|
|
|
if (nInsidePointCount == 1 && nOutsidePointCount == 2)
|
|
{
|
|
// Triangle should be clipped. As two points lie outside
|
|
// the plane, the triangle simply becomes a smaller triangle
|
|
|
|
// Copy appearance info to new triangle
|
|
out_tri1.col = in_tri.col;
|
|
out_tri1.tex = in_tri.tex;
|
|
|
|
// The inside point is valid, so keep that...
|
|
out_tri1.p[0] = *inside_points[0];
|
|
out_tri1.uv[0] = *inside_tex[0];
|
|
|
|
// but the two new points are at the locations where the
|
|
// original sides of the triangle (lines) intersect with the plane
|
|
float t;
|
|
out_tri1.p[1] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t);
|
|
out_tri1.uv[1].u = t * (outside_tex[0]->u - inside_tex[0]->u) + inside_tex[0]->u;
|
|
out_tri1.uv[1].v = t * (outside_tex[0]->v - inside_tex[0]->v) + inside_tex[0]->v;
|
|
out_tri1.uv[1].w = t * (outside_tex[0]->w - inside_tex[0]->w) + inside_tex[0]->w;
|
|
|
|
out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[1], t);
|
|
out_tri1.uv[2].u = t * (outside_tex[1]->u - inside_tex[0]->u) + inside_tex[0]->u;
|
|
out_tri1.uv[2].v = t * (outside_tex[1]->v - inside_tex[0]->v) + inside_tex[0]->v;
|
|
out_tri1.uv[2].w = t * (outside_tex[1]->w - inside_tex[0]->w) + inside_tex[0]->w;
|
|
|
|
return 1; // Return the newly formed single triangle
|
|
}
|
|
|
|
if (nInsidePointCount == 2 && nOutsidePointCount == 1)
|
|
{
|
|
// Triangle should be clipped. As two points lie inside the plane,
|
|
// the clipped triangle becomes a "quad". Fortunately, we can
|
|
// represent a quad with two new triangles
|
|
|
|
// Copy appearance info to new triangles
|
|
out_tri1.col = in_tri.col;
|
|
out_tri2.col = in_tri.col;
|
|
out_tri1.tex = in_tri.tex;
|
|
out_tri2.tex = in_tri.tex;
|
|
|
|
// The first triangle consists of the two inside points and a new
|
|
// point determined by the location where one side of the triangle
|
|
// intersects with the plane
|
|
out_tri1.p[0] = *inside_points[0];
|
|
out_tri1.p[1] = *inside_points[1];
|
|
out_tri1.uv[0] = *inside_tex[0];
|
|
out_tri1.uv[1] = *inside_tex[1];
|
|
|
|
float t;
|
|
out_tri1.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[0], *outside_points[0], t);
|
|
out_tri1.uv[2].u = t * (outside_tex[0]->u - inside_tex[0]->u) + inside_tex[0]->u;
|
|
out_tri1.uv[2].v = t * (outside_tex[0]->v - inside_tex[0]->v) + inside_tex[0]->v;
|
|
out_tri1.uv[2].w = t * (outside_tex[0]->w - inside_tex[0]->w) + inside_tex[0]->w;
|
|
|
|
// The second triangle is composed of one of he inside points, a
|
|
// new point determined by the intersection of the other side of the
|
|
// triangle and the plane, and the newly created point above
|
|
out_tri2.p[0] = *inside_points[1];
|
|
out_tri2.uv[0] = *inside_tex[1];
|
|
out_tri2.p[1] = out_tri1.p[2];
|
|
out_tri2.uv[1] = out_tri1.uv[2];
|
|
out_tri2.p[2] = Vector_IntersectPlane(plane_p, plane_n, *inside_points[1], *outside_points[0], t);
|
|
out_tri2.uv[2].u = t * (outside_tex[0]->u - inside_tex[1]->u) + inside_tex[1]->u;
|
|
out_tri2.uv[2].v = t * (outside_tex[0]->v - inside_tex[1]->v) + inside_tex[1]->v;
|
|
out_tri2.uv[2].w = t * (outside_tex[0]->w - inside_tex[1]->w) + inside_tex[1]->w;
|
|
return 2; // Return two newly formed triangles which form a quad
|
|
}
|
|
}
|
|
|
|
void FaceBall::RenderWorld() {
|
|
// Set up rotation matrices
|
|
mat4x4 matRotZ, matRotX, matWorld;
|
|
|
|
matRotZ = Matrix_MakeRotationZ(fTheta * 0.5f);
|
|
matRotX = Matrix_MakeRotationX(fTheta);
|
|
|
|
//matTrans = Matrix_MakeTranslation(0.0f, 0.0f, 0.0f);
|
|
matWorld = Matrix_MakeIdentity();
|
|
matWorld = Matrix_MultiplyMatrix(matRotZ, matRotX);
|
|
//matWorld = Matrix_MultiplyMatrix(matWorld, matTrans);
|
|
|
|
vec3d vUp = { 0,1,0 };
|
|
vec3d vTarget = { 0,sinf(pitch),cosf(pitch) };
|
|
mat4x4 matCameraRot = Matrix_MakeRotationY(fYaw);
|
|
vLookDir = Matrix_MultiplyVector(matCameraRot, vTarget);
|
|
vTarget = Vector_Add(freeRoam?freeRoamCamera:player.GetPos(), vLookDir);
|
|
mat4x4 matCamera = Matrix_PointAt(freeRoam?freeRoamCamera:player.GetPos(), vTarget, vUp);
|
|
mat4x4 matView = Matrix_QuickInverse(matCamera);
|
|
|
|
std::vector<Triangle>vecTrianglesToRaster;
|
|
|
|
// Draw Triangles
|
|
for (auto& obj : objects) {
|
|
for (auto& tri : obj.mesh.tris) {
|
|
Triangle triProjected, triTransformed, triViewed;
|
|
//mat4x4 matTrans = Matrix_MakeTranslation(-mesh.size.x/2, 0, -mesh.size.x/2);
|
|
mat4x4 localMat = Matrix_MakeIdentity();
|
|
//localMat = Matrix_MultiplyMatrix(localMat, matTrans);
|
|
mat4x4 rotMat = Matrix_MakeRotationY(obj.rot);
|
|
localMat = Matrix_MultiplyMatrix(localMat, rotMat);
|
|
mat4x4 matTrans = Matrix_MakeTranslation(obj.pos.x, 0, obj.pos.y);
|
|
localMat = Matrix_MultiplyMatrix(localMat, matTrans);
|
|
|
|
triTransformed.p[0] = Matrix_MultiplyVector(localMat, tri.p[0]);
|
|
triTransformed.p[1] = Matrix_MultiplyVector(localMat, tri.p[1]);
|
|
triTransformed.p[2] = Matrix_MultiplyVector(localMat, tri.p[2]);
|
|
triTransformed.uv[0] = tri.uv[0];
|
|
triTransformed.uv[1] = tri.uv[1];
|
|
triTransformed.uv[2] = tri.uv[2];
|
|
triTransformed.col = tri.col;
|
|
|
|
vec3d normal, line1, line2;
|
|
line1 = Vector_Sub(triTransformed.p[1], triTransformed.p[0]);
|
|
line2 = Vector_Sub(triTransformed.p[2], triTransformed.p[0]);
|
|
|
|
normal = Vector_CrossProduct(line1, line2);
|
|
normal = Vector_Normalise(normal);
|
|
|
|
vec3d vCameraRay = Vector_Sub(triTransformed.p[0], freeRoam?freeRoamCamera:player.GetPos());
|
|
|
|
if (Vector_DotProduct(normal, vCameraRay) < 0) {
|
|
vec3d light_dir = Vector_Mul(vLookDir, -1);
|
|
light_dir = Vector_Normalise(light_dir);
|
|
|
|
float dp = std::max(0.7f, Vector_DotProduct(light_dir, normal));
|
|
|
|
triViewed.p[0] = Matrix_MultiplyVector(matView, triTransformed.p[0]);
|
|
triViewed.p[1] = Matrix_MultiplyVector(matView, triTransformed.p[1]);
|
|
triViewed.p[2] = Matrix_MultiplyVector(matView, triTransformed.p[2]);
|
|
triViewed.uv[0] = triTransformed.uv[0];
|
|
triViewed.uv[1] = triTransformed.uv[1];
|
|
triViewed.uv[2] = triTransformed.uv[2];
|
|
triViewed.col = Pixel(triTransformed.col.r * dp * dp, triTransformed.col.g * dp * dp, triTransformed.col.b * dp * dp);
|
|
float dist = std::sqrtf(std::powf((freeRoam ? freeRoamCamera : player.GetPos()).x - tri.p[0].x, 2) + std::powf((freeRoam ? freeRoamCamera : player.GetPos()).y - tri.p[0].y, 2) + std::powf((freeRoam ? freeRoamCamera : player.GetPos()).z - tri.p[0].z, 2));
|
|
float colorMult = dist>5*PI/3?0:std::sinf(0.3 * dist + PI / 2);
|
|
triViewed.col = Pixel(triViewed.col.r * colorMult, triViewed.col.g * colorMult, triViewed.col.b * colorMult);
|
|
//triViewed.col = triTransformed.col;
|
|
|
|
int nClippedTriangles = 0;
|
|
Triangle clipped[2];
|
|
nClippedTriangles = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.1f }, { 0.0f, 0.0f, 1.0f }, triViewed, clipped[0], clipped[1]);
|
|
|
|
for (int n = 0; n < nClippedTriangles; n++) {
|
|
// Project triangles from 3D --> 2D
|
|
triProjected.p[0] = Matrix_MultiplyVector(matProj, clipped[n].p[0]);
|
|
triProjected.p[1] = Matrix_MultiplyVector(matProj, clipped[n].p[1]);
|
|
triProjected.p[2] = Matrix_MultiplyVector(matProj, clipped[n].p[2]);
|
|
triProjected.col = clipped[n].col;
|
|
triProjected.uv[0] = clipped[n].uv[0];
|
|
triProjected.uv[1] = clipped[n].uv[1];
|
|
triProjected.uv[2] = clipped[n].uv[2];
|
|
triProjected.uv[0].u = triProjected.uv[0].u / triProjected.p[0].w;
|
|
triProjected.uv[1].u = triProjected.uv[1].u / triProjected.p[1].w;
|
|
triProjected.uv[2].u = triProjected.uv[2].u / triProjected.p[2].w;
|
|
|
|
triProjected.uv[0].v = triProjected.uv[0].v / triProjected.p[0].w;
|
|
triProjected.uv[1].v = triProjected.uv[1].v / triProjected.p[1].w;
|
|
triProjected.uv[2].v = triProjected.uv[2].v / triProjected.p[2].w;
|
|
|
|
triProjected.uv[0].w = 1.0f / triProjected.p[0].w;
|
|
triProjected.uv[1].w = 1.0f / triProjected.p[1].w;
|
|
triProjected.uv[2].w = 1.0f / triProjected.p[2].w;
|
|
|
|
|
|
triProjected.p[0] = Vector_Div(triProjected.p[0], triProjected.p[0].w);
|
|
triProjected.p[1] = Vector_Div(triProjected.p[1], triProjected.p[1].w);
|
|
triProjected.p[2] = Vector_Div(triProjected.p[2], triProjected.p[2].w);
|
|
|
|
triProjected.p[0].x *= -1.0f;
|
|
triProjected.p[1].x *= -1.0f;
|
|
triProjected.p[2].x *= -1.0f;
|
|
triProjected.p[0].y *= -1.0f;
|
|
triProjected.p[1].y *= -1.0f;
|
|
triProjected.p[2].y *= -1.0f;
|
|
|
|
// Scale into view
|
|
vec3d vOffsetView = { 1,1,0 };
|
|
triProjected.p[0] = Vector_Add(triProjected.p[0], vOffsetView);
|
|
triProjected.p[1] = Vector_Add(triProjected.p[1], vOffsetView);
|
|
triProjected.p[2] = Vector_Add(triProjected.p[2], vOffsetView);
|
|
triProjected.p[0].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[0].y *= 0.5f * (float)ScreenHeight();
|
|
triProjected.p[1].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[1].y *= 0.5f * (float)ScreenHeight();
|
|
triProjected.p[2].x *= 0.5f * (float)ScreenWidth();
|
|
triProjected.p[2].y *= 0.5f * (float)ScreenHeight();
|
|
triProjected.tex = obj.mesh.texture;
|
|
|
|
vecTrianglesToRaster.push_back(triProjected);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//std::sort(vecTrianglesToRaster.begin(),vecTrianglesToRaster.end(),[](triangle&t1,triangle&t2){return (t1.p[0].z+t1.p[1].z+t1.p[2].z)/3.0f>(t2.p[0].z+t2.p[1].z+t2.p[2].z)/3.0f;});
|
|
ClearBuffer(BLACK, true);
|
|
int triRenderCount = 0;
|
|
|
|
for (auto& triToRaster : vecTrianglesToRaster) {
|
|
|
|
Triangle clipped[2];
|
|
std::list<Triangle>listTriangles;
|
|
listTriangles.push_back(triToRaster);
|
|
int nNewTriangles = 1;
|
|
|
|
for (int p = 0; p < 4; p++)
|
|
{
|
|
int nTrisToAdd = 0;
|
|
while (nNewTriangles > 0)
|
|
{
|
|
// Take triangle from front of queue
|
|
Triangle test = listTriangles.front();
|
|
listTriangles.pop_front();
|
|
nNewTriangles--;
|
|
|
|
// Clip it against a plane. We only need to test each
|
|
// subsequent plane, against subsequent new triangles
|
|
// as all triangles after a plane clip are guaranteed
|
|
// to lie on the inside of the plane. I like how this
|
|
// comment is almost completely and utterly justified
|
|
switch (p)
|
|
{
|
|
case 0: nTrisToAdd = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, test, clipped[0], clipped[1]); break;
|
|
case 1: nTrisToAdd = Triangle_ClipAgainstPlane({ 0.0f, (float)ScreenHeight() - 1, 0.0f }, { 0.0f, -1.0f, 0.0f }, test, clipped[0], clipped[1]); break;
|
|
case 2: nTrisToAdd = Triangle_ClipAgainstPlane({ 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, test, clipped[0], clipped[1]); break;
|
|
case 3: nTrisToAdd = Triangle_ClipAgainstPlane({ (float)ScreenWidth() - 1, 0.0f, 0.0f }, { -1.0f, 0.0f, 0.0f }, test, clipped[0], clipped[1]); break;
|
|
}
|
|
|
|
// Clipping may yield a variable number of triangles, so
|
|
// add these new ones to the back of the queue for subsequent
|
|
// clipping against next planes
|
|
for (int w = 0; w < nTrisToAdd; w++)
|
|
listTriangles.push_back(clipped[w]);
|
|
}
|
|
nNewTriangles = listTriangles.size();
|
|
}
|
|
|
|
for (auto& t : listTriangles) {
|
|
// Rasterize triangle
|
|
SetDecalStructure(DecalStructure::LIST);
|
|
SetDecalMode(DecalMode::NORMAL);
|
|
DrawPolygonDecal(t.tex, {
|
|
{t.p[0].x, t.p[0].y},
|
|
{t.p[1].x, t.p[1].y},
|
|
{t.p[2].x, t.p[2].y}
|
|
}, {
|
|
{t.uv[0].u,t.uv[0].v},
|
|
{t.uv[1].u,t.uv[1].v},
|
|
{t.uv[2].u,t.uv[2].v},
|
|
}, { t.uv[0].w,t.uv[1].w,t.uv[2].w }, { t.p[0].z,t.p[1].z,t.p[2].z }, { t.col,t.col,t.col });
|
|
/*SetDecalMode(DecalMode::WIREFRAME);
|
|
DrawPolygonDecal(nullptr,{
|
|
{t.p[0].x, t.p[0].y},
|
|
{t.p[1].x, t.p[1].y},
|
|
{t.p[2].x, t.p[2].y}
|
|
},{
|
|
{0,0},
|
|
{0,0},
|
|
{0,0},
|
|
}, { t.uv[0].w,t.uv[1].w,t.uv[2].w }, { t.p[0].z,t.p[1].z,t.p[2].z }, { BLACK,BLACK,BLACK });*/
|
|
SetDecalStructure(DecalStructure::FAN);
|
|
triRenderCount++;
|
|
}
|
|
}
|
|
|
|
SetDecalMode(DecalMode::NORMAL);
|
|
DrawStringDecal({ 0,0 }, "Triangles: " + std::to_string(triRenderCount), WHITE, { 2,2 });
|
|
}
|
|
|
|
void FaceBall::HandleKeys(float fElapsedTime) {
|
|
vec3d vForward = Vector_Mul(vLookDir, std::min(player.GetRadius()-0.00001f,2*fElapsedTime));
|
|
if (freeRoam) {
|
|
if (GetKey(DOWN).bHeld) {
|
|
pitch -= 1 * fElapsedTime;
|
|
}
|
|
if (GetKey(UP).bHeld) {
|
|
pitch += 1 * fElapsedTime;
|
|
}
|
|
}
|
|
else {
|
|
pitch = 0;
|
|
}
|
|
if (GetKey(W).bHeld) {
|
|
if (freeRoam) {
|
|
freeRoamCamera = Vector_Add(freeRoamCamera, vForward);
|
|
}
|
|
else {
|
|
if (!CheckCollision({ vForward.x,0,0 }, {player.GetPos().x,player.GetPos().z},player.GetRadius())) {
|
|
vec3d xMovement{ vForward.x,0,0 };
|
|
player.UpdatePos(Vector_Add(player.GetPos(), xMovement));
|
|
}
|
|
if (!CheckCollision({ 0,0,vForward.z }, { player.GetPos().x,player.GetPos().z }, player.GetRadius())) {
|
|
vec3d zMovement{ 0,0,vForward.z };
|
|
player.UpdatePos(Vector_Add(player.GetPos(), zMovement));
|
|
}
|
|
}
|
|
}
|
|
if (GetKey(S).bHeld) {
|
|
vec3d vReverse = { -vForward.x,-vForward.y,-vForward.z };
|
|
if (freeRoam) {
|
|
freeRoamCamera = Vector_Add(freeRoamCamera, vReverse);
|
|
}
|
|
else {
|
|
if (!CheckCollision({ vReverse.x,0,0 }, { player.GetPos().x,player.GetPos().z }, player.GetRadius())) {
|
|
vec3d xMovement{ vReverse.x,0,0 };
|
|
player.UpdatePos(Vector_Add(player.GetPos(), xMovement));
|
|
}
|
|
if (!CheckCollision({ 0,0,vReverse.z }, { player.GetPos().x,player.GetPos().z }, player.GetRadius())) {
|
|
vec3d zMovement{ 0,0,vReverse.z };
|
|
player.UpdatePos(Vector_Add(player.GetPos(), zMovement));
|
|
}
|
|
}
|
|
}
|
|
if (GetKey(olc::A).bHeld) {
|
|
fYaw -= 2 * fElapsedTime;
|
|
}
|
|
if (GetKey(olc::D).bHeld) {
|
|
fYaw += 2 * fElapsedTime;
|
|
}
|
|
if (GetKey(olc::F1).bPressed) {
|
|
freeRoam = !freeRoam;
|
|
}
|
|
}
|
|
|
|
void FaceBall::AddWall(int dir, vi2d gridSquare) {
|
|
if (dir & NORTH) {
|
|
map[gridSquare.y][gridSquare.x].wallN = dot;
|
|
if (gridSquare.y > 0) {
|
|
map[gridSquare.y - 1][gridSquare.x].wallS = dot;
|
|
}
|
|
}
|
|
if (dir & WEST) {
|
|
map[gridSquare.y][gridSquare.x].wallW = dot;
|
|
if (gridSquare.x > 0) {
|
|
map[gridSquare.y][gridSquare.x - 1].wallE = dot;
|
|
}
|
|
}
|
|
if (dir & SOUTH) {
|
|
map[gridSquare.y][gridSquare.x].wallS = dot;
|
|
if (gridSquare.y < map.size() - 1) {
|
|
map[gridSquare.y + 1][gridSquare.x].wallN = dot;
|
|
}
|
|
}
|
|
if (dir & EAST) {
|
|
map[gridSquare.y][gridSquare.x].wallE = dot;
|
|
if (gridSquare.x < map[0].size() - 1) {
|
|
map[gridSquare.y][gridSquare.x + 1].wallW = dot;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FaceBall::OnUserCreate()
|
|
{
|
|
game = this;
|
|
sAppName = "Faceball 2030";
|
|
matProj = Matrix_MakeProjection(90.0f, (float)ScreenHeight() / (float)ScreenWidth(), 0.1f, 1000.0f);
|
|
dot = new Decal(new Sprite("assets/dot.png"));
|
|
circle = new Decal(new Sprite("assets/circle.png"));
|
|
arrow = new Decal(new Sprite("assets/arrow.png"));
|
|
enemy_ShootMe_tex = new Decal(new Sprite("assets/enemies/ShootMe.png"));
|
|
YAZAWA = new Decal(new Sprite("assets/yazawa.png"));
|
|
|
|
InitializeEnemyData();
|
|
|
|
Mesh testEnemy("assets/enemies/ShootMe.obj", enemy_ShootMe_tex);
|
|
mapMesh.texture = YAZAWA;
|
|
|
|
LoadLevel(1);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FaceBall::OnUserUpdate(float fElapsedTime)
|
|
{
|
|
switch (mode) {
|
|
case GAME: {
|
|
HandleKeys(fElapsedTime);
|
|
RenderWorld();
|
|
}break;
|
|
case EDITOR: {
|
|
editor.Update(fElapsedTime);
|
|
}break;
|
|
}
|
|
if (GetKey(olc::F5).bPressed) {
|
|
mode = (GAMEMODE)!(mode&1);
|
|
if (mode == GAMEMODE::GAME) {
|
|
LoadLevel(level);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FaceBall::OnTextEntryComplete(const std::string& sText) {
|
|
switch (mode) {
|
|
case GAME: {
|
|
}break;
|
|
case EDITOR: {
|
|
editor.OnTextEntryComplete(sText);
|
|
}break;
|
|
}
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
FaceBall demo;
|
|
if (demo.Construct(1280, 720, 1, 1))
|
|
demo.Start();
|
|
return 0;
|
|
}
|
|
|