|
1 | 1 | #include "nfs.hpp" |
| 2 | +#include "components/parent.hpp" |
| 3 | +#include "render/model/3d/cube.hpp" |
2 | 4 | #include "render/model/3d/sphere.hpp" |
3 | 5 |
|
4 | 6 | #include <expected> |
@@ -130,6 +132,19 @@ static std::expected<void, std::string> startup(/* clang-format off */ |
130 | 132 |
|
131 | 133 | registry->emplace<components::Velocity>(ent, glm::vec3(0.0f, 0.0f, 0.0f)); |
132 | 134 | registry->emplace<components::AngularVelocity>(ent, glm::vec3(0.0f, 0.0f, 0.0f)); |
| 135 | + registry->emplace<scenes::nfs::components::Car>(ent); // mark as car entity |
| 136 | + |
| 137 | + auto cubeModel = std::make_shared<model::Cube>(glm::vec3(1.0f)); |
| 138 | + |
| 139 | + auto cubeEnt = registry->create(); |
| 140 | + registry->emplace<components::Position>(cubeEnt, -constants::WORLD_FORWARD * 5.0f); |
| 141 | + registry->emplace<components::Child>(cubeEnt, ent); // make it a child of the car |
| 142 | + registry->emplace<components::Model3D>(cubeEnt, cubeModel); |
| 143 | + |
| 144 | + std::vector<entt::entity> carChildren; |
| 145 | + carChildren.push_back(cubeEnt); |
| 146 | + |
| 147 | + registry->emplace<components::Parent>(ent, carChildren); // make car a parent of the cube |
133 | 148 | } |
134 | 149 |
|
135 | 150 | { // city |
@@ -178,8 +193,8 @@ static std::expected<void, std::string> update(/* clang-format off */ |
178 | 193 | ) { /* clang-format on */ |
179 | 194 |
|
180 | 195 | // Car controller system |
181 | | - auto carView = |
182 | | - registry->view<components::Position, components::Rotation, components::Velocity, components::AngularVelocity>(); |
| 196 | + auto carView = registry->view<components::Position, components::Rotation, components::Velocity, components::AngularVelocity, |
| 197 | + scenes::nfs::components::Car>(); |
183 | 198 |
|
184 | 199 | for (auto entity : carView) { |
185 | 200 | auto& position = carView.get<components::Position>(entity); |
@@ -268,81 +283,78 @@ static std::expected<void, std::string> update(/* clang-format off */ |
268 | 283 | angularVelocity.value.z = targetAngularVelocity; |
269 | 284 | } |
270 | 285 |
|
271 | | - // GTA 5-style camera system |
272 | | - auto carView = registry->view<components::Position, components::Rotation>(); |
273 | | - for (auto carEntity : carView) { |
274 | | - auto& carPosition = carView.get<components::Position>(carEntity); |
275 | | - auto& carRotation = carView.get<components::Rotation>(carEntity); |
| 286 | + // GTA 5-style camera system - use the current car entity |
| 287 | + auto& carPosition = position; // Use the current car's position |
| 288 | + auto& carRotation = rotation; // Use the current car's rotation |
276 | 289 |
|
277 | | - // Get car forward direction |
278 | | - glm::vec3 carForward = carRotation.value * glm::vec3(0.0f, 1.0f, 0.0f); |
| 290 | + // Get car forward direction |
| 291 | + glm::vec3 carForward = carRotation.value * glm::vec3(0.0f, 1.0f, 0.0f); |
279 | 292 |
|
280 | | - // Handle mouse input for camera rotation |
281 | | - glm::vec2 mouseDelta = input::Mouse::getPositionDelta(); |
282 | | - bool hasMouseInput = glm::length(mouseDelta) > 0.01f; |
| 293 | + // Handle mouse input for camera rotation |
| 294 | + glm::vec2 mouseDelta = input::Mouse::getPositionDelta(); |
| 295 | + bool hasMouseInput = glm::length(mouseDelta) > 0.01f; |
283 | 296 |
|
284 | | - if (hasMouseInput) { |
285 | | - // Apply mouse sensitivity with delta time for frame-rate independent movement |
286 | | - float deltaTimeSensitivity = cameraState->mouseSensitivity * time.deltaTime; |
287 | | - cameraState->yaw -= mouseDelta.x * deltaTimeSensitivity; |
288 | | - cameraState->pitch += mouseDelta.y * deltaTimeSensitivity; // Fixed inversion |
| 297 | + if (hasMouseInput) { |
| 298 | + // Apply mouse sensitivity with delta time for frame-rate independent movement |
| 299 | + float deltaTimeSensitivity = cameraState->mouseSensitivity * time.deltaTime; |
| 300 | + cameraState->yaw -= mouseDelta.x * deltaTimeSensitivity; |
| 301 | + cameraState->pitch += mouseDelta.y * deltaTimeSensitivity; // Fixed inversion |
289 | 302 |
|
290 | | - // Clamp pitch to prevent camera flipping |
291 | | - cameraState->pitch = std::clamp(cameraState->pitch, -1.4f, 0.5f); // About -80° to +30° |
| 303 | + // Clamp pitch to prevent camera flipping |
| 304 | + cameraState->pitch = std::clamp(cameraState->pitch, -1.4f, 0.5f); // About -80° to +30° |
292 | 305 |
|
293 | | - cameraState->isUserControlling = true; |
294 | | - cameraState->timeSinceLastInput = 0.0f; |
295 | | - } else { |
296 | | - cameraState->timeSinceLastInput += time.deltaTime; |
| 306 | + cameraState->isUserControlling = true; |
| 307 | + cameraState->timeSinceLastInput = 0.0f; |
| 308 | + } else { |
| 309 | + cameraState->timeSinceLastInput += time.deltaTime; |
297 | 310 |
|
298 | | - // Start auto-centering after delay |
299 | | - if (cameraState->timeSinceLastInput > cameraState->autoReturnDelay) { |
300 | | - cameraState->isUserControlling = false; |
| 311 | + // Start auto-centering after delay |
| 312 | + if (cameraState->timeSinceLastInput > cameraState->autoReturnDelay) { |
| 313 | + cameraState->isUserControlling = false; |
301 | 314 |
|
302 | | - // Calculate target yaw (behind the car) |
303 | | - glm::vec3 carForwardXY = glm::normalize(glm::vec3(carForward.x, carForward.y, 0.0f)); |
304 | | - cameraState->targetYaw = atan2(carForwardXY.y, carForwardXY.x) + constants::PI; // Behind the car |
| 315 | + // Calculate target yaw (behind the car) |
| 316 | + glm::vec3 carForwardXY = glm::normalize(glm::vec3(carForward.x, carForward.y, 0.0f)); |
| 317 | + cameraState->targetYaw = atan2(carForwardXY.y, carForwardXY.x) + constants::PI; // Behind the car |
305 | 318 |
|
306 | | - // Smoothly interpolate back to target position |
307 | | - float returnSpeed = cameraState->autoReturnSpeed * time.deltaTime; |
| 319 | + // Smoothly interpolate back to target position |
| 320 | + float returnSpeed = cameraState->autoReturnSpeed * time.deltaTime; |
308 | 321 |
|
309 | | - // Handle yaw wrapping (shortest rotation path) |
310 | | - float yawDiff = cameraState->targetYaw - cameraState->yaw; |
311 | | - while (yawDiff > glm::pi<float>()) |
312 | | - yawDiff -= 2.0f * glm::pi<float>(); |
313 | | - while (yawDiff < -glm::pi<float>()) |
314 | | - yawDiff += 2.0f * glm::pi<float>(); |
| 322 | + // Handle yaw wrapping (shortest rotation path) |
| 323 | + float yawDiff = cameraState->targetYaw - cameraState->yaw; |
| 324 | + while (yawDiff > glm::pi<float>()) |
| 325 | + yawDiff -= 2.0f * glm::pi<float>(); |
| 326 | + while (yawDiff < -glm::pi<float>()) |
| 327 | + yawDiff += 2.0f * glm::pi<float>(); |
315 | 328 |
|
316 | | - cameraState->yaw += yawDiff * returnSpeed; |
317 | | - cameraState->pitch += (cameraState->targetPitch - cameraState->pitch) * returnSpeed; |
318 | | - } |
| 329 | + cameraState->yaw += yawDiff * returnSpeed; |
| 330 | + cameraState->pitch += (cameraState->targetPitch - cameraState->pitch) * returnSpeed; |
319 | 331 | } |
| 332 | + } |
320 | 333 |
|
321 | | - // Calculate camera position based on yaw, pitch, and distance |
322 | | - float cosYaw = cos(cameraState->yaw); |
323 | | - float sinYaw = sin(cameraState->yaw); |
324 | | - float cosPitch = cos(cameraState->pitch); |
325 | | - float sinPitch = sin(cameraState->pitch); |
| 334 | + // Calculate camera position based on yaw, pitch, and distance |
| 335 | + float cosYaw = cos(cameraState->yaw); |
| 336 | + float sinYaw = sin(cameraState->yaw); |
| 337 | + float cosPitch = cos(cameraState->pitch); |
| 338 | + float sinPitch = sin(cameraState->pitch); |
326 | 339 |
|
327 | | - // Camera offset in spherical coordinates (negative distance to position behind car) |
328 | | - glm::vec3 cameraOffset; |
329 | | - cameraOffset.x = -cameraState->distance * cosYaw * cosPitch; |
330 | | - cameraOffset.y = -cameraState->distance * sinYaw * cosPitch; |
331 | | - cameraOffset.z = cameraState->height + cameraState->distance * sinPitch; |
| 340 | + // Camera offset in spherical coordinates (negative distance to position behind car) |
| 341 | + glm::vec3 cameraOffset; |
| 342 | + cameraOffset.x = -cameraState->distance * cosYaw * cosPitch; |
| 343 | + cameraOffset.y = -cameraState->distance * sinYaw * cosPitch; |
| 344 | + cameraOffset.z = cameraState->height + cameraState->distance * sinPitch; |
332 | 345 |
|
333 | | - glm::vec3 cameraPos = carPosition.value + cameraOffset; |
| 346 | + glm::vec3 cameraPos = carPosition.value + cameraOffset; |
334 | 347 |
|
335 | | - // Camera looks at the car (with slight forward offset) |
336 | | - glm::vec3 lookTarget = carPosition.value + carForward * 1.0f + glm::vec3(0.0f, 0.0f, 1.0f); |
337 | | - glm::vec3 cameraDir = glm::normalize(lookTarget - cameraPos); |
| 348 | + // Camera looks at the car (with slight forward offset) |
| 349 | + glm::vec3 lookTarget = carPosition.value + carForward * 1.0f + glm::vec3(0.0f, 0.0f, 1.0f); |
| 350 | + glm::vec3 cameraDir = glm::normalize(lookTarget - cameraPos); |
338 | 351 |
|
339 | | - // Update renderer camera |
340 | | - renderer->setCameraPos(cameraPos); |
341 | | - renderer->setCameraDir(cameraDir); |
| 352 | + // Update renderer camera |
| 353 | + renderer->setCameraPos(cameraPos); |
| 354 | + renderer->setCameraDir(cameraDir); |
342 | 355 |
|
343 | | - // Only handle the first car entity for now |
344 | | - break; |
345 | | - } |
| 356 | + // Only handle the first car entity for now |
| 357 | + break; |
346 | 358 | } |
347 | 359 |
|
348 | 360 | return {}; |
|
0 commit comments