From 223ac69b02c060421cb32e50ead47c9607e9d0e7 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Jun 22 2022 03:54:02 +0000 Subject: asteroid --- diff --git a/projects/asteroid/build.sh b/projects/asteroid/build.sh new file mode 100755 index 0000000..4649211 --- /dev/null +++ b/projects/asteroid/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +if [ -z "$CPP" ]; then + CPP=c++ +fi + +if [ -z "$TARGET" ]; then + TARGET="asteroid" +fi + +LIBS="sdl2" +FLAGS="-Wall -DGL_GLEXT_PROTOTYPES -lm -lGL" + +FLAGS="`pkg-config --cflags --libs $LIBS` $FLAGS " +if [ "$1" == "debug" ]; then + FLAGS="$FLAGS -O0 -g" +fi + +echo $CPP $FLAGS *.cpp -o "$TARGET" + $CPP $FLAGS *.cpp -o "$TARGET" diff --git a/projects/asteroid/geometry.cpp b/projects/asteroid/geometry.cpp new file mode 100644 index 0000000..2c897d7 --- /dev/null +++ b/projects/asteroid/geometry.cpp @@ -0,0 +1,123 @@ + +#include + +#include "geometry.h" + + +Real real_random() + { return rand()/(Real)RAND_MAX; } + + +Matrix4 Matrix4::operator*(const Matrix4 &m) const { + return Matrix4( + m00*m.m00 + m10*m.m01 + m20*m.m02 + m30*m.m02, + m01*m.m00 + m11*m.m01 + m21*m.m02 + m31*m.m02, + m02*m.m00 + m12*m.m01 + m22*m.m02 + m32*m.m02, + m03*m.m00 + m13*m.m01 + m23*m.m02 + m33*m.m02, + + m00*m.m10 + m10*m.m11 + m20*m.m12 + m30*m.m12, + m01*m.m10 + m11*m.m11 + m21*m.m12 + m31*m.m12, + m02*m.m10 + m12*m.m11 + m22*m.m12 + m32*m.m12, + m03*m.m10 + m13*m.m11 + m23*m.m12 + m33*m.m12, + + m00*m.m20 + m10*m.m21 + m20*m.m22 + m30*m.m22, + m01*m.m20 + m11*m.m21 + m21*m.m22 + m31*m.m22, + m02*m.m20 + m12*m.m21 + m22*m.m22 + m32*m.m22, + m03*m.m20 + m13*m.m21 + m23*m.m22 + m33*m.m22, + + m00*m.m30 + m10*m.m31 + m20*m.m32 + m30*m.m32, + m01*m.m30 + m11*m.m31 + m21*m.m32 + m31*m.m32, + m02*m.m30 + m12*m.m31 + m22*m.m32 + m32*m.m32, + m03*m.m30 + m13*m.m31 + m23*m.m32 + m33*m.m32 ); +} + +Real Matrix4::det() const { + return m00 * (m11*(m22*m33 - m23*m32) + m12*(m23*m31 - m21*m33) + m13*(m21*m32 - m22*m31)) + + m01 * (m01*(m23*m32 - m22*m33) + m02*(m21*m33 - m23*m31) + m03*(m22*m31 - m21*m32)) + + m02 * (m01*(m12*m33 - m13*m32) + m02*(m13*m31 - m11*m33) + m03*(m11*m32 - m12*m31)) + + m03 * (m01*(m13*m22 - m12*m23) + m02*(m11*m23 - m13*m21) + m03*(m12*m21 - m11*m22)); +} + +Matrix4 Matrix4::inv() const { + Matrix4 r; + r.m00 = m11*(m22*m33 - m23*m32) + m12*(m23*m31 - m21*m33) + m13*(m21*m32 - m22*m31); + r.m10 = m10*(m23*m32 - m22*m33) + m12*(m20*m33 - m23*m30) + m13*(m22*m30 - m20*m32); + r.m20 = m10*(m21*m33 - m23*m31) + m11*(m23*m30 - m20*m33) + m13*(m20*m31 - m21*m30); + r.m30 = m10*(m22*m31 - m21*m32) + m11*(m20*m32 - m22*m30) + m12*(m21*m30 - m20*m31); + + double det = m00*r.m00 + m01*r.m10 + m02*r.m20 + m03*r.m30; + if (fabs(det) <= precision) return zero(); + det = 1/det; + r.m00 *= det; + r.m10 *= det; + r.m20 *= det; + r.m30 *= det; + + r.m01 = det*(m01*(m23*m32 - m22*m33) + m02*(m21*m33 - m23*m31) + m03*(m22*m31 - m21*m32)); + r.m11 = det*(m00*(m22*m33 - m23*m32) + m02*(m23*m30 - m20*m33) + m03*(m20*m32 - m22*m30)); + r.m21 = det*(m00*(m23*m31 - m21*m33) + m01*(m20*m33 - m23*m30) + m03*(m21*m30 - m20*m31)); + r.m31 = det*(m00*(m21*m32 - m22*m31) + m01*(m22*m30 - m20*m32) + m02*(m20*m31 - m21*m30)); + r.m02 = det*(m01*(m12*m33 - m13*m32) + m02*(m13*m31 - m11*m33) + m03*(m11*m32 - m12*m31)); + r.m12 = det*(m00*(m13*m32 - m12*m33) + m02*(m10*m33 - m13*m30) + m03*(m12*m30 - m10*m32)); + r.m22 = det*(m00*(m11*m33 - m13*m31) + m01*(m13*m30 - m10*m33) + m03*(m10*m31 - m11*m30)); + r.m32 = det*(m00*(m12*m31 - m11*m32) + m01*(m10*m32 - m12*m30) + m02*(m11*m30 - m10*m31)); + r.m03 = det*(m01*(m13*m22 - m12*m23) + m02*(m11*m23 - m13*m21) + m03*(m12*m21 - m11*m22)); + r.m13 = det*(m00*(m12*m23 - m13*m22) + m02*(m13*m20 - m10*m23) + m03*(m10*m22 - m12*m20)); + r.m23 = det*(m00*(m13*m21 - m11*m23) + m01*(m10*m23 - m13*m20) + m03*(m11*m20 - m10*m21)); + r.m33 = det*(m00*(m11*m22 - m12*m21) + m01*(m12*m20 - m10*m22) + m02*(m10*m21 - m11*m20)); + return r; +} + +Matrix4 Matrix4::zero() { + return Matrix4( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 ); +} + +Matrix4 Matrix4::translation(const Vector3 &translate) { + return Matrix4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + translate.x, translate.y, translate.z, 1 ); +} + +Matrix4 Matrix4::scaling(const Vector3 &scale) { + return Matrix4( + scale.x, 0, 0, 0, + 0, scale.y, 0, 0, + 0, 0, scale.z, 0, + 0, 0, 0, 1 ); +} + +Matrix4 Matrix4::rotation(const Vector3 &axis, Real angle) { + Real len = axis.len(); + if (len < precision) return Matrix4(); + Real s = sin(angle); + Real c = cos(angle); + Vector3 a = axis/len; + Vector3 ac = a*(1 - c); + return Matrix4( + a.x*ac.x + c , a.y*ac.x + a.z*s, a.z*ac.x - a.y*s, 0, + a.x*ac.y - a.z*s, a.y*ac.y + c , a.z*ac.y + a.x*s, 0, + a.x*ac.z + a.y*s, a.y*ac.z - a.x*s, a.z*ac.z + c , 0, + 0 , 0 , 0 , 1 ); +} + +Matrix4 Matrix4::perspective(Real fovy, Real aspect, Real z_near, Real z_far) { + if (fabs(fovy) <= precision) return zero(); + if (fabs(fovy) >= 180.0-precision) return zero(); + if (fabs(aspect) <= precision) return zero(); + if (fabs(z_near - z_far) <= precision) return zero(); + + Real f = 1/tan(0.5*fovy/180.0*M_PI); + Real d = 1/(z_near - z_far); + return Matrix4( + f/aspect, 0, 0 , 0, + 0 , f, 0 , 0, + 0 , 0, (z_near + z_far)*d, -1, + 0 , 0, 2*z_near*z_far*d , 0 ); +} + diff --git a/projects/asteroid/geometry.h b/projects/asteroid/geometry.h new file mode 100644 index 0000000..04893bb --- /dev/null +++ b/projects/asteroid/geometry.h @@ -0,0 +1,182 @@ + +#ifndef GEOMETRY_H +#define GEOMETRY_H + + +#include + + +typedef double Real; + +const Real precision = 1e-10; +const Real pi = 3.14159265358979323846; +const Real sqrt2 = 0.7071067811865475244; + +inline int solve(Real* roots, Real k0, Real k1) { + if (fabs(k1) <= precision) return 0; + if (roots) roots[0] = -k0/k1; + return 1; +} + +inline int solve(Real* roots, Real k0, Real k1, Real k2) { + if (fabs(k2) <= precision*precision) return solve(roots, k0, k1); + Real D = k1*k1 - 4*k2*k0; + if (fabs(D) <= precision*precision) { + if (roots) roots[0] = -0.5*k1/k2; + return 1; + } else + if (D > 0) { + if (roots) { + Real a = sqrt(D); + Real b = -0.5/k2; + roots[0] = (k1 - a)*b; + roots[1] = (k1 + a)*b; + } + return 2; + } + return 0; +} + + +inline Real sign(Real x, Real precision) { + return x < -precision ? -1 + : x > precision ? 1 + : 0; +} + + +Real real_random(); + +inline Real real_random2() + { return real_random()*2 - 1; } +inline Real real_random_normal() + { return sqrt(-2*log(real_random())) * sin(2*pi*real_random()); } + + +class Vector2 { +public: + union { + struct { Real x, y; }; + struct { Real c[2]; }; + }; + + explicit Vector2(Real x = 0, Real y = 0): + x(x), y(y) { } + + Real len_sqr() const { return x*x + y*y; } + Real len() const { return sqrt(len_sqr()); } +}; + + +class Vector3 { +public: + union { + struct { Real x, y, z; }; + struct { Real r, g, b; }; + struct { Real c[3]; }; + }; + + explicit Vector3(Real x = 0, Real y = 0, Real z = 0): + x(x), y(y), z(z) { } + + Vector3 operator+(const Vector3 &v) const + { return Vector3(x+v.x, y+v.y, z+v.z); } + Vector3 operator-(const Vector3 &v) const + { return Vector3(x-v.x, y-v.y, z-v.z); } + + Real operator*(const Vector3 &v) const + { return x*v.x + y*v.y + z*v.z; } + Vector3 operator*(Real k) const + { return Vector3(x*k, y*k, z*k); } + Vector3 operator/(Real k) const + { return *this*(1/k); } + + Vector3& operator+=(const Vector3 &v) + { return *this = *this + v; } + Vector3& operator-=(const Vector3 &v) + { return *this = *this - v; } + + Vector3 operator*=(Real k) + { return *this = *this*k; } + Vector3 operator/=(Real k) + { return *this = *this/k; } + + Vector3 cross(const Vector3 &v) const + { return Vector3(y*v.z - z*v.y, z*v.x - x*v.z, x*v.y - y*v.x); } + Vector3 perp() const + { return fabs(y) > fabs(x) ? Vector3(0, z, -y) : Vector3(-z, 0, x); } + + Real len_sqr() const { return x*x + y*y + z*z; } + Real len() const { return sqrt(len_sqr()); } + + Vector3 norm() const + { Real l = len(); return l > precision ? *this/l : Vector3(); } +}; + + +class Vector4 { +public: + union { + struct { Real x, y, z, w; }; + struct { Real r, g, b, a; }; + struct { Real c[4]; }; + }; + + explicit Vector4(Real x = 0, Real y = 0, Real z = 0, Real w = 0): + x(x), y(y), z(z), w(w) { } +}; + + +class Matrix4 { +public: + union { + struct { + Real m00, m01, m02, m03, + m10, m11, m12, m13, + m20, m21, m22, m23, + m30, m31, m32, m33; + }; + struct { Real m[4][4]; }; + struct { Real a[16]; }; + }; + + explicit Matrix4( + Real m00 = 1, Real m01 = 0, Real m02 = 0, Real m03 = 0, + Real m10 = 0, Real m11 = 1, Real m12 = 0, Real m13 = 0, + Real m20 = 0, Real m21 = 0, Real m22 = 1, Real m23 = 0, + Real m30 = 0, Real m31 = 0, Real m32 = 0, Real m33 = 1 + ): + m00(m00), m01(m01), m02(m02), m03(m03), + m10(m10), m11(m11), m12(m12), m13(m13), + m20(m20), m21(m21), m22(m22), m23(m23), + m30(m30), m31(m31), m32(m32), m33(m33) + { } + + Vector4 operator*(const Vector4 &v) const { + return Vector4( + m00*v.x + m10*v.y + m20*v.z + m30*v.w, + m01*v.x + m11*v.y + m21*v.z + m31*v.w, + m02*v.x + m12*v.y + m22*v.z + m32*v.w, + m03*v.x + m13*v.y + m23*v.z + m33*v.w ); + } + + Vector3 transform(const Vector3 &v, Real w = 1) const { + return Vector3( + m00*v.x + m10*v.y + m20*v.z + m30*w, + m01*v.x + m11*v.y + m21*v.z + m31*w, + m02*v.x + m12*v.y + m22*v.z + m32*w ); + } + + Matrix4 operator*(const Matrix4 &m) const; + + Real det() const; + Matrix4 inv() const; + + static Matrix4 zero(); + static Matrix4 translation(const Vector3 &translate); + static Matrix4 scaling(const Vector3 &scale); + static Matrix4 rotation(const Vector3 &axis, Real angle); + static Matrix4 perspective(Real fovy, Real aspect, Real z_near, Real z_far); +}; + +#endif diff --git a/projects/asteroid/main.cpp b/projects/asteroid/main.cpp new file mode 100644 index 0000000..d75b772 --- /dev/null +++ b/projects/asteroid/main.cpp @@ -0,0 +1,173 @@ + +#include +#include + +#include +#include + +#include +#include + +#include "geometry.h" +#include "scene.h" + + +class Main { +public: + bool sdl_initialized; + SDL_Window *mainWindow; + SDL_GLContext mainContext; + Scene *scene; + + Main(): + sdl_initialized(), + mainWindow(), + mainContext(), + scene() + { } + + ~Main() { deinit(); } + + bool init() { + srand(time(NULL)); + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "SDL_Init failed" << std::endl; + deinit(); + return false; + } + sdl_initialized = true; + + mainWindow = SDL_CreateWindow( + "simu", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 512, + 512, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE ); + + if (!mainWindow) + { + std::cerr << "cannot create window" << std::endl; + std::cerr << SDL_GetError() << std::endl; + SDL_ClearError(); + deinit(); + return false; + } + + mainContext = SDL_GL_CreateContext(mainWindow); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // vsync + SDL_GL_SetSwapInterval(1); + + scene = new Scene(); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POINT_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); + + glEnable(GL_NORMALIZE); + glDisable(GL_DEPTH_TEST); + + glEnable(GL_LIGHT0); + float light0[] = {1, 1, 1, 0}; + glLightfv(GL_LIGHT0, GL_POSITION, light0); + + glEnable(GL_LIGHT1); + float light1[] = {-1, -1, -1, 0}; + float color[] = {0.5, 0.5, 0.5, 1}; + glLightfv(GL_LIGHT1, GL_POSITION, light1); + glLightfv(GL_LIGHT1, GL_DIFFUSE, color); + //glLightfv(GL_LIGHT1, GL_SPECULAR, color); + + glEnable(GL_COLOR_MATERIAL); + + resize(512, 512); + + return true; + } + + void deinit() { + if (scene) { delete scene; scene = 0; } + if (mainContext) { SDL_GL_DeleteContext(mainContext); mainContext = 0; } + if (mainWindow) { SDL_DestroyWindow(mainWindow); mainWindow = 0; } + if (sdl_initialized) { SDL_Quit(); sdl_initialized = false; } + } + + void resize(int width, int height) { + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadMatrixd( Matrix4::perspective(90.0, (Real)width/height, 0.1, 100).a ); + glMatrixMode(GL_MODELVIEW); + } + + void loop() { + bool quit = false; + Uint32 prev_time = SDL_GetTicks(); + while(!quit) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) + quit = true; + if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_RESIZED) { + resize(event.window.data1, event.window.data2); + } + } + if (event.type == SDL_KEYDOWN) { + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + quit = true; + break; + case SDLK_w: + scene->wireframe = !scene->wireframe; + break; + case SDLK_r: + scene->generate(); + break; + default: + break; + } + } + if (event.type == SDL_MOUSEMOTION) { + if (event.motion.state & SDL_BUTTON_LMASK) { + scene->angles.z += event.motion.xrel; + scene->angles.x += event.motion.yrel; + } + if (event.motion.state & SDL_BUTTON_RMASK) { + scene->offset.z *= pow(2, event.motion.yrel/100.0); + } + if (event.motion.state & SDL_BUTTON_MMASK) { + scene->offset.x += 0.1*event.motion.xrel; + scene->offset.y -= 0.1*event.motion.yrel; + } + } + } + + Uint32 curr_time = SDL_GetTicks(); + double dt = (curr_time - prev_time)/1000.0; + prev_time = curr_time; + + scene->update(dt); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + scene->draw(); + SDL_GL_SwapWindow(mainWindow); + } + } +}; + + +int main(int argc, char **argv) +{ + Main app; + if (!app.init()) return -1; + app.loop(); + return 0; +} diff --git a/projects/asteroid/mesh.cpp b/projects/asteroid/mesh.cpp new file mode 100644 index 0000000..a5cc74b --- /dev/null +++ b/projects/asteroid/mesh.cpp @@ -0,0 +1,364 @@ + +#include +#include + +#include +#include + +#include +#include + +#include "mesh.h" + + +void Mesh::createbuf() const { + if (ibuf && vbuf && elements) return; + dirty(); + if (vertices.empty() || triangles.empty()) return; + + glGenBuffers(1, &vbuf); + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertices.size(), &vertices.front(), GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenBuffers(1, &ibuf); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Triangle)*triangles.size(), &triangles.front(), GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + elements = triangles.size()*3u; +} + +void Mesh::dirty() const { + if (ibuf) { glDeleteBuffers(1, &ibuf); ibuf = 0; } + if (vbuf) { glDeleteBuffers(1, &vbuf); vbuf = 0; } + elements = 0; +} + +void Mesh::clear() { + dirty(); + triangles.clear(); + vertices.clear(); +} + +void Mesh::draw() const { + createbuf(); + if (!elements) return; + + glBindBuffer(GL_ARRAY_BUFFER, vbuf); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_NORMAL_ARRAY); + + glVertexPointer(3, GL_DOUBLE, sizeof(Vertex), 0); + glNormalPointer(GL_DOUBLE, sizeof(Vertex), (void*)sizeof(Vector3)); + glDrawElements(GL_TRIANGLES, elements, GL_UNSIGNED_INT, 0); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + //glBegin(GL_TRIANGLES); + //for(TriangleList::const_iterator i = triangles.begin(); i != triangles.end(); ++i) { + // for(int j = 0; j < 3; ++j) { + // glNormal3dv(vertices[ i->v[j] ].normal.c); + // glVertex3dv(vertices[ i->v[j] ].pos.c); + // } + //} + //glEnd(); +} + + + + +LinkedMesh::Vertex LinkedMesh::average(const Vertex &a, const Vertex &b) + { return Vertex((a.pos + b.pos)*0.5, (a.value + b.value)*0.5); } + +Vector3 LinkedMesh::sphere_func(const Vertex &v, Real r, Real kr) + { return v.pos.norm()*r*pow(1+kr, v.value); } + +Vector3 LinkedMesh::z_func(const Vertex &v, Real r, Real kr) + { return Vector3(v.pos.x, v.pos.y, v.pos.z + v.value*kr + r); } + +Real LinkedMesh::random() + { return real_random2(); } + + +void LinkedMesh::clear() { + levels.clear(); + triangles.clear(); + edges.clear(); + vertices.clear(); +} + + +void LinkedMesh::next_level(Real k) { + Level level; + if (!levels.empty()) { + const Level &l = levels.back(); + level.k = l.k; + level.vertex0 = l.vertex1; + level.edge0 = l.edge1; + level.triangle0 = l.triangle1; + } + + level.k *= k; + level.vertex1 = (int)vertices.size(); + level.edge1 = (int)edges.size(); + level.triangle1 = (int)triangles.size(); + + assert(level.vertex0 <= level.vertex1); + assert(level.edge0 <= level.edge1); + assert(level.triangle0 <= level.triangle1); + + levels.push_back(level); + check(); +} + + +void LinkedMesh::check() const { + if (levels.empty()) { + assert(vertices.empty()); + assert(edges.empty()); + assert(triangles.empty()); + } + + for(int li = 0; li < (int)levels.size(); ++li) { + const bool last_level = (li == (int)levels.size() - 1); + const Level &l = levels[li]; + const Level pl = li ? levels[li - 1] : Level(); + const Level nl = last_level ? Level() : levels[li + 1]; + + assert(l.k >= 0); + assert(l.k <= pl.k); + assert(l.vertex0 == pl.vertex1); + assert(l.edge0 == pl.edge1); + assert(l.triangle0 == pl.triangle1); + + assert(l.vertex0 >= 0); + assert(l.edge0 >= 0); + assert(l.triangle0 >= 0); + + assert(l.vertex1 <= (int)vertices.size()); + assert(l.edge1 <= (int)edges.size()); + assert(l.triangle1 <= (int)triangles.size()); + + assert(l.vertex0 <= l.vertex1); + assert(l.edge0 <= l.edge1); + assert(l.triangle0 <= l.triangle1); + + if (last_level) { + assert(l.vertex1 == (int)vertices.size()); + assert(l.edge1 == (int)edges.size()); + assert(l.triangle1 == (int)triangles.size()); + } + + for(int i = l.edge0; i < l.edge1; ++i) { + const Edge &e = edges[i]; + if (last_level) { + assert(e.e[0] == 0); + assert(e.e[1] == 0); + assert(e.v[1] == -1); + } else { + assert(e.e[0] != e.e[1]); + assert(e.e[0] >= nl.edge0 && e.e[0] < nl.edge1); + assert(e.e[1] >= nl.edge0 && e.e[1] < nl.edge1); + assert(e.v[1] >= nl.vertex0 && e.v[1] < nl.vertex1); + } + assert(e.v[0] != e.v[1]); + assert(e.v[0] >= 0 && e.v[0] < l.vertex1); + assert(e.v[2] >= 0 && e.v[2] < l.vertex1); + } + + for(int i = l.triangle0; i < l.triangle1; ++i) { + const Triangle &t = triangles[i]; + for(int j = 0; j < 3; ++j) { + int jj = (j+1)%3; + assert(t.e[j] >= l.edge0 && t.e[j] < l.edge1); + assert(edges[ t.e[j] ].v[ t.d[j] ? 0 : 2 ] == edges[ t.e[jj] ].v[ t.d[jj] ? 2 : 0 ]); + } + } + } +} + + +void LinkedMesh::generate_level(Real k) { + const Level level = levels.empty() ? Level() : levels.back(); + const Real kk = level.k * k; + + for(int i = level.triangle0; i < level.triangle1; ++i) { + Triangle t = triangles[i]; + + Edge ee[3]; + for(int j = 0; j < 3; ++j) { + Edge &e = edges[t.e[j]]; + if (e.v[1] < 0) { + Vertex v = average( vertices[e.v[0]], vertices[e.v[2]] ); + v.value += random()*kk; + e.v[1] = (int)vertices.size(); + vertices.push_back(v); + + e.e[0] = (int)edges.size(); + e.e[1] = e.e[0] + 1; + ee[j] = e; + edges.push_back(Edge(ee[j].v[0], ee[j].v[1])); + edges.push_back(Edge(ee[j].v[1], ee[j].v[2])); + } else { + ee[j] = e; + } + + if (t.d[j]) { + std::swap(ee[j].e[0], ee[j].e[1]); + std::swap(ee[j].v[0], ee[j].v[2]); + } + } + + int ei = (int)edges.size(); + edges.push_back(Edge(ee[0].v[1], ee[2].v[1])); + edges.push_back(Edge(ee[1].v[1], ee[0].v[1])); + edges.push_back(Edge(ee[2].v[1], ee[1].v[1])); + + triangles.push_back(Triangle( ee[0].e[0], t.d[0], ei + 0, false, ee[2].e[1], t.d[2] )); + triangles.push_back(Triangle( ee[1].e[0], t.d[1], ei + 1, false, ee[0].e[1], t.d[0] )); + triangles.push_back(Triangle( ee[2].e[0], t.d[2], ei + 2, false, ee[1].e[1], t.d[1] )); + + triangles.push_back(Triangle( ei + 0, true, ei + 1, true, ei + 2, true )); + } + + next_level(k); +} + + +void LinkedMesh::generate_levels(int count, Real k) { + for(int i = 0; i < count; ++i) + generate_level(k); +} + + +void LinkedMesh::smooth() { + typedef std::pair Sum; + typedef std::vector SumList; + + const Level &level = levels.back(); + SumList sum; + sum.reserve(level.vertex1); + for(int i = 0; i < level.vertex1; ++i) + sum.push_back(Sum(1, vertices[i].value)); + for(int i = level.edge0; i < level.edge1; ++i) { + const Edge &e = edges[i]; + Sum &s0 = sum[e.v[0]], &s1 = sum[e.v[2]]; + s0.first += 1; + s1.first += 1; + s0.second += vertices[e.v[2]].value; + s1.second += vertices[e.v[1]].value; + } + for(int i = 0; i < level.vertex1; ++i) { + const Sum &s = sum[i]; + vertices[i].value = s.second/s.first; + } +} + + +void LinkedMesh::smooth(int count) { + for(int i = 0; i < count; ++i) + smooth(); +} + + +void LinkedMesh::to_mesh(Mesh &mesh, VertexFunc func, Real r, Real kr, int level) const { + if (levels.empty()) return; + if (level < 0 || level >= (int)levels.size()) level = levels.size() - 1; + const Level &l = levels[level]; + + int v0 = (int)mesh.vertices.size(); + int v1 = v0 + l.vertex1; + mesh.vertices.reserve(v1); + for(int i = 0; i < l.vertex1; ++i) + mesh.vertices.push_back(Mesh::Vertex( func(vertices[i], r, kr) )); + + mesh.triangles.reserve(mesh.triangles.size() + (l.triangle1 - l.triangle0)); + for(int i = l.triangle0; i < l.triangle1; ++i) { + const Triangle &t = triangles[i]; + Mesh::Triangle tt; + for(int j = 0; j < 3; ++j) + tt.v[j] = v0 + edges[t.e[j]].v[ t.d[j] ? 2 : 0 ]; + Vector3 normal = (mesh.vertices[tt.v[1]].pos - mesh.vertices[tt.v[0]].pos).cross( + mesh.vertices[tt.v[2]].pos - mesh.vertices[tt.v[0]].pos ).norm(); + for(int j = 0; j < 3; ++j) + mesh.vertices[tt.v[j]].normal += normal; + mesh.triangles.push_back(tt); + } + + for(int i = v0; i < v1; ++i) + mesh.vertices[i].normal = mesh.vertices[i].normal.norm(); + + mesh.dirty(); +} + + +void LinkedMesh::triangle(Real k) { + clear(); + for(int i = 0; i < 3; ++i) { + Real a = 2*pi/3*i; + vertices.push_back(Vertex( Vector3(cos(a), sin(a), 0), random()*k )); + edges.push_back(Edge(i, (i+1)%3)); + } + triangles.push_back(Triangle(0, false, 1, false, 2, false)); + next_level(k); +} + +void LinkedMesh::tetrahedron(Real k) { + clear(); + const Real r = 2*sqrt2/3; + const Real z = -1/Real(3); + + vertices.push_back(Vertex( Vector3(0, 0, 1), random()*k )); + for(int i = 0; i < 3; ++i) { + Real a = 2*pi/3*i; + vertices.push_back(Vertex( Vector3(cos(a)*r, sin(a)*r, z), random()*k )); + edges.push_back(Edge(0, i+1)); + edges.push_back(Edge(i+1, (i+1)%3 + 1)); + triangles.push_back(Triangle(i*2, false, i*2+1, false, (i+1)%3*2, true)); + } + triangles.push_back(Triangle(5, true, 3, true, 1, true)); + next_level(k); +} + +void LinkedMesh::icosahedron(Real k) { + clear(); + const Real kk = 4*sin(pi/5)*sin(pi/5) - 1; + const Real r = 2*sqrt(kk)/(kk + 1); + const Real z = 1 - r*sqrt(kk); + + // vertices + vertices.push_back(Vertex( Vector3(0, 0, 1), random()*k )); + for(int i = 0; i < 5; ++i) { + Real a = 2*pi/5*i; + vertices.push_back(Vertex( Vector3(cos(a)*r, sin(a)*r, z), random()*k )); + } + for(int i = 0; i < 5; ++i) { + Real a = 2*pi/5*i + pi/5; + vertices.push_back(Vertex( Vector3(cos(a)*r, sin(a)*r, -z), random()*k )); + } + vertices.push_back(Vertex( Vector3(0, 0, -1), random()*k )); + + // edges + for(int i = 0; i < 5; ++i) edges.push_back(Edge(0, i+1)); + for(int i = 0; i < 5; ++i) edges.push_back(Edge(i+1, (i+1)%5 + 1)); + for(int i = 0; i < 5; ++i) edges.push_back(Edge(i+1, i+6)); + for(int i = 0; i < 5; ++i) edges.push_back(Edge(i+6, (i+1)%5 + 1)); + for(int i = 0; i < 5; ++i) edges.push_back(Edge(i+6, (i+1)%5 + 6)); + for(int i = 0; i < 5; ++i) edges.push_back(Edge(11, i+6)); + + // triangles + for(int i = 0; i < 5; ++i) { + int ii = (i+1)%5; + triangles.push_back(Triangle(i, false, i+5, false, ii, true)); + triangles.push_back(Triangle(i+10, false, i+15, false, i+5, true)); + triangles.push_back(Triangle(i+20, false, ii+10, true, i+15, true)); + triangles.push_back(Triangle(i+25, true, ii+25, false, i+20, true)); + } + + next_level(k); +} diff --git a/projects/asteroid/mesh.h b/projects/asteroid/mesh.h new file mode 100644 index 0000000..50607cd --- /dev/null +++ b/projects/asteroid/mesh.h @@ -0,0 +1,131 @@ +#ifndef MESH_H +#define MESH_H + + +#include + +#include "geometry.h" + + +class Mesh { +public: + class Vertex { + public: + Vector3 pos; + Vector3 normal; + explicit inline Vertex(const Vector3 &pos = Vector3(), const Vector3 &normal = Vector3()): + pos(pos), normal(normal) { } + }; + + class Triangle { + public: + int v[3]; + explicit inline Triangle(int v0 = 0, int v1 = 0, int v2 = 0) + { v[0] = v0; v[1] = v1; v[2] = v2; } + }; + + typedef std::vector VertexList; + typedef std::vector TriangleList; + + VertexList vertices; + TriangleList triangles; + + mutable unsigned int vbuf, ibuf, elements; + + inline Mesh(): vbuf(), ibuf() { } + inline ~Mesh() { dirty(); } + + void createbuf() const; + void dirty() const; + void clear(); + void draw() const; +}; + + +class LinkedMesh { +public: + class Vertex { + public: + Vector3 pos; + Real value; + explicit inline Vertex(const Vector3 &pos = Vector3(), Real value = Real()): + pos(pos), value(value) { } + }; + + class Edge { + public: + int v[3]; + int e[2]; + explicit inline Edge(int v0 = 0, int v2 = 0) + { v[0] = v0; v[1] = -1; v[2] = v2; e[0] = 0; e[1] = 0; } + }; + + class Triangle { + public: + int e[3]; + bool d[3]; + explicit inline Triangle( + int e0 = 0, bool d0 = false, + int e1 = 0, bool d1 = false, + int e2 = 0, bool d2 = false ) + { + e[0] = e0; e[1] = e1; e[2] = e2; + d[0] = d0; d[1] = d1; d[2] = d2; + } + }; + + class Level { + public: + Real k; + int vertex0, vertex1; + int edge0, edge1; + int triangle0, triangle1; + Level(): + k(1), + vertex0(), vertex1(), + edge0(), edge1(), + triangle0(), triangle1() { } + }; + + typedef std::vector VertexList; + typedef std::vector EdgeList; + typedef std::vector TriangleList; + typedef std::vector LevelList; + typedef Vector3 (*VertexFunc)(const Vertex&, Real, Real); + + + VertexList vertices; + EdgeList edges; + TriangleList triangles; + LevelList levels; + + + static Vertex average(const Vertex &a, const Vertex &b); + static Vector3 sphere_func(const Vertex &v, Real r, Real kr); + static Vector3 z_func(const Vertex &v, Real r, Real kr); + + static Real random(); + + void check() const; + + void clear(); + void next_level(Real k); + void generate_level(Real k = sqrt2); + void generate_levels(int count, Real k = sqrt2); + void smooth(); + void smooth(int count); + + void triangle(Real k = 1); + void tetrahedron(Real k = 1); + void icosahedron(Real k = 1); + + void to_mesh(Mesh &mesh, VertexFunc func, Real r = 0, Real kr = 1, int level = -1) const; + + inline void to_sphere_mesh(Mesh &mesh, Real r = 1, Real kr = 0.5, int level = -1) const + { to_mesh(mesh, &sphere_func, r, kr, level); } + inline void to_z_mesh(Mesh &mesh, Real r = 0, Real kr = 1, int level = -1) const + { to_mesh(mesh, &z_func, r, kr, level); } +}; + + +#endif diff --git a/projects/asteroid/scene.cpp b/projects/asteroid/scene.cpp new file mode 100644 index 0000000..1a328f6 --- /dev/null +++ b/projects/asteroid/scene.cpp @@ -0,0 +1,133 @@ + +#include + +#include + +#include "scene.h" + + +Scene::Scene(): + stars_seed(rand()), + offset(0, 0, -10), + angles(45.0, 0.0, 30.0), + wireframe(false), + water_level(1), + t(0) +{ + LinkedMesh lsphere; + lsphere.icosahedron(0); + lsphere.generate_levels(3); + lsphere.to_sphere_mesh(sphere, 1, 0); + + generate(); +} + +Scene::~Scene() { } + + +void Scene::generate() { + mesh.clear(); + LinkedMesh linked_mesh; + //linked_mesh.triangle(); + //linked_mesh.tetrahedron(); + linked_mesh.icosahedron(); + linked_mesh.generate_levels(2, 1); + linked_mesh.generate_levels(5, 0.5); + linked_mesh.smooth(); + //linked_mesh.to_z_mesh(mesh, 0, 0.1); + linked_mesh.to_sphere_mesh(mesh, 1, 0.2); +} + + +void Scene::update(Real dt) { + t += dt; + water_level = 1.05 + 0.025*sin(3*t/2/pi); +} + + +void Scene::draw_stars() { + unsigned int seed = rand(); + srand(stars_seed); + + glEnable(GL_FOG); + glFogf(GL_FOG_DENSITY, 0.02f); + + glColor4d(1, 1, 1, 1); + glBegin(GL_POINTS); + for(int i = 0; i < 1000; ++i) { + glVertex3d( + real_random2()*100.0, + real_random2()*100.0, + real_random2()*100.0 ); + } + glEnd(); + + glDisable(GL_FOG); + srand(seed); +} + + +void Scene::draw() { + glPushMatrix(); + + glTranslated(offset.x, offset.y, offset.z); + glRotated(-90, 1, 0, 0); + glRotated(angles.y, 0, 1, 0); + glRotated(angles.x, 1, 0, 0); + glRotated(angles.z, 0, 0, 1); + + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_DST_ALPHA); + + draw_stars(); + + + glEnable(GL_LIGHTING); + if (wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + glColor4d(0, 0.5, 0, 1); + mesh.draw(); + + // water + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); + glColor4d(0, 0, 1, 0.2); + for(int i = 10; i > 0; --i) { + Real s = water_level - 0.01*i; + glPushMatrix(); + glScaled(s, s, s); + sphere.draw(); + glPopMatrix(); + } + glDepthMask(GL_TRUE); + glEnable(GL_LIGHTING); + + glColor4d(0, 0, 1, 0.5); + glPushMatrix(); + glScaled(water_level, water_level, water_level); + sphere.draw(); + glPopMatrix(); + glColor4d(1, 1, 1, 1); + + // fog + glDisable(GL_LIGHTING); + glDepthMask(GL_FALSE); + glColor4d(1, 1, 1, 0.01); + for(int i = 1; i < 20; ++i) { + Real s = water_level + 0.01*i; + glPushMatrix(); + glScaled(s, s, s); + sphere.draw(); + glPopMatrix(); + } + glDepthMask(GL_TRUE); + glEnable(GL_LIGHTING); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_LIGHTING); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + glPopMatrix(); +} diff --git a/projects/asteroid/scene.h b/projects/asteroid/scene.h new file mode 100644 index 0000000..5d03043 --- /dev/null +++ b/projects/asteroid/scene.h @@ -0,0 +1,38 @@ + +#ifndef SCENE_H +#define SCENE_H + + +#include "geometry.h" +#include "mesh.h" + + +class Scene { +public: + unsigned int stars_seed; + Vector3 offset; + Vector3 angles; + bool wireframe; + Real water_level; + Real t; + + Mesh mesh; + Mesh sphere; + + Scene(); + ~Scene(); + + void update(Real dt); + + void generate(); + + void draw_stars(); + void draw(); +}; + + +#endif + + + +