Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2062f19631 | |||
| 16a5e403ce | |||
| f101bba5aa | |||
| 3263c7e45e | |||
| b504bf0222 | |||
| b274251501 | |||
| 421caed141 | |||
| 1bf3c556bd | |||
| e0ec2bf7e1 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -147,6 +147,4 @@ fabric.properties
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
.idea/
|
||||
*.app
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -5,3 +5,6 @@
|
||||
path = vendor/glad
|
||||
url = https://github.com/Dav1dde/glad.git
|
||||
branch = c
|
||||
[submodule "vendor/glm"]
|
||||
path = vendor/glm
|
||||
url = https://github.com/g-truc/glm.git
|
||||
|
||||
4
.idea/encodings.xml
generated
Normal file
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||
</project>
|
||||
2
.idea/hopf-fibration.iml
generated
Normal file
2
.idea/hopf-fibration.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
12
.idea/misc.xml
generated
Normal file
12
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
<component name="CidrRootsConfiguration">
|
||||
<sourceRoots>
|
||||
<file path="$PROJECT_DIR$/main" />
|
||||
</sourceRoots>
|
||||
<libraryRoots>
|
||||
<file path="$PROJECT_DIR$/vendor" />
|
||||
</libraryRoots>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/hopf-fibration.iml" filepath="$PROJECT_DIR$/.idea/hopf-fibration.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(hopf)
|
||||
project(hopf-fibration)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
@@ -11,4 +11,7 @@ option(GLFW_BUILD_EXAMPLES OFF)
|
||||
option(GLFW_BUILD_TESTS OFF)
|
||||
add_subdirectory(vendor/glfw)
|
||||
|
||||
add_subdirectory(main)
|
||||
option(GLM_TEST_ENABLE OFF)
|
||||
add_subdirectory(vendor/glm)
|
||||
|
||||
add_subdirectory(main)
|
||||
21
README.md
21
README.md
@@ -1,21 +0,0 @@
|
||||
# hopf-fibration
|
||||
|
||||
Generate images of the hopf fibration.
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
Hold keys `Q`, `W`, `E`, `A`, `S`, `D` to select from the six planes of rotation, and use the scroll wheel to rotate the fibration through the selected plane. The resulting Hopf links are stereographically projected into 3D space, and the resulting circles are rendered orthographically on screen.
|
||||
|
||||
Hold left-control `LCTL` and scroll to zoom in on the fibration. This affects only the orthographic rendering, not the fibration itself.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## More information
|
||||
|
||||
[Wikipedia: Hopf Fibration](https://en.wikipedia.org/wiki/Hopf_fibration)
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 86 KiB |
@@ -1,17 +1,18 @@
|
||||
project(main)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.cpp)
|
||||
src/main.cpp include/util.h)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
glad
|
||||
glm
|
||||
glfw)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE include)
|
||||
PRIVATE
|
||||
include)
|
||||
|
||||
set(SHADERS
|
||||
shaders/main.frag
|
||||
shaders/main.vert)
|
||||
add_custom_target(shaders DEPENDS ${SHADERS})
|
||||
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
#include <math.h>
|
||||
|
||||
namespace ga {
|
||||
struct Vec;
|
||||
struct Bivec;
|
||||
struct Mat;
|
||||
|
||||
struct Vec {
|
||||
float x, y, z, w;
|
||||
|
||||
Vec(float x, float y, float z, float w) :
|
||||
x(x), y(y), z(z), w(w) {}
|
||||
};
|
||||
|
||||
struct Bivec {
|
||||
float xy, yz, zw, wx, xz, yw;
|
||||
|
||||
Bivec(float xy, float yz, float zw, float wx, float xz, float yw) :
|
||||
xy(xy), yz(yz), zw(zw), wx(wx), xz(xz), yw(yw) {}
|
||||
};
|
||||
|
||||
struct Mat {
|
||||
Vec x, y, z, w;
|
||||
|
||||
Mat(Vec x, Vec y, Vec z, Vec w) : x(x), y(y), z(z), w(w) {}
|
||||
};
|
||||
|
||||
namespace unit {
|
||||
Vec x() { return Vec(1, 0, 0, 0); };
|
||||
|
||||
Vec y() { return Vec(0, 1, 0, 0); };
|
||||
|
||||
Vec z() { return Vec(0, 0, 1, 0); };
|
||||
|
||||
Vec w() { return Vec(0, 0, 0, 1); };
|
||||
|
||||
Bivec xy() { return Bivec(1, 0, 0, 0, 0, 0); }
|
||||
|
||||
Bivec yz() { return Bivec(0, 1, 0, 0, 0, 0); }
|
||||
|
||||
Bivec zw() { return Bivec(0, 0, 1, 0, 0, 0); }
|
||||
|
||||
Bivec wx() { return Bivec(0, 0, 0, 1, 0, 0); }
|
||||
|
||||
Bivec xz() { return Bivec(0, 0, 0, 0, 1, 0); }
|
||||
|
||||
Bivec yw() { return Bivec(0, 0, 0, 0, 0, 1); }
|
||||
|
||||
Mat identity() { return Mat(x(), y(), z(), w()); }
|
||||
}
|
||||
|
||||
float length2(Vec v) {
|
||||
return v.x * v.x + v.y * v.y + v.z * v.z + v.w * v.w;
|
||||
}
|
||||
|
||||
float length2(Bivec v) {
|
||||
return v.xy * v.xy + v.yz * v.yz + v.zw * v.zw
|
||||
+ v.wx * v.wx + v.xz * v.xz + v.yw * v.yw;
|
||||
}
|
||||
|
||||
float length(Vec v) { return sqrt(length2(v)); }
|
||||
|
||||
float length(Bivec v) { return sqrt(length2(v)); }
|
||||
|
||||
Vec add(Vec u, Vec v) {
|
||||
return Vec(u.x + v.x,
|
||||
u.y + v.y,
|
||||
u.z + v.z,
|
||||
u.w + v.w);
|
||||
}
|
||||
|
||||
Bivec add(Bivec u, Bivec v) {
|
||||
Bivec(u.xy + v.xy,
|
||||
u.yz + v.yz,
|
||||
u.zw + v.zw,
|
||||
u.wx + v.wx,
|
||||
u.xz + v.xz,
|
||||
u.yw + v.yw);
|
||||
}
|
||||
|
||||
Vec mul(float c, Vec v) {
|
||||
return Vec(c * v.x,
|
||||
c * v.y,
|
||||
c * v.z,
|
||||
c * v.w);
|
||||
}
|
||||
|
||||
Bivec mul(float c, Bivec v) {
|
||||
Bivec(c * v.xy,
|
||||
c * v.yz,
|
||||
c * v.zw,
|
||||
c * v.wx,
|
||||
c * v.xz,
|
||||
c * v.yw);
|
||||
}
|
||||
|
||||
Vec mul(Mat m, Vec v) {
|
||||
return add(add(mul(v.x, m.x), mul(v.y, m.y)), add(mul(v.z, m.z), mul(v.w, m.w)));
|
||||
}
|
||||
|
||||
Mat mul(Mat m, Mat n) {
|
||||
return Mat(
|
||||
mul(m, n.x),
|
||||
mul(m, n.y),
|
||||
mul(m, n.z),
|
||||
mul(m, n.w)
|
||||
);
|
||||
}
|
||||
|
||||
Mat mul(float c, Mat m) {
|
||||
return Mat(
|
||||
mul(c, m.x),
|
||||
mul(c, m.y),
|
||||
mul(c, m.z),
|
||||
mul(c, m.w)
|
||||
);
|
||||
}
|
||||
|
||||
Vec normalize(Vec v) {
|
||||
return mul(1 / length(v), v);
|
||||
}
|
||||
|
||||
Bivec normalize(Bivec v) {
|
||||
return mul(1 / length(v), v);
|
||||
}
|
||||
|
||||
Vec tform(float c, Vec v) {
|
||||
|
||||
}
|
||||
|
||||
Vec tform(Vec u, Vec v) {
|
||||
|
||||
}
|
||||
|
||||
Vec tform(Bivec u, Vec v) {
|
||||
|
||||
}
|
||||
|
||||
Mat matrix(float c) {
|
||||
auto m = unit::identity();
|
||||
m.x = tform(c, m.x);
|
||||
m.y = tform(c, m.y);
|
||||
m.z = tform(c, m.z);
|
||||
m.w = tform(c, m.w);
|
||||
return m;
|
||||
}
|
||||
|
||||
Mat matrix(Vec v) {
|
||||
auto m = unit::identity();
|
||||
m.x = tform(v, m.x);
|
||||
m.y = tform(v, m.y);
|
||||
m.z = tform(v, m.z);
|
||||
m.w = tform(v, m.w);
|
||||
return m;
|
||||
}
|
||||
|
||||
Mat matrix(Bivec v) {
|
||||
auto m = unit::identity();
|
||||
m.x = tform(v, m.x);
|
||||
m.y = tform(v, m.y);
|
||||
m.z = tform(v, m.z);
|
||||
m.w = tform(v, m.w);
|
||||
return m;
|
||||
}
|
||||
}
|
||||
113
main/include/util.h
Normal file
113
main/include/util.h
Normal file
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Created by allem on 2/7/2019.
|
||||
//
|
||||
|
||||
#ifndef HOPF_FIBRATION_UTIL_H
|
||||
#define HOPF_FIBRATION_UTIL_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace util {
|
||||
template<typename T>
|
||||
void bufferData(GLenum target, std::vector<T> data, GLenum usage) {
|
||||
glBufferData(target, data.size() * sizeof(T), &data.front(), usage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void bufferData(GLenum target, T &data, GLenum usage) {
|
||||
glBufferData(target, sizeof(T), &data, usage);
|
||||
}
|
||||
|
||||
std::string readFile(const std::string &path) {
|
||||
std::ifstream file(path);
|
||||
if (!file) return std::string();
|
||||
|
||||
file.ignore(std::numeric_limits<std::streamsize>::max());
|
||||
auto size = file.gcount();
|
||||
|
||||
if (size > 0x10000) return std::string();
|
||||
|
||||
file.clear();
|
||||
file.seekg(0, std::ios_base::beg);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << file.rdbuf();
|
||||
file.close();
|
||||
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
void shaderFiles(GLuint shader, std::vector<std::string> &paths) {
|
||||
std::vector<std::string> strs;
|
||||
std::vector<const char *> c_strs;
|
||||
|
||||
for (const auto &path : paths) strs.push_back(readFile(path));
|
||||
for (const auto &str:strs) c_strs.push_back(str.c_str());
|
||||
|
||||
glShaderSource(shader, (GLsizei) c_strs.size(), &c_strs.front(), nullptr);
|
||||
}
|
||||
|
||||
std::string shaderInfoLog(GLuint shader) {
|
||||
GLint log_len;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
|
||||
char log[log_len];
|
||||
glGetShaderInfoLog(shader, log_len, nullptr, log);
|
||||
return std::string(log);
|
||||
}
|
||||
|
||||
std::string programInfoLog(GLuint program) {
|
||||
GLint log_len;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
|
||||
char log[log_len];
|
||||
glGetProgramInfoLog(program, log_len, nullptr, log);
|
||||
return std::string(log);
|
||||
}
|
||||
|
||||
GLuint buildShader(GLenum kind, const std::string &name, std::vector<std::string> paths) {
|
||||
GLuint shader = glCreateShader(kind);
|
||||
shaderFiles(shader, paths);
|
||||
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint comp;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &comp);
|
||||
if (!comp) {
|
||||
std::string log = shaderInfoLog(shader);
|
||||
fprintf(stderr, "SHADER ERROR (%s):\n%s", name.c_str(), log.c_str());
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint buildProgram(bool separable, const std::string &name, std::vector<GLuint> shaders) {
|
||||
GLuint program = glCreateProgram();
|
||||
|
||||
if (separable)
|
||||
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
|
||||
for (GLuint shader : shaders)
|
||||
glAttachShader(program, shader);
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint link;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link);
|
||||
if (!link) {
|
||||
std::string log = programInfoLog(program);
|
||||
fprintf(stderr, "PROGRAM ERROR (%s):\n%s", name.c_str(), log.c_str());
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //HOPF_FIBRATION_UTIL_H
|
||||
@@ -1,20 +1,12 @@
|
||||
#version 440
|
||||
|
||||
#define PI 3.14159
|
||||
|
||||
uniform float bright;
|
||||
|
||||
in vec4 pos;
|
||||
in vec4 pos4;
|
||||
in vec4 pos3;
|
||||
layout(binding=1) uniform Unifs {
|
||||
mat4 uProj;
|
||||
vec4 uColor;
|
||||
};
|
||||
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
float light = -pos3.y / 10 + .5;
|
||||
|
||||
vec3 col = vec3(bright);
|
||||
|
||||
//color = vec4(col * light, 1);
|
||||
color = vec4(col, 1);
|
||||
color = uColor;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#version 440
|
||||
|
||||
#define CIRCLE_RES 128
|
||||
#define PI 3.14159
|
||||
|
||||
layout(points) in;
|
||||
layout(line_strip, max_vertices=CIRCLE_RES) out;
|
||||
|
||||
layout(binding=1) uniform Matrices {
|
||||
mat4 proj;
|
||||
mat4 model;
|
||||
};
|
||||
|
||||
in float xi_[];
|
||||
in float eta_[];
|
||||
|
||||
out vec4 pos;
|
||||
out vec4 pos4;
|
||||
out vec4 pos3;
|
||||
|
||||
void main(){
|
||||
for(int k = 0; k <= CIRCLE_RES; k++) {
|
||||
vec2 xi = vec2(xi_[0], 4 * PI * k / (CIRCLE_RES - 1));
|
||||
float eta = eta_[0];
|
||||
|
||||
// todo parameterize the projected circle so there aren't jagged edges.
|
||||
// should be smooth at <=32 segments, not just 128+
|
||||
float x = cos((xi.y + xi.x) / 2) * sin(eta);
|
||||
float y = sin((xi.y + xi.x) / 2) * sin(eta);
|
||||
float z = cos((xi.y - xi.x) / 2) * cos(eta);
|
||||
float w = sin((xi.y - xi.x) / 2) * cos(eta);
|
||||
|
||||
pos = vec4(x, y, z, w);
|
||||
pos4 = model * pos;
|
||||
pos3 = vec4(pos4.xyz / (1 - pos4.w), 1);
|
||||
gl_Position = proj * pos3;
|
||||
|
||||
if (length(gl_Position) > 4) {
|
||||
EndPrimitive();
|
||||
continue;
|
||||
}
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#version 440
|
||||
|
||||
layout(location=0) in float xi;
|
||||
layout(location=1) in float eta;
|
||||
layout(binding=1) uniform Unifs {
|
||||
mat4 uProj;
|
||||
vec4 uColor;
|
||||
};
|
||||
|
||||
out float xi_;
|
||||
out float eta_;
|
||||
in vec4 iPos;
|
||||
|
||||
void main(){
|
||||
xi_ = xi;
|
||||
eta_ = eta;
|
||||
gl_Position = uProj * vec4(iPos.xyz, 1);
|
||||
}
|
||||
@@ -1,356 +1,319 @@
|
||||
//
|
||||
// Created by allem on 2/7/2019.
|
||||
//
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#define GLM_FORCE_SWIZZLE
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
|
||||
#include "glga.hpp"
|
||||
#include "util.h"
|
||||
|
||||
#define PI 3.14159f
|
||||
#define RES_MAJOR 512
|
||||
#define RES_MINOR 8
|
||||
|
||||
namespace util {
|
||||
template<typename T>
|
||||
void bufferData(GLenum target, std::vector<T> data, GLenum usage) {
|
||||
glBufferData(target, data.size() * sizeof(T), &data.front(), usage);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void bufferData(GLenum target, T &data, GLenum usage) {
|
||||
glBufferData(target, sizeof(T), &data, usage);
|
||||
}
|
||||
#define Z_RADIUS 10.f
|
||||
|
||||
std::string readFile(const std::string &path) {
|
||||
std::ifstream file(path);
|
||||
if (!file) return std::string();
|
||||
#define PI (float) (M_PI)
|
||||
|
||||
file.ignore(std::numeric_limits<std::streamsize>::max());
|
||||
auto size = file.gcount();
|
||||
|
||||
if (size > 0x10000) return std::string();
|
||||
|
||||
file.clear();
|
||||
file.seekg(0, std::ios_base::beg);
|
||||
|
||||
std::stringstream sstr;
|
||||
sstr << file.rdbuf();
|
||||
file.close();
|
||||
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
void shaderFiles(GLuint shader, std::vector<std::string> &paths) {
|
||||
std::vector<std::string> strs;
|
||||
std::vector<const char *> c_strs;
|
||||
|
||||
for (const auto &path : paths) strs.push_back(readFile(path));
|
||||
for (const auto &str:strs) c_strs.push_back(str.c_str());
|
||||
|
||||
glShaderSource(shader, (GLsizei) c_strs.size(), &c_strs.front(), nullptr);
|
||||
}
|
||||
|
||||
std::string shaderInfoLog(GLuint shader) {
|
||||
GLint log_len;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
|
||||
char log[log_len];
|
||||
glGetShaderInfoLog(shader, log_len, nullptr, log);
|
||||
return std::string(log);
|
||||
}
|
||||
|
||||
std::string programInfoLog(GLuint program) {
|
||||
GLint log_len;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
|
||||
char log[log_len];
|
||||
glGetProgramInfoLog(program, log_len, nullptr, log);
|
||||
return std::string(log);
|
||||
}
|
||||
|
||||
GLuint buildShader(GLenum kind, const std::string &name, std::vector<std::string> paths) {
|
||||
GLuint shader = glCreateShader(kind);
|
||||
shaderFiles(shader, paths);
|
||||
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint comp;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &comp);
|
||||
if (!comp) {
|
||||
std::string log = shaderInfoLog(shader);
|
||||
fprintf(stderr, "SHADER ERROR (%s):\n%s", name.c_str(), log.c_str());
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint buildProgram(bool separable, const std::string &name, std::vector<GLuint> shaders) {
|
||||
GLuint program = glCreateProgram();
|
||||
|
||||
if (separable)
|
||||
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||
|
||||
for (GLuint shader : shaders)
|
||||
glAttachShader(program, shader);
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint link;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link);
|
||||
if (!link) {
|
||||
std::string log = programInfoLog(program);
|
||||
fprintf(stderr, "PROGRAM ERROR (%s):\n%s", name.c_str(), log.c_str());
|
||||
glDeleteProgram(program);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}
|
||||
|
||||
struct HopfCircle {
|
||||
float xi; // longitude
|
||||
float eta; // latitude
|
||||
|
||||
HopfCircle(float xi, float eta) : xi(xi), eta(eta) {}
|
||||
|
||||
HopfCircle() : HopfCircle(0, 0) {}
|
||||
struct Unifs {
|
||||
glm::mat4 proj = glm::identity<glm::mat4>();
|
||||
glm::vec4 color = glm::vec4(1);
|
||||
};
|
||||
|
||||
struct State {
|
||||
GLuint vao{};
|
||||
GLuint vbo{}, ubo{};
|
||||
GLuint unif_bright{};
|
||||
GLuint ubo_bp = 1;
|
||||
GLuint vao_wide{};
|
||||
GLuint vbo_wide{};
|
||||
GLuint ibo_wide{};
|
||||
|
||||
GLuint vao_thin{};
|
||||
GLuint vbo_thin{};
|
||||
GLuint ibo_thin{};
|
||||
|
||||
GLuint ubo{};
|
||||
|
||||
GLuint prog{};
|
||||
const GLuint UNIF_BINDING_POINT = 1;
|
||||
|
||||
std::vector<HopfCircle> circles{};
|
||||
ga::Mat rot = ga::unit::identity();
|
||||
float view = 4.f;
|
||||
std::vector<glm::vec4> verts_wide{};
|
||||
std::vector<unsigned> inds_wide{};
|
||||
|
||||
float t = 0;
|
||||
float dt = 0;
|
||||
std::vector<glm::vec4> verts_thin{};
|
||||
std::vector<unsigned> inds_thin{};
|
||||
|
||||
void regen() {
|
||||
circles.clear();
|
||||
Unifs unifs;
|
||||
|
||||
const int N = 32;
|
||||
const int M = 4;
|
||||
const float PAD = 0.0125;
|
||||
glm::vec4 hopf(float xi, float nu, float eta) {
|
||||
return glm::vec4( // todo find parameterization given quaternion
|
||||
cos((nu + xi) / 2) * sin(eta),
|
||||
sin((nu + xi) / 2) * sin(eta),
|
||||
cos((nu - xi) / 2) * cos(eta),
|
||||
sin((nu - xi) / 2) * cos(eta)
|
||||
);
|
||||
}
|
||||
|
||||
for (int x = 0; x <= N; ++x) {
|
||||
for (int e = 1; e < M; ++e) {
|
||||
float xi = 2 * PI * x / N;
|
||||
float eta = (PI / 2 - 2 * PAD) * e / M + PAD;
|
||||
circles.emplace_back(xi, eta);
|
||||
glm::vec3 stereo(float xi, float nu, float eta) {
|
||||
auto h = hopf(xi, nu, eta);
|
||||
|
||||
float xw = 0.8;
|
||||
float yw = -0.1;
|
||||
float zw = 2.5;
|
||||
float xy = 0.0;
|
||||
float yz = 0.5;
|
||||
float zx = 0.0;
|
||||
|
||||
auto r = glm::mat4(
|
||||
glm::vec4(cos(xw), 0, 0, sin(xw)),
|
||||
glm::vec4(0, 1, 0, 0),
|
||||
glm::vec4(0, 0, 1, 0),
|
||||
glm::vec4(-sin(xw), 0, 0, cos(xw))
|
||||
) * glm::mat4(
|
||||
glm::vec4(1, 0, 0, 0),
|
||||
glm::vec4(0, cos(yw), 0, sin(yw)),
|
||||
glm::vec4(0, 0, 1, 0),
|
||||
glm::vec4(0, -sin(yw), 0, cos(yw))
|
||||
) * glm::mat4(
|
||||
glm::vec4(1, 0, 0, 0),
|
||||
glm::vec4(0, 1, 0, 0),
|
||||
glm::vec4(0, 0, cos(zw), sin(zw)),
|
||||
glm::vec4(0, 0, -sin(zw), cos(zw))
|
||||
) * glm::mat4(
|
||||
glm::vec4(cos(xy), sin(xy), 0, 0),
|
||||
glm::vec4(-sin(xy), cos(xy), 0, 0),
|
||||
glm::vec4(0, 0, 1, 0),
|
||||
glm::vec4(0, 0, 0, 1)
|
||||
) * glm::mat4(
|
||||
glm::vec4(1, 0, 0, 0),
|
||||
glm::vec4(0, cos(yz), sin(yz), 0),
|
||||
glm::vec4(0, -sin(yz), cos(yz), 0),
|
||||
glm::vec4(0, 0, 0, 1)
|
||||
) * glm::mat4(
|
||||
glm::vec4(cos(zx), 0, -sin(zx), 0),
|
||||
glm::vec4(0, 1, 0, 0),
|
||||
glm::vec4(sin(zx), 0, cos(zx), 0),
|
||||
glm::vec4(0, 0, 0, 1)
|
||||
);
|
||||
|
||||
auto rot = r;
|
||||
|
||||
h = rot * h;
|
||||
|
||||
auto s = h.xyz() / (1 - h.w);
|
||||
|
||||
s /= 2;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
add_ring(std::vector<glm::vec4> &dest_verts, std::vector<unsigned> &dest_inds, float xi, float eta, float rad) {
|
||||
std::vector<glm::vec3> circle;
|
||||
std::vector<glm::vec3> torus;
|
||||
std::vector<unsigned> ind;
|
||||
|
||||
for (unsigned i = 0; i < RES_MAJOR; ++i) {
|
||||
auto nu = 4 * PI * i / RES_MAJOR;
|
||||
|
||||
auto v = stereo(xi, nu, eta);
|
||||
|
||||
circle.push_back(v);
|
||||
}
|
||||
|
||||
auto center = glm::vec3(0);
|
||||
for (auto v : circle) center += v;
|
||||
center /= (float) RES_MAJOR;
|
||||
|
||||
auto A = stereo(xi, 0 * PI, eta);
|
||||
auto B = stereo(xi, 1 * PI, eta);
|
||||
auto normal = glm::normalize(cross(A - center, B - center));
|
||||
|
||||
for (unsigned i = 0; i < circle.size(); ++i) {
|
||||
auto v = circle[i];
|
||||
|
||||
auto b1 = normal;
|
||||
auto b2 = glm::normalize(v - center);
|
||||
|
||||
for (int j = 0; j < RES_MINOR; ++j) {
|
||||
auto theta = 2 * PI * j / RES_MINOR;
|
||||
|
||||
auto p = v + (cos(theta) * b1 + sin(theta) * b2) * rad;
|
||||
|
||||
ind.push_back(i * RES_MINOR + j);
|
||||
ind.push_back((i + 1) * RES_MINOR + j);
|
||||
ind.push_back(i * RES_MINOR + (j + 1) % RES_MINOR);
|
||||
|
||||
ind.push_back((i + 1) * RES_MINOR + j);
|
||||
ind.push_back((i + 1) * RES_MINOR + (j + 1) % RES_MINOR);
|
||||
ind.push_back(i * RES_MINOR + (j + 1) % RES_MINOR);
|
||||
|
||||
torus.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
util::bufferData(GL_ARRAY_BUFFER, circles, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
auto offset = (unsigned) dest_verts.size();
|
||||
|
||||
for (auto v : torus)
|
||||
dest_verts.emplace_back(v, 1);
|
||||
|
||||
for (auto i : ind)
|
||||
dest_inds.push_back(offset + i % (RES_MAJOR * RES_MINOR));
|
||||
}
|
||||
|
||||
explicit State(GLFWwindow *window) {
|
||||
printf("vendor: %s\nrenderer: %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
|
||||
void updateUnifs() {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
|
||||
util::bufferData(GL_UNIFORM_BUFFER, unifs, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
float lw[2];
|
||||
glGetFloatv(GL_LINE_WIDTH_RANGE, lw);
|
||||
printf("line width range: %.2f to %.2f\n", lw[0], lw[1]);
|
||||
void regen() {
|
||||
printf("generating\n");
|
||||
|
||||
glGenBuffers(1, &vbo);
|
||||
glGenBuffers(1, &ubo);
|
||||
glGenVertexArrays(1, &vao);
|
||||
verts_wide.clear();
|
||||
verts_thin.clear();
|
||||
inds_wide.clear();
|
||||
inds_thin.clear();
|
||||
|
||||
const float XI_R = 28;
|
||||
const float ETA_R = 5;
|
||||
const float ETA_BUF = 0;
|
||||
|
||||
for (int i = 0; i < XI_R; ++i) {
|
||||
float xi = 2 * PI * i / XI_R;
|
||||
|
||||
for (int j = 1; j <= ETA_R - 1; ++j) {
|
||||
float eta = ETA_BUF + (PI / 2 - 2 * ETA_BUF) * j / ETA_R;
|
||||
|
||||
add_ring(verts_wide, inds_wide, xi, eta, .015);
|
||||
add_ring(verts_thin, inds_thin, xi, eta, .004);
|
||||
}
|
||||
}
|
||||
|
||||
add_ring(verts_wide, inds_wide, .001f, .0f, .015);
|
||||
add_ring(verts_thin, inds_thin, .001f, .0f, .008);
|
||||
|
||||
add_ring(verts_wide, inds_wide, .001f, PI / 2 - .0f, .015);
|
||||
add_ring(verts_thin, inds_thin, .001f, PI / 2 - .0f, .008);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_thin);
|
||||
util::bufferData(GL_ARRAY_BUFFER, verts_thin, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_thin);
|
||||
util::bufferData(GL_ELEMENT_ARRAY_BUFFER, inds_thin, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_wide);
|
||||
util::bufferData(GL_ARRAY_BUFFER, verts_wide, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_wide);
|
||||
util::bufferData(GL_ELEMENT_ARRAY_BUFFER, inds_wide, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void init(GLFWwindow *window) {
|
||||
GLuint vs = util::buildShader(GL_VERTEX_SHADER, "vs", {"shaders/main.vert"});
|
||||
GLuint fs = util::buildShader(GL_FRAGMENT_SHADER, "fs", {"shaders/main.frag"});
|
||||
prog = util::buildProgram(false, "prog", {vs, fs});
|
||||
|
||||
glGenBuffers(1, &vbo_wide);
|
||||
glGenBuffers(1, &ibo_wide);
|
||||
glGenBuffers(1, &vbo_thin);
|
||||
glGenBuffers(1, &ibo_thin);
|
||||
|
||||
regen();
|
||||
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, ubo_bp, ubo);
|
||||
GLint pos = glGetAttribLocation(prog, "iPos");
|
||||
|
||||
GLuint vs = util::buildShader(GL_VERTEX_SHADER, "vs", {"shaders/main.vert"});
|
||||
GLuint gs = util::buildShader(GL_GEOMETRY_SHADER, "gs", {"shaders/main.geom"});
|
||||
GLuint fs = util::buildShader(GL_FRAGMENT_SHADER, "fs", {"shaders/main.frag"});
|
||||
prog = util::buildProgram(false, "prog", {vs, gs, fs});
|
||||
glGenBuffers(1, &ubo);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, UNIF_BINDING_POINT, ubo);
|
||||
updateUnifs();
|
||||
|
||||
unif_bright = (GLuint) glGetUniformLocation(prog, "bright");
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
auto xi = glGetAttribLocation(prog, "xi");
|
||||
if (xi >= 0) {
|
||||
glEnableVertexAttribArray((unsigned) xi);
|
||||
glVertexAttribPointer((unsigned) xi, 1, GL_FLOAT, GL_FALSE,
|
||||
sizeof(HopfCircle),
|
||||
(void *) offsetof(HopfCircle, xi));
|
||||
glGenVertexArrays(1, &vao_wide);
|
||||
glBindVertexArray(vao_wide);
|
||||
if (pos >= 0) {
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_wide);
|
||||
glVertexAttribPointer((GLuint) pos, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), nullptr);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
auto eta = glGetAttribLocation(prog, "eta");
|
||||
if (eta >= 0) {
|
||||
glEnableVertexAttribArray((unsigned) eta);
|
||||
glVertexAttribPointer((unsigned) eta, 1, GL_FLOAT, GL_FALSE,
|
||||
sizeof(HopfCircle),
|
||||
(void *) offsetof(HopfCircle, eta));
|
||||
}
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_wide);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void update(GLFWwindow *window, float dt, int frame) {
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(window, &w, &h);
|
||||
auto ar = (float) w / h;
|
||||
|
||||
t += dt;
|
||||
|
||||
float c = std::cos(t / 8);
|
||||
float s = std::sin(t / 8);
|
||||
|
||||
float c_ = std::cos(3.f / 8);
|
||||
float s_ = std::sin(3.f / 8);
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
|
||||
util::bufferData<ga::Mat>(GL_UNIFORM_BUFFER, {
|
||||
ga::Mat(
|
||||
ga::Vec(1.f / view / ar, 0, 0, 0),
|
||||
ga::Vec(0, 0, 1.f / 10, 0),
|
||||
ga::Vec(0, 1.f / view, 0, 0),
|
||||
ga::Vec(0, 0, 0, 1.f)),
|
||||
rot
|
||||
}, GL_STREAM_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
glGenVertexArrays(1, &vao_thin);
|
||||
glBindVertexArray(vao_thin);
|
||||
if (pos >= 0) {
|
||||
glEnableVertexAttribArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_thin);
|
||||
glVertexAttribPointer((GLuint) pos, 4, GL_FLOAT, GL_FALSE, sizeof(glm::vec4), nullptr);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_thin);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void render(GLFWwindow *window, float dt, int frame) {
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(window, &w, &h);
|
||||
|
||||
glViewport(0, 0, w, h);
|
||||
glClearColor(1, 1, 1, 1);
|
||||
unifs.color = glm::vec4(0, 0, 0, 1);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
glUseProgram(prog);
|
||||
glBindVertexArray(vao);
|
||||
glBindVertexArray(vao_wide);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
updateUnifs();
|
||||
glUseProgram(prog);
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei) inds_wide.size(), GL_UNSIGNED_INT, 0);
|
||||
|
||||
glLineWidth(10);
|
||||
glUniform1f(unif_bright, 0);
|
||||
glDrawArrays(GL_POINTS, 0, (unsigned) circles.size());
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glLineWidth(1);
|
||||
glUniform1f(unif_bright, 1);
|
||||
glDrawArrays(GL_POINTS, 0, (unsigned) circles.size());
|
||||
glBindVertexArray(vao_thin);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
updateUnifs();
|
||||
glUseProgram(prog);
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei) inds_thin.size(), GL_UNSIGNED_INT, 0);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
|
||||
glFinish();
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
void on_scroll(GLFWwindow *window, double xoffset, double yoffset) {
|
||||
printf("scroll %.2f %.2f\n", xoffset, yoffset);
|
||||
void update(GLFWwindow *window, float dt, int frame) {
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(window, &w, &h);
|
||||
|
||||
int xw = glfwGetKey(window, GLFW_KEY_Q);
|
||||
int yw = glfwGetKey(window, GLFW_KEY_W);
|
||||
int zw = glfwGetKey(window, GLFW_KEY_E);
|
||||
int xy = glfwGetKey(window, GLFW_KEY_A);
|
||||
int yz = glfwGetKey(window, GLFW_KEY_S);
|
||||
int zx = glfwGetKey(window, GLFW_KEY_D);
|
||||
|
||||
int zoom = glfwGetKey(window, GLFW_KEY_LEFT_CONTROL);
|
||||
|
||||
float t = (float) yoffset * PI / 48;
|
||||
float c = std::cos(t);
|
||||
float s = std::sin(t);
|
||||
|
||||
if (xw) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(c, 0, 0, s),
|
||||
ga::Vec(0, 1, 0, 0),
|
||||
ga::Vec(0, 0, 1, 0),
|
||||
ga::Vec(-s, 0, 0, c)
|
||||
));
|
||||
}
|
||||
|
||||
if (yw) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(1, 0, 0, 0),
|
||||
ga::Vec(0, c, 0, s),
|
||||
ga::Vec(0, 0, 1, 0),
|
||||
ga::Vec(0, -s, 0, c)
|
||||
));
|
||||
}
|
||||
|
||||
if (zw) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(1, 0, 0, 0),
|
||||
ga::Vec(0, 1, 0, 0),
|
||||
ga::Vec(0, 0, c, s),
|
||||
ga::Vec(0, 0, -s, c)
|
||||
));
|
||||
}
|
||||
|
||||
if (xy) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(c, s, 0, 0),
|
||||
ga::Vec(-s, c, 0, 0),
|
||||
ga::Vec(0, 0, 1, 0),
|
||||
ga::Vec(0, 0, 0, 1)
|
||||
));
|
||||
}
|
||||
|
||||
if (yz) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(1, 0, 0, 0),
|
||||
ga::Vec(0, c, s, 0),
|
||||
ga::Vec(0, -s, c, 0),
|
||||
ga::Vec(0, 0, 0, 1)
|
||||
));
|
||||
}
|
||||
|
||||
if (zx) {
|
||||
rot = ga::mul(rot, ga::Mat(
|
||||
ga::Vec(c, 0, -s, 0),
|
||||
ga::Vec(0, 1, 0, 0),
|
||||
ga::Vec(s, 0, c, 0),
|
||||
ga::Vec(0, 0, 0, 1)
|
||||
));
|
||||
}
|
||||
|
||||
if (zoom) {
|
||||
view *= 1 - t / 5;
|
||||
}
|
||||
|
||||
std::ofstream matfile;
|
||||
matfile.open("matrix.txt");
|
||||
matfile << "vec((\n";
|
||||
matfile << "vec((" << rot.x.x << ", " << rot.x.y << ", " << rot.x.z << ", " << rot.x.w << ")),\n";
|
||||
matfile << "vec((" << rot.y.x << ", " << rot.y.y << ", " << rot.y.z << ", " << rot.y.w << ")),\n";
|
||||
matfile << "vec((" << rot.z.x << ", " << rot.z.y << ", " << rot.z.z << ", " << rot.z.w << ")),\n";
|
||||
matfile << "vec((" << rot.w.x << ", " << rot.w.y << ", " << rot.w.z << ", " << rot.w.w << ")),\n";
|
||||
matfile << ")),";
|
||||
matfile.close();
|
||||
float ar = (float) w / (float) h;
|
||||
unifs.proj = glm::ortho(-ar, ar, -1.f, 1.f, -Z_RADIUS, Z_RADIUS);
|
||||
}
|
||||
|
||||
void deinit(GLFWwindow *window) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) {
|
||||
auto *state = (State *) glfwGetWindowUserPointer(window);
|
||||
state->on_scroll(window, xoffset, yoffset);
|
||||
}
|
||||
|
||||
void run() {
|
||||
void run(State *state, const std::string &title) {
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
auto window = glfwCreateWindow(1280, 720, "Hopf Fibration", nullptr, nullptr);
|
||||
auto window = glfwCreateWindow(3840, 1249, title.c_str(), nullptr, nullptr);
|
||||
if (!window) {
|
||||
glfwTerminate();
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -360,10 +323,8 @@ void run() {
|
||||
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||
glfwSwapInterval(0);
|
||||
|
||||
auto state = State(window);
|
||||
glfwSetWindowUserPointer(window, &state);
|
||||
|
||||
glfwSetScrollCallback(window, scroll_callback);
|
||||
glfwSetWindowUserPointer(window, state);
|
||||
state->init(window);
|
||||
|
||||
double time = glfwGetTime();
|
||||
int frame = 0;
|
||||
@@ -371,8 +332,8 @@ void run() {
|
||||
double time_ = glfwGetTime();
|
||||
auto dt = (float) (time_ - time);
|
||||
|
||||
state.update(window, dt, frame);
|
||||
state.render(window, dt, frame);
|
||||
state->update(window, dt, frame);
|
||||
state->render(window, dt, frame);
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
@@ -380,7 +341,7 @@ void run() {
|
||||
frame += 1;
|
||||
}
|
||||
|
||||
state.deinit(window);
|
||||
state->deinit(window);
|
||||
|
||||
glfwDestroyWindow(window);
|
||||
}
|
||||
@@ -390,8 +351,11 @@ int main() {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
run();
|
||||
auto *state = new State();
|
||||
run(state, "Hopf Fibration");
|
||||
delete state;
|
||||
|
||||
glfwTerminate();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
1
vendor/glm
vendored
Submodule
1
vendor/glm
vendored
Submodule
Submodule vendor/glm added at 7590260cf8
Reference in New Issue
Block a user