Swapped example code to my old project frame.
30
assets/shaders/skybox.fs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
in vec3 fragPosition;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform samplerCube environmentMap;
|
||||||
|
uniform bool vflipped;
|
||||||
|
uniform bool doGamma;
|
||||||
|
|
||||||
|
// Output fragment color
|
||||||
|
out vec4 finalColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Fetch color from texture map
|
||||||
|
vec3 color = vec3(0.0);
|
||||||
|
|
||||||
|
if (vflipped) color = texture(environmentMap, vec3(fragPosition.x, -fragPosition.y, fragPosition.z)).rgb;
|
||||||
|
else color = texture(environmentMap, fragPosition).rgb;
|
||||||
|
|
||||||
|
if (doGamma)// Apply gamma correction
|
||||||
|
{
|
||||||
|
color = color/(color + vec3(1.0));
|
||||||
|
color = pow(color, vec3(1.0/2.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate final fragment color
|
||||||
|
finalColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
24
assets/shaders/skybox.vs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes
|
||||||
|
in vec3 vertexPosition;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform mat4 matProjection;
|
||||||
|
uniform mat4 matView;
|
||||||
|
|
||||||
|
// Output vertex attributes (to fragment shader)
|
||||||
|
out vec3 fragPosition;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Calculate fragment position based on model transformations
|
||||||
|
fragPosition = vertexPosition;
|
||||||
|
|
||||||
|
// Remove translation from the view matrix
|
||||||
|
mat4 rotView = mat4(mat3(matView));
|
||||||
|
vec4 clipPos = matProjection*rotView*vec4(vertexPosition, 1.0);
|
||||||
|
|
||||||
|
// Calculate final vertex position
|
||||||
|
gl_Position = clipPos;
|
||||||
|
}
|
||||||
BIN
assets/textures/Grass_01.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/textures/cubemaps/back.png
Normal file
|
After Width: | Height: | Size: 746 KiB |
BIN
assets/textures/cubemaps/bottom.png
Normal file
|
After Width: | Height: | Size: 753 KiB |
BIN
assets/textures/cubemaps/cubemap.png
Normal file
|
After Width: | Height: | Size: 5.1 MiB |
BIN
assets/textures/cubemaps/front.png
Normal file
|
After Width: | Height: | Size: 1017 KiB |
BIN
assets/textures/cubemaps/left.png
Normal file
|
After Width: | Height: | Size: 807 KiB |
BIN
assets/textures/cubemaps/right.png
Normal file
|
After Width: | Height: | Size: 838 KiB |
BIN
assets/textures/cubemaps/top.png
Normal file
|
After Width: | Height: | Size: 830 KiB |
560
src/main.c
@@ -1,23 +1,12 @@
|
|||||||
/*******************************************************************************************
|
|
||||||
*
|
|
||||||
* raylib [core] example - 3d camera fps
|
|
||||||
*
|
|
||||||
* Example complexity rating: [★★★☆] 3/4
|
|
||||||
*
|
|
||||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
|
||||||
*
|
|
||||||
* Example contributed by Agnis Aldins (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
|
|
||||||
*
|
|
||||||
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
|
|
||||||
* BSD-like license that allows static linking with closed source software
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Agnis Aldins (@nezvers)
|
|
||||||
*
|
|
||||||
********************************************************************************************/
|
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
#include "rcamera.h"
|
||||||
#include "raymath.h"
|
#include "raymath.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "stddef.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "rlgl.h"
|
||||||
|
#include "float.h"
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Defines and Macros
|
// Defines and Macros
|
||||||
@@ -28,47 +17,81 @@
|
|||||||
#define CROUCH_SPEED 5.0f
|
#define CROUCH_SPEED 5.0f
|
||||||
#define JUMP_FORCE 12.0f
|
#define JUMP_FORCE 12.0f
|
||||||
#define MAX_ACCEL 150.0f
|
#define MAX_ACCEL 150.0f
|
||||||
|
|
||||||
// Grounded drag
|
// Grounded drag
|
||||||
#define FRICTION 0.86f
|
#define FRICTION 0.86f
|
||||||
|
|
||||||
// Increasing air drag, increases strafing speed
|
// Increasing air drag, increases strafing speed
|
||||||
#define AIR_DRAG 0.98f
|
#define AIR_DRAG 0.98f
|
||||||
|
|
||||||
// Responsiveness for turning movement direction to looked direction
|
// Responsiveness for turning movement direction to looked direction
|
||||||
#define CONTROL 15.0f
|
#define CONTROL 15.0f
|
||||||
#define CROUCH_HEIGHT 0.0f
|
#define CROUCH_HEIGHT 0.0f
|
||||||
#define STAND_HEIGHT 1.0f
|
#define STAND_HEIGHT 2.0f
|
||||||
#define BOTTOM_HEIGHT 0.5f
|
#define BOTTOM_HEIGHT 0.5f
|
||||||
|
|
||||||
|
// Additional definitions
|
||||||
#define NORMALIZE_INPUT 0
|
#define NORMALIZE_INPUT 0
|
||||||
|
#define ARRLEN(a) (sizeof(a) / sizeof((a)[0]))
|
||||||
|
|
||||||
|
#define FLOOR_EXTENT 25
|
||||||
|
#define TILE_SIZE 5.0f
|
||||||
|
|
||||||
|
#define PLAYER_RADIUS 0.5f
|
||||||
|
#define PLAYER_HEIGHT 1.8f
|
||||||
|
|
||||||
|
enum game_state {
|
||||||
|
STATE_MENU, // 0 by default
|
||||||
|
STATE_GAME, // 1
|
||||||
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Types and Structures Definition
|
//Defining program structure
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
// Body structure
|
typedef struct
|
||||||
typedef struct {
|
{
|
||||||
|
Camera camera; // <- now part of the game state
|
||||||
|
Model model; // you can keep model & its position here too
|
||||||
Vector3 position;
|
Vector3 position;
|
||||||
Vector3 velocity;
|
Vector3 velocity;
|
||||||
Vector3 dir;
|
Vector3 dir;
|
||||||
|
Vector2 sensitivity;
|
||||||
|
Vector2 lookRotation;
|
||||||
|
Vector2 lean;
|
||||||
|
float headTimer;
|
||||||
|
float walkLerp;
|
||||||
|
float headLerp;
|
||||||
bool isGrounded;
|
bool isGrounded;
|
||||||
} Body;
|
int cameraMode;
|
||||||
|
} Player;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
typedef struct
|
||||||
// Global Variables Definition
|
{
|
||||||
//----------------------------------------------------------------------------------
|
int index;
|
||||||
static Vector2 sensitivity = { 0.001f, 0.001f };
|
enum game_state state;
|
||||||
|
int shouldExit;
|
||||||
|
Player player;
|
||||||
|
int nearAcorn;
|
||||||
|
int isJumpscared;
|
||||||
|
float jumpscareTimer;
|
||||||
|
|
||||||
static Body player = { 0 };
|
Sound jumpScare;
|
||||||
static Vector2 lookRotation = { 0 };
|
Music bgm;
|
||||||
static float headTimer = 0.0f;
|
} Master;
|
||||||
static float walkLerp = 0.0f;
|
|
||||||
static float headLerp = STAND_HEIGHT;
|
|
||||||
static Vector2 lean = { 0 };
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Module Functions Declaration
|
// Function Initation
|
||||||
//----------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
static void DrawLevel(void);
|
void Simple_Center_Box(double percent_verticle_gap, double percent_horizontal_gap, int screenWidth, int screenHeight, Color color);
|
||||||
static void UpdateCameraFPS(Camera *camera);
|
void Thick_Center_Box(double percent_verticle_gap, double percent_horizontal_gap, float line_width, int screenWidth, int screenHeight, Color color);
|
||||||
static void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold);
|
void Simple_Menu(const char *array[], int count, int index, int x1, int y1, int total_width, int total_height, int line_thickess, int font_size, Color color, Color alt_color);
|
||||||
|
void UpdateBody(Master* g, float rot, char side, char forward, bool jumpPressed, bool crouchHold);
|
||||||
|
static void UpdateCameraAngle(Master* g);
|
||||||
|
static void DrawLevel(Model mdlTile);
|
||||||
|
static void CenterMouseAndFlush(void);
|
||||||
|
int Random(int x);
|
||||||
|
|
||||||
|
float square(float num);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------
|
||||||
// Program main entry point
|
// Program main entry point
|
||||||
@@ -77,105 +100,363 @@ int main(void)
|
|||||||
{
|
{
|
||||||
// Initialization
|
// Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
const int screenWidth = 1980;
|
|
||||||
const int screenHeight = 1080;
|
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera fps");
|
// Before InitWindow
|
||||||
|
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE | FLAG_MSAA_4X_HINT | FLAG_FULLSCREEN_MODE);
|
||||||
|
|
||||||
// Initialize camera variables
|
InitWindow(0, 0, "raylib");
|
||||||
// NOTE: UpdateCameraFPS() takes care of the rest
|
|
||||||
Camera camera = { 0 };
|
|
||||||
camera.fovy = 60.0f;
|
|
||||||
camera.projection = CAMERA_PERSPECTIVE;
|
|
||||||
camera.position = (Vector3){
|
|
||||||
player.position.x,
|
|
||||||
player.position.y + (BOTTOM_HEIGHT + headLerp),
|
|
||||||
player.position.z,
|
|
||||||
};
|
|
||||||
|
|
||||||
UpdateCameraFPS(&camera); // Update camera parameters
|
int screenWidth = GetRenderWidth();
|
||||||
|
int screenHeight = GetRenderHeight();
|
||||||
|
|
||||||
DisableCursor(); // Limit cursor to relative movement inside the window
|
DisableCursor();
|
||||||
|
CenterMouseAndFlush();
|
||||||
|
|
||||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||||
|
|
||||||
|
//Define menu characteristics
|
||||||
|
const char *menu[] = {"Game", "Option 2", "Exit"};
|
||||||
|
int index_length = ARRLEN(menu);
|
||||||
|
|
||||||
|
//General game state initiation
|
||||||
|
Master g = (Master){0};
|
||||||
|
g.index = 0;
|
||||||
|
g.state = STATE_MENU;
|
||||||
|
g.shouldExit = 0;
|
||||||
|
g.nearAcorn = 0;
|
||||||
|
g.isJumpscared = 0;
|
||||||
|
|
||||||
|
//Define player values
|
||||||
|
g.player.sensitivity = (Vector2){ 0.001f, 0.001f };
|
||||||
|
g.player.lookRotation = (Vector2){ 0 };
|
||||||
|
g.player.headTimer = 0.0f;
|
||||||
|
g.player.walkLerp = 0.0f;
|
||||||
|
g.player.headLerp = STAND_HEIGHT;
|
||||||
|
g.player.lean = (Vector2){ 0 };
|
||||||
|
|
||||||
|
//Define the player camera
|
||||||
|
g.player.camera = (Camera){0};
|
||||||
|
g.player.camera.position = (Vector3){
|
||||||
|
g.player.position.x,
|
||||||
|
g.player.position.y + (BOTTOM_HEIGHT + g.player.headLerp),
|
||||||
|
g.player.position.z,
|
||||||
|
};
|
||||||
|
g.player.camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
|
||||||
|
g.player.camera.fovy = 60.0f;
|
||||||
|
g.player.camera.projection = CAMERA_PERSPECTIVE;
|
||||||
|
g.player.cameraMode = CAMERA_FIRST_PERSON;
|
||||||
|
|
||||||
|
g.player.position.x = 15.0;
|
||||||
|
g.player.position.z = 15.0;
|
||||||
|
|
||||||
|
UpdateCameraAngle(&g);
|
||||||
|
|
||||||
|
// Grass tile setup
|
||||||
|
Texture2D texGrass = LoadTexture("assets/textures/Grass_01.png");
|
||||||
|
GenTextureMipmaps(&texGrass);
|
||||||
|
SetTextureFilter(texGrass, TEXTURE_FILTER_TRILINEAR); // nicer at angles
|
||||||
|
SetTextureWrap(texGrass, TEXTURE_WRAP_REPEAT);
|
||||||
|
|
||||||
|
// A 3D plane (centered at origin) of size tileSize x tileSize
|
||||||
|
const float tileSize = TILE_SIZE;
|
||||||
|
Mesh meshTile = GenMeshPlane(tileSize, tileSize, 1, 1);
|
||||||
|
Model mdlTile = LoadModelFromMesh(meshTile);
|
||||||
|
mdlTile.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = texGrass;
|
||||||
|
|
||||||
|
|
||||||
|
// --- Skybox setup using 6 face images ---
|
||||||
|
Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
|
||||||
|
Model skybox = LoadModelFromMesh(cube);
|
||||||
|
|
||||||
|
// Load skybox shader
|
||||||
|
Shader skyboxShader = LoadShader(
|
||||||
|
"assets/shaders/skybox.vs",
|
||||||
|
"assets/shaders/skybox.fs"
|
||||||
|
);
|
||||||
|
skybox.materials[0].shader = skyboxShader;
|
||||||
|
|
||||||
|
// Load the 6 faces (+X, -X, +Y, -Y, +Z, -Z)
|
||||||
|
const char* facePaths[6] = {
|
||||||
|
"assets/textures/cubemaps/right.png", // +X
|
||||||
|
"assets/textures/cubemaps/left.png", // -X
|
||||||
|
"assets/textures/cubemaps/top.png", // +Y
|
||||||
|
"assets/textures/cubemaps/bottom.png", // -Y
|
||||||
|
"assets/textures/cubemaps/front.png", // +Z
|
||||||
|
"assets/textures/cubemaps/back.png" // -Z
|
||||||
|
};
|
||||||
|
|
||||||
|
Image faces[6];
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
faces[i] = LoadImage(facePaths[i]);
|
||||||
|
// Ensure consistent format
|
||||||
|
if (faces[i].format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
|
||||||
|
ImageFormat(&faces[i], PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify same size
|
||||||
|
int fw = faces[0].width, fh = faces[0].height;
|
||||||
|
for (int i = 1; i < 6; i++) {
|
||||||
|
if (faces[i].width != fw || faces[i].height != fh) {
|
||||||
|
TraceLog(LOG_ERROR, "Cubemap face sizes differ");
|
||||||
|
// (optional) handle error/cleanup here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stitch into a vertical strip (CUBEMAP_LAYOUT_LINE_VERTICAL)
|
||||||
|
Image strip = GenImageColor(fw, fh*6, BLANK);
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
ImageDraw(&strip, faces[i],
|
||||||
|
(Rectangle){0,0,(float)fw,(float)fh},
|
||||||
|
(Rectangle){0,(float)(i*fh),(float)fw,(float)fh},
|
||||||
|
WHITE);
|
||||||
|
UnloadImage(faces[i]); // free each face once copied
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: skip CPU mipmap generation to avoid the buggy path
|
||||||
|
strip.mipmaps = 1;
|
||||||
|
|
||||||
|
// Load cubemap from the vertical strip
|
||||||
|
TextureCubemap cubeTex = LoadTextureCubemap(strip, CUBEMAP_LAYOUT_LINE_VERTICAL);
|
||||||
|
UnloadImage(strip);
|
||||||
|
|
||||||
|
// Assign to material & use a non-mipmap filter (skyboxes don't need mips)
|
||||||
|
skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = cubeTex;
|
||||||
|
SetTextureFilter(cubeTex, TEXTURE_FILTER_BILINEAR);
|
||||||
|
|
||||||
|
// Tell the shader which material map is the environmentMap
|
||||||
|
int mapType = MATERIAL_MAP_CUBEMAP;
|
||||||
|
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "environmentMap"),
|
||||||
|
&mapType, SHADER_UNIFORM_INT);
|
||||||
|
|
||||||
|
// Optional flags (keep as you had)
|
||||||
|
int vflipped = 0, doGamma = 0;
|
||||||
|
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "vflipped"),
|
||||||
|
&vflipped, SHADER_UNIFORM_INT);
|
||||||
|
SetShaderValue(skyboxShader, GetShaderLocation(skyboxShader, "doGamma"),
|
||||||
|
&doGamma, SHADER_UNIFORM_INT);
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Main game loop
|
// Main game loop
|
||||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
//--------------------------------------------------------------------------------------
|
||||||
|
while (!g.shouldExit && !WindowShouldClose()) // Detect window close button or ESC key
|
||||||
{
|
{
|
||||||
// Update
|
// Update
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
switch (g.state)
|
||||||
|
{
|
||||||
|
case STATE_MENU:
|
||||||
|
{
|
||||||
|
if (IsKeyPressed(KEY_UP) && g.index > 0)
|
||||||
|
{
|
||||||
|
g.index -= 1;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_DOWN) && g.index < index_length - 1)
|
||||||
|
{
|
||||||
|
g.index += 1;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IsKeyPressed(KEY_ENTER))
|
||||||
|
{
|
||||||
|
switch (g.index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
g.state = 1;
|
||||||
|
CenterMouseAndFlush();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
g.shouldExit = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case STATE_GAME:
|
||||||
|
{
|
||||||
Vector2 mouseDelta = GetMouseDelta();
|
Vector2 mouseDelta = GetMouseDelta();
|
||||||
lookRotation.x -= mouseDelta.x*sensitivity.x;
|
g.player.lookRotation.x -= mouseDelta.x*g.player.sensitivity.x;
|
||||||
lookRotation.y += mouseDelta.y*sensitivity.y;
|
g.player.lookRotation.y += mouseDelta.y*g.player.sensitivity.y;
|
||||||
|
|
||||||
char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A));
|
char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A));
|
||||||
char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S));
|
char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S));
|
||||||
bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
|
bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
|
||||||
UpdateBody(&player, lookRotation.x, sideway, forward, IsKeyPressed(KEY_SPACE), crouching);
|
UpdateBody(&g, g.player.lookRotation.x, sideway, forward, IsKeyDown(KEY_SPACE), crouching);
|
||||||
|
|
||||||
float delta = GetFrameTime();
|
float delta = GetFrameTime();
|
||||||
headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta);
|
g.player.headLerp = Lerp(g.player.headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta);
|
||||||
camera.position = (Vector3){
|
g.player.camera.position = (Vector3){
|
||||||
player.position.x,
|
g.player.position.x,
|
||||||
player.position.y + (BOTTOM_HEIGHT + headLerp),
|
g.player.position.y + (BOTTOM_HEIGHT + g.player.headLerp),
|
||||||
player.position.z,
|
g.player.position.z,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (player.isGrounded && ((forward != 0) || (sideway != 0)))
|
if (g.player.isGrounded && ((forward != 0) || (sideway != 0)))
|
||||||
{
|
{
|
||||||
headTimer += delta*3.0f;
|
g.player.headTimer += delta*3.0f;
|
||||||
walkLerp = Lerp(walkLerp, 1.0f, 10.0f*delta);
|
g.player.walkLerp = Lerp(g.player.walkLerp, 1.0f, 10.0f*delta);
|
||||||
camera.fovy = Lerp(camera.fovy, 55.0f, 5.0f*delta);
|
g.player.camera.fovy = Lerp(g.player.camera.fovy, 55.0f, 5.0f*delta);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
walkLerp = Lerp(walkLerp, 0.0f, 10.0f*delta);
|
g.player.walkLerp = Lerp(g.player.walkLerp, 0.0f, 10.0f*delta);
|
||||||
camera.fovy = Lerp(camera.fovy, 60.0f, 5.0f*delta);
|
g.player.camera.fovy = Lerp(g.player.camera.fovy, 60.0f, 5.0f*delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
lean.x = Lerp(lean.x, sideway*0.02f, 10.0f*delta);
|
g.player.lean.x = Lerp(g.player.lean.x, sideway*0.02f, 10.0f*delta);
|
||||||
lean.y = Lerp(lean.y, forward*0.015f, 10.0f*delta);
|
g.player.lean.y = Lerp(g.player.lean.y, forward*0.015f, 10.0f*delta);
|
||||||
|
|
||||||
UpdateCameraFPS(&camera);
|
UpdateCameraAngle(&g);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
|
switch (g.state)
|
||||||
|
{
|
||||||
|
case STATE_MENU:
|
||||||
|
{
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
ClearBackground(RAYWHITE);
|
Simple_Menu(menu, ARRLEN(menu), g.index, screenWidth * 0.15, screenHeight * 0.15, 500, screenHeight * 0.7, 10, 50, GREEN, LIGHTGRAY);
|
||||||
|
|
||||||
|
Thick_Center_Box(0.1, 0.1, 10, screenWidth, screenHeight, GREEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STATE_GAME:
|
||||||
|
{
|
||||||
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
|
Vector3 camPos = g.player.camera.position;
|
||||||
|
Vector3 camFwd = Vector3Normalize(Vector3Subtract(g.player.camera.target, camPos));
|
||||||
|
|
||||||
|
// Camera forward (normalize target - position)
|
||||||
|
Vector3 forward = Vector3Normalize(Vector3Subtract(g.player.camera.target, g.player.camera.position));
|
||||||
|
|
||||||
|
BeginMode3D(g.player.camera);
|
||||||
|
|
||||||
|
rlDisableBackfaceCulling();
|
||||||
|
rlDisableDepthMask();
|
||||||
|
|
||||||
|
// keep it centered on the camera
|
||||||
|
skybox.transform = MatrixTranslate(
|
||||||
|
g.player.camera.position.x,
|
||||||
|
g.player.camera.position.y,
|
||||||
|
g.player.camera.position.z
|
||||||
|
);
|
||||||
|
|
||||||
|
DrawModel(skybox, (Vector3){0}, 1.0f, WHITE);
|
||||||
|
|
||||||
|
rlEnableDepthMask();
|
||||||
|
rlEnableBackfaceCulling();
|
||||||
|
|
||||||
|
DrawLevel(mdlTile);
|
||||||
|
|
||||||
BeginMode3D(camera);
|
|
||||||
DrawLevel();
|
|
||||||
EndMode3D();
|
EndMode3D();
|
||||||
|
|
||||||
// Draw info box
|
// Draw info box
|
||||||
DrawRectangle(5, 5, 330, 75, Fade(SKYBLUE, 0.5f));
|
DrawRectangle(5, 5, 330*1.5, 75*1.25, Fade(SKYBLUE, 0.5f));
|
||||||
DrawRectangleLines(5, 5, 330, 75, BLUE);
|
DrawRectangleLines(5, 5, 330*1.5, 75*1.25, BLUE);
|
||||||
|
|
||||||
DrawText("Camera controls:", 15, 15, 10, BLACK);
|
DrawText("Camera controls:", 15, 15, 20, BLACK);
|
||||||
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK);
|
DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 20, BLACK);
|
||||||
DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK);
|
DrawText("- Look around: arrow keys or mouse", 15, 45, 20, BLACK);
|
||||||
DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK);
|
DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ g.player.velocity.x, g.player.velocity.z })), 15, 60, 20, BLACK);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
}
|
};
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------
|
||||||
// De-Initialization
|
// De-Initialization
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
// Detach the texture from the model so UnloadModel won't try to free it.
|
||||||
|
mdlTile.materials[0].maps[MATERIAL_MAP_ALBEDO].texture = (Texture2D){0};
|
||||||
|
|
||||||
|
// Now free resources you loaded:
|
||||||
|
UnloadModel(mdlTile); // frees the model and its mesh/VBOs
|
||||||
|
UnloadTexture(texGrass); // frees the grass texture you loaded
|
||||||
|
|
||||||
CloseWindow(); // Close window and OpenGL context
|
CloseWindow(); // Close window and OpenGL context
|
||||||
//--------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
// Function Definitions
|
||||||
|
//------------------------------------------------------------------------------------
|
||||||
|
//Function creates a simple box outline centered in the screen with a symetric ofset from the screen borders.
|
||||||
|
void Simple_Center_Box(double percent_verticle_gap, double percent_horizontal_gap, int screenWidth, int screenHeight, Color color)
|
||||||
|
{
|
||||||
|
int x1 = percent_horizontal_gap * screenWidth;
|
||||||
|
int y1 = percent_verticle_gap * screenHeight;
|
||||||
|
|
||||||
|
int w = screenWidth * (1 - (2 * percent_horizontal_gap));
|
||||||
|
int h = screenHeight * (1 - (2 * percent_verticle_gap));
|
||||||
|
|
||||||
|
DrawRectangleLines(x1, y1, w, h, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//Function creates a thick walled box outline centered in the screen with a symetric ofset from the screen borders.
|
||||||
// Module Functions Definition
|
void Thick_Center_Box(double percent_verticle_gap, double percent_horizontal_gap, float line_width, int screenWidth, int screenHeight, Color color)
|
||||||
//----------------------------------------------------------------------------------
|
{
|
||||||
// Update body considering current world state
|
int x1 = percent_horizontal_gap * screenWidth;
|
||||||
void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold)
|
int y1 = percent_verticle_gap * screenHeight;
|
||||||
|
|
||||||
|
int w = screenWidth * (1 - (2 * percent_horizontal_gap));
|
||||||
|
int h = screenHeight * (1 - (2 * percent_verticle_gap));
|
||||||
|
|
||||||
|
struct Rectangle rec = {x1,y1,w,h};
|
||||||
|
|
||||||
|
DrawRectangleLinesEx(rec, line_width, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make a simple menu with selection features
|
||||||
|
void Simple_Menu(const char *array[], int count, int index, int x1, int y1, int total_width, int total_height, int line_thickess, int font_size, Color color, Color alt_color)
|
||||||
|
{
|
||||||
|
float height = 0.8f * total_height;
|
||||||
|
int space = 0.2 * total_height / (count - 1);
|
||||||
|
|
||||||
|
struct Rectangle rec = {x1,y1,total_width,(float)height/count};
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
int text_width = MeasureText(array[i], font_size);
|
||||||
|
|
||||||
|
if(i == index)
|
||||||
|
{
|
||||||
|
DrawRectangleLinesEx(rec, line_thickess, alt_color);
|
||||||
|
DrawText(array[i], rec.x + (((total_width - 2.0 * line_thickess) - text_width)/2), rec.y + (height/count/2) - font_size/2.0f, font_size, alt_color);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawRectangleLinesEx(rec, line_thickess, color);
|
||||||
|
DrawText(array[i], rec.x + (((total_width - 2.0 * line_thickess) - text_width)/2), rec.y + (height/count/2) - font_size/2.0f, font_size, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.y = rec.y + (float)height/count + space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateBody(Master* g, float rot, char side, char forward, bool jumpPressed, bool crouchHold)
|
||||||
{
|
{
|
||||||
Vector2 input = (Vector2){ (float)side, (float)-forward };
|
Vector2 input = (Vector2){ (float)side, (float)-forward };
|
||||||
|
|
||||||
@@ -186,12 +467,12 @@ void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed
|
|||||||
|
|
||||||
float delta = GetFrameTime();
|
float delta = GetFrameTime();
|
||||||
|
|
||||||
if (!body->isGrounded) body->velocity.y -= GRAVITY*delta;
|
if (!g->player.isGrounded) g->player.velocity.y -= GRAVITY*delta;
|
||||||
|
|
||||||
if (body->isGrounded && jumpPressed)
|
if (g->player.isGrounded && jumpPressed)
|
||||||
{
|
{
|
||||||
body->velocity.y = JUMP_FORCE;
|
g->player.velocity.y = JUMP_FORCE;
|
||||||
body->isGrounded = false;
|
g->player.isGrounded = false;
|
||||||
|
|
||||||
// Sound can be played at this moment
|
// Sound can be played at this moment
|
||||||
//SetSoundPitch(fxJump, 1.0f + (GetRandomValue(-100, 100)*0.001));
|
//SetSoundPitch(fxJump, 1.0f + (GetRandomValue(-100, 100)*0.001));
|
||||||
@@ -202,91 +483,92 @@ void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed
|
|||||||
Vector3 right = (Vector3){ cos(-rot), 0.f, sin(-rot) };
|
Vector3 right = (Vector3){ cos(-rot), 0.f, sin(-rot) };
|
||||||
|
|
||||||
Vector3 desiredDir = (Vector3){ input.x*right.x + input.y*front.x, 0.0f, input.x*right.z + input.y*front.z, };
|
Vector3 desiredDir = (Vector3){ input.x*right.x + input.y*front.x, 0.0f, input.x*right.z + input.y*front.z, };
|
||||||
body->dir = Vector3Lerp(body->dir, desiredDir, CONTROL*delta);
|
g->player.dir = Vector3Lerp(g->player.dir, desiredDir, CONTROL*delta);
|
||||||
|
|
||||||
float decel = (body->isGrounded ? FRICTION : AIR_DRAG);
|
float decel = (g->player.isGrounded ? FRICTION : AIR_DRAG);
|
||||||
Vector3 hvel = (Vector3){ body->velocity.x*decel, 0.0f, body->velocity.z*decel };
|
Vector3 hvel = (Vector3){ g->player.velocity.x*decel, 0.0f, g->player.velocity.z*decel };
|
||||||
|
|
||||||
float hvelLength = Vector3Length(hvel); // Magnitude
|
float hvelLength = Vector3Length(hvel); // Magnitude
|
||||||
if (hvelLength < (MAX_SPEED*0.01f)) hvel = (Vector3){ 0 };
|
if (hvelLength < (MAX_SPEED*0.01f)) hvel = (Vector3){ 0 };
|
||||||
|
|
||||||
// This is what creates strafing
|
// This is what creates strafing
|
||||||
float speed = Vector3DotProduct(hvel, body->dir);
|
float speed = Vector3DotProduct(hvel, g->player.dir);
|
||||||
|
|
||||||
// Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
|
// Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
|
||||||
// a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
|
// a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
|
||||||
// More info here: https://youtu.be/v3zT3Z5apaM?t=165
|
// More info here: https://youtu.be/v3zT3Z5apaM?t=165
|
||||||
float maxSpeed = (crouchHold? CROUCH_SPEED : MAX_SPEED);
|
float maxSpeed = (crouchHold? CROUCH_SPEED : MAX_SPEED);
|
||||||
float accel = Clamp(maxSpeed - speed, 0.f, MAX_ACCEL*delta);
|
float accel = Clamp(maxSpeed - speed, 0.f, MAX_ACCEL*delta);
|
||||||
hvel.x += body->dir.x*accel;
|
hvel.x += g->player.dir.x*accel;
|
||||||
hvel.z += body->dir.z*accel;
|
hvel.z += g->player.dir.z*accel;
|
||||||
|
|
||||||
body->velocity.x = hvel.x;
|
g->player.velocity.x = hvel.x;
|
||||||
body->velocity.z = hvel.z;
|
g->player.velocity.z = hvel.z;
|
||||||
|
|
||||||
body->position.x += body->velocity.x*delta;
|
g->player.position.x += g->player.velocity.x*delta;
|
||||||
body->position.y += body->velocity.y*delta;
|
g->player.position.y += g->player.velocity.y*delta;
|
||||||
body->position.z += body->velocity.z*delta;
|
g->player.position.z += g->player.velocity.z*delta;
|
||||||
|
|
||||||
// Fancy collision system against the floor
|
// Fancy collision system against the floor
|
||||||
if (body->position.y <= 0.0f)
|
if (g->player.position.y <= 0.0f)
|
||||||
{
|
{
|
||||||
body->position.y = 0.0f;
|
g->player.position.y = 0.0f;
|
||||||
body->velocity.y = 0.0f;
|
g->player.velocity.y = 0.0f;
|
||||||
body->isGrounded = true; // Enable jumping
|
g->player.isGrounded = true; // Enable jumping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update camera for FPS behaviour
|
// Update camera
|
||||||
static void UpdateCameraFPS(Camera *camera)
|
static void UpdateCameraAngle(Master* g)
|
||||||
{
|
{
|
||||||
const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f };
|
const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f };
|
||||||
const Vector3 targetOffset = (Vector3){ 0.0f, 0.0f, -1.0f };
|
const Vector3 targetOffset = (Vector3){ 0.0f, 0.0f, -1.0f };
|
||||||
|
|
||||||
// Left and right
|
// Left and right
|
||||||
Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x);
|
Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, g->player.lookRotation.x);
|
||||||
|
|
||||||
// Clamp view up
|
// Clamp view up
|
||||||
float maxAngleUp = Vector3Angle(up, yaw);
|
float maxAngleUp = Vector3Angle(up, yaw);
|
||||||
maxAngleUp -= 0.001f; // Avoid numerical errors
|
maxAngleUp -= 0.001f; // Avoid numerical errors
|
||||||
if ( -(lookRotation.y) > maxAngleUp) { lookRotation.y = -maxAngleUp; }
|
if ( -(g->player.lookRotation.y) > maxAngleUp) { g->player.lookRotation.y = -maxAngleUp; }
|
||||||
|
|
||||||
// Clamp view down
|
// Clamp view down
|
||||||
float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
|
float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
|
||||||
maxAngleDown *= -1.0f; // Downwards angle is negative
|
maxAngleDown *= -1.0f; // Downwards angle is negative
|
||||||
maxAngleDown += 0.001f; // Avoid numerical errors
|
maxAngleDown += 0.001f; // Avoid numerical errors
|
||||||
if ( -(lookRotation.y) < maxAngleDown) { lookRotation.y = -maxAngleDown; }
|
if ( -(g->player.lookRotation.y) < maxAngleDown) { g->player.lookRotation.y = -maxAngleDown; }
|
||||||
|
|
||||||
// Up and down
|
// Up and down
|
||||||
Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));
|
Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));
|
||||||
|
|
||||||
// Rotate view vector around right axis
|
// Rotate view vector around right axis
|
||||||
float pitchAngle = -lookRotation.y - lean.y;
|
float pitchAngle = -g->player.lookRotation.y -
|
||||||
pitchAngle = Clamp(pitchAngle, -PI/2 + 0.0001f, PI/2 - 0.0001f); // Clamp angle so it doesn't go past straight up or straight down
|
g->player.lean.y;
|
||||||
|
pitchAngle = Clamp(pitchAngle, -PI / 2 + 0.0001f, PI / 2 - 0.0001f); // Clamp angle so it doesn't go past straight up or straight down
|
||||||
Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, pitchAngle);
|
Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, pitchAngle);
|
||||||
|
|
||||||
// Head animation
|
// Head animation
|
||||||
// Rotate up direction around forward axis
|
// Rotate up direction around forward axis
|
||||||
float headSin = sin(headTimer*PI);
|
float headSin = sin(g->player.headTimer*PI);
|
||||||
float headCos = cos(headTimer*PI);
|
float headCos = cos(g->player.headTimer*PI);
|
||||||
const float stepRotation = 0.01f;
|
const float stepRotation = 0.005f;
|
||||||
camera->up = Vector3RotateByAxisAngle(up, pitch, headSin*stepRotation + lean.x);
|
g->player.camera.up = Vector3RotateByAxisAngle(up, pitch, headSin*stepRotation + g->player.lean.x);
|
||||||
|
|
||||||
// Camera BOB
|
// Camera BOB
|
||||||
const float bobSide = 0.1f;
|
const float bobSide = 0.01f;
|
||||||
const float bobUp = 0.15f;
|
const float bobUp = 0.01f;
|
||||||
Vector3 bobbing = Vector3Scale(right, headSin*bobSide);
|
Vector3 bobbing = Vector3Scale(right, headSin*bobSide);
|
||||||
bobbing.y = fabsf(headCos*bobUp);
|
bobbing.y = fabsf(headCos*bobUp);
|
||||||
|
|
||||||
camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp));
|
g->player.camera.position = Vector3Add(g->player.camera.position, Vector3Scale(bobbing, g->player.walkLerp));
|
||||||
camera->target = Vector3Add(camera->position, pitch);
|
g->player.camera.target = Vector3Add(g->player.camera.position, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw game level
|
// Draw game level
|
||||||
static void DrawLevel(void)
|
static void DrawLevel(Model mdlTile)
|
||||||
{
|
{
|
||||||
const int floorExtent = 25;
|
const int floorExtent = FLOOR_EXTENT;
|
||||||
const float tileSize = 5.0f;
|
const float tileSize = TILE_SIZE;
|
||||||
const Color tileColor1 = (Color){ 150, 200, 200, 255 };
|
const Color tileColor1 = (Color){ 150, 200, 200, 255 };
|
||||||
|
|
||||||
// Floor tiles
|
// Floor tiles
|
||||||
@@ -294,6 +576,7 @@ static void DrawLevel(void)
|
|||||||
{
|
{
|
||||||
for (int x = -floorExtent; x < floorExtent; x++)
|
for (int x = -floorExtent; x < floorExtent; x++)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
if ((y & 1) && (x & 1))
|
if ((y & 1) && (x & 1))
|
||||||
{
|
{
|
||||||
DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, tileColor1);
|
DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, tileColor1);
|
||||||
@@ -302,9 +585,15 @@ static void DrawLevel(void)
|
|||||||
{
|
{
|
||||||
DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, LIGHTGRAY);
|
DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, LIGHTGRAY);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
Vector3 pos = (Vector3){ x*tileSize, 0.0f, y*tileSize };
|
||||||
|
// mdlTile must be accessible here: make it global/static or pass it in
|
||||||
|
DrawModel(mdlTile, pos, 1.0f, WHITE);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
const Vector3 towerSize = (Vector3){ 16.0f, 32.0f, 16.0f };
|
const Vector3 towerSize = (Vector3){ 16.0f, 32.0f, 16.0f };
|
||||||
const Color towerColor = (Color){ 150, 200, 200, 255 };
|
const Color towerColor = (Color){ 150, 200, 200, 255 };
|
||||||
|
|
||||||
@@ -326,4 +615,25 @@ static void DrawLevel(void)
|
|||||||
|
|
||||||
// Red sun
|
// Red sun
|
||||||
DrawSphere((Vector3){ 300.0f, 300.0f, 0.0f }, 100.0f, (Color){ 255, 0, 0, 255 });
|
DrawSphere((Vector3){ 300.0f, 300.0f, 0.0f }, 100.0f, (Color){ 255, 0, 0, 255 });
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//fix mouse
|
||||||
|
static void CenterMouseAndFlush()
|
||||||
|
{
|
||||||
|
SetMousePosition(GetRenderWidth()/2, GetRenderHeight()/2);
|
||||||
|
(void)GetMouseDelta();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Random number gen
|
||||||
|
int Random(int x)
|
||||||
|
{
|
||||||
|
int r = (rand() % x) + 1; // 1-40
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
float square(float num)
|
||||||
|
{
|
||||||
|
float out = num * num;
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||