This commit is contained in:
Tomas G.
2026-03-13 08:16:01 +01:00
commit 23bb2d2749
104 changed files with 205490 additions and 0 deletions

3
projekt_win/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/*
boat_sim
boat_sim.exe

View File

@ -0,0 +1,16 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/include"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-clang-x64"
}
],
"version": 4
}

38
projekt_win/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,38 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/boat_sim.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"preLaunchTask": "Make",
"windows": {
"MIMode": "gdb",
"miDebuggerPath": "${workspaceFolder}/../mingw64/bin/gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Reduce gdb verbosity",
"text": "set print thread-events on",
"ignoreFailures": true
}
],
"logging": {
"trace": false,
"traceResponse": false,
"engineLogging": false
}
}
}
]
}

68
projekt_win/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,68 @@
{
"files.associations": {
"iostream": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"forward_list": "cpp",
"list": "cpp",
"unordered_set": "cpp",
"optional": "cpp",
"typeindex": "cpp"
}
}

40
projekt_win/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,40 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Make",
"type": "shell",
"command": "${workspaceFolder}/../mingw64/bin/mingw32-make.exe", // Or "mingw32-make" if you are using MinGW on Windows and "make" is not in your PATH
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Clean Object Files (Windows)",
"type": "shell",
"command": "del",
"args": [
"build/*.o"
],
"problemMatcher": [],
"group": "cleanup",
"detail": "Deletes all object files (*.o) in the current directory"
},
{
"label": "Run",
"type": "shell",
"command": "${workspaceFolder}/../mingw64/bin/mingw32-make.exe run",
"options": {
"cwd": "${workspaceFolder}"
},
"dependsOn": "Make"
}
]
}

65
projekt_win/Makefile Normal file
View File

@ -0,0 +1,65 @@
# Makefile for boat_sim project
# Compiler and flags
BIN_PATH = ../mingw64/bin/
CXX = $(BIN_PATH)g++
AS = $(BIN_PATH)as
CXXFLAGS = -Wall -g -mavx -msse4 -DGLM_ENABLE_EXPERIMENTAL -DUSE_SOIL
INC_DIRS = ../include include
INCLUDES = $(addprefix -I,$(INC_DIRS))
LIBS = -lfreeglut -lglew32 -lSOIL -lopengl32 -lglu32
LIB_DIR = ..\libs
LDFLAGS = -L$(LIB_DIR)
# Directories
SRC_DIR = src
BUILD_DIR = build
# Source files - includes .cpp and .s from src and src_project
SOURCES = $(wildcard $(SRC_DIR)/*.cpp) $(wildcard $(SRC_DIR)/src_project/*.cpp) $(wildcard $(SRC_DIR)/*.s) $(wildcard $(SRC_DIR)/src_project/*.s)
# Object files in build directory - creates corresponding object file paths in build dir
OBJECTS = $(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(filter $(SRC_DIR)/%.cpp,$(SOURCES)))
OBJECTS += $(patsubst $(SRC_DIR)/%.s,$(BUILD_DIR)/%.o,$(filter $(SRC_DIR)/%.s,$(SOURCES)))
EXECUTABLE = boat_sim
EXECUTABLE_PATH = $(EXECUTABLE)
# Default target
all: $(EXECUTABLE_PATH)
# Rule to create the executable in build dir
$(EXECUTABLE_PATH): $(OBJECTS)
$(CXX) -o $@ $^ $(LDFLAGS) $(LIBS)
# General rule to compile/assemble source files to object files in build dir
# For .cpp files in src directory
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
# For .s files in src directory - USING 'as' directly
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.s
$(CXX) -g -c $< -o $@
# Clean target
clean:
rm -rf $(BUILD_DIR)
# Debug build target
debug: CXXFLAGS += -DDEBUG -g
# Run target
run: all
./$(EXECUTABLE_PATH)
# Run debug target
rundebug: debug
gdb ./$(EXECUTABLE_PATH)
.PHONY: all clean debug run rundebug

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 KiB

View File

@ -0,0 +1,28 @@
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 08.06.2011 15:26:00
newmtl _10634_SpeedBoat_v01_LOD310634_SpeedBoat_v01
Ns 53.0000
Ni 1.5000
d 1.0000
Tr 0.0000
Tf 1.0000 1.0000 1.0000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.2000 0.2000 0.2000
Ke 0.0000 0.0000 0.0000
map_Ka boat.jpg
map_Kd boat.jpg
newmtl glass
Ns 80.0000
Ni 1.5000
d 0.2000
Tr 0.8000
Tf 0.2000 0.2000 0.2000
illum 2
Ka 0.5882 0.5882 0.5882
Kd 0.5882 0.5882 0.5882
Ks 0.5000 0.5000 0.5000
Ke 0.0000 0.0000 0.0000

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
#version 330 core
in vec4 vColor;
in vec2 vLocal;
in vec4 vShape;
in float vHeightFactor;
out vec4 FragColor;
float hash12(vec2 p) {
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
void main() {
float c = cos(vShape.z);
float s = sin(vShape.z);
mat2 rot = mat2(c, -s, s, c);
vec2 p = rot * vLocal;
p /= max(vShape.xy, vec2(0.3));
float angle = atan(p.y, p.x);
float radius = length(p);
float wave = 1.0 + vShape.w * (0.45 * sin(3.0 * angle) + 0.35 * cos(5.0 * angle));
float envelope = radius / max(wave, 0.35);
if (envelope > 1.0) {
discard;
}
float shell = smoothstep(1.0, 0.06, envelope);
float core = smoothstep(0.66, 0.0, envelope);
float edge = smoothstep(0.98, 0.55, envelope) * (1.0 - core);
float noise1 = hash12(p * 6.0 + vec2(vShape.w * 13.7, vShape.z));
float noise2 = hash12(p * 11.0 + vec2(vShape.z * 0.3, vShape.w * 19.1));
float densityNoise = mix(noise1, noise2, 0.5);
float density = mix(0.70, 1.18, densityNoise);
vec3 lightDir = normalize(vec3(-0.35, 0.68, 0.64));
vec3 normal = normalize(vec3(p * 0.9, sqrt(max(0.0, 1.0 - clamp(envelope * envelope, 0.0, 1.0)))));
float diff = max(dot(normal, lightDir), 0.0);
float ashLift = mix(0.92, 1.10, vHeightFactor);
vec3 smokeLit = vColor.rgb * (0.52 + 0.30 * diff) * ashLift;
vec3 emberTint = vec3(0.95, 0.40, 0.08) * core * (1.0 - vHeightFactor) * 0.35;
vec3 rimCool = vec3(0.10, 0.11, 0.12) * edge * 0.30;
float alpha = vColor.a * shell * density;
alpha *= mix(1.05, 0.70, vHeightFactor);
alpha = clamp(alpha, 0.0, 1.0);
if (alpha < 0.01) {
discard;
}
FragColor = vec4(smokeLit + emberTint - rimCool, alpha);
}

View File

@ -0,0 +1,38 @@
#version 330 core
layout (location = 0) in vec2 aCorner;
layout (location = 1) in vec3 aCenter;
layout (location = 2) in vec3 aRight;
layout (location = 3) in vec3 aUp;
layout (location = 4) in float aHalfSize;
layout (location = 5) in vec4 aColor;
layout (location = 6) in vec4 aShape;
out vec4 vColor;
out vec2 vLocal;
out vec4 vShape;
out float vHeightFactor;
uniform mat4 view;
uniform mat4 projection;
uniform float uTime;
void main() {
float cornerTop = clamp((aCorner.y + 1.0) * 0.5, 0.0, 1.0);
float heightFactor = clamp(aCenter.y * 0.055, 0.0, 1.0);
float swayA = sin(uTime * 0.48 + aCenter.y * 0.16 + aShape.w * 13.0);
float swayB = cos(uTime * 0.37 + aCenter.x * 0.09 + aShape.w * 19.0);
float shear = (0.06 + 0.28 * heightFactor) * cornerTop;
vec3 dynamicOffset = aRight * (swayA * shear * aHalfSize) +
aUp * (swayB * 0.08 * shear * aHalfSize);
vec3 worldPos = aCenter + aRight * (aCorner.x * aHalfSize) + aUp * (aCorner.y * aHalfSize) + dynamicOffset;
gl_Position = projection * view * vec4(worldPos, 1.0);
vColor = aColor;
vLocal = aCorner;
vShape = aShape;
vHeightFactor = heightFactor;
}

View File

@ -0,0 +1,54 @@
#version 330 core
// Fragment Shader for Ocean Rendering with Texture Normals
in vec3 NormalInterp; // **IN variable declaration - crucial!**
// Input from Vertex Shader
in vec2 TexCoord;
in vec3 FragPosWorld;
in vec3 NormalWorld;
// Output fragment color
out vec4 FragColor;
// Uniforms (textures, lighting parameters, camera position)
uniform sampler2D oceanTexture; // Sampler for ocean color texture
uniform sampler2D normalMap; // Sampler for normal map texture
uniform vec3 lightDir; // Directional light direction (world space)
uniform vec3 lightColor; // Light color
uniform vec3 viewPosWorld; // Camera position in world space
void main() {
// 1. Sample textures
vec3 albedoColor = texture(oceanTexture, TexCoord).rgb; // Sample ocean color texture
//vec3 normalMapSample = texture(normalMap, TexCoord).rgb; // Sample normal map
// 2. Unpack and transform normal from normal map (Tangent Space to World Space - Simplified)
//vec3 normalMapNormal = normalize(normalMapSample * 2.0 - 1.0); // Unpack from [0, 1] to [-1, 1] and normalize
vec3 normalWorld = normalize(NormalInterp); // Get interpolated geometric normal from vertex shader
// **Basic Tangent Space Normal Mapping Approximation:**
// For truly correct tangent-space normal mapping, you'd need to construct a proper TBN matrix.
vec3 finalNormal = normalize(normalWorld ); // **Blend/Perturb geometric normal with texture normal**
// 3. Lighting calculations (Blinn-Phong example)
vec3 lightDirNorm = normalize(lightDir);
vec3 viewDirNorm = normalize(viewPosWorld - FragPosWorld);
vec3 reflectDir = reflect(-lightDirNorm, finalNormal);
// Diffuse component
float diff = max(dot(finalNormal, lightDirNorm), 0.0);
vec3 diffuse = diff * lightColor * albedoColor;
// Specular component (Blinn-Phong)
float spec = pow(max(dot(viewDirNorm, reflectDir), 0.0), 32.0);
vec3 specular = spec * lightColor * vec3(0.8); // Example specular color
// Ambient component
vec3 ambient = 0.3 * lightColor * albedoColor; // Example ambient
// 4. Combine lighting components for final color
vec3 finalColor = ambient + diffuse + specular;
FragColor = vec4(finalColor, 1.0); // Output final fragment color
}

View File

@ -0,0 +1,34 @@
#version 330 core
// Vertex Shader for Ocean Rendering
// Input vertex attributes (from VBOs)
layout (location = 0) in vec3 aPos; // Vertex position (from Ocean::vertices VBO)
layout (location = 1) in vec3 aNormal; // Vertex normal (from Ocean::normals VBO)
layout (location = 2) in vec2 aTexCoord; // Texture coordinates (from Ocean::texCoords VBO)
// Output to Fragment Shader
out vec2 TexCoord;
out vec3 FragPosWorld; // Fragment position in world space
out vec3 NormalWorld; // Normal vector in world space
out vec3 NormalInterp; // **OUT variable declaration - crucial!**
// Uniforms (matrices, light parameters, etc.)
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normalMatrix; // Normal matrix for correct normal transformation
void main() {
// 1. Transform vertex position to clip space
gl_Position = projection * view * vec4(aPos, 1.0);
// 2. Pass texture coordinates to fragment shader
TexCoord = aTexCoord;
// 3. Calculate fragment position in world space
FragPosWorld = vec3(model * vec4(aPos, 1.0));
// 4. Transform normal vector to world space using the Normal Matrix
NormalWorld = normalize(normalMatrix * aNormal);
}

View File

@ -0,0 +1,61 @@
#version 330 core
// Fragment Shader for Terrain Rendering
in vec2 TexCoord;
in vec3 FragPosWorld;
in vec3 NormalWorld;
out vec4 FragColor;
uniform sampler2D terrainTexture; // Sampler for terrain color texture (no normal map for now)
uniform sampler2D heightMapTexture; // **New: Sampler for heightmap texture**
uniform vec3 lightDir;
uniform vec3 lightColor;
uniform vec3 viewPosWorld;
uniform vec3 volcanoCenterWorld;
uniform float volcanoRadius;
uniform float volcanoHeat;
void main() {
// 1. Sample textures
vec3 albedoColor = texture(terrainTexture, TexCoord * 8.0).rgb;
float heightValue = texture(heightMapTexture, TexCoord).r;
// 2. Lighting calculations (Blinn-Phong - similar to ocean shader)
vec3 normal = normalize(NormalWorld); // Use geometric normal for terrain
vec3 lightDirNorm = normalize(lightDir);
vec3 viewDirNorm = normalize(viewPosWorld - FragPosWorld);
vec3 reflectDir = reflect(-lightDirNorm, normal);
// Diffuse component
float diff = max(dot(normal, lightDirNorm), 0.0);
vec3 diffuse = diff * lightColor * albedoColor;
// Specular component
float spec = pow(max(dot(viewDirNorm, reflectDir), 0.0), 16.0); // Adjust shininess (exponent)
vec3 specular = spec * lightColor * vec3(0.3); // Example specular color - less shiny than ocean
// Ambient component
vec3 ambient = 0.2 * lightColor * albedoColor;
vec3 lowColor = vec3(0.78, 0.73, 0.64);
vec3 highColor = vec3(0.42, 0.36, 0.30);
vec3 heightTint = mix(lowColor, highColor, heightValue);
// 4. Combine lighting components for final color
vec3 litTerrain = (ambient + diffuse + specular) * albedoColor * heightTint;
float distToVolcano = length(FragPosWorld.xz - volcanoCenterWorld.xz);
float heightFromVolcanoBase = max(FragPosWorld.y - volcanoCenterWorld.y, 0.0);
float coneHeightMask = smoothstep(1.5, 8.0, heightFromVolcanoBase);
float lavaMask = 1.0 - smoothstep(volcanoRadius * 0.15, volcanoRadius, distToVolcano);
lavaMask = pow(clamp(lavaMask, 0.0, 1.0), 2.4);
lavaMask *= coneHeightMask;
lavaMask *= clamp(volcanoHeat, 0.0, 1.0);
vec3 lavaColor = mix(vec3(0.95, 0.18, 0.03), vec3(1.0, 0.75, 0.2), heightValue);
vec3 finalColor = mix(litTerrain, litTerrain * 0.78 + lavaColor * 0.72, lavaMask);
FragColor = vec4(finalColor, 1.0);
}

View File

@ -0,0 +1,22 @@
#version 330 core
// Vertex Shader for Terrain Rendering
layout (location = 0) in vec3 aPos; // Vertex position
layout (location = 1) in vec3 aNormal; // Vertex normal
layout (location = 2) in vec2 aTexCoord; // Texture coordinates
out vec2 TexCoord;
out vec3 FragPosWorld;
out vec3 NormalWorld;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normalMatrix;
void main() {
gl_Position = projection * view * vec4(aPos, 1.0);
TexCoord = aTexCoord;
FragPosWorld = vec3(model * vec4(aPos, 1.0));
NormalWorld = normalize(normalMatrix * aNormal);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
projekt_win/glew32.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,80 @@
#ifndef BOAT_H
#define BOAT_H
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <vector>
#include "Input.h"
#include "Ocean.h"
#include "Terrain.h"
#include <string>
#include <tiny_obj_loader.h> // Include tinyobjloader
class Boat {
public:
Boat();
~Boat();
bool init(const char* modelPath, const char* texturePath); // Pass model and texture paths
void cleanup();
void update(const Input& input, const Ocean& ocean, const Terrain& terrain, float deltaTime);
glm::vec3 getPosition() const { return position; }
glm::quat getRotation() const { return rotation; }
float getSpeed() const { return speed; }
bool hasTerrainCollision() const { return lastCollisionWithTerrain; }
bool canDisembark() const { return lastCollisionAllowsDisembark; }
glm::vec3 getDisembarkPosition() const { return lastCollisionPoint; }
void clearCollisionState();
glm::vec3 getBoundingBoxMin() const { return boundingBoxMin; } // **Getter for boundingBoxMin**
glm::vec3 getBoundingBoxMax() const { return boundingBoxMax; } // **Getter for boundingBoxMax**
// Getters for model data to pass to Renderer
const std::vector<glm::vec3>& getVertices() const { return vertices; }
const std::vector<glm::vec3>& getNormals() const { return normals; }
const std::vector<glm::vec2>& getTexCoords() const { return texCoords; }
const std::string& getTexturePath() const { return boatTexturePath; } // Getter for texture path
const std::vector<tinyobj::material_t>& getMaterials() const { return materials; } // Getter for materials
const std::vector<int>& getMaterialIndices() const { return materialIndices; } // Getter for materials
// New: Getter and Setter for boatScale
float getScale() const { return boatScale; }
void setScale(float scale) { boatScale = scale; }
private:
glm::vec3 position;
glm::quat rotation;
float speed;
float steeringSpeed;
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> texCoords;
std::vector<int> materialIndices; // New: Store material indices per vertex
std::vector<tinyobj::material_t> materials; // New: Store materials
void handleInput(const Input& input, const Ocean& ocean, const Terrain& terrain, float deltaTime);
void applyWaveMotion(const Ocean& ocean);
bool collidesWithTerrain(const Terrain& terrain, const Ocean& ocean, const glm::vec3& candidatePosition, glm::vec3* collisionPoint) const;
bool isGradientLowForDisembark(const Terrain& terrain, const glm::vec3& collisionPoint) const;
float getCollisionRadius() const;
bool loadModel(const char* path); // Function to load OBJ model
std::string boatTexturePath; // Store texture path for Renderer to access
glm::vec3 boundingBoxMin;
glm::vec3 boundingBoxMax;
int getGridIndex(int x, int z) const; // Helper function to get 1D index from 2D grid indices
float boatScale;
bool speedUpKeyWasDown;
bool speedDownKeyWasDown;
bool lastCollisionWithTerrain;
bool lastCollisionAllowsDisembark;
glm::vec3 lastCollisionPoint;
};
#endif // BOAT_H

View File

@ -0,0 +1,76 @@
// Camera.h
#ifndef CAMERA_H
#define CAMERA_H
#include <glm/glm.hpp>
#include "Input.h" // Optional Shader class
#include "Terrain.h"
class Camera {
public:
enum class Mode {
BoatOrbit,
Fly,
OnFoot
};
Camera();
~Camera();
void init();
void update(const Input& input, const glm::vec3& boatPosition, const Terrain& terrain, float deltaTime);
void lookAt() const;
void toggleFlyMode();
void startOnFoot(const glm::vec3& worldPosition, const Terrain& terrain);
void setBoatMode();
bool isFlyMode() const { return mode == Mode::Fly; }
bool isBoatMode() const { return mode == Mode::BoatOrbit; }
bool isOnFootMode() const { return mode == Mode::OnFoot; }
const char* getModeName() const;
void setAspectRatio(float ratio) { aspectRatio = ratio; }
glm::vec3 getPosition() const { return position; } // Public getter for position
glm::mat4 getViewMatrix() const; // **Declare getViewMatrix() method**
// New: Camera Rotation Control
void handleMouseInput(const Input& input, float deltaTime);
void rotateYaw(float angle);
void rotatePitch(float angle);
private:
glm::vec3 position;
glm::vec3 target; // Point to look at
glm::vec3 up;
float aspectRatio;
float fov;
float nearPlane;
float farPlane;
// New: Camera Rotation State
float yawAngle=0.0f;
float pitchAngle=0.0f;
float rotationSpeed;
float orbitDistance;
float minOrbitDistance;
float maxOrbitDistance;
float targetHeightOffset;
Mode mode;
float flyMoveSpeed;
float flyVerticalSpeed;
float walkMoveSpeed;
float maxWalkGradient;
float onFootEyeHeight;
glm::vec2 onFootVelocityXZ;
float onFootVerticalVelocity;
bool onFootGrounded;
bool jumpKeyWasDown;
float walkAcceleration;
float walkFriction;
float jumpSpeed;
float gravity;
float slideStartGradient;
float slideAcceleration;
float maxSlideSpeed;
};
#endif // CAMERA_H

View File

@ -0,0 +1,54 @@
// Game.h
#ifndef GAME_H
#define GAME_H
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "Renderer.h"
#include "Input.h"
#include "Ocean.h"
#include "Boat.h"
#include "Camera.h"
#include <cstdio>
#include <iostream>
#include "Terrain.h" // Include Terrain header
#include "VolcanoSim.h"
#include <vector>
class Game {
public:
Game();
~Game();
bool init(int argc, char** argv);
void run();
void cleanup();
private:
Renderer renderer;
std::vector<VolcanoSimulation> volcanoes;
Input input;
Ocean ocean;
Boat boat;
Camera camera;
Terrain terrain; // Add Terrain member
bool flyToggleKeyWasDown;
bool interactKeyWasDown;
float boardBoatCooldown;
static void displayCallback();
static void reshapeCallback(int width, int height);
static void keyboardCallback(unsigned char key, int x, int y);
static void keyboardUpCallback(unsigned char key, int x, int y);
static void specialCallback(int key, int x, int y);
static void specialUpCallback(int key, int x, int y);
static void mouseCallback(int button, int state, int x, int y);
static void motionCallback(int x, int y);
static void passiveMotionCallback(int x, int y);
static void timerCallback(int value);
static void updateGame();
static Game* instance; // Singleton for callbacks to access game instance
};
#endif // GAME_H

View File

@ -0,0 +1,48 @@
// Input.h
#ifndef INPUT_H
#define INPUT_H
#include <set>
class Input {
public:
Input();
~Input();
void init();
void update();
void handleKeyPress(unsigned char key);
void handleKeyRelease(unsigned char key);
void handleSpecialKeyPress(int key);
void handleSpecialKeyRelease(int key);
void handleMouseClick(int button, int state, int x, int y);
void handleMouseMove(int x, int y);
bool isKeyDown(unsigned char key) const;
bool isSpecialKeyDown(int key) const;
// Mouse input methods if needed
// New: Mouse Input Methods
bool isMouseButtonDown(int button) const { return mouseButtonsDown[button]; }
int getMouseX() const { return mouseX; }
int getMouseY() const { return mouseY; }
int getMouseDeltaX() const { return mouseDeltaX; }
int getMouseDeltaY() const { return mouseDeltaY; }
int getMouseWheelDelta() const { return mouseWheelDelta; }
private:
std::set<unsigned char> keysDown;
std::set<int> specialKeysDown;
// Mouse state variables if needed
bool mouseButtonsDown[5]; // Up to 5 mouse buttons (GLUT_LEFT_BUTTON, etc.)
int mouseX, mouseY; // Current mouse position
int lastMouseX, lastMouseY; // Last frame's mouse position
int mouseDeltaX, mouseDeltaY; // Mouse movement delta since last frame
int mouseWheelDelta;
int mouseWheelDeltaPending;
};
#endif // INPUT_H

102
projekt_win/include/Ocean.h Normal file
View File

@ -0,0 +1,102 @@
// Ocean.h
#ifndef OCEAN_H
#define OCEAN_H
#include <glm/glm.hpp>
#include <vector>
#include <GL/glew.h> // Include GLEW for OpenGL types like GLuint
#include "utils.h" // **Include utils.h to use checkGLError**
#include <immintrin.h>
#include <x86intrin.h>
#define ASM_TYPE CLEAR_ASM
#define INTRINSIC 1
#define CLEAR_ASM 2
// Structure to hold parameters for a single Gerstner wave component
struct GerstnerWave {
float amplitude; // Wave amplitude (height)
float wavelength; // Wavelength (distance between crests)
float speed; // Wave speed
glm::vec2 direction; // Wave direction (normalized 2D vector in XZ plane)
float phase; // Phase offset
};
struct SimdSGerstnerWave {
float amplitude; // Wave amplitude (height)
float wavelength; // Wavelength (distance between crests)
float speed; // Wave speed
float dir_x;
float dir_y;
float phase; // Phase offset
float k; // k
float w; // w
};
class Ocean {
public:
Ocean(int gridSize);
~Ocean();
bool init();
void cleanup();
void update(float deltaTime);
glm::vec3 getVertex(int x, int z) const;
float getWaveHeight(float x, float z, float time) const;
glm::vec3 getWaveNormal(float x, float z, float time) const; // Calculate wave normal
float getSeaLevelOffset() const { return seaLevelOffset; }
void setGridSize(int newGridSize); // Setter function
int getGridSize() const { return gridSize; }
float getGridSpacing() const { return gridSpacing; }
GLuint getVAO() const; // Get the Vertex Array Object ID
GLuint getIndexCount() const; // Get the number of indices for rendering
float time;
private:
int gridSize;
float gridSpacing;
std::vector<glm::vec3> vertices; // Store vertices for optimization (optional)
std::vector<GerstnerWave> gerstnerWaves; // Vector to store multiple Gerstner wave components
// Wave parameters (adjustable)
float amplitude;
float wavelength;
float frequency;
glm::vec2 direction; // Wave direction
float phase; // Initial phase
GLuint vertexBufferID; // VBO ID for vertex positions
GLuint normalBufferID; // VBO ID for vertex normals
GLuint texCoordBufferID; // VBO ID for texture coordinates
GLuint indexBufferID; // IBO ID for indices
GLuint vaoID; // VAO ID (Vertex Array Object)
unsigned int indexCount; // Number of indices for rendering
std::vector<float> originalWorldX; // Vector to store original undisplaced World X coordinates
std::vector<float> originalWorldZ; // Vector to store original undisplaced World Z coordinates
float baseAmplitude; // Base (maximum) wave amplitude for periodic modulation
float seaLevelOffset;
void generateGrid();
void createBuffers(); // Create and populate VBOs and IBO
void updateBuffers(const std::vector<glm::vec3>& updatedVertices, const std::vector<glm::vec3>& updatedNormals); // Update VBO data
//void updateVertices(std::vector<glm::vec3> * updatedVertices, std::vector<glm::vec3> * updatedNormals, float time); // Update vertex Y positions based on wave function
void updateVertices(std::vector<glm::vec3> * updatedVertices, std::vector<glm::vec3> * updatedNormals, float * originalWorldX_, float * originalWorldZ_, int _grid_size, float time);
int getGridIndex(int x, int z) const; // Helper function to get 1D index from 2D grid indices
float getGerstnerWaveHeight(const GerstnerWave& wave, float x, float z, float time) const; // Calculate height for a single Gerstner wave
glm::vec3 getGerstnerWaveDisplacement(const GerstnerWave& wave, float x, float z, float time) const; // Calculate horizontal displacement for a Gerstner wave
};
#endif // OCEAN_H

View File

@ -0,0 +1,64 @@
// Renderer.h
#ifndef RENDERER_H
#define RENDERER_H
#include <GL/glew.h>
#include <GL/freeglut.h>
#include "Ocean.h"
#include "Boat.h"
#include "Camera.h"
#include "Shader.h" // Optional Shader class
#include "Terrain.h" // Include Terrain header
#include "VolcanoSim.h"
#include <vector>
#include <string>
#define SHOW_GRID 0
#define SHOW_NORM 0
#define SHOW_BOUDING_BOX 1
class Renderer {
public:
Renderer();
~Renderer();
GLuint oceanTextureID;
GLuint boatTextureID;
GLuint terrainTextureID; // Texture ID for terrain
GLuint heightMapTextureID; // **Add heightMapTextureID**
bool init();
void cleanup();
void renderScene(const Ocean &ocean, const Boat &boat, const Camera &camera, const Terrain &Terrain, const std::vector<VolcanoSimulation>& volcanoes);
void reshape(int width, int height);
void drawOcean(const Ocean& ocean, const Camera& camera); // Camera argument added
GLuint getTerrainTextureID() const { return terrainTextureID; } // Getter for terrain texture ID
private:
// Shader shaderProgram; // Optional shader program
GLuint normalMapTextureID;
Shader oceanShader; // Shader program for ocean
Shader terrainShader; // **Add terrainShader member variable**
Shader lavaParticleShader;
GLuint lavaParticleVAO;
GLuint lavaParticleMeshVBO;
GLuint lavaParticleInstanceVBO;
std::string lastBoatTexturePath = "";
bool loadTexture(const char* filename, GLuint& textureID);
void setupLighting();
void drawBoat(const Boat& boat);
void drawMesh(const std::vector<glm::vec3>& vertices, const std::vector<glm::vec3>& normals, const std::vector<glm::vec2>& texCoords,
const std::vector<int>& materialIndices, const std::vector<tinyobj::material_t>& materials); // Modified drawMesh
void drawMeshVBO(const Ocean& ocean); // **Declare drawMeshVBO**
void drawTerrain(const Terrain& terrain, const Camera& camera, const std::vector<VolcanoSimulation>& volcanoes);
void drawLavaFlowsOnTerrain(const Terrain& terrain, const VolcanoSimulation& volcano);
void drawMeshTerainVBO(const Terrain& terrain);
void drawVolcano(const VolcanoSimulation& volcano, const Camera& camera);
void drawHud(const Boat& boat, const Camera& camera);
void drawText2D(float x, float y, const std::string& text, void* font = GLUT_BITMAP_HELVETICA_18);
};
#endif // RENDERER_H

View File

@ -0,0 +1,39 @@
#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <GL/glew.h> // Include GLEW for OpenGL types
#include <glm/glm.hpp> // **ADD THIS LINE - Include GLM header!**
#include <glm/gtc/type_ptr.hpp>
class Shader {
public:
Shader(); // Constructor
~Shader(); // Destructor
bool loadShader(const char* vertexShaderPath, const char* fragmentShaderPath); // Load and compile shaders from files
bool isLoaded() const { return programID != 0; } // Check if shader program is loaded
void use(); // Use (activate) the shader program
void unuse(); // Unuse (deactivate) the shader program
void cleanup(); // Release shader resources
// Uniform setting functions (add more as needed for different uniform types)
void setInt(const std::string& name, int value) const;
void setFloat(const std::string& name, float value) const;
void setVec3(const std::string& name, const glm::vec3& value) const;
void setMat4(const std::string& name, const glm::mat4& mat) const;
void setMat3(const std::string& name, const glm::mat3& mat) const;
private:
GLuint vertexShaderID; // Vertex shader object ID
GLuint fragmentShaderID; // Fragment shader object ID
GLuint programID; // Shader program ID
bool compileShader(GLuint& shaderID, GLenum shaderType, const char* shaderPath);
bool linkShaderProgram();
};
#endif // SHADER_H

View File

@ -0,0 +1,50 @@
// include/Terrain.h
#ifndef TERRAIN_H
#define TERRAIN_H
#include <glm/glm.hpp>
#include <vector>
#include <GL/glew.h> // Include GLEW for OpenGL types like GLuint
#include "Shader.h"
class Terrain {
public:
Terrain(int gridSize, float gridSpacing);
~Terrain();
bool init(GLuint heightMapTextureID);
void cleanup();
glm::vec3 getVertex(int x, int z) const; // Get vertex position at grid index (x, z)
float sampleHeightWorld(float worldX, float worldZ) const;
float sampleGradientMagnitudeWorld(float worldX, float worldZ) const;
float sampleDirectionalGradientWorld(float worldX, float worldZ, const glm::vec2& directionWorld) const;
bool isInsideBoundsWorld(float worldX, float worldZ) const;
int getGridSize() const { return gridSize; }
float getGridSpacing() const { return gridSpacing; }
GLuint getVAO() const { return vaoID; }
GLuint getIndexCount() const { return indexCount; }
glm::vec3 getNormal(int x, int z) const;
glm::vec3 getHighestPoint() const { return highestPoint; }
std::vector<glm::vec3> getHighestPeaks(int count, float minDistanceWorld) const;
private:
int gridSize;
float gridSpacing;
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
std::vector<glm::vec2> texCoords;
GLuint vertexBufferID;
GLuint normalBufferID;
GLuint texCoordBufferID;
GLuint indexBufferID;
GLuint vaoID;
unsigned int indexCount;
glm::vec3 highestPoint;
void generateGrid(GLuint heightMapTextureID); // Generate the initial grid of vertices
void createBuffers(); // Create and populate VBOs and IBO
void updateBuffers(); // Update VBO data (not needed for static terrain in this example, but good to have)
};
#endif // TERRAIN_H

View File

@ -0,0 +1,144 @@
#ifndef VOLCANO_SIM_H
#define VOLCANO_SIM_H
#include <cstddef>
#include <vector>
class Terrain;
class VolcanoSimulation {
public:
struct RenderConfig {
float worldX;
float worldY;
float worldZ;
float coneRadius;
float coneHeight;
float lavaFieldRadius;
float particleSpreadScale;
};
VolcanoSimulation();
~VolcanoSimulation();
bool init(std::size_t particleCount, int gridWidth, int gridHeight, unsigned int seed = 1337u);
void stepScalar(float dt);
void stepSIMD(float dt);
std::size_t getParticleCount() const { return particles.size(); }
const float* getParticlePosX() const { return particles.posX.data(); }
const float* getParticlePosY() const { return particles.posY.data(); }
const float* getParticlePosZ() const { return particles.posZ.data(); }
const float* getParticleTemperature() const { return particles.temperature.data(); }
int getGridWidth() const { return width; }
int getGridHeight() const { return height; }
const float* getLavaHeightGrid() const { return lavaHeight.data(); }
const float* getTemperatureGrid() const { return temperature.data(); }
void setRenderConfig(const RenderConfig& config) { renderConfig = config; }
const RenderConfig& getRenderConfig() const { return renderConfig; }
void setCollisionTerrain(const Terrain& terrain, float waterLevelWorld = 0.0f);
float getAverageLavaHeight() const { return averageLavaHeight; }
float getAverageTemperature() const { return averageTemperature; }
private:
class AlignedFloatArray {
public:
AlignedFloatArray();
~AlignedFloatArray();
AlignedFloatArray(const AlignedFloatArray& other);
AlignedFloatArray& operator=(const AlignedFloatArray& other);
AlignedFloatArray(AlignedFloatArray&& other) noexcept;
AlignedFloatArray& operator=(AlignedFloatArray&& other) noexcept;
bool resize(std::size_t count);
float* data();
const float* data() const;
std::size_t size() const;
private:
float* ptr;
std::size_t count;
};
struct ParticleSoA {
AlignedFloatArray posX;
AlignedFloatArray posY;
AlignedFloatArray posZ;
AlignedFloatArray velX;
AlignedFloatArray velY;
AlignedFloatArray velZ;
AlignedFloatArray life;
AlignedFloatArray temperature;
bool resize(std::size_t count);
std::size_t size() const;
};
ParticleSoA particles;
int width;
int height;
AlignedFloatArray terrainHeight;
AlignedFloatArray lavaHeight;
AlignedFloatArray lavaHeightNext;
AlignedFloatArray temperature;
AlignedFloatArray temperatureNext;
float gravity;
float damping;
float particleCooling;
float lavaViscosity;
float lavaCooling;
float diffusionAlpha;
float ambientTemperature;
float ventTemperature;
float lavaSourceRate;
float windX;
float windZ;
float plumeBuoyancy;
float eruptionClock;
float ventPulseLevel;
RenderConfig renderConfig;
float averageLavaHeight;
float averageTemperature;
int collisionTerrainSize;
float collisionTerrainOriginX;
float collisionTerrainOriginZ;
float collisionTerrainSpacing;
float collisionWaterLevelWorld;
std::vector<float> collisionTerrainHeights;
unsigned int rngState;
std::size_t idx(int x, int y) const;
float random01();
float sampleCollisionTerrainHeightWorld(float worldX, float worldZ) const;
void sampleCollisionTerrainGradientWorld(float worldX, float worldZ, float& dHdX, float& dHdZ) const;
void respawnParticle(std::size_t i);
void initTerrainAndLava();
void injectVentSources(float dt);
void updateParticlesScalar(float dt);
void updateParticlesSIMD(float dt);
void updateLavaFluxScalar(float dt);
void updateLavaFluxSIMD(float dt);
void diffuseHeatScalar(float dt);
void diffuseHeatSIMD(float dt);
void updateStats();
};
void runVolcanoBenchmark();
void runVolcanoReference(int frames = 10);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
#ifndef UTILS_H
#define UTILS_H
#include <GL/glew.h>
#include <iostream>
#include <glm/glm.hpp>
#include <x86intrin.h>
#include <vector>
// Helper function to check for OpenGL errors and print a message
void checkGLError(const char* operation);
float perlinNoise(float x, float y); // Placeholder declaration
void convert_vec3_to_float_array(const std::vector<glm::vec3>& src, float * dst);
void convert_float_array_to_vec3(float * src, std::vector<glm::vec3>& dst);
uint64_t rdtsc();
#endif // UTILS_H

BIN
projekt_win/libatomic-1.dll Normal file

Binary file not shown.

BIN
projekt_win/libfreeglut.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
projekt_win/libstdc++-6.dll Normal file

Binary file not shown.

Binary file not shown.

BIN
projekt_win/libzstd.dll Normal file

Binary file not shown.

325
projekt_win/src/Boat.cpp Normal file
View File

@ -0,0 +1,325 @@
/*
* File: Boat.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: Class for representng boat
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Boat.h"
#include <iostream>
#include <algorithm>
#include <cmath>
#define TINYOBJLOADER_IMPLEMENTATION
#include <tiny_obj_loader.h> // Include tinyobjloader
#include <glm/gtx/norm.hpp>
Boat::Boat() : position(0.0f, 0.5f, 0.0f), rotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)), speed(0.0f), steeringSpeed(1.0f), materials(), boatScale(1.0f), speedUpKeyWasDown(false), speedDownKeyWasDown(false), lastCollisionWithTerrain(false), lastCollisionAllowsDisembark(false), lastCollisionPoint(0.0f) {} // Initialize boatScale to 1.0f
Boat::~Boat() {}
bool Boat::init(const char* modelPath, const char* texturePath) {
materials.clear(); // Explicitly clear materials before loading model
if (!loadModel(modelPath)) {
std::cerr << "Error loading boat model: " << modelPath << std::endl;
return false;
}
boatTexturePath = texturePath;
return true;
}
void Boat::cleanup() {
// No dynamic memory cleanup in this simple example, but if you used dynamic allocation in loadModel, clean it up here.
}
void Boat::update(const Input& input, const Ocean& ocean, const Terrain& terrain, float deltaTime) {
handleInput(input, ocean, terrain, deltaTime);
applyWaveMotion(ocean);
}
void Boat::clearCollisionState() {
lastCollisionWithTerrain = false;
lastCollisionAllowsDisembark = false;
}
void Boat::handleInput(const Input& input, const Ocean& ocean, const Terrain& terrain, float deltaTime) {
clearCollisionState();
const bool speedUpDown = input.isKeyDown('+') || input.isKeyDown('=');
if (speedUpDown && !speedUpKeyWasDown) {
speed += 0.25f;
}
speedUpKeyWasDown = speedUpDown;
const bool speedDownDown = input.isKeyDown('-') || input.isKeyDown('_');
if (speedDownDown && !speedDownKeyWasDown) {
speed -= 0.25f;
}
speedDownKeyWasDown = speedDownDown;
if (input.isKeyDown('w')) {
speed += 0.5f * deltaTime; // Accelerate forward
}
if (input.isKeyDown('s')) {
speed -= 0.5f * deltaTime; // Decelerate/reverse
}
if (input.isKeyDown('a')) {
rotation = glm::rotate(rotation, steeringSpeed * deltaTime, glm::vec3(0.0f, 1.0f, 0.0f)); // Steer left
}
if (input.isKeyDown('d')) {
rotation = glm::rotate(rotation, -steeringSpeed * deltaTime, glm::vec3(0.0f, 1.0f, 0.0f)); // Steer right
}
speed = glm::clamp(speed, -0.5f, 8.0f); // Clamp speed
if (!input.isKeyDown('w') && !input.isKeyDown('s')) {
speed *= (1.0f - 0.5f * deltaTime); // Natural deceleration
if (fabs(speed) < 0.01f) speed = 0.0f; // Stop completely
}
// Move boat forward/backward based on rotation and speed
glm::vec3 forwardVector = rotation * glm::vec3(0.0f, 0.0f, -1.0f); // Boat's forward direction
const glm::vec3 candidatePosition = position + forwardVector * speed * deltaTime;
glm::vec3 collisionPoint = candidatePosition;
if (collidesWithTerrain(terrain, ocean, candidatePosition, &collisionPoint)) {
speed = 0.0f;
lastCollisionWithTerrain = true;
lastCollisionPoint = collisionPoint;
lastCollisionAllowsDisembark = isGradientLowForDisembark(terrain, collisionPoint);
return;
}
position = candidatePosition;
}
float Boat::getCollisionRadius() const {
const float extentX = std::max(std::fabs(boundingBoxMin.x), std::fabs(boundingBoxMax.x)) * boatScale;
const float extentZ = std::max(std::fabs(boundingBoxMin.z), std::fabs(boundingBoxMax.z)) * boatScale;
const float radiusFromModel = std::sqrt(extentX * extentX + extentZ * extentZ) * 0.5f;
return std::max(0.8f, radiusFromModel);
}
bool Boat::collidesWithTerrain(const Terrain& terrain, const Ocean& ocean, const glm::vec3& candidatePosition, glm::vec3* collisionPoint) const {
const float radius = getCollisionRadius();
const float boatDraft = 0.25f;
const glm::vec3 forward = glm::normalize(rotation * glm::vec3(0.0f, 0.0f, -1.0f));
glm::vec3 right = glm::cross(forward, glm::vec3(0.0f, 1.0f, 0.0f));
if (glm::length(right) > 1.0e-5f) {
right = glm::normalize(right);
} else {
right = glm::vec3(1.0f, 0.0f, 0.0f);
}
const glm::vec3 samples[5] = {
candidatePosition,
candidatePosition + forward * radius,
candidatePosition - forward * radius,
candidatePosition + right * radius,
candidatePosition - right * radius
};
for (const glm::vec3& samplePos : samples) {
if (!terrain.isInsideBoundsWorld(samplePos.x, samplePos.z)) {
continue;
}
const float terrainY = terrain.sampleHeightWorld(samplePos.x, samplePos.z);
const float waterY = ocean.getWaveHeight(samplePos.x, samplePos.z, ocean.time);
if (terrainY + boatDraft >= waterY) {
if (collisionPoint != nullptr) {
*collisionPoint = samplePos;
}
return true;
}
}
return false;
}
bool Boat::isGradientLowForDisembark(const Terrain& terrain, const glm::vec3& collisionPoint) const {
if (!terrain.isInsideBoundsWorld(collisionPoint.x, collisionPoint.z)) {
return false;
}
constexpr float kMaxAllowedGradient = 0.85f;
constexpr float kCheckRadius = 1.5f;
const glm::vec2 samples[] = {
glm::vec2(0.0f, 0.0f),
glm::vec2(1.0f, 0.0f),
glm::vec2(-1.0f, 0.0f),
glm::vec2(0.0f, 1.0f),
glm::vec2(0.0f, -1.0f),
glm::vec2(0.7071f, 0.7071f),
glm::vec2(-0.7071f, 0.7071f),
glm::vec2(0.7071f, -0.7071f),
glm::vec2(-0.7071f, -0.7071f)
};
for (const glm::vec2& sample : samples) {
const float x = collisionPoint.x + sample.x * kCheckRadius;
const float z = collisionPoint.z + sample.y * kCheckRadius;
if (!terrain.isInsideBoundsWorld(x, z)) {
return false;
}
const float gradient = terrain.sampleGradientMagnitudeWorld(x, z);
if (gradient > kMaxAllowedGradient) {
return false;
}
}
return true;
}
void Boat::applyWaveMotion(const Ocean& ocean) {
// Sample wave height at boat's position
position.y = ocean.getWaveHeight(position.x, position.z, ocean.time);
// Get wave normal
const glm::vec3 waveNormal = ocean.getWaveNormal(position.x, position.z, ocean.time);
//std::cout << "Wave Normal: (" << waveNormal.x << ", " << waveNormal.y << ", " << waveNormal.z << ")" << std::endl;
// Get current boat forward direction
const glm::vec3 currentBoatForward = rotation * glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 horizontalForward(currentBoatForward.x, 0.0f, currentBoatForward.z);
if (glm::length2(horizontalForward) > 1.0e-8f) {
horizontalForward = glm::normalize(horizontalForward);
} else {
horizontalForward = glm::vec3(0.0f, 0.0f, -1.0f); // Default if boat is pointing straight up/down
}
const glm::vec3 targetUp = waveNormal;
glm::vec3 targetForward = glm::cross(glm::cross(targetUp, horizontalForward), targetUp); // Project horizontalForward onto plane perpendicular to targetUp
if (glm::length2(targetForward) > 1.0e-8f) {
targetForward = glm::normalize(targetForward);
} else {
targetForward = horizontalForward; // Fallback if projection fails (waveNormal is vertical or horizontalForward is parallel to waveNormal somehow)
}
glm::vec3 targetRight = glm::cross(targetForward, targetUp);
if (glm::length2(targetRight) > 1.0e-8f) {
targetRight = glm::normalize(targetRight);
} else {
targetRight = glm::vec3(1.0f, 0.0f, 0.0f);
}
// Create rotation matrix from basis vectors
glm::mat3 targetRotationMatrix(targetRight, targetUp, -targetForward); // Boat forward is -Z
glm::quat targetRotation = glm::quat_cast(targetRotationMatrix);
rotation = glm::slerp(rotation, targetRotation, 0.1f);
}
bool Boat::loadModel(const char* path) {
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::string warn, err;
// Extract the directory path from the OBJ file path
std::string inputfile_dir = "./";
std::string path_str = path;
size_t last_slash_pos = path_str.find_last_of("/\\"); // Handle both / and \ path separators
if (last_slash_pos != std::string::npos) {
inputfile_dir = path_str.substr(0, last_slash_pos + 1); // Include the last slash
}
bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, path, inputfile_dir.c_str()); // Pass mtl_basepath
if (!warn.empty()) {
std::cout << "tinyobjloader warning: " << warn << std::endl;
}
if (!err.empty()) {
std::cerr << "tinyobjloader error: " << err << std::endl;
}
if (!ret) {
return false;
}
vertices.clear();
normals.clear();
texCoords.clear();
materialIndices.clear(); // Clear material indices
// Loop through each shape in the OBJ file
for (const auto& shape : shapes) {
// Loop over faces(polygon)
for (size_t f = 0; f < shape.mesh.indices.size() / 3; f++) {
int material_index = shape.mesh.material_ids[f]; // Get material index for this face
// Loop over vertices in the face (triangle)
for (size_t v = 0; v < 3; v++) {
tinyobj::index_t idx = shape.mesh.indices[3 * f + v];
tinyobj::real_t vx = attrib.vertices[3 * idx.vertex_index + 0];
tinyobj::real_t vy = attrib.vertices[3 * idx.vertex_index + 1];
tinyobj::real_t vz = attrib.vertices[3 * idx.vertex_index + 2];
vertices.push_back(glm::vec3(vx, vy, vz));
if (!attrib.normals.empty()) {
tinyobj::real_t nx = attrib.normals[3 * idx.normal_index + 0];
tinyobj::real_t ny = attrib.normals[3 * idx.normal_index + 1];
tinyobj::real_t nz = attrib.normals[3 * idx.normal_index + 2];
normals.push_back(glm::vec3(nx, ny, nz));
} else {
normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); // Default normal if no normals in OBJ
}
if (!attrib.texcoords.empty()) {
tinyobj::real_t tx = attrib.texcoords[2 * idx.texcoord_index + 0];
tinyobj::real_t ty = attrib.texcoords[2 * idx.texcoord_index + 1];
texCoords.push_back(glm::vec2(tx, 1.0f - ty)); // Flip V texture coord (OpenGL convention)
} else {
texCoords.push_back(glm::vec2(0.0f, 0.0f)); // Default texcoord if no texcoords in OBJ
}
materialIndices.push_back(material_index); // Store material index for this vertex
}
}
}
if (ret) {
glm::quat modelRotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); // Identity quaternion (no rotation initially)
// **Experiment with these rotations to find the correct orientation!**
// Example 1: Rotate 180 degrees around Y-axis (to flip the boat horizontally if it's backwards)
modelRotation = glm::rotate(modelRotation, glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f)); // Rotate 180 degrees yaw
modelRotation = glm::rotate(modelRotation, glm::radians(180.f), glm::vec3(1.0f, 0.0f, 0.0f)); // Yaw rotation (example: 0 degrees)
modelRotation = glm::rotate(modelRotation, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); // Pitch rotation (example: 0 degrees)
// Example 2: Rotate around X-axis (Pitch) - Adjust angle as needed
// modelRotation = glm::rotate(modelRotation, glm::radians(0.0f), glm::vec3(1.0f, 0.0f, 0.0f)); // No pitch rotation in this example
// Example 3: Rotate around Z-axis (Roll) - Adjust angle as needed
// modelRotation = glm::rotate(modelRotation, glm::radians(0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); // No roll rotation in this example
// **Apply rotation to vertices AFTER model loading**
for (glm::vec3& vertex : vertices) {
vertex = modelRotation * vertex; // Apply rotation to each vertex in model space
}
// **Calculate Bounding Box AFTER model loading**
if (!vertices.empty()) {
boundingBoxMin = vertices[0]; // Initialize with first vertex
boundingBoxMax = vertices[0];
// **Bounding Box Calculation Loop - This is where boundingBoxMin and boundingBoxMax are calculated and set**
for (const auto& vertex : vertices) {
boundingBoxMin.x = glm::min(boundingBoxMin.x, vertex.x);
boundingBoxMin.y = glm::min(boundingBoxMin.y, vertex.y);
boundingBoxMin.z = glm::min(boundingBoxMin.z, vertex.z);
boundingBoxMax.x = glm::max(boundingBoxMax.x, vertex.x);
boundingBoxMax.y = glm::max(boundingBoxMax.y, vertex.y);
boundingBoxMax.z = glm::max(boundingBoxMax.z, vertex.z);
}
}
}
return ret;
}

285
projekt_win/src/Camera.cpp Normal file
View File

@ -0,0 +1,285 @@
/*
* File: Camera.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: Camera
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Camera.h"
#include <GL/freeglut.h>
#include <algorithm>
#include <cmath>
#include <glm/gtc/matrix_transform.hpp>
Camera::Camera() : position(10.0f, 10.0f, 10.0f), target(0.0f, 0.0f, 0.0f), up(0.0f, 1.0f, 0.0f),
aspectRatio(1.0f), fov(45.0f), nearPlane(0.1f), farPlane(100.0f),
orbitDistance(10.0f), minOrbitDistance(4.0f), maxOrbitDistance(70.0f), targetHeightOffset(0.0f),
mode(Mode::BoatOrbit), flyMoveSpeed(16.0f), flyVerticalSpeed(12.0f), walkMoveSpeed(5.5f), maxWalkGradient(0.9f), onFootEyeHeight(1.7f),
onFootVelocityXZ(0.0f), onFootVerticalVelocity(0.0f), onFootGrounded(true), jumpKeyWasDown(false),
walkAcceleration(24.0f), walkFriction(14.0f), jumpSpeed(7.0f), gravity(18.5f),
slideStartGradient(3.5f), slideAcceleration(1.0f), maxSlideSpeed(0.8f) {}
Camera::~Camera() {}
void Camera::init() {
// Initial camera setup if needed
}
void Camera::update(const Input& input, const glm::vec3& boatPosition, const Terrain& terrain, float deltaTime) {
handleMouseInput(input, deltaTime);
if (mode == Mode::OnFoot) {
const float keyboardLookSpeed = 95.0f * deltaTime;
if (input.isSpecialKeyDown(GLUT_KEY_LEFT)) {
yawAngle -= keyboardLookSpeed;
}
if (input.isSpecialKeyDown(GLUT_KEY_RIGHT)) {
yawAngle += keyboardLookSpeed;
}
if (input.isSpecialKeyDown(GLUT_KEY_UP)) {
pitchAngle += keyboardLookSpeed;
}
if (input.isSpecialKeyDown(GLUT_KEY_DOWN)) {
pitchAngle -= keyboardLookSpeed;
}
pitchAngle = glm::clamp(pitchAngle, -89.0f, 89.0f);
}
glm::vec3 lookDirection;
lookDirection.x = cos(glm::radians(pitchAngle)) * cos(glm::radians(yawAngle));
lookDirection.y = sin(glm::radians(pitchAngle));
lookDirection.z = cos(glm::radians(pitchAngle)) * sin(glm::radians(yawAngle));
lookDirection = glm::normalize(lookDirection);
if (mode == Mode::Fly) {
const glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
const glm::vec3 forward = lookDirection;
const glm::vec3 right = glm::normalize(glm::cross(forward, worldUp));
const float moveSpeed = flyMoveSpeed * deltaTime;
const float verticalSpeed = flyVerticalSpeed * deltaTime;
if (input.isKeyDown('w') || input.isKeyDown('W')) {
position += forward * moveSpeed;
}
if (input.isKeyDown('s') || input.isKeyDown('S')) {
position -= forward * moveSpeed;
}
if (input.isKeyDown('a') || input.isKeyDown('A')) {
position -= right * moveSpeed;
}
if (input.isKeyDown('d') || input.isKeyDown('D')) {
position += right * moveSpeed;
}
if (input.isKeyDown(' ')) {
position += worldUp * verticalSpeed;
}
if (input.isKeyDown('c') || input.isKeyDown('C')) {
position -= worldUp * verticalSpeed;
}
target = position + forward;
up = glm::normalize(glm::cross(right, forward));
return;
}
if (mode == Mode::OnFoot) {
const glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
glm::vec3 flatForward(lookDirection.x, 0.0f, lookDirection.z);
if (glm::length(flatForward) > 1.0e-5f) {
flatForward = glm::normalize(flatForward);
} else {
flatForward = glm::vec3(0.0f, 0.0f, -1.0f);
}
const glm::vec3 right = glm::normalize(glm::cross(flatForward, worldUp));
glm::vec2 moveDir(0.0f);
if (input.isKeyDown('w') || input.isKeyDown('W')) {
moveDir += glm::vec2(flatForward.x, flatForward.z);
}
if (input.isKeyDown('s') || input.isKeyDown('S')) {
moveDir -= glm::vec2(flatForward.x, flatForward.z);
}
if (input.isKeyDown('a') || input.isKeyDown('A')) {
moveDir -= glm::vec2(right.x, right.z);
}
if (input.isKeyDown('d') || input.isKeyDown('D')) {
moveDir += glm::vec2(right.x, right.z);
}
const bool hasMoveInput = glm::length(moveDir) > 1.0e-5f;
if (hasMoveInput) {
const glm::vec2 desiredVelocity = glm::normalize(moveDir) * walkMoveSpeed;
glm::vec2 velocityDelta = desiredVelocity - onFootVelocityXZ;
const float accelScale = onFootGrounded ? 1.0f : 0.35f;
const float maxVelDelta = walkAcceleration * accelScale * deltaTime;
const float deltaLen = glm::length(velocityDelta);
if (deltaLen > maxVelDelta && deltaLen > 1.0e-6f) {
velocityDelta *= maxVelDelta / deltaLen;
}
onFootVelocityXZ += velocityDelta;
} else {
// No movement input => never drift automatically.
onFootVelocityXZ = glm::vec2(0.0f);
}
if (hasMoveInput && onFootGrounded) {
const float speed = glm::length(onFootVelocityXZ);
if (speed > 1.0e-5f) {
const float newSpeed = std::max(0.0f, speed - walkFriction * deltaTime);
onFootVelocityXZ *= newSpeed / speed;
}
}
const float stepX = onFootVelocityXZ.x * deltaTime;
const float stepZ = onFootVelocityXZ.y * deltaTime;
if (std::abs(stepX) > 1.0e-6f || std::abs(stepZ) > 1.0e-6f) {
const glm::vec3 candidate(position.x + stepX, position.y, position.z + stepZ);
bool canMove = false;
if (terrain.isInsideBoundsWorld(candidate.x, candidate.z)) {
const glm::vec2 moveDirection(stepX, stepZ);
const float directionalGrad = terrain.sampleDirectionalGradientWorld(position.x, position.z, moveDirection);
if (hasMoveInput) {
canMove = std::abs(directionalGrad) <= maxWalkGradient;
}
}
if (canMove) {
position.x = candidate.x;
position.z = candidate.z;
} else {
if (hasMoveInput) {
onFootVelocityXZ *= 0.2f;
} else {
onFootVelocityXZ = glm::vec2(0.0f);
}
}
}
const float groundY = terrain.sampleHeightWorld(position.x, position.z) + onFootEyeHeight;
const bool jumpDown = input.isKeyDown(' ');
if (onFootGrounded) {
position.y = groundY;
onFootVerticalVelocity = 0.0f;
if (jumpDown && !jumpKeyWasDown) {
onFootVerticalVelocity = jumpSpeed;
onFootGrounded = false;
}
} else {
onFootVerticalVelocity -= gravity * deltaTime;
position.y += onFootVerticalVelocity * deltaTime;
if (position.y <= groundY) {
position.y = groundY;
onFootVerticalVelocity = 0.0f;
onFootGrounded = true;
}
}
jumpKeyWasDown = jumpDown;
target = position + lookDirection;
up = worldUp;
return;
}
target = boatPosition;
position = target - lookDirection * orbitDistance;
position.y = glm::max(position.y, 2.0f);
up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 forward = glm::normalize(target - position);
glm::vec3 right = glm::normalize(glm::cross(forward, up));
up = glm::normalize(glm::cross(right, forward));
const int wheel = input.getMouseWheelDelta();
if (wheel != 0) {
orbitDistance -= static_cast<float>(wheel) * 1.2f;
orbitDistance = std::clamp(orbitDistance, minOrbitDistance, maxOrbitDistance);
}
}
void Camera::lookAt() const {
gluLookAt(position.x, position.y, position.z,
target.x, target.y, target.z,
up.x, up.y, up.z);
}
glm::mat4 Camera::getViewMatrix() const { // Keep `const` method
// Recalculate camera position and up vector based on yawAngle and pitchAngle
// **Calculate view matrix based on CURRENT position, target, up - NO MODIFICATION of members in getViewMatrix**
return glm::lookAt(position, target, up); // Use current position, target, up
}
void Camera::handleMouseInput(const Input& input, float deltaTime) {
(void)deltaTime;
if (input.isMouseButtonDown(GLUT_RIGHT_BUTTON)) { // Rotate only when right mouse button is pressed
float mouseSensitivity = 0.1f; // Adjust sensitivity
yawAngle += input.getMouseDeltaX() * mouseSensitivity;
pitchAngle -= input.getMouseDeltaY() * mouseSensitivity; // Invert vertical mouse motion
// Clamp pitch angle to prevent camera flipping
pitchAngle = glm::clamp(pitchAngle, -89.0f, 89.0f);
}
}
void Camera::rotateYaw(float angle) {
yawAngle += angle;
}
void Camera::rotatePitch(float angle) {
pitchAngle += angle;
}
void Camera::toggleFlyMode() {
if (mode == Mode::Fly) {
mode = Mode::BoatOrbit;
} else {
mode = Mode::Fly;
}
}
void Camera::startOnFoot(const glm::vec3& worldPosition, const Terrain& terrain) {
mode = Mode::OnFoot;
position.x = worldPosition.x;
position.z = worldPosition.z;
position.y = terrain.sampleHeightWorld(worldPosition.x, worldPosition.z) + onFootEyeHeight;
onFootVelocityXZ = glm::vec2(0.0f);
onFootVerticalVelocity = 0.0f;
onFootGrounded = true;
jumpKeyWasDown = false;
glm::vec3 lookDirection;
lookDirection.x = cos(glm::radians(pitchAngle)) * cos(glm::radians(yawAngle));
lookDirection.y = sin(glm::radians(pitchAngle));
lookDirection.z = cos(glm::radians(pitchAngle)) * sin(glm::radians(yawAngle));
lookDirection = glm::normalize(lookDirection);
target = position + lookDirection;
up = glm::vec3(0.0f, 1.0f, 0.0f);
}
void Camera::setBoatMode() {
mode = Mode::BoatOrbit;
onFootVelocityXZ = glm::vec2(0.0f);
onFootVerticalVelocity = 0.0f;
onFootGrounded = true;
jumpKeyWasDown = false;
}
const char* Camera::getModeName() const {
switch (mode) {
case Mode::BoatOrbit:
return "BOAT";
case Mode::Fly:
return "FLY";
case Mode::OnFoot:
return "ON_FOOT";
default:
return "UNKNOWN";
}
}

214
projekt_win/src/Game.cpp Normal file
View File

@ -0,0 +1,214 @@
/*
* File: Game.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: This class integrates all items into the game world.
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Game.h"
#include <algorithm>
#include <cmath>
Game* Game::instance = nullptr;
Game::Game() : renderer(), volcanoes(), input(), ocean(200), boat(), camera(), terrain(150, 1.0f), flyToggleKeyWasDown(false), interactKeyWasDown(false), boardBoatCooldown(0.0f) {
instance = this;
}
Game::~Game() {
instance = nullptr;
}
bool Game::init(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(1200, 800);
glutCreateWindow("3D Boat Simulation");
// **Initialize GLEW AFTER creating the window:**
if (glewInit() != GLEW_OK) {
//std::cerr << "GLEW initialization failed!" << std::endl;
return false;
}
glutDisplayFunc(displayCallback);
glutReshapeFunc(reshapeCallback);
glutKeyboardFunc(keyboardCallback);
glutKeyboardUpFunc(keyboardUpCallback);
glutSpecialFunc(specialCallback);
glutSpecialUpFunc(specialUpCallback);
glutMouseFunc(mouseCallback);
glutMotionFunc(motionCallback);
glutPassiveMotionFunc(passiveMotionCallback);
glutTimerFunc(16, timerCallback, 0); // ~60 FPS
glEnable(GL_DEPTH_TEST);
renderer.init();
//int oceanGridSize = 1000; // Default gridSize (you can change this)
//ocean = Ocean(oceanGridSize); // Pass gridSize to constructor
ocean.init();
if (!boat.init("assets/models/boat.obj", "assets/models/boat.jpg")) {
std::cerr << "Boat initialization failed!" << std::endl;
return false;
}
boat.setScale(0.01f); // Example: Make the boat half its original size
std::cerr << "Terrain init" << std::endl;
// Initialize Terrain
if (!terrain.init(renderer.heightMapTextureID)) {
std::cerr << "Terrain initialization failed!" << std::endl;
return false;
}
std::cerr << "Camera init" << std::endl;
camera.init();
std::cerr << "Input init" << std::endl;
input.init();
std::cerr << "Controls: F toggle fly mode | E board/disembark when prompted | +/- set boat speed | on-foot: WASD + Space(jump) | fly: WASD + Space/C | hold RMB to look around" << std::endl;
const int volcanoCount = 5;
const std::vector<glm::vec3> peaks = terrain.getHighestPeaks(volcanoCount, 12.0f);
if (peaks.empty()) {
std::cerr << "No terrain peaks found for volcano placement!" << std::endl;
return false;
}
volcanoes.clear();
volcanoes.resize(peaks.size());
for (std::size_t i = 0; i < peaks.size(); ++i) {
if (!volcanoes[i].init(8192, 128, 128, 2026u + static_cast<unsigned int>(i * 97u))) {
std::cerr << "Volcano simulation initialization failed at index " << i << std::endl;
return false;
}
VolcanoSimulation::RenderConfig cfg = volcanoes[i].getRenderConfig();
cfg.worldX = peaks[i].x;
cfg.worldZ = peaks[i].z;
cfg.worldY = peaks[i].y - cfg.coneHeight;
volcanoes[i].setRenderConfig(cfg);
volcanoes[i].setCollisionTerrain(terrain, ocean.getSeaLevelOffset());
std::cerr << "Volcano[" << i << "] anchored at peak: x=" << peaks[i].x
<< " y=" << peaks[i].y
<< " z=" << peaks[i].z << std::endl;
}
return true;
}
void Game::run() {
glutMainLoop();
}
void Game::cleanup() {
renderer.cleanup();
ocean.cleanup();
boat.cleanup();
terrain.cleanup(); // Cleanup terrain
}
void Game::displayCallback() {
instance->renderer.renderScene(instance->ocean, instance->boat, instance->camera, instance->terrain, instance->volcanoes); // **Pass instance->terrain**
glutSwapBuffers();
}
void Game::reshapeCallback(int width, int height) {
instance->renderer.reshape(width, height);
instance->camera.setAspectRatio(static_cast<float>(width) / height);
}
void Game::keyboardCallback(unsigned char key, int x, int y) {
instance->input.handleKeyPress(key);
}
void Game::keyboardUpCallback(unsigned char key, int x, int y) {
instance->input.handleKeyRelease(key);
}
void Game::specialCallback(int key, int x, int y) {
instance->input.handleSpecialKeyPress(key);
}
void Game::specialUpCallback(int key, int x, int y) {
instance->input.handleSpecialKeyRelease(key);
}
void Game::mouseCallback(int button, int state, int x, int y) {
instance->input.handleMouseClick(button, state, x, y);
}
void Game::motionCallback(int x, int y) {
instance->input.handleMouseMove(x, y);
}
void Game::passiveMotionCallback(int x, int y) {
instance->input.handleMouseMove(x, y);
}
void Game::updateGame() {
float deltaTime = 1.0f / 60.0f; // Fixed timestep for simplicity
instance->input.update();
instance->boardBoatCooldown = std::max(0.0f, instance->boardBoatCooldown - deltaTime);
const bool flyToggleDown = instance->input.isKeyDown('f') || instance->input.isKeyDown('F');
if (flyToggleDown && !instance->flyToggleKeyWasDown) {
instance->camera.toggleFlyMode();
std::cerr << "Camera mode: " << instance->camera.getModeName() << std::endl;
}
instance->flyToggleKeyWasDown = flyToggleDown;
const bool interactDown = instance->input.isKeyDown('e') || instance->input.isKeyDown('E');
const bool interactPressedThisFrame = interactDown && !instance->interactKeyWasDown;
bool boardedBoatThisFrame = false;
if (instance->camera.isOnFootMode() && instance->boardBoatCooldown <= 0.0f && interactPressedThisFrame) {
const glm::vec3 camPos = instance->camera.getPosition();
const glm::vec3 boatPos = instance->boat.getPosition();
const float dx = camPos.x - boatPos.x;
const float dz = camPos.z - boatPos.z;
const float dy = std::abs(camPos.y - boatPos.y);
const float distXZ2 = dx * dx + dz * dz;
const float dist3D2 = dx * dx + dy * dy + dz * dz;
constexpr float kBoardRadius = 6.0f;
constexpr float kMaxHeightDelta = 4.5f;
constexpr float kBoardDistance3D = 6.5f;
const bool closeInXZ = distXZ2 <= kBoardRadius * kBoardRadius && dy <= kMaxHeightDelta;
const bool closeIn3D = dist3D2 <= kBoardDistance3D * kBoardDistance3D;
if (closeInXZ || closeIn3D) {
instance->camera.setBoatMode();
boardedBoatThisFrame = true;
std::cerr << "Boarded boat: switched to BOAT mode" << std::endl;
}
}
if (instance->camera.isBoatMode() && !boardedBoatThisFrame) {
instance->boat.update(instance->input, instance->ocean, instance->terrain, deltaTime);
if (instance->boat.hasTerrainCollision() && instance->boat.canDisembark() && interactPressedThisFrame) {
instance->camera.startOnFoot(instance->boat.getDisembarkPosition(), instance->terrain);
instance->boardBoatCooldown = 0.8f;
std::cerr << "Boat touched shore with low gradient: switched to ON_FOOT mode" << std::endl;
}
}
instance->interactKeyWasDown = interactDown;
instance->camera.update(instance->input, instance->boat.getPosition(), instance->terrain, deltaTime);
instance->ocean.update(deltaTime);
for (VolcanoSimulation& volcano : instance->volcanoes) {
volcano.stepSIMD(deltaTime);
}
}
void Game::timerCallback(int value) {
instance->updateGame();
glutTimerFunc(16, timerCallback, 0); // Re-register timer
glutPostRedisplay(); // Request redraw
}

98
projekt_win/src/Input.cpp Normal file
View File

@ -0,0 +1,98 @@
/*
* File: Input.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: This class defines the game inputs.
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Input.h"
#include <GL/freeglut.h>
#include <cctype>
#include <cstring>
Input::Input() {
memset(mouseButtonsDown, false, sizeof(mouseButtonsDown)); // Initialize mouse button states to false
mouseX = mouseY = lastMouseX = lastMouseY = 0;
mouseDeltaX = mouseDeltaY = 0;
mouseWheelDelta = 0;
mouseWheelDeltaPending = 0;
}
Input::~Input() {}
void Input::init() {}
void Input::update() {
// Calculate mouse delta
mouseDeltaX = mouseX - lastMouseX;
mouseDeltaY = mouseY - lastMouseY;
// Update last mouse positions for next frame
lastMouseX = mouseX;
lastMouseY = mouseY;
mouseWheelDelta = mouseWheelDeltaPending;
mouseWheelDeltaPending = 0;
}
void Input::handleKeyPress(unsigned char key) {
if (std::isalpha(static_cast<unsigned char>(key))) {
key = static_cast<unsigned char>(std::tolower(static_cast<unsigned char>(key)));
}
keysDown.insert(key);
}
void Input::handleKeyRelease(unsigned char key) {
if (std::isalpha(static_cast<unsigned char>(key))) {
key = static_cast<unsigned char>(std::tolower(static_cast<unsigned char>(key)));
}
keysDown.erase(key);
}
void Input::handleSpecialKeyPress(int key) {
specialKeysDown.insert(key);
}
void Input::handleSpecialKeyRelease(int key) {
specialKeysDown.erase(key);
}
void Input::handleMouseClick(int button, int state, int x, int y) {
mouseX = x;
mouseY = y;
// Prevent large first-frame jump when mouse drag starts.
if (state == GLUT_DOWN) {
lastMouseX = x;
lastMouseY = y;
}
if (state == GLUT_DOWN && button == 3) {
mouseWheelDeltaPending += 1;
return;
}
if (state == GLUT_DOWN && button == 4) {
mouseWheelDeltaPending -= 1;
return;
}
if (button >= 0 && button < 5) { // Check button index is within range
mouseButtonsDown[button] = (state == GLUT_DOWN);
}
}
void Input::handleMouseMove(int x, int y) {
mouseX = x;
mouseY = y;
}
bool Input::isKeyDown(unsigned char key) const {
if (std::isalpha(static_cast<unsigned char>(key))) {
key = static_cast<unsigned char>(std::tolower(static_cast<unsigned char>(key)));
}
return keysDown.count(key);
}
bool Input::isSpecialKeyDown(int key) const {
return specialKeysDown.count(key);
}

361
projekt_win/src/Ocean.cpp Normal file
View File

@ -0,0 +1,361 @@
/*
* File: Ocean.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: This class defines ocean (surface). The core functions of this class are optimized by the IPA Project 2025.
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Ocean.h"
#include <cmath>
#include <glm/gtc/constants.hpp> // For pi
Ocean::Ocean(int gridSize) : time(0.0f),gridSize(gridSize), gridSpacing(1.0f),
amplitude(0.8f), wavelength(10.0f), frequency(1.0f), // Adjusted amplitude slightly
direction(glm::vec2(1.0f, 0.0f)), phase(0.0f), seaLevelOffset(-2.0f)
{
//gerstnerWaves.push_back({1.0f, 10.0f, 1.0f, glm::normalize(glm::vec2(1.0f, 0.0f)), 0.0f});
//gerstnerWaves.push_back({0.3f, 5.0f, 2.0f, glm::normalize(glm::vec2(1.0f, 1.0f)), 0.0f});
//gerstnerWaves.push_back({1.0f, 3.0f, 1.0f, glm::normalize(glm::vec2(1.0f, 0.5f)), 0.0f});
//gerstnerWaves.push_back({0.3f, 5.0f, 2.0f, glm::normalize(glm::vec2(0.5f, 0.5f)), 0.0f});
//gerstnerWaves.push_back({1.0f, 10.0f, 1.0f, glm::normalize(glm::vec2(1.0f, 0.0f)), 0.0f});
//gerstnerWaves.push_back({0.5f, 2.0f, 3.0f, glm::normalize(glm::vec2(1.0f, 1.0f)), 0.0f});
//gerstnerWaves.push_back({1.0f, 1.0f, 0.2f, glm::normalize(glm::vec2(0.7f, 0.2f)), 0.0f});
//gerstnerWaves.push_back({0.5f, 1.2f, 2.0f, glm::normalize(glm::vec2(0.9f, 0.8f)), 0.0f});
#if (0)
gerstnerWaves.push_back({0.12f, 6.5f, 1.2f, glm::normalize(glm::vec2(1.0f, 0.8f)), 0.0f});
gerstnerWaves.push_back({0.10f, 9.0f, 1.0f, glm::normalize(glm::vec2(-1.0f, 0.4f)), 1.0f});
gerstnerWaves.push_back({0.08f, 13.0f, 0.8f, glm::normalize(glm::vec2(0.2f, -1.0f)), 2.2f});
gerstnerWaves.push_back({0.07f, 16.0f, 0.6f, glm::normalize(glm::vec2(-0.6f, -1.0f)), 3.0f});
gerstnerWaves.push_back({0.11f, 7.5f, 1.1f, glm::normalize(glm::vec2(0.0f, 1.0f)), 0.5f});
#endif
#if (0)
gerstnerWaves.push_back({0.18f, 7.0f, 0.9f, glm::normalize(glm::vec2(0.8f, 0.6f)), 0.0f});
gerstnerWaves.push_back({0.15f, 10.0f, 0.8f, glm::normalize(glm::vec2(-0.9f, 0.4f)), 1.1f});
gerstnerWaves.push_back({0.13f, 14.0f, 0.7f, glm::normalize(glm::vec2(0.3f, -1.0f)), 2.3f});
gerstnerWaves.push_back({0.12f, 18.0f, 0.6f, glm::normalize(glm::vec2(-0.6f, -1.0f)), 3.0f});
gerstnerWaves.push_back({0.16f, 8.5f, 0.85f, glm::normalize(glm::vec2(0.0f, 1.0f)), 0.7f});
#endif
#if (1)
gerstnerWaves.push_back({0.3f, 5.0f, 2.0f, glm::normalize(glm::vec2(1.0f, 1.0f)), 0.0f});
gerstnerWaves.push_back({0.2f, 8.0f, 1.5f, glm::normalize(glm::vec2(-1.0f, 0.5f)), 1.0f});
gerstnerWaves.push_back({0.15f, 12.0f, 1.0f, glm::normalize(glm::vec2(0.3f, -1.0f)), 2.5f});
gerstnerWaves.push_back({0.1f, 15.0f, 0.8f, glm::normalize(glm::vec2(-0.7f, -0.7f)), 3.7f});
gerstnerWaves.push_back({0.25f, 7.0f, 1.8f, glm::normalize(glm::vec2(0.0f, 1.0f)), 0.8f});
#endif
}
bool Ocean::init()
{
generateGrid();
createBuffers(); // Create VBOs and IBO
return true;
}
void Ocean::update(float deltaTime)
{
time += deltaTime;
std::vector<glm::vec3> updatedVertices_vec = vertices;
std::vector<glm::vec3> updatedNormals_vec(vertices.size());
updateVertices(&updatedVertices_vec, &updatedNormals_vec, originalWorldX.data(), originalWorldZ.data(), gridSize, time);
updateBuffers(updatedVertices_vec, updatedNormals_vec);
}
void Ocean::generateGrid()
{
//std::cout << "Ocean::generateGrid - gridSize: " << gridSize << " " << vertices.size()<< std::endl;
vertices.resize(gridSize * gridSize);
// Resize original coordinate vectors
originalWorldX.resize(gridSize * gridSize);
originalWorldZ.resize(gridSize * gridSize);
for (int x = 0; x < gridSize; ++x) {
for (int z = 0; z < gridSize; ++z) {
float worldX = (x - gridSize / 2.0f) * gridSpacing;
float worldZ = (z - gridSize / 2.0f) * gridSpacing;
vertices[x * gridSize + z] = glm::vec3(worldX, 0.0f, worldZ);
// Store original world coordinates
originalWorldX[x * gridSize + z] = worldX;
originalWorldZ[x * gridSize + z] = worldZ;
}
}
}
void Ocean::updateVertices(std::vector<glm::vec3> * updatedVertices, std::vector<glm::vec3> * updatedNormals, float * originalWorldX_, float * originalWorldZ_, int _grid_size, float time)
{
for (int x = 0; x < _grid_size; ++x)
{
for (int z = 0; z < _grid_size; ++z)
{
glm::vec3 &vertex = (*updatedVertices)[x * _grid_size + z]; // Use reference to modify directly
float originalX = originalWorldX_[x * _grid_size + z];
float originalZ = originalWorldZ_[x * _grid_size + z];
vertex.y = getWaveHeight(vertex.x, vertex.z, time);
(*updatedNormals)[x * gridSize + z] = getWaveNormal(originalX, originalZ, time); // Calculate normal using original coords
}
}
}
float Ocean::getWaveHeight(float x, float z, float time) const {
float totalHeight = 0.0f;
for (const auto& wave : gerstnerWaves) {
totalHeight += getGerstnerWaveHeight(wave, x, z, time);
}
return totalHeight + seaLevelOffset;
}
float Ocean::getGerstnerWaveHeight(const GerstnerWave& wave, float x, float z, float time) const {
float k = 2.0f * glm::pi<float>() / wave.wavelength;
float w = wave.speed * k;
float dotProduct = glm::dot(wave.direction, glm::vec2(x, z));
float periodicAmplitude = wave.amplitude * 0.5f * (1.0f + sin(2.0f * glm::pi<float>() * time / wave.wavelength));
float waveHeightValue = periodicAmplitude * sin(k * dotProduct - w * time + wave.phase);
if (fabs(x) < 0.5f && fabs(z) < 0.5f) {
//std::cout << " getGerstnerWaveHeight - time: " << time << ", periodicAmplitude: " << periodicAmplitude << ", waveHeightValue: " << waveHeightValue << std::endl;
}
/*
if(c<8){
float a = 2.0f * glm::pi<float>() * time / wave.wavelength;
puts("");
printf("2*pi*time/wave.wavelength: %f \n",a);
printf("sin(2*pi*time/wave.wavelength): %f \n",sin(a));
printf("k*dot: %f \n",k * dotProduct);
printf("w*time: %f \n",w * time);
printf("wave phase: %f \n",wave.phase);
printf("k*dot - w*time + phase: %f \n",k * dotProduct-w*time+wave.phase);
printf("sin(k*dot - w*time + phase: %f )\n",sin(k * dotProduct-w*time+wave.phase));
printf("res: %f pariodic: %f dot: %f time: %f wave_x x: %f %f wave_y z: %f %f\n",waveHeightValue,periodicAmplitude,dotProduct,time,wave.direction.x,x,wave.direction.y,z);
puts("");
c++;
if(c==7){
puts("");
}
}
*/
return waveHeightValue;
}
glm::vec3 Ocean::getGerstnerWaveDisplacement(const GerstnerWave& wave, float x, float z, float time) const {
float k = 2.0f * glm::pi<float>() / wave.wavelength;
float w = wave.speed * k;
float dotProduct = glm::dot(wave.direction, glm::vec2(x, z));
float cosTerm = cos(k * dotProduct - w * time + wave.phase);
float periodicAmplitude = wave.amplitude * 0.5f * (1.0f + sin(2.0f * glm::pi<float>() * time / wave.wavelength));
return glm::vec3(wave.direction.x * periodicAmplitude * cosTerm,
0.0f,
wave.direction.y * periodicAmplitude * cosTerm);
}
glm::vec3 Ocean::getWaveNormal(float x, float z, float time) const {
glm::vec3 tangentX = glm::vec3(1.0f, 0.0f, 0.0f);
glm::vec3 tangentZ = glm::vec3(0.0f, 0.0f, 1.0f);
for (const auto& wave : gerstnerWaves) {
float k = 2.0f * glm::pi<float>() / wave.wavelength;
float w = wave.speed * k;
float dotProduct = glm::dot(wave.direction, glm::vec2(x, z));
float sinTerm = sin(k * dotProduct - w * time + wave.phase);
float cosTerm = cos(k * dotProduct - w * time + wave.phase);
float periodicAmplitude = wave.amplitude * 0.5f * (1.0f + sin(2.0f * glm::pi<float>() * time / wave.wavelength));
float modulatedAmplitude = periodicAmplitude;
// Calculate tangent vectors for EACH wave component and ACCUMULATE them directly
tangentX += glm::vec3(
-modulatedAmplitude * wave.direction.x * wave.direction.x * k * sinTerm, // dx_dx
modulatedAmplitude * wave.direction.x * k * cosTerm, // dy_dx
-modulatedAmplitude * wave.direction.x * wave.direction.y * k * sinTerm // dz_dx
);
tangentZ += glm::vec3(
-modulatedAmplitude * wave.direction.x * wave.direction.y * k * sinTerm, // dx_dz
modulatedAmplitude * wave.direction.y * k * cosTerm, // dy_dz
-modulatedAmplitude * wave.direction.y * wave.direction.y * k * sinTerm // dz_dz
);
}
/*
auto cros = glm::cross(tangentZ, tangentX);
if(c==0&&time!=0){
puts("tangent:");
c++;
std::cout<<tangentX.x<<std::endl;
std::cout<<tangentX.z<<std::endl;
std::cout<<tangentZ.x<<std::endl;
std::cout<<tangentZ.z<<std::endl;
std::cout<<tangentX.y<<std::endl;
std::cout<<tangentZ.y<<std::endl;
puts("cross:");
std::cout<<cros.x<<std::endl;
std::cout<<cros.y<<std::endl;
std::cout<<cros.z<<std::endl;
}
*/
return glm::normalize(glm::cross(tangentZ, tangentX));
}
void Ocean::updateBuffers(const std::vector<glm::vec3> &updatedVertices, const std::vector<glm::vec3> &updatedNormals)
{
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glBufferSubData(GL_ARRAY_BUFFER, 0, updatedVertices.size() * sizeof(glm::vec3), updatedVertices.data()); // Update vertex positions
glBindBuffer(GL_ARRAY_BUFFER, normalBufferID);
glBufferSubData(GL_ARRAY_BUFFER, 0, updatedNormals.size() * sizeof(glm::vec3), updatedNormals.data()); // Update normals
}
glm::vec3 Ocean::getVertex(int x, int z) const
{
return vertices[x * gridSize + z];
}
void Ocean::setGridSize(int newGridSize)
{
gridSize = newGridSize;
generateGrid(); // Re-generate the grid with the new size
std::vector<glm::vec3> updatedVertices = vertices; // Create a copy to update
std::vector<glm::vec3> updatedNormals(vertices.size()); // Vector to store updated normals
updateVertices(&updatedVertices, &updatedNormals, originalWorldX.data(), originalWorldZ.data(), gridSize, time);
updateBuffers(updatedVertices, updatedNormals);
//updateVertices(); // Re-update vertices based on waves (optional, if needed immediately)
}
GLuint Ocean::getVAO() const
{
return vaoID;
}
GLuint Ocean::getIndexCount() const
{
return indexCount;
}
int Ocean::getGridIndex(int x, int z) const {
return x * gridSize + z;
}
void Ocean::createBuffers()
{
glGenVertexArrays(1, &vaoID);
checkGLError("glGenVertexArrays"); // Check after glGenVertexArrays
glBindVertexArray(vaoID);
checkGLError("glBindVertexArray"); // Check after glBindVertexArray
// Generate VBOs
glGenBuffers(1, &vertexBufferID);
checkGLError("glGenBuffers - vertexBufferID"); // Check after glGenBuffers
glGenBuffers(1, &normalBufferID);
checkGLError("glGenBuffers - normalBufferID"); // Check after glGenBuffers
glGenBuffers(1, &texCoordBufferID);
checkGLError("glGenBuffers - texCoordBufferID"); // Check after glGenBuffers
glGenBuffers(1, &indexBufferID);
checkGLError("glGenBuffers - indexBufferID"); // Check after glGenBuffers
// 1. Vertex Positions VBO
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
checkGLError("glBindBuffer - vertexBufferID"); // Check after glBindBuffer
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_DYNAMIC_DRAW);
checkGLError("glBufferData - vertexBufferID"); // Check after glBufferData
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
checkGLError("glVertexAttribPointer - vertexBufferID"); // Check after glVertexAttribPointer
glEnableVertexAttribArray(0);
checkGLError("glEnableVertexAttribArray - location 0"); // Check after glEnableVertexAttribArray
// 2. Vertex Normals VBO (initially flat normals - updated in updateVertices/updateBuffers)
std::vector<glm::vec3> normals(vertices.size(), glm::vec3(0.0f, 1.0f, 0.0f)); // Initialize with flat normals
glBindBuffer(GL_ARRAY_BUFFER, normalBufferID);
checkGLError("glBindBuffer - normalBufferID"); // Check after glBindBuffer
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_DYNAMIC_DRAW);
checkGLError("glBufferData - normalBufferID"); // Check after glBufferData
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
checkGLError("glVertexAttribPointer - normalBufferID"); // Check after glVertexAttribPointer
glEnableVertexAttribArray(1);
checkGLError("glEnableVertexAttribArray - location 1"); // Check after glEnableVertexAttribArray
// 3. Texture Coordinates VBO
std::vector<glm::vec2> texCoords(vertices.size());
for (int x = 0; x < gridSize; ++x)
{
for (int z = 0; z < gridSize; ++z)
{
float texU = static_cast<float>(x) / 70.0f;
float texV = static_cast<float>(z) / 70.0f;
texCoords[x * gridSize + z] = glm::vec2(texU, texV);
}
}
glBindBuffer(GL_ARRAY_BUFFER, texCoordBufferID);
checkGLError("glBindBuffer - texCoordBufferID"); // Check after glBindBuffer
glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(glm::vec2), texCoords.data(), GL_STATIC_DRAW);
checkGLError("glBufferData - texCoordBufferID"); // Check after glBufferData
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
checkGLError("glVertexAttribPointer - texCoordBufferID"); // Check after glVertexAttribPointer
glEnableVertexAttribArray(2);
checkGLError("glEnableVertexAttribArray - location 2"); // Check after glEnableVertexAttribArray
// 4. Index Buffer Object (IBO) for Quads
std::vector<unsigned int> indices;
for (int x = 0; x < gridSize - 1; ++x)
{
for (int z = 0; z < gridSize - 1; ++z)
{
unsigned int v00 = x * gridSize + z;
unsigned int v10 = (x + 1) * gridSize + z;
unsigned int v11 = (x + 1) * gridSize + (z + 1);
unsigned int v01 = x * gridSize + (z + 1);
indices.insert(indices.end(), {v00, v10, v11, v01}); // Quad indices (counter-clockwise)
}
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
checkGLError("glBindBuffer - indexBufferID"); // Check after glBindBuffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
checkGLError("glBufferData - indexBufferID"); // Check after glBufferData
glBindVertexArray(0); // Unbind VAO
checkGLError("glBindVertexArray(0)"); // Check after unbinding VAO
glBindBuffer(GL_ARRAY_BUFFER, 0); // Unbind VBO - Optional, VAO unbinding often unbinds VBOs
checkGLError("glBindBuffer(0) - ARRAY_BUFFER"); // Check after unbinding ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind IBO - Optional, VAO unbinding often unbinds IBO
checkGLError("glBindBuffer(0) - ELEMENT_ARRAY_BUFFER"); // Check after unbinding ELEMENT_ARRAY_BUFFER
indexCount = indices.size(); // Store index count for rendering
}
Ocean::~Ocean()
{
cleanup(); // Call cleanup to release OpenGL resources
}
void Ocean::cleanup()
{
// No dynamic memory allocation in this simple version
// Release OpenGL resources (VBOs, IBO, VAO)
glDeleteBuffers(1, &vertexBufferID);
glDeleteBuffers(1, &normalBufferID);
glDeleteBuffers(1, &texCoordBufferID);
glDeleteBuffers(1, &indexBufferID);
glDeleteVertexArrays(1, &vaoID);
vertexBufferID = 0;
normalBufferID = 0;
texCoordBufferID = 0;
indexBufferID = 0;
vaoID = 0;
}

126
projekt_win/src/Shader.cpp Normal file
View File

@ -0,0 +1,126 @@
/*
* File: Shader.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: Shader controll
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
Shader::Shader() : vertexShaderID(0), fragmentShaderID(0), programID(0) {}
Shader::~Shader() {
cleanup();
}
bool Shader::loadShader(const char* vertexShaderPath, const char* fragmentShaderPath) {
// 1. Compile vertex and fragment shaders
if (!compileShader(vertexShaderID, GL_VERTEX_SHADER, vertexShaderPath)) return false;
if (!compileShader(fragmentShaderID, GL_FRAGMENT_SHADER, fragmentShaderPath)) return false;
// 2. Link shader program
if (!linkShaderProgram()) return false;
return true; // Shader program loaded and linked successfully
}
bool Shader::compileShader(GLuint& shaderID, GLenum shaderType, const char* shaderPath) {
shaderID = glCreateShader(shaderType);
std::string shaderCode;
std::ifstream shaderFile;
shaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); // Enable exception throwing
try {
shaderFile.open(shaderPath);
std::stringstream shaderStream;
shaderStream << shaderFile.rdbuf();
shaderFile.close();
shaderCode = shaderStream.str();
} catch (std::ifstream::failure& e) {
std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << shaderPath << std::endl;
return false;
}
const char* shaderCodeCStr = shaderCode.c_str();
glShaderSource(shaderID, 1, &shaderCodeCStr, NULL);
glCompileShader(shaderID);
// Check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shaderID, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::COMPILATION_FAILED: " << shaderPath << "\n" << infoLog << std::endl;
return false;
}
return true;
}
bool Shader::linkShaderProgram() {
programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
// Check for linking errors
int success;
char infoLog[512];
glGetProgramiv(programID, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(programID, 512, NULL, infoLog);
std::cerr << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
return false;
}
glDeleteShader(vertexShaderID); // Delete shaders as they are linked into program now and no longer necessary
glDeleteShader(fragmentShaderID); // Shader objects are deleted after linking
vertexShaderID = 0;
fragmentShaderID = 0;
return true;
}
void Shader::use() {
glUseProgram(programID);
}
void Shader::unuse() {
glUseProgram(0); // Unuse shader program (use fixed-function pipeline)
}
void Shader::cleanup() {
if (programID != 0) {
glDeleteProgram(programID);
programID = 0;
}
}
void Shader::setInt(const std::string& name, int value) const {
glUniform1i(glGetUniformLocation(programID, name.c_str()), value);
}
void Shader::setFloat(const std::string& name, float value) const {
glUniform1f(glGetUniformLocation(programID, name.c_str()), value);
}
void Shader::setVec3(const std::string& name, const glm::vec3& value) const {
glUniform3fv(glGetUniformLocation(programID, name.c_str()), 1, glm::value_ptr(value));
}
void Shader::setMat4(const std::string& name, const glm::mat4& mat) const {
glUniformMatrix4fv(glGetUniformLocation(programID, name.c_str()), 1, GL_FALSE, glm::value_ptr(mat));
}
void Shader::setMat3(const std::string& name, const glm::mat3& mat) const {
glUniformMatrix3fv(glGetUniformLocation(programID, name.c_str()), 1, GL_FALSE, glm::value_ptr(mat));
}

358
projekt_win/src/Terrain.cpp Normal file
View File

@ -0,0 +1,358 @@
/*
* File: Terran.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: This class defines terrain (surface).
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Terrain.h"
#include <algorithm>
#include <cmath>
#include <glm/gtc/constants.hpp>
#include <iostream>
#include "utils.h" // Include utils.h for checkGLError
#include <GL/glew.h>
#include <limits>
#include <numeric>
#include <cassert>
Terrain::Terrain(int gridSize, float gridSpacing)
: gridSize(gridSize),
gridSpacing(gridSpacing),
vertices(gridSize * gridSize),
normals(gridSize * gridSize),
texCoords(gridSize * gridSize),
vertexBufferID(0),
normalBufferID(0),
texCoordBufferID(0),
indexBufferID(0),
vaoID(0),
indexCount(0),
highestPoint(0.0f, std::numeric_limits<float>::lowest(), 0.0f) {}
Terrain::~Terrain() {
cleanup();
}
bool Terrain::init(GLuint heightMapTextureID) {
generateGrid(heightMapTextureID);
createBuffers();
return true;
}
void Terrain::cleanup() {
if (vertexBufferID != 0) {
glDeleteBuffers(1, &vertexBufferID);
vertexBufferID = 0;
}
if (normalBufferID != 0) {
glDeleteBuffers(1, &normalBufferID);
normalBufferID = 0;
}
if (texCoordBufferID != 0) {
glDeleteBuffers(1, &texCoordBufferID);
texCoordBufferID = 0;
}
if (indexBufferID != 0) {
glDeleteBuffers(1, &indexBufferID);
indexBufferID = 0;
}
if (vaoID != 0) {
glDeleteVertexArrays(1, &vaoID);
vaoID = 0;
}
}
void Terrain::generateGrid(GLuint heightMapTextureID) {
vertices.resize(gridSize * gridSize);
normals.resize(gridSize * gridSize);
texCoords.resize(gridSize * gridSize);
// **Access Heightmap Texture Data**
glBindTexture(GL_TEXTURE_2D, heightMapTextureID); // Bind heightmap texture
GLint textureWidth, textureHeight;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &textureWidth); // Get texture width
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &textureHeight); // Get texture height
std::vector<unsigned char> heightmapData(textureWidth * textureHeight); // Assuming 8-bit grayscale heightmap
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED, GL_UNSIGNED_BYTE, heightmapData.data()); // Get texture pixel data (Red channel = grayscale height)
glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture
float heightScale = 28.0f; // Smaller vertical scale so the volcano is less tall
const float terrainBaseOffset = -10.0f; // Lower whole terrain a bit relative to sea level.
highestPoint = glm::vec3(0.0f, std::numeric_limits<float>::lowest(), 0.0f);
for (int x = 0; x < gridSize; ++x) {
for (int z = 0; z < gridSize; ++z) {
float worldX = (x - gridSize / 2.0f) * gridSpacing;
float worldZ = (z - gridSize / 2.0f) * gridSpacing;
const float uNorm = static_cast<float>(x) / static_cast<float>(gridSize - 1);
const float vNorm = static_cast<float>(z) / static_cast<float>(gridSize - 1);
const int texX = std::clamp(static_cast<int>(std::round(uNorm * static_cast<float>(textureWidth - 1))), 0, textureWidth - 1);
const int texZ = std::clamp(static_cast<int>(std::round(vNorm * static_cast<float>(textureHeight - 1))), 0, textureHeight - 1);
const std::size_t texIndex = static_cast<std::size_t>(texZ) * static_cast<std::size_t>(textureWidth) + static_cast<std::size_t>(texX);
// Height is sampled from volcano heatmap and shifted down, so low values stay near sea level.
const float height = static_cast<float>(heightmapData[texIndex]) / 255.0f * heightScale + terrainBaseOffset;
vertices[x * gridSize + z] = glm::vec3(worldX, height, worldZ);
texCoords[x * gridSize + z] = glm::vec2(uNorm, vNorm);
if (height > highestPoint.y) {
highestPoint = vertices[x * gridSize + z];
}
}
}
for (int x = 0; x < gridSize; ++x) {
for (int z = 0; z < gridSize; ++z) {
const int xL = std::max(0, x - 1);
const int xR = std::min(gridSize - 1, x + 1);
const int zD = std::max(0, z - 1);
const int zU = std::min(gridSize - 1, z + 1);
const float hL = vertices[xL * gridSize + z].y;
const float hR = vertices[xR * gridSize + z].y;
const float hD = vertices[x * gridSize + zD].y;
const float hU = vertices[x * gridSize + zU].y;
const glm::vec3 normal = glm::normalize(glm::vec3(hL - hR, 2.0f * gridSpacing, hD - hU));
normals[x * gridSize + z] = normal;
}
}
}
void Terrain::createBuffers() {
glGenVertexArrays(1, &vaoID);
checkGLError("1"); // Check after drawMeshVBO call
glBindVertexArray(vaoID);
checkGLError("2"); // Check after drawMeshVBO call
glGenBuffers(1, &vertexBufferID);
checkGLError("3"); // Check after drawMeshVBO call
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
checkGLError("4"); // Check after drawMeshVBO call
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);
checkGLError("5"); // Check after drawMeshVBO call
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
checkGLError("6"); // Check after drawMeshVBO call
glEnableVertexAttribArray(0);
checkGLError("7"); // Check after drawMeshVBO call
glGenBuffers(1, &normalBufferID);
checkGLError("8"); // Check after drawMeshVBO call
glBindBuffer(GL_ARRAY_BUFFER, normalBufferID);
checkGLError("9"); // Check after drawMeshVBO call
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), normals.data(), GL_STATIC_DRAW);
checkGLError("10"); // Check after drawMeshVBO call
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
checkGLError("11"); // Check after drawMeshVBO call
glEnableVertexAttribArray(1);
checkGLError("12"); // Check after drawMeshVBO call
glGenBuffers(1, &texCoordBufferID);
checkGLError("13"); // Check after drawMeshVBO call
glBindBuffer(GL_ARRAY_BUFFER, texCoordBufferID);
checkGLError("14"); // Check after drawMeshVBO call
glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(glm::vec2), texCoords.data(), GL_STATIC_DRAW);
checkGLError("15"); // Check after drawMeshVBO call
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0);
checkGLError("16"); // Check after drawMeshVBO call
glEnableVertexAttribArray(2);
checkGLError("17"); // Check after drawMeshVBO call
std::vector<unsigned int> indices;
for (int x = 0; x < gridSize - 1; ++x) {
for (int z = 0; z < gridSize - 1; ++z) {
unsigned int v00 = x * gridSize + z;
unsigned int v10 = (x + 1) * gridSize + z;
unsigned int v11 = (x + 1) * gridSize + (z + 1);
unsigned int v01 = x * gridSize + (z + 1);
indices.insert(indices.end(), {v00, v10, v11, v01});
}
}
glGenBuffers(1, &indexBufferID);
checkGLError("glGenBuffers - indexBufferID"); // Check after glGenBuffers
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
checkGLError("18"); // Check after drawMeshVBO call
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
checkGLError("19"); // Check after drawMeshVBO call
glBindVertexArray(0);
checkGLError("20"); // Check after drawMeshVBO call
indexCount = indices.size();
}
void Terrain::updateBuffers() {
// Not needed for static terrain in this basic example
}
glm::vec3 Terrain::getVertex(int x, int z) const {
assert(x >= 0 && x < gridSize);
assert(z >= 0 && z < gridSize);
return vertices[x * gridSize + z];
}
bool Terrain::isInsideBoundsWorld(float worldX, float worldZ) const {
if (gridSize <= 1 || gridSpacing <= 0.0f || vertices.empty()) {
return false;
}
const float minX = vertices[0].x;
const float minZ = vertices[0].z;
const float maxX = vertices[(gridSize - 1) * gridSize + (gridSize - 1)].x;
const float maxZ = vertices[(gridSize - 1) * gridSize + (gridSize - 1)].z;
return worldX >= minX && worldX <= maxX && worldZ >= minZ && worldZ <= maxZ;
}
float Terrain::sampleHeightWorld(float worldX, float worldZ) const {
if (gridSize <= 1 || gridSpacing <= 0.0f || vertices.empty()) {
return 0.0f;
}
const glm::vec3 origin = vertices[0];
const float fx = (worldX - origin.x) / gridSpacing;
const float fz = (worldZ - origin.z) / gridSpacing;
if (!std::isfinite(fx) || !std::isfinite(fz)) {
return origin.y;
}
const float maxIdx = static_cast<float>(gridSize - 1);
const float clampedX = std::clamp(fx, 0.0f, maxIdx);
const float clampedZ = std::clamp(fz, 0.0f, maxIdx);
const int x0 = static_cast<int>(std::floor(clampedX));
const int z0 = static_cast<int>(std::floor(clampedZ));
const int x1 = std::min(x0 + 1, gridSize - 1);
const int z1 = std::min(z0 + 1, gridSize - 1);
const float tx = clampedX - static_cast<float>(x0);
const float tz = clampedZ - static_cast<float>(z0);
const auto h = [&](int x, int z) {
return vertices[x * gridSize + z].y;
};
const float h00 = h(x0, z0);
const float h10 = h(x1, z0);
const float h01 = h(x0, z1);
const float h11 = h(x1, z1);
const float hx0 = h00 + tx * (h10 - h00);
const float hx1 = h01 + tx * (h11 - h01);
return hx0 + tz * (hx1 - hx0);
}
float Terrain::sampleGradientMagnitudeWorld(float worldX, float worldZ) const {
if (gridSize <= 1 || gridSpacing <= 0.0f || vertices.empty()) {
return 0.0f;
}
const float step = std::max(0.25f, 0.5f * gridSpacing);
const float hL = sampleHeightWorld(worldX - step, worldZ);
const float hR = sampleHeightWorld(worldX + step, worldZ);
const float hD = sampleHeightWorld(worldX, worldZ - step);
const float hU = sampleHeightWorld(worldX, worldZ + step);
const float dHdx = (hR - hL) / (2.0f * step);
const float dHdz = (hU - hD) / (2.0f * step);
return std::sqrt(dHdx * dHdx + dHdz * dHdz);
}
float Terrain::sampleDirectionalGradientWorld(float worldX, float worldZ, const glm::vec2& directionWorld) const {
if (gridSize <= 1 || gridSpacing <= 0.0f || vertices.empty()) {
return 0.0f;
}
const float dirLen = glm::length(directionWorld);
if (dirLen <= 1.0e-5f) {
return 0.0f;
}
const glm::vec2 dir = directionWorld / dirLen;
const float step = std::max(0.25f, gridSpacing);
const float h0 = sampleHeightWorld(worldX, worldZ);
const float h1 = sampleHeightWorld(worldX + dir.x * step, worldZ + dir.y * step);
return (h1 - h0) / step;
}
glm::vec3 Terrain::getNormal(int x, int z) const
{
assert(x >= 0 && x < gridSize);
assert(z >= 0 && z < gridSize);
return normals[x * gridSize + z];
}
std::vector<glm::vec3> Terrain::getHighestPeaks(int count, float minDistanceWorld) const
{
std::vector<glm::vec3> peaks;
if (count <= 0 || vertices.empty()) {
return peaks;
}
std::vector<std::size_t> order(vertices.size());
std::iota(order.begin(), order.end(), static_cast<std::size_t>(0));
std::sort(order.begin(), order.end(), [&](std::size_t a, std::size_t b) {
return vertices[a].y > vertices[b].y;
});
const float minDist2 = std::max(0.0f, minDistanceWorld * minDistanceWorld);
for (std::size_t id : order) {
const glm::vec3& candidate = vertices[id];
bool tooClose = false;
for (const glm::vec3& p : peaks) {
const float dx = candidate.x - p.x;
const float dz = candidate.z - p.z;
if (dx * dx + dz * dz < minDist2) {
tooClose = true;
break;
}
}
if (tooClose) {
continue;
}
peaks.push_back(candidate);
if (static_cast<int>(peaks.size()) >= count) {
break;
}
}
return peaks;
}

File diff suppressed because it is too large Load Diff

38
projekt_win/src/main.cpp Normal file
View File

@ -0,0 +1,38 @@
/*
* File: main.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: Main
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "Game.h"
#include "VolcanoSim.h"
#include <cstdlib>
#include <cstring>
int main(int argc, char** argv) {
if (argc > 1 && std::strcmp(argv[1], "--volcano-benchmark") == 0) {
runVolcanoBenchmark();
return 0;
}
if (argc > 1 && std::strcmp(argv[1], "--volcano-reference") == 0) {
int frames = 10;
if (argc > 2) {
const int parsed = std::atoi(argv[2]);
frames = (parsed > 0) ? parsed : 1;
}
runVolcanoReference(frames);
return 0;
}
Game game;
if (!game.init(argc, argv)) {
return 1;
}
game.run();
game.cleanup();
return 0;
}

1223
projekt_win/src/render.cpp Normal file

File diff suppressed because it is too large Load Diff

93
projekt_win/src/utils.cpp Normal file
View File

@ -0,0 +1,93 @@
/*
* File: utils.cpp
* Author: Tomas Goldmann
* Date: 2025-03-23
* Description: Utils - perlinNoise is does not use in this project
*
* Copyright (c) 2025, Brno University of Technology. All rights reserved.
* Licensed under the MIT.
*/
#include "utils.h"
uint64_t rdtsc() {
return __rdtsc(); // Read Time-Stamp Counter
}
void checkGLError(const char* operation) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
std::cerr << "OpenGL Error after " << operation << ": ";
switch (error) {
case GL_INVALID_ENUM:
std::cerr << "GL_INVALID_ENUM - An unacceptable value is specified for an enumerated argument.";
break;
case GL_INVALID_VALUE:
std::cerr << "GL_INVALID_VALUE - A numeric argument is out of range.";
break;
case GL_INVALID_OPERATION:
std::cerr << "GL_INVALID_OPERATION - The specified operation is not allowed in the current state.";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
std::cerr << "GL_INVALID_FRAMEBUFFER_OPERATION - The framebuffer object is not complete.";
break;
case GL_OUT_OF_MEMORY:
std::cerr << "GL_OUT_OF_MEMORY - There is not enough memory left to execute the command.";
break;
case GL_STACK_UNDERFLOW:
std::cerr << "GL_STACK_UNDERFLOW - An attempt has been made to perform an operation that would cause the stack to underflow.";
break;
case GL_STACK_OVERFLOW:
std::cerr << "GL_STACK_OVERFLOW - An attempt has been made to perform an operation that would cause a stack overflow.";
break;
// Add more cases for other specific OpenGL errors if needed
default:
std::cerr << "Unknown OpenGL error code: " << error;
}
std::cerr << std::endl;
}
}
// Basic Perlin Noise implementation (simplified 2D version)
float perlinNoise(float x, float y) {
// ... (Implement Perlin noise algorithm here - this is a simplified example) ...
// For a complete Perlin noise implementation, refer to online resources.
// This is a placeholder for demonstration - it will produce very basic noise.
float value = 0.0f;
float frequency = 1.0f;
float amplitude = 1.0f;
float maxAmplitude = 0.0f;
for (int i = 0; i < 4; ++i) { // 4 octaves
//float sampleX = x * frequency;
//float sampleY = y * frequency;
//value += glm::perlin(glm::vec2(sampleX, sampleY)) * amplitude; // Using glm::perlin for simplicity
maxAmplitude += amplitude;
amplitude *= 0.5f; // Reduce amplitude for each octave
frequency *= 2.0f; // Increase frequency for each octave
}
return value / maxAmplitude; // Normalize to [0, 1] range
}
void convert_vec3_to_float_array(const std::vector<glm::vec3>& src, float * dst) {
// Ensure dst is the correct size (numVertices * 3) - should be handled by resize in constructor
size_t numVertices = src.size();
for (size_t i = 0; i < numVertices; ++i) {
dst[i * 3 + 0] = src[i].x;
dst[i * 3 + 1] = src[i].y;
dst[i * 3 + 2] = src[i].z;
}
}
void convert_float_array_to_vec3(float * src, std::vector<glm::vec3>& dst) {
size_t numVertices = dst.size(); // Assume dst has correct size already
for (size_t i = 0; i < numVertices; ++i) {
dst[i].x = src[i * 3 + 0];
dst[i].y = src[i * 3 + 1];
dst[i].z = src[i * 3 + 2];
}
}

View File

@ -0,0 +1,134 @@
.intel_syntax noprefix
.code64
.section .rodata
.align 32
# Scalar constants
vk_zero: .float 0.0
vk_one: .float 1.0
vk_eps: .float 0.0001
vk_inv_temp_s: .float 0.00111111111
vk_temp_norm_max_s: .float 1.6
vk_shape_bias: .float 0.35
vk_shape_scale: .float 0.65
vk_buoyancy_k: .float 0.09
vk_wind_bias: .float 0.4
vk_wind_scale: .float 0.5
vk_damp_scale: .float 0.02
vk_damp_min: .float 0.90
vk_damp_max: .float 0.998
vk_min_rad2_s: .float 0.04
vk_vent_rad2_s: .float 1.2
vk_vent_scale_s: .float 5.2
vk_life_base_s: .float 0.14
vk_life_scale_s: .float 0.10
vk_cool_base_s: .float 0.55
vk_cool_scale_s: .float 0.6
vk_cool_rad_s: .float 0.1
# Vector constants
vk_vec_zero: .float 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0
vk_vec_one: .float 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
vk_vec_four: .float 4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
vk_vec_six: .float 6.0,6.0,6.0,6.0,6.0,6.0,6.0,6.0
vk_vec_eps: .float 0.000001,0.000001,0.000001,0.000001,0.000001,0.000001,0.000001,0.000001
vk_vec_drop_eps: .float 0.0001,0.0001,0.0001,0.0001,0.0001,0.0001,0.0001,0.0001
vk_vec_shape_bias: .float 0.35,0.35,0.35,0.35,0.35,0.35,0.35,0.35
vk_vec_shape_scale: .float 0.65,0.65,0.65,0.65,0.65,0.65,0.65,0.65
vk_vec_inv_temp: .float 0.00111111111,0.00111111111,0.00111111111,0.00111111111,0.00111111111,0.00111111111,0.00111111111,0.00111111111
vk_vec_temp_max: .float 1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6
vk_vec_min_rad2: .float 0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04
vk_vec_vent_rad2: .float 1.2,1.2,1.2,1.2,1.2,1.2,1.2,1.2
vk_vec_vent_scale: .float 5.2,5.2,5.2,5.2,5.2,5.2,5.2,5.2
vk_vec_buoy_scale: .float 0.09,0.09,0.09,0.09,0.09,0.09,0.09,0.09
vk_vec_wind_bias: .float 0.4,0.4,0.4,0.4,0.4,0.4,0.4,0.4
vk_vec_wind_scale: .float 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5
vk_vec_damp_scale: .float 0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02
vk_vec_damp_min: .float 0.90,0.90,0.90,0.90,0.90,0.90,0.90,0.90
vk_vec_damp_max: .float 0.998,0.998,0.998,0.998,0.998,0.998,0.998,0.998
vk_vec_life_base: .float 0.14,0.14,0.14,0.14,0.14,0.14,0.14,0.14
vk_vec_life_scale: .float 0.10,0.10,0.10,0.10,0.10,0.10,0.10,0.10
vk_vec_cool_base: .float 0.55,0.55,0.55,0.55,0.55,0.55,0.55,0.55
vk_vec_cool_scale: .float 0.6,0.6,0.6,0.6,0.6,0.6,0.6,0.6
vk_vec_cool_rad: .float 0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1
vk_vec_fluid_min: .float 650.0,650.0,650.0,650.0,650.0,650.0,650.0,650.0
vk_vec_inv_fluid: .float 0.00153846154,0.00153846154,0.00153846154,0.00153846154,0.00153846154,0.00153846154,0.00153846154,0.00153846154
vk_vec_k_base: .float 0.18,0.18,0.18,0.18,0.18,0.18,0.18,0.18
vk_vec_k_scale: .float 1.22,1.22,1.22,1.22,1.22,1.22,1.22,1.22
vk_vec_max_out: .float 0.94,0.94,0.94,0.94,0.94,0.94,0.94,0.94
vk_vec_max_temp: .float 1520.0,1520.0,1520.0,1520.0,1520.0,1520.0,1520.0,1520.0
vk_vec_lava_heat_b: .float 420.0,420.0,420.0,420.0,420.0,420.0,420.0,420.0
vk_vec_lava_heat_s: .float 100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0
.text
.macro FLOW_FROM_DROP_AVX dst, drop, kval, tmp
# TODO: implement vector version of flowFromDrop:
# max(0, drop) -> shape -> result = pos * k * shape
.endm
# -----------------------------------------------------------------------------
# float volcanoFlowFromDropAsm(float drop, float k);
# -----------------------------------------------------------------------------
.global volcanoFlowFromDropAsm
volcanoFlowFromDropAsm:
# TODO: scalar helper used by lava flow
ret
# -----------------------------------------------------------------------------
# void volcanoParticleStepAsm(...)
# -----------------------------------------------------------------------------
.global volcanoParticleStepAsm
volcanoParticleStepAsm:
# TODO: scalar single-particle step
ret
# -----------------------------------------------------------------------------
# void volcanoParticleStepBatchAsm(...)
# -----------------------------------------------------------------------------
.global volcanoParticleStepBatchAsm
volcanoParticleStepBatchAsm:
# TODO:
# 1. split count into simdCount and scalar tail
# 2. call volcanoParticleStepSIMDAsm for full 8-wide blocks
# 3. finish remaining particles with scalar path
ret
# -----------------------------------------------------------------------------
# void volcanoParticleStepSIMDAsm(...)
# -----------------------------------------------------------------------------
.global volcanoParticleStepSIMDAsm
volcanoParticleStepSIMDAsm:
# TODO:
# - load 8 particles (SoA)
# - compute tempNorm, radius2, invR, ventPush
# - update velX/velY/velZ
# - apply damping
# - update posX/posY/posZ
# - update life and temp
ret
# -----------------------------------------------------------------------------
# void volcanoUpdateLavaFluxSIMDAsm(...)
# -----------------------------------------------------------------------------
.global volcanoUpdateLavaFluxSIMDAsm
volcanoUpdateLavaFluxSIMDAsm:
# TODO:
# - iterate interior cells by rows
# - process 8 cells at once
# - compute fluidity, k coefficients, in/out flow
# - clamp output and write lavaHeightNext
ret
# -----------------------------------------------------------------------------
# void volcanoDiffuseHeatSIMDAsm(...)
# -----------------------------------------------------------------------------
.global volcanoDiffuseHeatSIMDAsm
volcanoDiffuseHeatSIMDAsm:
# TODO:
# - iterate interior cells by rows
# - compute laplacian
# - add lava heating, subtract cooling
# - clamp to [ambientTemperature, 1520]
ret