diff --git a/data/shader/particle.frag b/data/shader/particle.frag index 87e9819..98e8854 100644 --- a/data/shader/particle.frag +++ b/data/shader/particle.frag @@ -1,10 +1,13 @@ #version 120 +varying vec3 position; varying vec2 vertex; varying vec3 velocity; varying float decay; varying float explCenterDist; +float pi = 3.14159; + vec3 hsv2rgb(vec3 c) { vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); @@ -12,15 +15,28 @@ vec3 hsv2rgb(vec3 c) return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); } +float hash(float n) +{ + return fract(sin(n)*43758.5453); +} + void main() { - float r = length(vertex); - if (r > 1.0) { + // normalize radius from 0..1 + vec2 vn = 2.0*vertex; + float rn = length(vn); + float angle = atan(vn.y, vn.x); + float hs = 20.0; + float removeCorners = 0.5 * (hash(fract(hs*vertex.x)) + hash(fract(hs*vertex.y))); + if (rn+removeCorners > 1.0) { discard; } + //float rf = 0.5-0.5*rn; + float rf = 1.0; + float d = decay/(1.0+max(0.0, 1.0-2.0*explCenterDist)); - float h = (1.0-d)/6.0; + float h = max(0.0, (1.0-d)/6.0) * rf; float v = max(0.0, (1.0-log(d))); float s = max(0.0, min(30.0 * sqrt(decay) * explCenterDist, 1.0)); @@ -30,4 +46,5 @@ void main() gl_FragColor = vec4(hsv2rgb(vec3(h, s, v)), 1.0); //gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); + //gl_FragColor.rgb = vec3(0.5+0.5*(a/pi)); } \ No newline at end of file diff --git a/data/shader/particle.vert b/data/shader/particle.vert index b445d97..a064753 100644 --- a/data/shader/particle.vert +++ b/data/shader/particle.vert @@ -14,6 +14,7 @@ varying float decay; varying vec3 velocity; varying vec2 vertex; varying float explCenterDist; +varying vec3 position; uniform float aspectRatio; @@ -31,9 +32,9 @@ void main() // faster particles are smaller // TODO: scale by time too! scale down fast after 3 halfAges float scaleBySpeed = (1.0 - 0.95*length(in_velocity)/maxVelocity); - float finalSize = size * scaleBySpeed; + float finalSize = size * scaleBySpeed * (1.0-max(0.0, decay-3.0*halfAge)/2.0); vec2 base = in_geometry; - vec3 p = finalSize*vec3(base, 0.0); + vec3 p = vec3(finalSize*base, 0.0); vec3 move = (0.2*age + log(1.0+age*5.0)) * in_velocity; float md = length(move); if (md > in_maxDist) { @@ -46,6 +47,7 @@ void main() vertex = base.xy; velocity = in_velocity; + position = in_position; explCenterDist = length(explCenter - offset); } \ No newline at end of file diff --git a/game/renderer_polygon_3d/particle_batch.cpp b/game/renderer_polygon_3d/particle_batch.cpp index 755c6af..8189c83 100644 --- a/game/renderer_polygon_3d/particle_batch.cpp +++ b/game/renderer_polygon_3d/particle_batch.cpp @@ -25,10 +25,10 @@ namespace endofthejedi { // TODO // it is transformed before uploading so it looks at the camera , m_data_geometry({ - 1.0f, -1.0f, - 1.0f, 1.0f, - -1.0f, 1.0f, - -1.0f, -1.0f}) + 0.5f, -0.5f, + 0.5f, 0.5f, + -0.5f, 0.5f, + -0.5f, -0.5f}) { m_num_vertex_buffers = 4; diff --git a/game/renderer_polygon_3d/renderer_polygon_3d.cpp b/game/renderer_polygon_3d/renderer_polygon_3d.cpp index 47295ac..d4f2d8e 100644 --- a/game/renderer_polygon_3d/renderer_polygon_3d.cpp +++ b/game/renderer_polygon_3d/renderer_polygon_3d.cpp @@ -49,9 +49,9 @@ namespace endofthejedi { // TODO: add dust particles // TODO: add little rocks flying around - glClearColor(0.0, 0.0, 0.0, 1.0); - //float s = 0.1; - //glClearColor(s, s, s, 1.0); + //glClearColor(0.0, 0.0, 0.0, 1.0); + float s = 0.1; + glClearColor(s, s, 1.2*s, 1.0); m_shader_game_objects.bind(); @@ -94,11 +94,11 @@ namespace endofthejedi { } } - void RendererPolygon3d::addExplosionEffect(size_t id, const glm::vec2 &pos, size_t n, float duration) + void RendererPolygon3d::addExplosionEffect(size_t id, const glm::vec2 &explCenter, size_t n, float duration) { //float particleRadius = 0.005; //float particleRadius = 0.003; - float particleRadius = 0.005; + float particleRadius = 0.02; // TODO: use this as shader input too and make the area 2x around this // so that it stays hot/yellow for 2/3 of the time @@ -107,7 +107,7 @@ namespace endofthejedi { ParticleBatch *batch = new ParticleBatch(id, n, particleRadius, duration); batch->setup(&m_shader_particles); - batch->setCenter(glm::vec3(pos, 0.0)); + batch->setCenter(glm::vec3(explCenter, 0.0)); batch->setMaxVelocity(maxVelocity); for (size_t i=0; isetParticle(i, glm::vec3(pos, 0.0) + glm::ballRand(explCoreSize), v, maxDist); + // find collisions with planetns and limit max distance so particles + // won't fly through planets + //float maxDist = 0.1; + //float maxDist = 0.1*util::randf_0_1(); + bool isInsidePlanet = false; + float maxParticleDist = INFINITY; + + const game::Planet *nearestPlanet = nullptr; + for (const game::Planet *planet : m_state->planets) { + const glm::vec3 ppos3 = glm::vec3(planet->position, 0.0f); + // TODO: check if inside planet! + float dist = glm::distance(ppos3, pos); + if (dist <= planet->radius) { + isInsidePlanet = true; + nearestPlanet = planet; + } + if (isInsidePlanet) { + // skip searching for nearer planets once we are inside some + // planet as the position/velocity will be changed to start + // at the surface of the planet we were in with + // reflected or planet-normal velocity. + continue; + } + // TODO: if inside, move position so that it looks like + // reflecting the particle from the planet + bool fliesInPlanetDirection = glm::dot(v, ppos3-pos) > 0.0f; + if (dist < maxParticleDist && fliesInPlanetDirection) { + nearestPlanet = planet; + maxParticleDist = dist; + } + } + + if (isInsidePlanet) { + util::IntersectionTest intersect; + if (!intersect.raySphere( + pos, v, + glm::vec3(nearestPlanet->position, 0.0f), nearestPlanet->radius)) + { + //std::cout<<"warning: intersection should be valid!" << std::endl; + // TODO: must be as they lie on a plane and the dist is < as + // the radius. + // handle if this is wrong. + + } else { + // set position to the intersection point between explosion + // center and planet surface + //const glm::vec3 planetNormal = glm::vec3(glm::normalize(explCenter - nearestPlanet->position), 0.0f); + + //pos = glm::vec3(nearestPlanet->position, 0.0f) + nearestPlanet->radius*planetNormal; + + // build new velocity by reflecting the old velocity on the + // planet normal + // TODO: add a bit random + // TODO: add reflection + // TODO: distribute particles around main reflection angle and + // TODO: add material exhaust that is specific for the planet. + // TODO: spawn waves on water planet + // TODO: start fire on gas planet + //v = glm::length(v) * planetNormal; + //v = v - 2.0f*glm::dot(v, planetNormal) * planetNormal; + //v = glm::length(v) * planetNormal; + + //glm::vec3 r = v - 2.0f*glm::dot(v, planetNormal) * planetNormal; + //glm::vec3 vn = glm::length(v) * planetNormal; + //v = (r+vn) / 2.0f; + + //glm::vec3 vc = glm::vec3(nearestPlanet->position-explCenter, 0.0f); + //glm::vec3 r = vc - 2.0f*glm::dot(vc, planetNormal) * planetNormal; + //v = r; + } + } + + batch->setParticle(i, pos, v, maxParticleDist); } batch->upload(); @@ -189,6 +262,8 @@ namespace endofthejedi { void RendererPolygon3d::renderMissiles() { + // TODO: add fire trail for missiles near the sun + m_missileModel->bind(); for (const game::Player *player : m_state->players) { diff --git a/game/util.cpp b/game/util.cpp index 4f758b8..3556228 100644 --- a/game/util.cpp +++ b/game/util.cpp @@ -33,4 +33,71 @@ namespace util { { return 180.0 * rad / M_PI; } + + IntersectionTest::IntersectionTest() : m_valid(false), m_distance(INFINITY) + { + } + + IntersectionTest::IntersectionTest(float d) : m_valid(true), m_distance(d) + { + } + + float IntersectionTest::distance() const { assert(m_valid); return m_distance; } + bool IntersectionTest::valid() const { return m_valid; } + + bool IntersectionTest::raySphere( + const glm::vec3 &rayPos, const glm::vec3 &rayDir, + const glm::vec3 &spherePos, float sphereRadius) + { + m_valid = false; + + // TODO: get code + return m_valid; + +#if 0 + const glm::vec3 &o = ray.pos(); + const glm::vec3 &l = ray.dir(); + const glm::vec3 &c = m_pos; + const float &r = m_radius; + + const glm::vec3 omc = o-c; + const float dot = glm::dot(l, omc); + const float s = dot*dot - util::dotSelf(omc) + r*r; + + if (s < 0.001) { + return RayIntersection(); + } + + const float t = -dot; + +#if 0 + // TODO: is that meant as t? + if (s == 0) { + // not interested in touch point + return RayIntersection(); + + //d1 = t; + //return RayTouchesOnePoint; + } +#endif + + const float sr = sqrt(s); + + d1 = t - sr; + d2 = t + sr; + + // if end intersection lies behind it, this is completely uninteresting for us + // 0.01 because to get rid of rounding/precision errors + if (d2 <= 0.01) { + return RayIntersection(); + } + + // if the start of the interval lies behind us, make it 0 because we just + // want to know whats before us. + // 0.01 because to get rid of rounding/precision errors + if (d1 < 0.0) { + d1 = 0.0; + } +#endif + } } diff --git a/game/util.hpp b/game/util.hpp index c99bb8d..ff80995 100644 --- a/game/util.hpp +++ b/game/util.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace util { @@ -12,4 +13,32 @@ namespace util { float deg2rad(float deg); float rad2deg(float rad); + + /** + * Test for intersection in positive direction of a ray. + * TODO: support inside? + */ + class IntersectionTest { + public: + IntersectionTest(); + + // creates a valid intersection point at distance d + IntersectionTest(float d); + + // intersection distance + float distance() const; + + // returns true if there's an interseciton. + bool valid() const; + + // return true if there's an intersection. then intersectionPoint is filled + // with details. + bool raySphere( + const glm::vec3 &rayPos, const glm::vec3 &rayDir, + const glm::vec3 &spherePos, float sphereRadius); + + private: + bool m_valid; + float m_distance; + }; }