diff --git a/CMakeLists.txt b/CMakeLists.txt index 89195ba..13f111b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.7) -project(boatshit) +project(boat-racing-game) set(CMAKE_CXX_STANDARD 14) @@ -16,14 +16,16 @@ find_package(DevIL REQUIRED) find_package(ASSIMP REQUIRED) find_package(OpenAL REQUIRED) -add_executable(main main.cpp) #water.cpp sky.cpp) +add_executable(game main.cpp particles.cpp) #water.cpp sky.cpp) -target_include_directories(main PUBLIC - ${GLM_INCLUDE_DIRS} ${SDL2_INCLUDE_DIRS} ${CURL_INCLUDE_DIR} ${GLEW_INCLUDE_DIR} ${ASSIMP_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} +target_compile_definitions(game PUBLIC -DGLM_ENABLE_EXPERIMENTAL) + +target_include_directories(game PUBLIC + ${GLM_INCLUDE_DIRS} ${CURL_INCLUDE_DIR} ${GLEW_INCLUDE_DIR} ${ASSIMP_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ) -target_link_libraries(main - ${SDL2_LIBRARIES} ${CURL_LIBRARY} ${GLEW_LIBRARY} -lGL -lGLU ${IL_LIBRARIES} ${ILU_LIBRARIES} ${ASSIMP_LIBRARIES} ${OPENAL_LIBRARY} -lalut +target_link_libraries(game + SDL2::SDL2 ${CURL_LIBRARY} ${GLEW_LIBRARY} -lGL -lGLU ${IL_LIBRARIES} ${ILU_LIBRARIES} ${ASSIMP_LIBRARIES} ${OPENAL_LIBRARY} -lalut ) add_subdirectory(data) diff --git a/boat.cpp b/boat.cpp index e858333..1e52cb6 100644 --- a/boat.cpp +++ b/boat.cpp @@ -88,14 +88,14 @@ class Boat { float cornerRadius = finRudderDistance / sinf(rudderAngle); cornerRadius *= 1.0f + distance * distance * 0.5f; // FIXME: Hack to limit sharp corners at high speeds - position.x -= cornerRadius * sinf(-heading); - position.y -= cornerRadius * cosf(-heading); - heading -= distance / cornerRadius; - position.x += cornerRadius * sinf(-heading); - position.y += cornerRadius * cosf(-heading); + position.x += cornerRadius * cosf(-heading); + position.y += cornerRadius * sinf(-heading); + heading += distance / cornerRadius; + position.x -= cornerRadius * cosf(-heading); + position.y -= cornerRadius * sinf(-heading); } else { - position.x += distance * cosf(heading); - position.y += distance * sinf(heading); + position.x += distance * sinf(heading); + position.y += distance * cosf(heading); } input.steer = 0.0f; //FIXME: Clamp only! @@ -118,11 +118,20 @@ class Boat { return position; } + glm::mat4 matrix() const { + glm::mat4 m = glm::mat4(1.0f); + m *= glm::translate(glm::vec3(position.x, position.y, position.z)); + m *= glm::rotate(-heading, glm::vec3(0.0f, 0.0f, 1.0f)); + return m; + } + void draw() { //FIXME! Do translation etc glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glRotatef(glm::degrees(heading), 0.0f, 0.0f, 1.0f); + // glTranslatef(position.x, position.y, position.z); + //glRotatef(glm::degrees(heading), ); + glMultMatrixf(glm::value_ptr(matrix())); + glRotatef(90.0f, 0.0f, 0.0f, 1.0f); model->draw(); glPopMatrix(); } diff --git a/camera.cpp b/camera.cpp index 3a2b2b8..35040a5 100644 --- a/camera.cpp +++ b/camera.cpp @@ -19,7 +19,7 @@ class Camera { public: //FIXME: Supply fov in radians.. - Camera() : fov(0.7853981634f), nearPlane(1.0f), farPlane(1000.0f) {} + Camera() : fov(0.7853981634f * 1.5f), nearPlane(1.0f), farPlane(1000.0f) {} void setFov(float fov) { this->fov = fov; diff --git a/main.cpp b/main.cpp index 662c089..817764e 100644 --- a/main.cpp +++ b/main.cpp @@ -26,12 +26,31 @@ #include "water.h" #include "sparks.h" +#include "particles.h" + #if 0 //FIXME!!! #include "glad/glad.h" #endif +static void draw_axis(glm::mat4 matrix) { + + glm::vec3 root = matrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + glm::vec3 x = matrix * glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); + glm::vec3 y = matrix * glm::vec4(0.0f, 1.0f, 0.0f, 1.0f); + glm::vec3 z = matrix * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f); + + // Draw a small axis system in legacy GL + glBegin(GL_LINES); + glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(root.x, root.y, root.z); glVertex3f(x.x, x.y, x.z); + glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(root.x, root.y, root.z); glVertex3f(y.x, y.y, y.z); + glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(root.x, root.y, root.z); glVertex3f(z.x, z.y, z.z); + glEnd(); + + return; +} + int main(int argc, char* argv[]) { std::cout << "Hey!" << std::endl; @@ -179,10 +198,49 @@ int main(int argc, char* argv[]) { boat.position.x = i * 12.0f; } - std::shared_ptr testModel = std::make_shared("data/model/test"); +// std::shared_ptr testModel = std::make_shared("data/model/test"); + std::shared_ptr testModel = std::make_shared("/home/fox/Data/Projects/boat-game-proto-data/data/monaco"); + + + Particles particles; + + // Load particles from file + { + FILE* f = fopen("/home/fox/box-constraint.obj", "rb"); + + // Keep track of the first index + unsigned int base = particles.particles.size(); + + while(!feof(f)) { + char buf[512]; + fgets(buf, sizeof(buf), f); + + float x; + float y; + float z; + + unsigned int a; + unsigned int b; + + if (sscanf(buf, "v %f %f %f", &x, &y, &z) == 3) { + std::cout << "Adding particle at " << glm::to_string(glm::vec3(x, y, z)) << std::endl; + particles.addParticle(glm::vec3(x, y, z) + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f)); + } else if (sscanf(buf, "l %u %u", &a, &b) == 2) { + particles.addDistanceConstraint(base + (a - 1), base + (b - 1)); + } else { + printf("Unknown %s\n", buf); + } + } + fclose(f); + } + + bool warp_camera = true; while(!Platform::hasQuit()) { +std::cout << "[0] " << glm::to_string(particles.particles[0].m_x) << std::endl; +std::cout << "[1] " << glm::to_string(particles.particles[1].m_x) << std::endl; + static float t = -1000.0f; t += 0.01f; @@ -248,6 +306,75 @@ int main(int argc, char* argv[]) { } #endif + // Calculate target points for the AI + glm::vec3 target_l = boat.matrix() * glm::vec4(-6.0f, 8.0f, 0.0f, 1.0f); + glm::vec3 target_r = boat.matrix() * glm::vec4(+6.0f, 8.0f, 0.0f, 1.0f); + +#if 1 + for(int i = 0; i < 2; i++) { + + auto& ai_boat = boats[1 + i]; + const glm::vec3 targets[] = { target_l, target_r }; + + // Update AI boat input + auto& ai_input = ai_boat.input; + + glm::vec3 target_local = glm::inverse(ai_boat.matrix()) * glm::vec4(targets[i], 1.0f); + + //std::cout << glm::to_string(target_local) << std::endl; + + // Check if the point is infront of the AI and set speed accordingly + float target_speed; + if (target_local.y > 0.0f) { + target_speed = 0.2f; + + if (target_local.x < 0.0f) { + ai_input.steer = -1.0f; + } else { + ai_input.steer = +1.0f; + } + + if (target_local.y > 10.0f) { + + ai_input.brake = 0.0f; + target_speed = 2.0f; + if (target_speed > 2.0f) { + target_speed = 2.0f; + } + + } + + } else { + ai_input.accelerate = 0.0f; + target_speed = 0.0f; + } + + // Go reverse if we are too far in front + if (target_local.y < -50.0f) { + ai_input.steer = 0.0f; + ai_input.accelerate = 0.0f; + + target_speed = -0.5f; + + if (target_local.x > 0.0f) { + ai_input.steer = -1.0f; + } else { + ai_input.steer = +1.0f; + } + } + + if (ai_boat.engineSpeed > target_speed) { + ai_input.brake = 1.0f; + ai_input.accelerate = 0.0f; + } else { + ai_input.brake = 0.0f; + ai_input.accelerate = 1.0f; + } + + } + +#endif + #if 1 // Update boats for(unsigned int i = 0; i < boats.size(); i++) { @@ -279,21 +406,39 @@ int main(int argc, char* argv[]) { #if 1 // Set camera to follow player boat - glm::vec2 o(cosf(boat.heading), sinf(boat.heading)); - glm::vec3 cp(boat.getPosition() + glm::vec3(o * 5.0f, 5.0f)); + float maxDist = 10.0f; + glm::vec2 o(sinf(boat.heading), cosf(boat.heading)); + glm::vec3 cp(boat.getPosition() + glm::vec3(o * 5.0f, 3.0f)); //cp = glm::mix(camera->getPosition(), cp, 0.5f); - glm::vec3 d = camera->getPosition() - cp; - float maxDist = 15.0f; - if (glm::length(d) > maxDist) { - cp = glm::mix(camera->getPosition(), cp + glm::normalize(d) * maxDist, 0.3f); - camera->setPosition(cp); + if (warp_camera) { + camera->setPosition(cp - glm::vec3(o, 0.0f) * maxDist); + warp_camera = false; + } else { + + glm::vec3 d = camera->getPosition() - cp; + if (glm::length(d) > maxDist) { + cp = glm::mix(camera->getPosition(), cp + glm::normalize(d) * maxDist, 0.3f); + camera->setPosition(cp); + } + } + camera->lookAt(boat.getPosition() + glm::vec3(0.0f, 0.0f, 1.0f)); + + +#endif + +#if 0 + // Birds eye view + camera->setPosition(glm::vec3(0.0, -1.0, 300.0)); + camera->lookAt(glm::vec3(0.0f, 0.0f, 0.0f)); +#endif - camera->lookAt(boat.getPosition() + glm::vec3(0.0f, 0.0f, 2.0f)); updateMatrix(); + +#if 1 glm::vec3 p = camera->getPosition(); alListener3f(AL_POSITION, p.x, p.y, p.z); //FIXME: velocity, forward and up @@ -328,6 +473,60 @@ int main(int argc, char* argv[]) { } #endif + bool particle_test_stuck = state[SDL_SCANCODE_P]; + bool particle_test_forward = state[SDL_SCANCODE_O]; + static bool was_stuck = false; + if (particle_test_stuck) { + if (!was_stuck) { + particles.particles[3].m_x = particles.particles[3].m_oldx; + particles.particles[7].m_x = particles.particles[7].m_oldx; + was_stuck = true; + } + } else { + was_stuck = false; + } + if (particle_test_forward) { + for(int i = 0; i < particles.particles.size(); i++) { + //particles.particles[i].m_x.x = particles.particles[i].m_oldx.x + 70.0f / 60.0f; + } + + particles.particles[2].m_x = boats[0].matrix() * glm::vec4(-1.0f,-1.0f,0.0f,1.0f); + particles.particles[6].m_x = boats[0].matrix() * glm::vec4(+1.0f,-1.0f,0.0f,1.0f); + + particles.particles[3].m_x = boats[0].matrix() * glm::vec4(-1.0f,+1.0f,0.0f,1.0f); + particles.particles[7].m_x = boats[0].matrix() * glm::vec4(+1.0f,+1.0f,0.0f,1.0f); + + // Hack to fix a collapsed box + particles.particles[0].m_x = boats[0].matrix() * glm::vec4(-1.0f,-1.0f,2.0f,1.0f); + } + + particles.Step(); + + glUseProgram(0); + + glLineWidth(5.0f); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + for(int i = 0; i < particles.distance_constraints.size(); i++) { + const auto& c = particles.distance_constraints[i]; + const auto& a = c.a; + const auto& b = c.b; + Platform::drawLine(particles.particles[a].m_x, particles.particles[b].m_x); + } + + glPointSize(10.0f); + glColor4f(0.0f, 1.0f, 0.0f, 1.0f); + for(int i = 0; i < particles.particles.size(); i++) { + Platform::drawPoint(particles.particles[i].m_x); + } + + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glPointSize(15.0f); + Platform::drawPoint(particles.particles[3].m_x); + Platform::drawPoint(particles.particles[7].m_x); + + + glLineWidth(1.0f); + glPointSize(1.0f); struct Checkpoint { glm::vec3 origin; @@ -348,7 +547,7 @@ int main(int argc, char* argv[]) { genericShader->activate(); Plane cpPlane = Plane(glm::rotateZ(glm::vec3(0.0f, 1.0f, 0.0f), cp.angle), cp.origin); glColor3f(1.0f, 0.0f, 1.0f); - cpPlane.draw(1.0f, 100, 100, glm::vec3(0.0f, 0.0f, 1.0f)); + cpPlane.draw(1.0f, 30, 30, glm::vec3(0.0f, 0.0f, 1.0f)); // Draw tag text genericTexturedShader->activate(); @@ -363,8 +562,8 @@ int main(int argc, char* argv[]) { genericShader->activate(); - float gap = 10.0f; - glColor3f(1.0f, 1.0f, 1.0f); + float gap = 1.0f; + glColor4f(1.0f, 1.0f, 1.0f, 0.2f); slicePlane.draw(gap, 1100.0f / gap, 1100.0f / gap); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -422,17 +621,19 @@ int main(int argc, char* argv[]) { // FIXME: Draw the boats #if 1 - // Draw a small axis system in legacy GL - glBegin(GL_LINES); - glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f); - glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.0f, 0.0f); - glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.0f); - glEnd(); + // Draw an axis on the coordinate system origin + draw_axis(glm::translate(glm::vec3(0.0f, 0.0f, 0.0f))); +#endif + +#if 1 + // Draw an axis on the AI targets + draw_axis(glm::translate(target_l)); + draw_axis(glm::translate(target_r)); #endif glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); -#if 0 +#if 1 // Test models genericTexturedShader->activate(); genericTexturedShader->set("texture", 0u); @@ -507,7 +708,11 @@ int main(int argc, char* argv[]) { #if 1 glColor4f(0.1f, 0.2f, 0.5f, 1.0f); char symbolMicro = '\xB5'; - drawText(glm::vec2(-0.95f, 0.9f - 0.2f), std::string("boats: ") + std::to_string(boats.size()) + std::string("\nsparks: ") + std::to_string(sparks.size()) + "\n" + symbolMicro + "/F: " + std::to_string(uspf.count())); + drawText(glm::vec2(-0.95f, 0.9f - 0.2f), + std::string("boats: ") + std::to_string(boats.size()) + + std::string("\nsparks: ") + std::to_string(sparks.size()) + "\n" + + std::string("speed: ") + std::to_string(60.0f * boats[0].engineSpeed * 3.6f) + "km/h\n" + + symbolMicro + "/F: " + std::to_string(uspf.count())); #endif glFinish(); diff --git a/model.cpp b/model.cpp index 848a332..8a610ff 100644 --- a/model.cpp +++ b/model.cpp @@ -83,13 +83,13 @@ class Model { Model(std::string path) : Model(path, path) {} Model(std::shared_ptr mesh) : mesh(mesh) {} - void draw(glm::mat4 matrix = glm::mat4()) { + void draw(glm::mat4 matrix = glm::mat4(1.0f)) { // FIXME: Draw children if (texture != nullptr) { texture->activate(0); } if (scene != nullptr) { - recursive_render(glm::mat4(), scene, scene->mRootNode); + recursive_render(glm::mat4(1.0f), scene, scene->mRootNode); } /* mesh->draw(); diff --git a/particles.cpp b/particles.cpp new file mode 100644 index 0000000..82558d2 --- /dev/null +++ b/particles.cpp @@ -0,0 +1,82 @@ +// Mostly taken from http://graphics.cs.cmu.edu/nsp/course/15-869/2006/papers/jakobsen.htm + +#include "particles.h" + +#include + +#define NUM_ITERATIONS 10 +#define TIMESTEP (1.0f / 60.0f) + +// Verlet integration step +void Particles::Verlet() { + for(int i = 0; i < particles.size(); i++) { + auto& x = particles[i].m_x; + auto temp = x; + auto& oldx = particles[i].m_oldx; + auto& a = particles[i].m_a; + float dt = TIMESTEP; + x += x - oldx + a * dt * dt; + oldx = temp; + } + return; +} + +void Particles::AccumulateForces() { + + // All particles are influenced by gravity + for(int i = 0; i < particles.size(); i++) { + particles[i].m_a = m_vGravity; + } + + return; +} + +void Particles::SatisfyConstraints() { + for(int j = 0; j < NUM_ITERATIONS; j++) { + + // First satisfy (C1) + for(int i = 0; i < particles.size(); i++) { // For all particles + auto& x = particles[i].m_x; + x = glm::clamp(x, glm::vec3(-1000.0f, -1000.0f, 0.0f), glm::vec3(1000.0f, 1000.0f, 1000.0f)); + } + + // Then satisfy (C2) + for(int i = 0; i < distance_constraints.size(); i++) { + const auto& c = distance_constraints[i]; + + auto& x1 = particles[c.a].m_x; + auto& x2 = particles[c.b].m_x; + auto delta = x2 - x1; +#if 1 + float deltalength = glm::length(delta); + std::cout << "delta: " << deltalength << std::endl; + float diff = (deltalength - c.distance) / deltalength; + x1 += delta * 0.5f * diff; + x2 -= delta * 0.5f * diff; +#if 0 + // Pseudo-code to satisfy (C2) while respecting particle masses + float diff = (deltalength - c.distance) / (deltalength * (invmass1 + invmass2)); + x1 += invmass1*delta*diff; + x2 -= invmass2*delta*diff; +#endif + +#else + // Pseudo-code for satisfying (C2) using sqrt approximation + float precalc = c.distance * c.distance; + delta *= precalc / (delta * delta + precalc) - 0.5f; + x1 += delta; + x2 -= delta; +#endif + } + + } + + return; +} + +void Particles::Step() { + AccumulateForces(); + Verlet(); + SatisfyConstraints(); + return; +} diff --git a/particles.h b/particles.h new file mode 100644 index 0000000..0831140 --- /dev/null +++ b/particles.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#include + +// Sample code for physics simulation +class Particles { + +public: + struct Particle { + glm::vec3 m_x; // Current positions + glm::vec3 m_oldx; // Previous positions + glm::vec3 m_a; // Force accumulators + }; + + struct DistanceConstraint { + unsigned int a; + unsigned int b; + float distance; + }; + + std::vector particles; + std::vector distance_constraints; + + glm::vec3 m_vGravity = glm::vec3(0.0f, 0.0f, -9.81f); // Gravity + +public: + + void addDistanceConstraint(unsigned int a, unsigned int b) { + DistanceConstraint c; + c.a = a; + c.b = b; + //FIXME: Take length from parameters instead + c.distance = glm::distance(particles[a].m_x, particles[b].m_x); + distance_constraints.emplace_back(c); + } + + void addParticle(glm::vec3 position, glm::vec3 velocity) { + Particle p; + p.m_x = position; + p.m_oldx = position - velocity; + particles.emplace_back(p); + } + + void Step(); + +private: + void Verlet(); + void SatisfyConstraints(); + void AccumulateForces(); +}; diff --git a/platform.cpp b/platform.cpp index d2f28d4..debfa25 100644 --- a/platform.cpp +++ b/platform.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "SDL.h" #include #include