init
3
projekt_linux/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
build/*
|
||||
boat_sim
|
||||
boat_sim.exe
|
||||
16
projekt_linux/.vscode/c_cpp_properties.json
vendored
Normal 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_linux/.vscode/launch.json
vendored
Normal 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_linux/.vscode/settings.json
vendored
Normal 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_linux/.vscode/tasks.json
vendored
Normal 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": "${workspaceFolder}/../mingw64/bin/mingw32-make.exe clean",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
80
projekt_linux/Makefile
Normal file
@ -0,0 +1,80 @@
|
||||
# Makefile for boat_sim project
|
||||
|
||||
# Compiler and flags
|
||||
CXX = g++
|
||||
CXXFLAGS = -Wall -g -mavx2 -msse4.2 # -Wall for more warnings, -g for debugging symbols
|
||||
DEPFLAGS = -MMD -MP
|
||||
INC_DIR = include
|
||||
SOIL_INCLUDE_HINT_SOIL2 = /usr/include/SOIL2/SOIL2.h
|
||||
SOIL_INCLUDE_HINT_SOIL = /usr/include/SOIL/SOIL.h
|
||||
|
||||
ifeq ($(wildcard $(SOIL_INCLUDE_HINT_SOIL2)), $(SOIL_INCLUDE_HINT_SOIL2))
|
||||
CXXFLAGS += -DUSE_SOIL2
|
||||
SOIL_LIB = -lSOIL2
|
||||
else ifeq ($(wildcard $(SOIL_INCLUDE_HINT_SOIL)), $(SOIL_INCLUDE_HINT_SOIL))
|
||||
CXXFLAGS += -DUSE_SOIL
|
||||
SOIL_LIB = -lSOIL
|
||||
else
|
||||
$(error Could not find SOIL2 or SOIL headers. Install libsoil2-dev or libsoil-dev.)
|
||||
endif
|
||||
|
||||
LIBS = -lglut $(SOIL_LIB) -lGL -lGLEW -lGLU
|
||||
LIB_DIRS = /usr/lib /usr/lib/x86_64-linux-gnu
|
||||
|
||||
LDFLAGS = $(addprefix -L, $(LIB_DIRS))
|
||||
|
||||
# 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)))
|
||||
DEPS = $(OBJECTS:.o=.d)
|
||||
|
||||
|
||||
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) $(DEPFLAGS) -I$(INC_DIR) -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 $(BUILD_DIR)/*
|
||||
rm $(EXECUTABLE_PATH)
|
||||
|
||||
# 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
|
||||
|
||||
-include $(DEPS)
|
||||
148
projekt_linux/README.md
Normal file
@ -0,0 +1,148 @@
|
||||
# ZADÁNÍ PROJEKTU: Optimalizace vizualizace vulkanické činnosti pomocí SIMD
|
||||
|
||||
|
||||
Cílem projektu je optimalizovat výpočetně náročné algoritmy pro vizualizaci činnosti sopky pomocí vektorových instrukcí (SIMD/AVX). Projekt je rozdělen do dvou hlavních částí: jedna řeší dynamiku vytékající lávy (včetně šíření tepla) a druhá fyziku částic vystupujících ze sopky (vulkanický popel a materiál). Můžete použít SSE, AVX a AVX2. Nezapomínejte, že je zapotřebí myslet i na skalární zpracování prvků, které se vám nevlezou do registru.
|
||||
|
||||
## Prostředí a implementace
|
||||
Projekt můžete řešit jak na operačním systému Linux, tak i pro Windows (zde si dejte velký pozor na rozdíly ve volacích konvencích / Calling Conventions). Stejně jako na cvičeních budete pracovat s čistým GAS asemblerem v souboru volcano_kernels.s, kde jsou připravené signatury funkcí, které budete implementovat.
|
||||
|
||||
V aplikaci se pro referenci a kontrolu defaultně volá výchozí skalární implementace v jazyce C++, kterou naleznete v souboru VolcanoSim.cpp. Veškerá data o částicích a mřížce jsou navržena ve formátu SoA (Structure of Arrays) s ohledem na snadnější vektorizaci a zarovnána do paměti.
|
||||
|
||||
## Testování, validace a benchmark
|
||||
V případě, že chcete provádět benchmark a nebo získat referenční hodnoty pouze pro algoritmy vulkánu (bez nutnosti spouštět kompletní grafické rozhraní), můžete při spuštění programu z příkazové řádky použít následující přepínače:
|
||||
|
||||
--volcano-reference [počet_framů]
|
||||
Tento režim spustí simulaci pro zadaný počet framů (výchozí hodnota je 10). Během běhu nejprve vypisuje referenční hodnoty skalární simulace (např. průměrnou výšku lávy, pozice a teplotu částic), což vám poslouží jako vzor pro ladění.
|
||||
|
||||
--volcano-benchmark
|
||||
Spustí výkonnostní test pro pevně daný počet kroků. Aplikace změří celkový čas běhu (v milisekundách) samostatně pro skalární a vaši SIMD cestu. Výstupem bude naměřený čas obou verzí a výsledný Speedup faktor (poměr zrychlení), který bude hrát klíčovou roli v hodnocení optimalizace.
|
||||
|
||||
## Doporučený postup řešení
|
||||
Optimalizaci doporučuji provádět postupně na základě zdrojového kódu v C++ (VolcanoSim.cpp). Před samotným psaním kódu v asembleru si důkladně analyzujte klíčové části výpočtů (zejména matematické smyčky uvnitř funkcí) a ujasněte si, jaké datové struktury se předávají a jak chcete paměť mapovat do SIMD registrů (např. YMM).
|
||||
|
||||
## Hodnocení a podmínky získání bodů
|
||||
Vzhledem k tomu, že je v dnešní době prakticky nemožné kontrolovat, kdo z vás použil při generování kódu umělou inteligenci a do jaké míry, je zisk bodů z projektu striktně podmíněn osobní obhajobou.
|
||||
|
||||
Na obhajobě byste měli být schopni prokázat, že vašemu řešení plně rozumíte, víte, co která část vámi odevzdaného kódu dělá, a stejně tak umíte detailně vysvětlit, jak fungují vámi použité asemblerové instrukce.
|
||||
|
||||
|
||||
## Algoritmy pro implementaci
|
||||
|
||||
---
|
||||
|
||||
### 1. Dynamika částic (`updateParticlesSIMD`)
|
||||
|
||||
Tento algoritmus počítá pohyb, chladnutí a životnost částeček popela a materiálu vyvržených ze sopky. Pro každý krok `dt` a částici `i` s pozicí $(x_i, y_i, z_i)$, rychlostí $(v_{xi}, v_{yi}, v_{zi})$ a teplotou $T_i$ se provedou následující výpočty:
|
||||
|
||||
**A. Pomocné proměnné a normalizace:**
|
||||
Nejprve se zjistí vzdálenost částice od středu sopky (jícnu) a normalizuje se její teplota.
|
||||
|
||||
* **Vzdálenost a inverzní poloměr:** $r^2 = x_i^2 + z_i^2$, $invR = \frac{1}{\sqrt{\max(r^2, \epsilon_r)}}$
|
||||
* **Normalizovaná teplota:** $T_{norm} = \text{clamp}\left(\frac{T_i - T_{amb}}{T_{span}}, 0, T_{normMax}\right)$
|
||||
|
||||
**B. Výpočet zrychlení (sil působících na částici):**
|
||||
|
||||
* **Osa Y (Gravitace a vztlak):** Horké částice stoupají vzhůru, ale vztlak slábne s výškou.
|
||||
|
||||
$$a_y = g + B \cdot T_{norm} \cdot \frac{1}{1 + k_b \cdot \max(y_i, 0)}$$
|
||||
|
||||
|
||||
* **Osy X a Z (Vítr a radiální síla jícnu):** Jícen částice odtlačuje do stran a do toho fouká vítr, jehož vliv závisí na teplotě částice.
|
||||
|
||||
$$a_{vent} = \max(0, R_{vent}^2 - r^2) \cdot K_{vent} \cdot T_{norm}$$
|
||||
|
||||
|
||||
$$a_x = windX \cdot (c_{w0} + c_{w1} \cdot T_{norm}) + x_i \cdot invR \cdot a_{vent}$$
|
||||
|
||||
|
||||
$$a_z = windZ \cdot (c_{w0} + c_{w1} \cdot T_{norm}) + z_i \cdot invR \cdot a_{vent}$$
|
||||
|
||||
|
||||
|
||||
**C. Integrace pohybu (Euler) a tlumení odporu prostředí:**
|
||||
|
||||
* Aktualizace rychlosti o zrychlení: $\vec{v}_i = \vec{v}_i + \vec{a}_i \cdot dt$
|
||||
* Výpočet lokálního tlumení (odporu vzduchu): $d_{local} = \text{clamp}(d_0 - d_1 \cdot T_{norm}, d_{min}, d_{max})$
|
||||
* Aplikace tlumení na rychlost: $\vec{v}_i = \vec{v}_i \cdot d_{local}$
|
||||
* Posun pozice: $\vec{p}_i = \vec{p}_i + \vec{v}_i \cdot dt$
|
||||
|
||||
**D. Životnost a chladnutí:**
|
||||
|
||||
* Úbytek života: $life_i = life_i - dt \cdot (c_{l0} + c_{l1} \cdot T_{norm})$
|
||||
* Ztráta tepla: $T_i = T_i - k_{cool} \cdot dt \cdot (c_{t0} + c_{t1} \cdot \sqrt{r^2 + \epsilon_t})$
|
||||
|
||||
*(Pozn.: Kolize s terénem se po provedení tohoto SIMD bloku řeší ve skalární smyčce, to v assembleru pravděpodobně dělat nebudete, resp. závisí na přesném zadání struktury souboru).*
|
||||
|
||||
---
|
||||
|
||||
### 2. Tok lávy na mřížce (`updateLavaFluxSIMD`)
|
||||
|
||||
Láva se šíří po 2D mřížce jako buněčný automat. Tok probíhá vždy mezi středovou buňkou $C$ a jejími čtyřmi sousedy (Left, Right, Up, Down).
|
||||
|
||||
**A. Fluidita lávy:**
|
||||
Láva teče tím lépe, čím je teplejší.
|
||||
|
||||
* $f_C = \text{clamp}\left(\frac{T_C - T_{liq}}{T_{liqSpan}}, 0, 1\right)$
|
||||
* Koeficient toku: $k_C = viscosity \cdot dt \cdot (k_0 + k_1 \cdot f_C)$
|
||||
|
||||
**B. Výpočet toku do okolí (funkce Flow):**
|
||||
Nejprve se spočítá celková výška v buňce ($H_C = terrain_C + lava_C$) a v sousední buňce (např. $H_L$). Poté se určí "spád" (drop): $drop_{C \to L} = H_C - H_L$.
|
||||
|
||||
* Samotný tok z $C$ do $L$ počítá nelineární funkce (teče to jen z kopce, tj. pro kladný spád):
|
||||
|
||||
$$out_L = \text{flow}(drop, k_C) = \max(0, drop) \cdot k_C \cdot \left(a_0 + a_1 \cdot \sqrt{\max(0, drop) + \epsilon}\right)$$
|
||||
|
||||
|
||||
|
||||
Tímto způsobem se vypočítá tok do všech 4 směrů: $out_L, out_R, out_U, out_D$.
|
||||
|
||||
**C. Ochrana proti "vysátí" buňky:**
|
||||
Nelze nechat odtéct více lávy, než v buňce fyzicky je (a navíc je zde limitující konstanta $\alpha_{out}$).
|
||||
|
||||
* Suma odtoku: $outSum = out_L + out_R + out_U + out_D$
|
||||
* Maximální povolený odtok: $maxOut = lava_C \cdot \alpha_{out}$
|
||||
* Pokud by chtělo odtéct víc, toky se zmenší: Je-li $outSum > maxOut$, pak každý směr vynásobíme zlomkem $\frac{maxOut}{outSum}$.
|
||||
|
||||
**D. Aktualizace stavu buňky:**
|
||||
Spočítají se celkové přítoky ze sousedů ($inSum$) a zapíše se nová hodnota výšky lávy.
|
||||
|
||||
* $lavaNext_C = \text{clamp}(lava_C + inSum - outSum, 0, lavaMax)$
|
||||
|
||||
---
|
||||
|
||||
### 3. Difuze teploty (`diffuseHeatSIMD`)
|
||||
|
||||
Teplota se po mřížce šíří vedením tepla, ale je také generována přítomností horké lávy a naopak se ztrácí ochlazováním do okolního prostředí.
|
||||
|
||||
**A. Vedení tepla (Laplaceův operátor):**
|
||||
Zhodnocení rozdílu teploty vůči 4 přímým sousedům.
|
||||
|
||||
* $lap = T_L + T_R + T_U + T_D - 4 \cdot T_C$
|
||||
|
||||
**B. Zdroje a ztráty tepla:**
|
||||
|
||||
* Fluidita (stejný vzorec jako u lávy): $f = \text{clamp}\left(\frac{T_C - T_{liq}}{T_{liqSpan}}, 0, 1\right)$
|
||||
* Ohřev od lávy přítomné v buňce: $Q_{lava} = lava_C \cdot (q_0 + q_1 \cdot f)$
|
||||
* Ochlazování do okolí: $Q_{cool} = k_{loss} \cdot (T_C - T_{amb})$
|
||||
|
||||
**C. Integrace a aktualizace (Euler):**
|
||||
|
||||
* Výpočet nové teploty: $T_{next} = T_C + dt \cdot (\alpha \cdot lap + Q_{lava} - Q_{cool})$
|
||||
* Zamezení nesmyslných teplot (clamp): $T_{next} = \text{clamp}(T_{next}, T_{amb}, T_{max})$
|
||||
|
||||
---
|
||||
|
||||
### Algoritmický postup implementace (pro SIMD AVX2)
|
||||
|
||||
Až budete psát asembler (`.s` soubor), postup pro každý výše zmíněný Kernel bude zhruba následující:
|
||||
|
||||
1. **Vstupní parametry:** Ukazatele na pole (`posX`, `velX`, `lavaHeight`, atd.) si namapujete do 64bitových registrů (např. `rdi`, `rsi` u Linuxu / `rcx`, `rdx` u Windows). Ostatní konstanty (jako $dt$, $g$, $k_b$) si načtete pomocí `vbroadcastss` do YMM registrů.
|
||||
2. **Smyčka po 8 prvcích (YMM registry pojmou 8x 32-bit float):**
|
||||
* Pomocí instrukcí `vmovups` (příp. `vmovaps` pro zarovnaná pole) načtete 8 prvků dané vlastnosti.
|
||||
* Běžné operace přeložíte na `vaddps`, `vmulps`, `vsubps`.
|
||||
* Funkce `clamp(x, min, max)` nebo `max(0, x)` zkonstruujete jednoduše složením instrukcí `vmaxps` a `vminps`.
|
||||
* Matematické funkce provedete přes `vsqrtps` a inverzní dělení (`1 / x`) ideálně přes `vrcpps` (nebo `vrsqrtps` pro $1/\sqrt{x}$).
|
||||
|
||||
|
||||
3. **Zápis:** Spočítané hodnoty opět zapíšete do příslušných indexů pomocí `vmovups`.
|
||||
4. **Tail processing:** Pokud celkový počet prvků není dělitelný 8, zbytek se na konci spočítá po jedné (skalárně). U částic se dělá skalární smyčka s `XMM` registry nebo volání C++ fallbacku.
|
||||
BIN
projekt_linux/assets/models/boat.jpg
Normal file
|
After Width: | Height: | Size: 717 KiB |
28
projekt_linux/assets/models/boat.mtl
Normal 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
|
||||
93618
projekt_linux/assets/models/boat.obj
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
54
projekt_linux/assets/shaders/ocean_fragment_shader.glsl
Normal 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
|
||||
}
|
||||
34
projekt_linux/assets/shaders/ocean_vertex_shader.glsl
Normal 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);
|
||||
}
|
||||
61
projekt_linux/assets/shaders/terrain_fragment_shader.glsl
Normal 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);
|
||||
}
|
||||
22
projekt_linux/assets/shaders/terrain_vertex_shader.glsl
Normal 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);
|
||||
}
|
||||
BIN
projekt_linux/assets/textures/boat.jpeg
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
projekt_linux/assets/textures/boat_texture.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
projekt_linux/assets/textures/ocean_texture.jpg
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
projekt_linux/assets/textures/ocean_texture.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
projekt_linux/assets/textures/ocean_texture2.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
projekt_linux/assets/textures/rock_texture.jpg
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
projekt_linux/assets/textures/rock_texture.png
Normal file
|
After Width: | Height: | Size: 4.4 MiB |
BIN
projekt_linux/assets/textures/terain.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
projekt_linux/assets/textures/volcano.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
80
projekt_linux/include/Boat.h
Normal 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
|
||||
76
projekt_linux/include/Camera.h
Normal 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
|
||||
54
projekt_linux/include/Game.h
Normal 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
|
||||
48
projekt_linux/include/Input.h
Normal 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_linux/include/Ocean.h
Normal 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
|
||||
64
projekt_linux/include/Renderer.h
Normal 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
|
||||
39
projekt_linux/include/Shader.h
Normal 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
|
||||
50
projekt_linux/include/Terrain.h
Normal 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
|
||||
144
projekt_linux/include/VolcanoSim.h
Normal 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
|
||||
3500
projekt_linux/include/tiny_obj_loader.h
Normal file
16
projekt_linux/include/utils.h
Normal 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
|
||||
325
projekt_linux/src/Boat.cpp
Normal 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_linux/src/Camera.cpp
Normal 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_linux/src/Game.cpp
Normal 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_linux/src/Input.cpp
Normal 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_linux/src/Ocean.cpp
Normal 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_linux/src/Shader.cpp
Normal 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_linux/src/Terrain.cpp
Normal 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;
|
||||
}
|
||||
1016
projekt_linux/src/VolcanoSim.cpp
Normal file
38
projekt_linux/src/main.cpp
Normal 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_linux/src/render.cpp
Normal file
93
projekt_linux/src/utils.cpp
Normal 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];
|
||||
}
|
||||
}
|
||||
143
projekt_linux/src/volcano_kernels.s
Normal file
@ -0,0 +1,143 @@
|
||||
.intel_syntax noprefix
|
||||
|
||||
.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
|
||||
|
||||
# Linux / System V AMD64 notes:
|
||||
# Integer/pointer args: rdi, rsi, rdx, rcx, r8, r9, then stack
|
||||
# Float args in scalar/vector registers: xmm0-xmm7 (depending on signature)
|
||||
# Callee-saved GPRs: rbx, rbp, r12-r15
|
||||
# Red zone exists on Linux, but for student code prefer explicit stack frame.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# float volcanoFlowFromDropAsm(float drop, float k);
|
||||
# drop -> xmm0, k -> xmm1
|
||||
# -----------------------------------------------------------------------------
|
||||
.global volcanoFlowFromDropAsm
|
||||
volcanoFlowFromDropAsm:
|
||||
# TODO: scalar helper used by lava flow
|
||||
ret
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# void volcanoParticleStepAsm(...)
|
||||
# Pointer args arrive in:
|
||||
# rdi=posX, rsi=posY, rdx=posZ, rcx=velX, r8=velY, r9=velZ
|
||||
# Remaining scalar floats follow System V classification rules.
|
||||
# -----------------------------------------------------------------------------
|
||||
.global volcanoParticleStepAsm
|
||||
volcanoParticleStepAsm:
|
||||
# TODO: scalar single-particle step for Linux ABI
|
||||
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
|
||||