initial
This commit is contained in:
151
.gitignore
vendored
Normal file
151
.gitignore
vendored
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### C template
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
### CMake template
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-debug/
|
||||||
|
cmake-build-release/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
### C++ template
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
|
||||||
7
.gitmodules
vendored
Normal file
7
.gitmodules
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[submodule "vendor/glfw"]
|
||||||
|
path = vendor/glfw
|
||||||
|
url = https://github.com/glfw/glfw.git
|
||||||
|
[submodule "vendor/glad"]
|
||||||
|
path = vendor/glad
|
||||||
|
url = https://github.com/Dav1dde/glad.git
|
||||||
|
branch = c
|
||||||
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>
|
||||||
14
CMakeLists.txt
Normal file
14
CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(main)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
add_library(glad vendor/glad/src/glad.c)
|
||||||
|
target_include_directories(glad PUBLIC vendor/glad/include)
|
||||||
|
|
||||||
|
option(GLFW_BUILD_DOCS OFF)
|
||||||
|
option(GLFW_BUILD_EXAMPLES OFF)
|
||||||
|
option(GLFW_BUILD_TESTS OFF)
|
||||||
|
add_subdirectory(vendor/glfw)
|
||||||
|
|
||||||
|
add_subdirectory(main)
|
||||||
29
main/CMakeLists.txt
Normal file
29
main/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
project(main)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
src/main.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
glad
|
||||||
|
glfw)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
PRIVATE include)
|
||||||
|
|
||||||
|
set(SHADERS
|
||||||
|
shaders/main.frag
|
||||||
|
shaders/main.vert)
|
||||||
|
add_custom_target(shaders DEPENDS ${SHADERS})
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
TARGET shaders PRE_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/shaders/
|
||||||
|
COMMENT "clearing shaders"
|
||||||
|
)
|
||||||
|
add_custom_command(
|
||||||
|
TARGET shaders POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/shaders/ ${CMAKE_CURRENT_BINARY_DIR}/shaders/
|
||||||
|
COMMENT "copying shaders"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(${PROJECT_NAME} shaders)
|
||||||
7
main/shaders/main.frag
Normal file
7
main/shaders/main.frag
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
color = vec4(1);
|
||||||
|
}
|
||||||
31
main/shaders/main.geom
Normal file
31
main/shaders/main.geom
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
#define CIRCLE_RES 256
|
||||||
|
#define PI 3.14159
|
||||||
|
|
||||||
|
layout(points) in;
|
||||||
|
layout(line_strip, max_vertices=CIRCLE_RES) out;
|
||||||
|
|
||||||
|
in float xi_[];
|
||||||
|
in float eta_[];
|
||||||
|
|
||||||
|
layout(binding=1) uniform Matrices {
|
||||||
|
mat4 proj;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
for(int k = 0; k <= CIRCLE_RES; k++) {
|
||||||
|
vec2 xi = vec2(xi_[0], 4 * PI * k / CIRCLE_RES);
|
||||||
|
float eta = eta_[0];
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
vec4 pos = vec4(x / (1 - w), y / (1 - w), z / (1 - w), 1);
|
||||||
|
|
||||||
|
gl_Position = proj * pos;
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
main/shaders/main.vert
Normal file
12
main/shaders/main.vert
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location=0) in float xi;
|
||||||
|
layout(location=1) in float eta;
|
||||||
|
|
||||||
|
out float xi_;
|
||||||
|
out float eta_;
|
||||||
|
|
||||||
|
void main(){
|
||||||
|
xi_ = xi;
|
||||||
|
eta_ = eta;
|
||||||
|
}
|
||||||
262
main/src/main.cpp
Normal file
262
main/src/main.cpp
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
#include <glad/glad.h>
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define PI 3.14159f
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HopfCircle {
|
||||||
|
float xi;
|
||||||
|
float eta;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
GLuint vao{};
|
||||||
|
GLuint vbo{}, ubo{};
|
||||||
|
GLuint ubo_bp = 1;
|
||||||
|
|
||||||
|
GLuint prog{};
|
||||||
|
|
||||||
|
std::vector<HopfCircle> circles{};
|
||||||
|
|
||||||
|
explicit State(GLFWwindow *window) {
|
||||||
|
const int N = 32;
|
||||||
|
for (int k = 0; k < N; ++k) {
|
||||||
|
circles.push_back({2 * PI * k / N, 0.025});
|
||||||
|
circles.push_back({2 * PI * k / N, PI / 2 - 0.025});
|
||||||
|
|
||||||
|
circles.push_back({2 * PI * k / N, 0.3f});
|
||||||
|
circles.push_back({2 * PI * k / N, 0.4f});
|
||||||
|
circles.push_back({2 * PI * k / N, 0.5f});
|
||||||
|
|
||||||
|
circles.push_back({2 * PI * k / N, 1.1f});
|
||||||
|
circles.push_back({2 * PI * k / N, 1.2f});
|
||||||
|
circles.push_back({2 * PI * k / N, 1.3f});
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("vendor: %s\nrenderer: %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
|
||||||
|
|
||||||
|
glGenBuffers(1, &vbo);
|
||||||
|
glGenBuffers(1, &ubo);
|
||||||
|
glGenVertexArrays(1, &vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
util::bufferData<HopfCircle>(GL_ARRAY_BUFFER, circles, GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, ubo_bp, ubo);
|
||||||
|
|
||||||
|
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});
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(GLFWwindow *window, float dt, int frame) {
|
||||||
|
int w, h;
|
||||||
|
glfwGetFramebufferSize(window, &w, &h);
|
||||||
|
auto ar = (float) w / h;
|
||||||
|
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
|
||||||
|
util::bufferData<float>(GL_UNIFORM_BUFFER, {
|
||||||
|
1.f / 4 / ar, 0, 0, 0,
|
||||||
|
0, 0, 1.f / 10, 0,
|
||||||
|
0, 1.f / 4, 0, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
}, GL_STREAM_DRAW);
|
||||||
|
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(GLFWwindow *window, float dt, int frame) {
|
||||||
|
int w, h;
|
||||||
|
glfwGetFramebufferSize(window, &w, &h);
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
glUseProgram(prog);
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
glPointSize(5);
|
||||||
|
glLineWidth(3);
|
||||||
|
glDrawArrays(GL_POINTS, 0, (unsigned) circles.size());
|
||||||
|
glBindVertexArray(0);
|
||||||
|
glUseProgram(0);
|
||||||
|
|
||||||
|
glFinish();
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit(GLFWwindow *window) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
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);
|
||||||
|
if (!window) {
|
||||||
|
glfwTerminate();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
|
||||||
|
glfwSwapInterval(0);
|
||||||
|
|
||||||
|
auto state = State(window);
|
||||||
|
|
||||||
|
double time = glfwGetTime();
|
||||||
|
int frame = 0;
|
||||||
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
double time_ = glfwGetTime();
|
||||||
|
auto dt = (float) (time_ - time);
|
||||||
|
|
||||||
|
state.update(window, dt, frame);
|
||||||
|
state.render(window, dt, frame);
|
||||||
|
|
||||||
|
glfwPollEvents();
|
||||||
|
|
||||||
|
time = time_;
|
||||||
|
frame += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.deinit(window);
|
||||||
|
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (!glfwInit()) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
||||||
|
glfwTerminate();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
1
vendor/glad
vendored
Submodule
1
vendor/glad
vendored
Submodule
Submodule vendor/glad added at 5bf3eda6da
1
vendor/glfw
vendored
Submodule
1
vendor/glfw
vendored
Submodule
Submodule vendor/glfw added at 72c3908e14
Reference in New Issue
Block a user