-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
310 lines (259 loc) · 11.2 KB
/
main.cpp
File metadata and controls
310 lines (259 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include <iostream>
#include <vector>
#include <cmath>
#include <chrono>
#include <optional>
#include <set>
#include <random>
#include "Objects/Box.h"
#include "Ray.h"
#include "Constants.h"
#include <SFML/Graphics.hpp>
#include "RayMarchingRender.h"
#include "Objects/Mandelbulb.h"
#include "Objects/QuaternionJulia.h"
#include "Objects/Plane.h"
#include "Objects/Sphere.h"
#include "Objects/Terrain.h"
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Random number generator for QuaternionJulia animation
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> signDist(0, 1); // 0 or 1 for sign
// ---------------- CAMERA ----------------
// Start camera at standing height (Z = 2) looking forward
// Z is up, Y is forward, X is right
Ray camera({0, 0, 10}, Z*-1+X*0.001);
// FPS camera state - using proper spherical coordinates
// Yaw: rotation around Z axis (horizontal look left/right)
// Pitch: angle from horizontal plane (vertical look up/down)
double yaw = 0.0; // 0 = looking along +Y
double pitch = 0.0; // 0 = looking horizontally
const double mouseSensitivity = 0.003;
const double maxPitch = PI / 2.1;
// ---------------- SCENE ----------------
// Shadow demonstration scene:
// - Big box at the top
// - Sphere below the box (should be in shadow, but isn't without shadow implementation)
std::vector<Object*> scene;
// Terrain: gentle hills around origin. originXZ = (0,0,0) -> we use x,z for horizontal domain, y stores seed
// auto* terrain = new Terrain({0, 0, 0}, /*amplitude*/ 30.0f, /*frequency*/ 0.005f, /*seed*/ 3.0f, sf::Color(30, 140, 40));
// auto* terrain2 = new Terrain({0, 0, -50}, /*amplitude*/ 80.0f, /*frequency*/ 0.005f, /*seed*/ 3.0f, sf::Color(30, 35, 40));
// terrain->setWarp(2.0f, true).setRidged(false);
// terrain2->setWarp(2.0f, true).setRidged(false);
// scene.push_back(terrain);
// scene.push_back(terrain2);
// A sphere above terrain to look at
// scene.push_back(new Sphere({0, 10, 6}, 1.0, sf::Color::Red));
// Green floor plane at Z = 0 (ground level)
scene.push_back(new Plane({0, 0, 0}, Z, sf::Color::Green, 0.5f));
// A sphere on the floor to look at (at position Y=10, Z=1 for radius)
scene.push_back(new Sphere({0, 10, 1}, 5.0, sf::Color::Red, "textures/petyb.jpg"));
scene.push_back(new Mandelbulb({0, 5, 50}, 8, 1.0, sf::Color::Red, 30, "textures/Texturelabs_Atmosphere_126M.jpg"));
//scene.push_back(new Mandelbulb({0, 5, 50}, 8, 1.0, sf::Color::Red, 30, "textures/petyb.jpg"));
scene.push_back(new Box({1, 1, 1}, {1, 2, 1}, sf::Color::Blue, "textures/petyb.jpg"));
scene.push_back(new Box({5, 1, 1}, {1, 1, 1}, sf::Color::Blue, "textures/Pavel.png"));
scene.push_back(new Box({9, 1, 1}, {1, 1, 1}, sf::Color::Blue, "textures/Anatoly.png"));
scene.push_back(new QuaternionJulia(
{0, 5, 30}, // center position
{0.3, 0.5, 0.1}, // Julia constant c (affects the fractal shape)
12, // iterations (more = more detail)
20.0, // scale
sf::Color::Magenta, // color
"textures/fire.jpg" // optional texture
));
// Big box floating above (at Z = 8, centered at Y = 10)
// This box should cast a shadow on the sphere below
scene.push_back(new Box({0, 10, 8}, {2.0, 2.0, 1.0}, sf::Color::White, 1));
// // Optional: keep fractal far away
// scene.push_back(new Mandelbulb({0, 5, 50}, 8, 1.0, sf::Color::Red, 40));
// Sphere below the box (at Y = 10, Z = 2)
// Give it some reflectivity so reflections are visible (0 = none, 1 = mirror)
scene.push_back(new Sphere({0, 20, 2}, 1.5, sf::Color::White, 0.8f));
// scene.push_back(new Box({1, 1, 1}, {1, 1, 1}, sf::Color::Blue, "textures/Texturelabs_Atmosphere_126M.jpg"));
// Sun-like light source (bright yellow sphere in the sky)
//scene.push_back(new Sphere({0, 20, 15}, 2.0, sf::Color(255, 255, 200)));
// Light direction (pointing from sun position)
// Light source positioned above and to the side
// This creates a clear shadow that should fall on the sphere
scene.push_back(new Sphere({-5, 10, 12}, 1.0, sf::Color::White));
// Light direction (pointing from light position toward the scene)
Vector3 lightDir = (Vector3(0, -20, 15) - Vector3(0, 0, 2)).normalized();
scene.push_back(new Sphere({0, -21, 16}, 0.2, sf::Color::Yellow));
RayMarchingRender renderer(
1280,
720,
PI / 3, // 60 degrees FOV - more natural, less warping
lightDir,
scene
);
auto& window = renderer.window;
// Mouse control state
bool mouseInWindow = false;
bool firstMouseMove = true;
bool lmbDown = false;
double lastMouseX = renderer.width / 2.0;
double lastMouseY = renderer.height / 2.0;
double moveSpeed = 10;
window.setMouseCursorVisible(true); // Show mouse cursor
// Track pressed keys for event-based input (avoids permission issues)
std::set<sf::Keyboard::Key> pressedKeys;
double fps = 1;
// ---------------- MAIN LOOP ----------------
while (window.isOpen())
{
auto start = std::chrono::high_resolution_clock::now();
while (const std::optional<sf::Event> event = window.pollEvent())
{
if (event->is<sf::Event::Closed>())
{
window.close();
}
else if (event->is<sf::Event::Resized>())
{
const auto* resized = event->getIf<sf::Event::Resized>();
renderer.setSize(resized->size.x, resized->size.y);
// Update center position after resize
lastMouseX = resized->size.x / 2.0;
lastMouseY = resized->size.y / 2.0;
firstMouseMove = true;
}
else if (event->is<sf::Event::MouseEntered>())
{
mouseInWindow = true;
firstMouseMove = true;
}
else if (event->is<sf::Event::MouseLeft>())
{
mouseInWindow = false;
lmbDown = false;
}
else if (event->is<sf::Event::MouseButtonPressed>())
{
const auto* mb = event->getIf<sf::Event::MouseButtonPressed>();
if (mb->button == sf::Mouse::Button::Left)
{
lmbDown = true;
firstMouseMove = true; // чтобы не дёргалось при первом движении
}
}
else if (event->is<sf::Event::MouseButtonReleased>())
{
const auto* mb = event->getIf<sf::Event::MouseButtonReleased>();
if (mb->button == sf::Mouse::Button::Left)
{
lmbDown = false;
firstMouseMove = true;
}
}
else if (event->is<sf::Event::MouseMoved>())
{
// if (!mouseInWindow || !lmbDown)
// continue;
if (!lmbDown)
continue;
const auto* mouseMoved = event->getIf<sf::Event::MouseMoved>();
double mouseX = mouseMoved->position.x;
double mouseY = mouseMoved->position.y;
if (firstMouseMove)
{
lastMouseX = mouseX;
lastMouseY = mouseY;
firstMouseMove = false;
continue;
}
double deltaX = mouseX - lastMouseX;
double deltaY = mouseY - lastMouseY;
lastMouseX = mouseX;
lastMouseY = mouseY;
yaw += deltaX * mouseSensitivity;
pitch -= deltaY * mouseSensitivity;
if (pitch > maxPitch) pitch = maxPitch;
if (pitch < -maxPitch) pitch = -maxPitch;
Vector3 forward(
std::cos(pitch) * std::sin(yaw),
std::cos(pitch) * std::cos(yaw),
std::sin(pitch)
);
camera.setDirection(forward.normalized());
}
else if (event->is<sf::Event::KeyPressed>())
{
const auto* keyPressed = event->getIf<sf::Event::KeyPressed>();
pressedKeys.insert(keyPressed->code);
}
else if (event->is<sf::Event::KeyReleased>())
{
const auto* keyReleased = event->getIf<sf::Event::KeyReleased>();
pressedKeys.erase(keyReleased->code);
}
}
// WASD Movement - check keyboard state
Vector3 moveDirection(0, 0, 0);
// Get current camera direction
Vector3 forward = camera.getDirection().normalized();
// Calculate right vector (for strafing)
// Right = forward cross world up (Z)
Vector3 right = forward.cross(Z).normalized();
if (right.magnitude() < 0.001) {
right = X; // Fallback if forward is parallel to Z
}
right = right.normalized();
// Calculate forward movement (project forward onto horizontal plane, remove Z component)
Vector3 forwardHorizontal = Vector3(forward.getX(), forward.getY(), 0).normalized();
// W - Move forward
if (pressedKeys.contains(sf::Keyboard::Key::W)) {
moveDirection = moveDirection + forwardHorizontal;
}
// S - Move backward
if (pressedKeys.contains(sf::Keyboard::Key::S)) {
moveDirection = moveDirection - forwardHorizontal;
}
// A - Strafe left
if (pressedKeys.contains(sf::Keyboard::Key::A)) {
moveDirection = moveDirection - right;
}
// D - Strafe right
if (pressedKeys.contains(sf::Keyboard::Key::D)) {
moveDirection = moveDirection + right;
}
// Q - Move up (positive Z)
if (pressedKeys.contains(sf::Keyboard::Key::Q)) {
moveDirection = moveDirection - Z;
}
// E - Move down (negative Z)
if (pressedKeys.contains(sf::Keyboard::Key::E)) {
moveDirection = moveDirection + Z;
}
if (pressedKeys.contains(sf::Keyboard::Key::LShift)) {
moveSpeed *= pow(2, 1/fps);
}
if (pressedKeys.contains(sf::Keyboard::Key::LControl)) {
moveSpeed /= pow(2, 1/fps);
if (moveSpeed < 0.01) moveSpeed = 0.01;
}
cout << moveSpeed << endl;
// Apply movement if any key is pressed
if (moveDirection.magnitude() > 0.001) {
moveDirection = moveDirection.normalized() * moveSpeed / fps;
camera.move(moveDirection);
}
// Render
renderer.renderFrame(camera);
window.display();
window.clear();
if (dynamic_cast<Mandelbulb*>(scene[2]))
dynamic_cast<Mandelbulb*>(scene[2])->power += 0.5 / fps;
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration = end - start;
std::cout << "FPS: " << 1000.0 / duration.count() << "\n";
fps = 1000.0 / duration.count();
}
for (auto* object : scene)
delete object;
return 0;
}