15 Commits

Author SHA1 Message Date
bcb105ecaf Add ComputeShader header 2024-04-10 20:30:55 -04:00
29dd5d12f5 Migrate to latest J3ML Release and latest ReWindow Release 2024-04-09 16:41:32 -04:00
8100d5b333 Migrate to latest J3ML Release 2024-04-08 13:43:36 -04:00
d12d8c8d65 Implement Camera3D class 2024-04-04 18:18:59 -04:00
39ae1fa494 Screen Draw 2024-04-04 17:46:54 -04:00
76e6e7cec7 File Load Success 2024-04-04 14:30:57 -04:00
44c150b395 unable to load file 2024-04-02 18:32:03 -04:00
580ff0dd7f throws on glCreateShader 2024-04-02 15:18:32 -04:00
6d70019858 Working on Grid.h (missing opengl functions) 2024-04-01 13:10:24 -04:00
9bf2bb79c0 create SkeletalAnimationDemo 2024-03-31 14:18:14 -04:00
95a87d0d11 Update J3ML 2024-03-21 12:43:37 -04:00
17861e17fa Finish FrameBuffer class 2024-03-04 00:33:01 -05:00
scientiist
268abb6f18 Document CMakeLists.txt while geeked 2024-03-02 01:40:16 -06:00
scientiist
4939dd9d23 Fixed Headers 2024-03-02 01:07:42 -06:00
77e7a4974a Add Files 2024-02-29 02:16:39 -05:00
49 changed files with 14031 additions and 127 deletions

View File

@@ -1,15 +1,36 @@
cmake_minimum_required(VERSION 3.27)
# @file CMakeLists.txt - LearnOpenGL library's root CMakeList
# @auth Joshua O'Leary
# @edited 2 March 2024
# @copyright 2024 Redacted Software LLC
# @contact josh@redacted.cc, william@redacted.cc
# @license Explicitly granted to the Public Domain by Redacted Software.
# So, what's up with this file?
# C++ is old as hell, basically.
# We use a funky scripting language to tell the compiler how to build our program
# which (if done correctly) should work automatically and identically on any
# developer environment.
# It's well-integrated with every serious C++ Editor that exists to mortals.
# Allow CMake versions between and including 3.18 and 3.27.
cmake_minimum_required(VERSION 3.18...3.27)
project(LearnOpenGL
VERSION 1.0
VERSION 1.5
LANGUAGES CXX
)
# Don't let dummies compile the program right in the source directory
# (Try it yourself if you don't yet know)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed!")
endif()
# Modern C++ Please
set(CMAKE_CXX_STANDARD 20)
# Windows Compatibility Flag?
# (TODO: Test, IIRC this line failed in one of the projects on the Billiam VM)
if (WIN32)
set(CMAKE_CXX_FLAGS "-municode")
endif()
@@ -17,75 +38,74 @@ endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}")
# Enable Package Manager
# CPM will automagically download and build required libraries in a neat and hassle-free manner
# (That is our hope and intention)
include(cmake/CPM.cmake)
# Most dependencies
# Grab Josh Math Lib
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Prerelease-18.zip
URL https://git.redacted.cc/josh/j3ml/archive/Release-1.zip
)
# Grab GLAD
# GLAD as I understand essentially generates an OpenGL API to spec with the
# proper "API extensions" based on available graphics hardware features.
CPMAddPackage(
NAME GLAD
URL https://git.redacted.cc/Redacted/glad/archive/v2.1.zip
)
#https://github.com/assimp/assimp/blob/master/LICENSE
# Open Asset Importer - 3D Model File IO Library
# https://github.com/assimp/assimp/blob/master/LICENSE
CPMAddPackage(
NAME ASSIMP
URL https://github.com/assimp/assimp/archive/refs/tags/v5.3.1.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/vA0.2.18.zip
)
# Redacted Software's ReWindow Library
# One Class Header, Implemented for multiple platforms
#CPMAddPackage(
# NAME ReWindow
# URL https://git.redacted.cc/Redacted/ReWindow/archive/vA0.2.22.zip
#)
# Grab OpenGL from the "System"
find_package(OpenGL REQUIRED)
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.hpp")
file(GLOB_RECURSE SOURCES "src/*.c" "src/*.cpp")
file(GLOB_RECURSE SOURCES "src/LearnOpenGL/*.c" "src/LearnOpenGL/*.cpp")
# TODO: Wrangle target_include_directories
include_directories(${PROJECT_SOURCE_DIR}/include)
add_library(LearnOpenGL SHARED ${SOURCES}
include/LearnOpenGL/LearnOpenGL.h
src/LearnOpenGL/LearnOpenGL.cpp
src/LearnOpenGL/Mesh.cpp
src/LearnOpenGL/Model.cpp
include/LearnOpenGL/Texture3D.h
src/LearnOpenGL/Texture3D.cpp
include/LearnOpenGL/Camera3D.h
include/LearnOpenGL/CubeMap.h
include/LearnOpenGL/FrameBuffer.h
include/LearnOpenGL/IndexBuffer.h
include/LearnOpenGL/RenderBuffer.h
include/LearnOpenGL/VertexArray.h
include/LearnOpenGL/VertexBuffer.h
include/LearnOpenGL/Skybox.h
include/LearnOpenGL/Texture.h
include/LearnOpenGL/Camera2D.h
)
add_library(LearnOpenGL SHARED ${SOURCES})
target_include_directories(LearnOpenGL PUBLIC ${J3ML_SOURCE_DIR}/include)
target_include_directories(LearnOpenGL PUBLIC ${glad_SOURCE_DIR}/include)
target_include_directories(LearnOpenGL PUBLIC ${ASSIMP_SOURCE_DIR}/include)
target_link_libraries(LearnOpenGL PUBLIC J3ML)
target_link_libraries(LearnOpenGL PUBLIC GL)
target_link_libraries(LearnOpenGL PUBLIC glad)
target_link_libraries(LearnOpenGL PUBLIC assimp)
set_target_properties(LearnOpenGL PROPERTIES LINKER_LANGUAGE CXX)
add_executable(LearnOpenGLDemo main.cpp)
#add_executable(LearnOpenGLDemo main.cpp)
target_include_directories(LearnOpenGLDemo PRIVATE ${ReWindow_SOURCE_DIR}/include)
# Privately include and link ReWindowLib DIRECTLY to LearnOpenGLDemo executable
# so it doesn't become a second-order-dependency
# On downstream projects that use LearnOpenGL as a dependency
#target_include_directories(LearnOpenGLDemo PRIVATE ${ReWindow_SOURCE_DIR}/include)
#target_link_libraries(LearnOpenGLDemo PRIVATE ReWindowLibrary)
target_link_libraries(LearnOpenGLDemo PRIVATE ReWindowLibrary)
target_link_libraries(LearnOpenGLDemo PUBLIC LearnOpenGL)
#target_link_libraries(LearnOpenGLDemo PUBLIC LearnOpenGL)
add_subdirectory(src/demos/SkeletalAnim)
# Copy resources into build directory!
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/resources/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources)

View File

@@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include "J3ML/LinearAlgebra/Matrix4x4.h"
#include <assimp/Importer.hpp>
#include <LearnOpenGL/Model.h>
#include <LearnOpenGL/Bone.h>
#include <LearnOpenGL/BoneInfo.h>
namespace LearnOpenGL
{
class Animation
{
public:
Animation() = default;
Animation(const std::string& animationPath, Model* model);
~Animation();
Bone* FindBone(const std::string& name);
inline float GetTicksPerSecond() { return TicksPerSecond; }
inline float GetDuration() { return Duration; }
inline const AssimpNodeData& GetRootNode() { return RootNode; }
inline const std::map<std::string, BoneInfo>& GetBoneIDMap()
{
return BoneInfoMap;
}
float Duration;
int TicksPerSecond;
std::vector<Bone> Bones;
AssimpNodeData RootNode;
std::map<std::string, BoneInfo> BoneInfoMap;
private:
void ReadMissingBones(const aiAnimation* animation, Model& model);
Matrix4x4 OMFGConvert(aiMatrix4x4)
{
}
void ReadHierarchyData(AssimpNodeData& dest, const aiNode* src);
};
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include <LearnOpenGL/Animation.h>
#include <LearnOpenGL/Bone.h>
#include <vector>
#include <J3ML/LinearAlgebra/Matrix4x4.h>
#include <string>
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
namespace LearnOpenGL
{
using J3ML::LinearAlgebra::Matrix4x4;
class Animator
{
public:
Animator() {}
Animator(Animation* animation);
void UpdateAnimations(float dt);
void PlayAnimation(Animation* pAnimation);
void CalculateBoneTransform(const AssimpNodeData* node, Matrix4x4 parentTransform);
std::vector<Matrix4x4> GetFinalBoneMatrices();
private:
std::vector<Matrix4x4> FinalBoneMatrices;
Animation* CurrentAnimation;
float CurrentTime;
float DeltaTime;
};
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <J3ML/LinearAlgebra/Matrix4x4.h>
#include <J3ML/LinearAlgebra/Vector3.h>
#include <J3ML/LinearAlgebra/Quaternion.h>
#include <cassert>
#include <vector>
#include "assimp/anim.h"
namespace LearnOpenGL
{
using namespace J3ML::LinearAlgebra;
struct KeyPosition
{
Vector3 Position;
float Timestamp;
};
struct KeyRotation
{
Quaternion Orientation;
float Timestamp;
};
struct KeyScale
{
Vector3 Scale;
float Timestamp;
};
class Bone
{
public:
Bone(const std::string& name, int ID, const aiNodeAnim* channel);
void Update(float animationTime);
Matrix4x4 GetLocalTransform() const;
std::string GetBoneName() const;
int GetBoneID() const;
int GetPositionIndex(float animationTime);
int GetRotationIndex(float animationTime);
int GetScaleIndex(float animationTime);
// Private?
static float GetScaleFactor(float lastTimestamp, float nextTimestamp, float animationTime);
Matrix4x4 InterpolatePosition(float animationTime);
Matrix4x4 InterpolateRotation(float animationTime);
Matrix4x4 InterpolateScaling(float animationTime);
std::vector<KeyPosition> Positions;
std::vector<KeyRotation> Rotations;
std::vector<KeyScale> Scales;
int NumPositions;
int NumRotations;
int NumScalings;
Matrix4x4 LocalTransform;
std::string Name;
int ID;
};
}

View File

@@ -0,0 +1,20 @@
#pragma once
namespace LearnOpenGL
{
using J3ML::LinearAlgebra::Matrix4x4;
struct BoneInfo
{
int ID; // index in finalBoneMatrices
Matrix4x4 Offset; // Offset matrix transforms vertex from model space to bone space
};
struct AssimpNodeData
{
Matrix4x4 Transformation;
std::string Name;
int ChildCount;
std::vector<AssimpNodeData> Children;
};
}

View File

@@ -1,9 +1,121 @@
#pragma once
#include <J3ML/LinearAlgebra.h>
// Default Camera Values
const float YAW = -90.f;
const float PITCH = 0.f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
namespace LearnOpenGL
{
struct CameraInputState
{
bool MoveForward;
bool MoveBackward;
bool MoveLeft;
bool MoveRight;
bool MoveUp;
bool MoveDown;
};
class Camera3D
{
public:
Vector3 Position;
Vector3 Front;
Vector3 Up;
Vector3 Right;
Vector3 WorldUp;
float Yaw;
float Pitch;
float MovementSpeed;
float MouseSensitivity;
float Zoom;
Camera3D(Vector3 position = Vector3::Zero, Vector3 up = Vector3::Up, float yaw = YAW, float pitch = PITCH)
: Front{0, 0, -1}, MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
{
}
Matrix4x4 GetViewMatrix()
{
return Matrix4x4::LookAt(Position, Position + Front, Up, Up);
}
void UpdateState()
{
// calculate the new Front vector
Vector3 front;
front.x = std::cos(Math::Radians(Yaw) * cos(Math::Radians(Pitch)));
front.y = std::sin(Math::Radians(Pitch));
front.z = std::sin(Math::Radians(Yaw) * std::cos(Math::Radians(Pitch)));
Front = Vector3::Normalize(front);
// normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
Right = Vector3::Normalize(Vector3::Cross(Front, WorldUp));
Up = Vector3::Normalize(Vector3::Cross(Right, Front));
}
void ProcessKeyboard(CameraInputState state, float dt)
{
float scalar_velocity = MovementSpeed * dt;
Vector3 Velocity = Vector3::Zero;
if (state.MoveForward)
Velocity += Front * scalar_velocity;
if (state.MoveBackward)
Velocity -= Front * scalar_velocity;
if (state.MoveLeft)
Velocity -= Right * scalar_velocity;
if (state.MoveRight)
Velocity += Right * scalar_velocity;
if (state.MoveUp)
Velocity += WorldUp * scalar_velocity;
if (state.MoveDown)
Velocity -= WorldUp * scalar_velocity;
Position += Velocity;
UpdateState();
}
void ProcessMouseMovement(float dx, float dy, bool constrainPitch = true)
{
dx *= MouseSensitivity;
dy *= MouseSensitivity;
Yaw += dx;
Pitch += dy;
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
UpdateState();
}
// processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
void ProcessMouseScroll(float scroll)
{
Zoom -= (float)scroll;
if (Zoom < 1.0f)
Zoom = 1.0f;
if (Zoom > 45.0f)
Zoom = 45.0f;
}
public:
protected:
private:
};
}

View File

@@ -0,0 +1,10 @@
#pragma once
namespace LearnOpenGL::BillPlsFix
{
class Coompoopchute
{
};
}

View File

@@ -1,8 +1,22 @@
//
// Created by dawsh on 2/23/24.
//
#pragma once
#include <glad/glad.h>
#include <vector>
#include <iostream>
#ifndef LEARNOPENGL_CUBEMAP_H
#define LEARNOPENGL_CUBEMAP_H
class CubeMap
{
private:
std::vector<const char*> CubemapTextures;
GLuint TID;
public:
CubeMap();
~CubeMap();
#endif //LEARNOPENGL_CUBEMAP_H
void Bind();
void Unbind();
inline GLuint getCubMapTextureID() const {return TID;}
public:
GLint loadTextures(std::vector<const char*> cubemapTextures);
};

View File

@@ -1,8 +1,27 @@
//
// Created by dawsh on 2/23/24.
//
#ifndef LEARNOPENGL_FRAMEBUFFER_H
#define LEARNOPENGL_FRAMEBUFFER_H
#endif //LEARNOPENGL_FRAMEBUFFER_H
#pragma once
#include <vector>
#include "glad/glad.h"
#include <J3ML/J3ML.h>
#include <iostream>
namespace LearnOpenGL
{
class FrameBuffer
{
public: // data
GLuint BufferID;
u32 DepthAttachment;
std::vector<u32> Attachments;
public: // methods
FrameBuffer();
~FrameBuffer();
void Create(int width, int height, bool enableDepth);
void Bind();
void Unbind();
void AddAttachment(int idx, int internalFormat, int format, int width, int height);
};
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <vector>
#include "J3ML/LinearAlgebra/Vector3.h"
#include <glad/glad.h>
namespace LearnOpenGL
{
// Renders a reference floor-plane grid in 3D space
class Grid
{
public:
Grid() {
setup_buffers();
}
~Grid() {
//glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
}
void Render() const
{
//glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, (int)vertex_count);
//glBindVertexArray(0);
}
protected:
private:
unsigned int VAO{}, VBO{};
unsigned int vertex_count{};
// Adapted from OpenGL Shading Language Cookbook by David Wolff
void setup_buffers()
{
std::vector<J3ML::LinearAlgebra::Vector3> vertices;
float size = 10.f;
const int GRID_DIMS = 10;
for (auto column = 0; column <= GRID_DIMS * 2; column++)
{
float x = (float)column - size;
vertices.emplace_back(x, 0, -size);
vertices.emplace_back(x, 0, size);
}
for (auto row = 0; row <= GRID_DIMS * 2; row++)
{
float z = (float)row - size;
vertices.emplace_back(-size, 0, z);
vertices.emplace_back(size, 0, z);
}
vertex_count = vertices.size();
//glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
//glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size()* sizeof(J3ML::LinearAlgebra::Vector3), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(0);
//glBindVertexArray(0);
}
};
}

View File

@@ -1,8 +1,19 @@
//
// Created by dawsh on 2/23/24.
//
#pragma once
#ifndef LEARNOPENGL_INDEXBUFFER_H
#define LEARNOPENGL_INDEXBUFFER_H
namespace LearnOpenGL
{
class IndexBuffer
{
private:
unsigned int RendererID;
unsigned int Count;
public:
IndexBuffer(const unsigned int* data, unsigned int count);
~IndexBuffer();
#endif //LEARNOPENGL_INDEXBUFFER_H
void Bind();
void Unbind();
inline unsigned int GetCount() const { return Count;}
};
}

View File

@@ -1,8 +1,30 @@
//
// Created by dawsh on 2/22/24.
//
#pragma once
#ifndef LEARNOPENGL_LEARNOPENGL_H
#define LEARNOPENGL_LEARNOPENGL_H
#define INCLUDE_SKELETAL
#endif //LEARNOPENGL_LEARNOPENGL_H
#ifdef INCLUDE_SKELETAL
// TODO: consider moving to 'Skeletal' directory
#include <LearnOpenGL/Animator.h>
#include <LearnOpenGL/Animation.h>
#include <LearnOpenGL/Bone.h>
#include <LearnOpenGL/BoneInfo.h>
#include <LearnOpenGL/Model.h>
#endif
#include <LearnOpenGL/Camera2D.h>
#include <LearnOpenGL/Camera3D.h>
#include <LearnOpenGL/ComputeShader.h>
#include <LearnOpenGL/CubeMap.h>
#include <LearnOpenGL/FrameBuffer.h>
#include <LearnOpenGL/IndexBuffer.h>
#include <LearnOpenGL/Mesh.h>
#include <LearnOpenGL/Shader.h>
#include <LearnOpenGL/RenderBuffer.h>
#include <LearnOpenGL/Skybox.h>
#include <LearnOpenGL/Texture.h>
#include <LearnOpenGL/Texture2D.h>
#include <LearnOpenGL/Vertex.h>
#include <LearnOpenGL/VertexArray.h>
#include <LearnOpenGL/VertexBuffer.h>
using namespace LearnOpenGL;

View File

@@ -17,41 +17,41 @@
#include <string>
#include <vector>
#include "Shader.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "IndexBuffer.h"
#include "Vertex.h"
using J3ML::LinearAlgebra::Vector2;
using J3ML::LinearAlgebra::Vector3;
using J3ML::u32;
constexpr int MAX_BONE_INFLUENCE = 4;
namespace LearnOpenGL
{
struct Vertex
{
Vector3 Position;
Vector3 Normal;
Vector2 TexCoords;
Vector3 Tangent;
Vector3 BiTangent;
int BoneIDs[MAX_BONE_INFLUENCE];
float BoneWeights[MAX_BONE_INFLUENCE];
// A structure that represents a handle to a texture on the GPU
struct Texture {
uint ID;
std::string Type;
std::string Path;
};
// Different from Texture2D in purpose
class Mesh {
public:
std::vector<Vertex> vertices;
std::vector<uint> indices;
std::vector<Texture2D> textures;
std::vector<Texture> textures;
uint VAO;
Mesh(std::vector<Vertex> verts, std::vector<uint> indices, std::vector<Texture2D> textures)
Mesh(const std::vector<Vertex>& verts, const std::vector<uint>& indices, const std::vector<Texture>& textures)
{
this->vertices = verts;
this->indices = indices;
this->textures = textures;
this->vertices = vertices;
setupMesh();
}

View File

@@ -16,8 +16,12 @@
#include <LearnOpenGL/Mesh.h>
#include <LearnOpenGL/Shader.h>
#include <LearnOpenGL/BoneInfo.h>
#include <map>
namespace LearnOpenGL
{
uint TextureFromFile(const char *path, const std::string& directory, bool gamma = false);
@@ -25,30 +29,44 @@ namespace LearnOpenGL
{
public:
// model data
std::vector<LearnOpenGL::Texture2D> textures_loaded; // Stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once
std::vector<LearnOpenGL::Texture> textures_loaded; // Stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once
std::vector<LearnOpenGL::Mesh> meshes;
std::string directory;
bool gammaCorrection;
// Expects a filepath to a 3D model
Model(const std::string& path, bool gamma = false) : gammaCorrection(gamma)
{
loadModel(path);
}
Model() {}
Model(const std::string& path, bool gamma = false);
static Model LoadFromDAEFile(std::string& path, bool gamma = false);
static Model LoadFromOBJFile(std::string& path);
// Draws the model, and this all its meshes
void Draw(Shader& shader)
{
for(auto& mesh : meshes)
mesh.Draw(shader);
}
void Draw(Shader& shader);
std::map<std::string, BoneInfo> GetBoneInfoMap() const { return BoneInfoMap;}
int GetBoneCount() const { return BoneCounter;}
protected:
private:
std::map<std::string, BoneInfo> BoneInfoMap;
int BoneCounter = 0;
// loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector
void loadModel(const std::string& path)
{
// read file via ASSIMP
Assimp::Importer importer;
}
// NOTE: if you catch a bottleneck on model load, make sure ASSIMP is built in "Release" mode!!
void loadModel(const std::string& path);
// processes a node in a recursive manner. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
void processNode(aiNode *node, const aiScene* scene);
Mesh processMesh(aiMesh* mesh, const aiScene* scene);
// checks all material textures of a given type and loads the textures if they're not loaded yet.
// the required info is returned as a Texture struct.
std::vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName);
void SetVertexBoneDataToDefault(Vertex& vertex);
void SetVertexBoneData(Vertex& vertex, int boneID, float weight);
void ExtractBoneWeightForVertices(std::vector<Vertex>& vertices, aiMesh* mesh, const aiScene* scene);
};
}

View File

@@ -1,8 +1,17 @@
//
// Created by dawsh on 2/23/24.
//
#pragma once
#include <glad/glad.h>
#ifndef LEARNOPENGL_RENDERBUFFER_H
#define LEARNOPENGL_RENDERBUFFER_H
#endif //LEARNOPENGL_RENDERBUFFER_H
namespace LearnOpenGL
{
class RenderBuffer
{
public:
GLuint BufferID;
RenderBuffer(int width, int height);
~RenderBuffer();
void Bind() const;
void Unbind() const;
};
}

View File

@@ -27,14 +27,35 @@ namespace LearnOpenGL {
class Shader
{
public:
bool loaded;
public:
unsigned int ID{};
Shader();
Shader(std::filesystem::path vertexProgramPath, std::filesystem::path fragmentProgramPath);
Shader(std::string vertexProgramSrc, std::string fragmentProgramSrc);
~Shader()
{
if (loaded)
unload();
}
void unload()
{
// TODO: Recycle OpenGL shader handle
loaded = false;
}
void use();
void reload();
[[nodiscard]] std::string VertexPath() const {
return vertexPath;
}
[[nodiscard]] std::string FragmentPath() const {
return fragmentPath;
}
// Utility uniform functions
void setBool(const std::string& name, bool value) const;
void setInt(const std::string& name, int value) const;
@@ -53,6 +74,8 @@ namespace LearnOpenGL {
GLint getUniform(const std::string& name) const;
private:
std::string vertexPath;
std::string fragmentPath;
void checkCompileErrors(GLuint shader, std::string type);
};
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include "Texture2D.h"
namespace LearnOpenGL
{
// TODO: Migrate Re3D::Skybox into here

View File

@@ -2,17 +2,5 @@
namespace LearnOpenGL
{
/// Abstract "Texture" Base - derived to create Texture2D and Texture3D
class Texture
{
public:
Texture()
{
}
Texture(const Texture&) = delete;
void operator = (const Texture&) = delete;
void operator = (Texture&&) = delete;
};
}

View File

@@ -17,9 +17,13 @@ namespace LearnOpenGL {
};
class RenderTarget {
};
// Texture2D is able to store and configure a texture in OpenGL
// It also hosts utility functions for easy management.
class Texture2D : public Texture {
class Texture2D {
public: // Member Data
unsigned int ID; // ID of the texture object
unsigned int SlotID; // ID of the graphics texture unit slot
@@ -34,10 +38,15 @@ namespace LearnOpenGL {
std::string Type;
std::string Path;
public: // Functions
// Constructor fills with empty data;
Texture2D();
static Texture2D LoadFromFilepath(const std::filesystem::path& resource);
static Texture2D LoadFromPixelDataBuffer(uint8_t* data);
static Texture2D FromPNG();
static Texture2D FromBMP();
static Texture2D FromFile(const std::filesystem::path& resource);
static Texture2D FromPixelDataBuffer(uint8_t* data, uint width, uint height);
void Generate(unsigned int width, unsigned int height, unsigned char *data);

View File

@@ -0,0 +1,28 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector3.h>
using J3ML::LinearAlgebra::Vector2;
using J3ML::LinearAlgebra::Vector3;
namespace LearnOpenGL
{
constexpr auto MAX_BONE_INFLUENCE = 4;
/// Define a point with texture coordinates, and more
struct Vertex
{
// TODO: Integrate JGL or split Color into separate module?
//Color3 Color;
Vector3 Position;
Vector3 Normal;
Vector2 TexCoords;
Vector3 Tangent;
Vector3 BiTangent;
int BoneIDs[MAX_BONE_INFLUENCE];
float BoneWeights[MAX_BONE_INFLUENCE];
};
}

View File

@@ -1,8 +1,25 @@
//
// Created by dawsh on 2/23/24.
//
#pragma once
#ifndef LEARNOPENGL_VERTEXARRAY_H
#define LEARNOPENGL_VERTEXARRAY_H
#include <vector>
#include <LearnOpenGL/Vertex.h>
#include <glad/glad.h>
#endif //LEARNOPENGL_VERTEXARRAY_H
// TODO: Bring upstream of Re3D::VertexArray
namespace LearnOpenGL
{
class VertexArray {
private:
std::vector<Vertex> boundingVolume;
public:
std::string name;
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
VertexArray getBoundingVolume(Vector3 angle);
void rotate(const Vector3& angle);
void load(const std::string& filename);
static void erase(const VertexArray& vArray);
void drawWireframe();
void draw();
GLuint vbo = NULL;
};
}

View File

@@ -1,8 +1,53 @@
//
// Created by dawsh on 2/23/24.
//
#pragma once
#ifndef LEARNOPENGL_VERTEXBUFFER_H
#define LEARNOPENGL_VERTEXBUFFER_H
namespace LearnOpenGL
{
/// Describes how this buffer is intended to be used.
enum class Usage
{
Stream, // Constantly changing data
Dynamic, // Occasionally changing data (Good compromise)
Static // Rarely changing data
};
#endif //LEARNOPENGL_VERTEXBUFFER_H
enum class PrimitiveType{
Points,
Lines,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
Quads,
};
class VertexBuffer
{
public:
u32 RendererID;
VertexBuffer(const void* data, u32 size)
{
glGenBuffers(1, &RendererID);
}
~VertexBuffer()
{
glDeleteBuffers(1, &RendererID);
}
void Bind() const
{
glBindBuffer(GL_ARRAY_BUFFER, RendererID);
}
void Unbind() const
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public:
};
}

View File

@@ -2,28 +2,37 @@
#include <LearnOpenGL/Texture2D.h>
#include <LearnOpenGL/Shader.h>
#include <LearnOpenGL/Mesh.h>
#include <LearnOpenGL/Model.h>
#include <rewindow/types/window.h>
class LearnOpenGLDemoWindow : public ReWindow::RWindow
{
public:
LearnOpenGLDemoWindow() : ReWindow::RWindow("LearnOpenGL Window", 1000, 600, RenderingAPI::OPENGL)
{
}
void OnRefresh(float dt) override
{
}
};
int main() {
std::cout << "OpenGL Utility Classes" << std::endl;
auto window = new LearnOpenGLDemoWindow();
window->setRenderer(RenderingAPI::OPENGL);
window->Open();
gladLoadGL();
LearnOpenGL::Texture2D testTexture;
while (window->isAlive())
{
window->pollEvents();
window->refresh();
}
return 0;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
resources/entity/jump.fbx Normal file

Binary file not shown.

BIN
resources/entity/run.fbx Normal file

Binary file not shown.

BIN
resources/entity/skin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -0,0 +1,7 @@
#version 450 core
out vec4 FragColor;
void main() {
FragColor = vec4(0.9, 0.2, 0.2, 1.0);
}

View File

@@ -0,0 +1,10 @@
#version 450 core
layout (location = 0) in vec3 vertex_coord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(vertex_coord, 1.0);
}

View File

@@ -0,0 +1,37 @@
#version 450 core
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tex;
layout (location = 2) in ivec4 boneIds;
layout (location = 3) in vec4 boneWeights;
const int MAX_BONES = 128;
const int MAX_BONE_INFLUENCE = 4;
uniform mat4 skinning_matrices[MAX_BONES];
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
out vec2 TexCoords;
void main() {
vec4 totalPosition = vec4(0.0f);
for (int i = 0; i < MAX_BONE_INFLUENCE; i++) {
if (boneIds[i] == -1) {
continue;
}
if (boneIds[i] >= MAX_BONES) {
totalPosition = vec4(pos, 1.0);
break;
}
vec4 localPosition = skinning_matrices[boneIds[i]] * vec4(pos, 1.0);
totalPosition += localPosition * boneWeights[i];
}
gl_Position = projection * view * model * totalPosition;
TexCoords = tex;
}

View File

@@ -0,0 +1,11 @@
#version 450 core
uniform sampler2D InputTexture;
out vec4 FragColor;
in vec2 TexCoords;
void main() {
vec3 sampled = texture(InputTexture, TexCoords).rgb;
FragColor = vec4(sampled.xyz, 1.0);
}

View File

@@ -0,0 +1,68 @@
#include <LearnOpenGL/Animation.h>
namespace LearnOpenGL
{
Animation::Animation(const std::string &animationPath, Model *model) {
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate);
assert(scene && scene->mRootNode);
auto animation = scene->mAnimations[0];
Duration = animation->mDuration;
TicksPerSecond = animation->mTicksPerSecond;
aiMatrix4x4 globalTransformation = scene->mRootNode->mTransformation;
globalTransformation = globalTransformation.Inverse();
ReadHierarchyData(RootNode, scene->mRootNode);
ReadMissingBones(animation, *model);
}
Animation::~Animation() { }
Bone *Animation::FindBone(const std::string &name) {
auto iter = std::find_if(Bones.begin(), Bones.end(),
[&](const Bone& bone)
{
return bone.GetBoneName() == name;
}
);
if (iter == Bones.end()) return nullptr;
else return &(*iter);
}
void Animation::ReadMissingBones(const aiAnimation *animation, Model &model) {
int size = animation->mNumChannels;
auto boneInfoMap = model.GetBoneInfoMap();
int boneCount = model.GetBoneCount();
// Reading channels (bones engaged in an animation and their keyframes)
for (int i = 0; i < size; i++)
{
auto channel = animation->mChannels[i];
std::string boneName = channel->mNodeName.data;
if (boneInfoMap.find(boneName) == boneInfoMap.end())
{
boneInfoMap[boneName].ID = boneCount;
boneCount++;
}
Bones.push_back(Bone(channel->mNodeName.data, boneInfoMap[channel->mNodeName.data].ID, channel));
}
BoneInfoMap = boneInfoMap;
}
void Animation::ReadHierarchyData(AssimpNodeData &dest, const aiNode *src) {
assert(src);
dest.Name = src->mName.data;
dest.Transformation = Matrix4x4(&(src->mTransformation.a1));
dest.ChildCount = src->mNumChildren;
for (int i = 0; i < src->mNumChildren; i++)
{
AssimpNodeData newData;
ReadHierarchyData(newData, src->mChildren[i]);
dest.Children.push_back(newData);
}
}
}

View File

@@ -0,0 +1,62 @@
#include <LearnOpenGL/Animator.h>
namespace LearnOpenGL
{
Animator::Animator(Animation *animation) {
this->CurrentTime = 0.0;
this->CurrentAnimation = animation;
this->FinalBoneMatrices.reserve(100);
for (int i = 0; i < 100; i++)
this->FinalBoneMatrices.push_back(Matrix4x4(1.f));
}
void Animator::UpdateAnimations(float dt) {
this->DeltaTime = dt;
if (this->CurrentAnimation != nullptr)
{
this->CurrentTime += this->CurrentAnimation->GetTicksPerSecond() * dt;
this->CurrentTime = fmod(this->CurrentTime, this->CurrentAnimation->GetDuration());
CalculateBoneTransform(&this->CurrentAnimation->GetRootNode(), Matrix4x4(1.f));
}
}
void Animator::PlayAnimation(Animation *pAnimation) {
this->CurrentAnimation = pAnimation;
this->CurrentTime = 0.0f;
}
void Animator::CalculateBoneTransform(const AssimpNodeData *node, Matrix4x4 parentTransform) {
std::string nodeName = node->Name;
Matrix4x4 nodeTransform = node->Transformation;
Bone* bone = CurrentAnimation->FindBone(nodeName);
if (bone != nullptr)
{
bone->Update(this->CurrentTime);
nodeTransform = bone->GetLocalTransform();
}
Matrix4x4 globalTransformation = parentTransform * nodeTransform;
auto boneInfoMap = this->CurrentAnimation->GetBoneIDMap();
if (boneInfoMap.find(nodeName) != boneInfoMap.end())
{
int index = boneInfoMap[nodeName].ID;
Matrix4x4 offset = boneInfoMap[nodeName].Offset;
this->FinalBoneMatrices[index] = globalTransformation * offset;
}
for (int i = 0; i < node->ChildCount; i++)
{
CalculateBoneTransform(&node->Children[i], globalTransformation);
}
}
std::vector<Matrix4x4> Animator::GetFinalBoneMatrices() {
return this->FinalBoneMatrices;
}
}

139
src/LearnOpenGL/Bone.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include <LearnOpenGL/Bone.h>
namespace LearnOpenGL
{
Bone::Bone(const std::string &name, int ID, const aiNodeAnim *channel)
: Name(name), ID(ID), LocalTransform(1.f)
{
NumPositions = channel->mNumPositionKeys;
for (int positionIndex = 0; positionIndex < NumPositions; ++positionIndex)
{
aiVector3D aiPosition = channel->mPositionKeys[positionIndex].mValue;
float timestamp = channel->mPositionKeys[positionIndex].mTime;
KeyPosition data;
data.Position = Vector3(aiPosition.x, aiPosition.y, aiPosition.z);
data.Timestamp = timestamp;
Positions.push_back(data);
}
NumRotations = channel->mNumRotationKeys;
for (int rotationIndex = 0; rotationIndex < NumRotations; ++rotationIndex)
{
aiQuaternion aiOrientation = channel->mRotationKeys[rotationIndex].mValue;
float timestamp = channel->mRotationKeys[rotationIndex].mTime;
KeyRotation data;
data.Orientation = Quaternion(aiOrientation.x, aiOrientation.y, aiOrientation.z, aiOrientation.w);
data.Timestamp = timestamp;
Rotations.push_back(data);
}
NumScalings = channel->mNumScalingKeys;
for (int keyIndex = 0; keyIndex < NumScalings; ++keyIndex)
{
aiVector3D scale = channel->mScalingKeys[keyIndex].mValue;
float timestamp = channel->mScalingKeys[keyIndex].mTime;
KeyScale data;
data.Scale = Vector3(scale.x, scale.y, scale.z);
data.Timestamp = timestamp;
Scales.push_back(data);
}
}
void Bone::Update(float animationTime) {
Matrix4x4 Translation = InterpolatePosition(animationTime);
Matrix4x4 Rotation = InterpolateRotation(animationTime);
Matrix4x4 Scale = InterpolateRotation(animationTime);
LocalTransform = Translation * Rotation * Scale;
//LocalTransform = Matrix4x4::FromTRS(Translation, Rotation, Scale)
}
Matrix4x4 Bone::GetLocalTransform() const { return LocalTransform;}
std::string Bone::GetBoneName() const { return Name;}
int Bone::GetBoneID() const { return ID;}
int Bone::GetPositionIndex(float animationTime) {
for (int index = 0; index < NumPositions - 1; ++index)
{
if (animationTime < Positions[index+1].Timestamp)
return index;
}
assert(0);
}
int Bone::GetRotationIndex(float animationTime) {
for (int index = 0; index < NumRotations - 1; ++index)
{
if (animationTime < Rotations[index + 1].Timestamp)
return index;
}
assert(0);
}
int Bone::GetScaleIndex(float animationTime) {
for (int index = 0; index < NumScalings - 1; ++index)
{
if (animationTime < Scales[index+1].Timestamp)
return index;
}
assert(0);
}
float Bone::GetScaleFactor(float lastTimestamp, float nextTimestamp, float animationTime) {
float scaleFactor = 0.f;
float midwayLength = animationTime - lastTimestamp;
float framesDiff = nextTimestamp - lastTimestamp;
scaleFactor = midwayLength / framesDiff;
return scaleFactor;
}
Matrix4x4 Bone::InterpolatePosition(float animationTime) {
if (1 == NumPositions)
return Matrix4x4(1.f).Translate(Positions[0].Position);
int p0Index = GetPositionIndex(animationTime);
int p1Index = p0Index + 1;
float scaleFactor = GetScaleFactor(Positions[p0Index].Timestamp,
Positions[p1Index].Timestamp, animationTime);
Vector3 finalPosition = Vector3::Lerp(Positions[p0Index].Position, Positions[p1Index].Position, scaleFactor);
return Matrix4x4(1.f).Translate(finalPosition);
}
Matrix4x4 Bone::InterpolateRotation(float animationTime) {
if (1 == NumRotations)
{
auto rotation = Rotations[0].Orientation.Normalize();
return Matrix4x4(rotation);
}
int p0Index = GetRotationIndex(animationTime);
int p1Index = p0Index + 1;
float scaleFactor = GetScaleFactor(Rotations[p0Index].Timestamp,
Rotations[p1Index].Timestamp, animationTime);
Quaternion finalRotation = Rotations[p0Index].Orientation.Lerp(Rotations[p1Index].Orientation, scaleFactor);
finalRotation = finalRotation.Normalize();
return Matrix4x4(finalRotation);
}
Matrix4x4 Bone::InterpolateScaling(float animationTime) {
if (1 == NumScalings)
{
return Matrix4x4(1.f).Scale(Scales[0].Scale);
}
int p0Index = GetScaleIndex(animationTime);
int p1Index = p0Index + 1;
float scaleFactor = GetScaleFactor(Scales[p0Index].Timestamp,
Scales[p1Index].Timestamp, animationTime);
Vector3 finalScale = Scales[p0Index].Scale.Lerp(Scales[p1Index].Scale, scaleFactor);
return Matrix4x4(1.f).Scale(finalScale);
}
}

View File

@@ -0,0 +1 @@
#include <LearnOpenGL/Camera3D.h>

View File

@@ -0,0 +1,52 @@
#include <LearnOpenGL/CubeMap.h>
namespace LearnOpenGL
{
}
void CubeMap::Bind() {
glBindTexture(GL_TEXTURE_CUBE_MAP, TID);
}
void CubeMap::Unbind() {
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
GLint CubeMap::loadTextures(std::vector<const char *> cubemapTextures) {
CubemapTextures = cubemapTextures;
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, bpp;
unsigned char* image;
for (int tex = 0; tex < CubemapTextures.size(); tex++)
{
//image = stbi_load(CubemapTextures[tex], &width, &height, &bpp, 0);
if (!image)
{
std::cout << "Failed to load texture: " << CubemapTextures[tex] << std::endl;
} else {
std::cout << "Image details: " << std::endl;
std::cout << "\tFile path: " << CubemapTextures[tex] << " ID: " << tex << std::endl;
std::cout << "\tDimensions: (" << width << ", " << height << ")" << std::endl;
std::cout << "\tBits per pixel: " << bpp << std::endl;
}
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + tex, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
TID = texture;
return texture;
}

View File

@@ -0,0 +1,67 @@
#include <LearnOpenGL/FrameBuffer.h>
// TODO: Upgrade to a version of OpenGL that supports FrameBuffer operations???
namespace LearnOpenGL
{
FrameBuffer::FrameBuffer() {
}
FrameBuffer::~FrameBuffer() {
//glDeleteFramebuffers(1, &BufferID);
}
void FrameBuffer::Create(int width, int height, bool enableDepth) {
//glGenFramebuffers(1, &BufferID);
//glBindFramebuffer(GL_FRAMEBUFFER, BufferID);
std::vector<unsigned int> attachments {};
for (size_t i = 0; i < Attachments.size(); i++)
{
//attachments.push_back(GL_COLOR_ATTACHMENT0 + i);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, Attachments[i], 0);
}
glDrawBuffers(attachments.size(), attachments.data());
if (enableDepth)
{
glGenTextures(1, &DepthAttachment);
glBindBuffer(GL_TEXTURE_2D, DepthAttachment);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, DepthAttachment, 0);
}
//if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
// std::cout << "ERROR: Framebuffer is not complete" << std::endl;
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::Bind() {
//glBindFramebuffer(GL_FRAMEBUFFER, BufferID);
}
void FrameBuffer::Unbind() {
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::AddAttachment(int idx, int internalFormat, int format, int width, int height) {
// TODO: Kick Rocks Until We Update OpenGL
unsigned int rt;
glGenTextures(1, &rt);
glBindTexture(GL_TEXTURE_2D, rt);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + idx, GL_TEXTURE_2D, rt, 0);
Attachments.push_back(rt);
}
}

7
src/LearnOpenGL/Grid.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include <LearnOpenGL/Grid.h>
namespace LearnOpenGL
{
}

View File

@@ -0,0 +1,5 @@
namespace LearnOpenGL
{
}

View File

@@ -1,3 +1,271 @@
//
// Created by dawsh on 2/23/24.
//
#include <LearnOpenGL/Model.h>
namespace LearnOpenGL
{
Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) {
// data to fill
std::vector<Vertex> vertices;
std::vector<uint> indices;
std::vector<Texture> textures;
// walk through each of the mesh's vertices
for (uint i = 0; i < mesh->mNumVertices; i++)
{
Vertex vertex;
Vector3 vector; // declare a placeholder vector since assimp uses its own vector class that won't directly convert to ours
// position
vector.x = mesh->mVertices[i].x;
vector.y = mesh->mVertices[i].y;
vector.z = mesh->mVertices[i].z;
vertex.Position = vector;
// normals
if (mesh->HasNormals())
{
vector.x = mesh->mNormals[i].x;
vector.y = mesh->mNormals[i].y;
vector.z = mesh->mNormals[i].z;
vertex.Normal = vector;
}
// texture coordinates
if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
{
Vector2 vec;
// a vertex can contain up to 8 different texture coordinates
// we thus make the assumption that we won't use models where
// a vertex can have multiple texture coordinates so we always take the first set (0).
vec.x = mesh->mTextureCoords[0][i].x;
vec.y = mesh->mTextureCoords[0][i].y;
// tangent
if (mesh->mTangents != nullptr)
{
vector.x = mesh->mTangents[i].x;
vector.y = mesh->mTangents[i].y;
vector.z = mesh->mTangents[i].z;
vertex.Tangent = vector;
}
// bitangent
if (mesh->mBitangents != nullptr)
{
vector.x = mesh->mBitangents[i].x;
vector.y = mesh->mBitangents[i].y;
vector.z = mesh->mBitangents[i].z;
vertex.BiTangent = vector;
}
} else {
vertex.TexCoords = {0, 0};
}
vertices.push_back(vertex);
}
// now walk back through each of the mesh's faces and retrieve the corresponding vertex indices
for (uint i = 0; i < mesh->mNumFaces; i++)
{
aiFace face = mesh->mFaces[i];
// retrive all indices of the face and store them in the indices vector
for (uint j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
// process materials
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// we assume a convention for sampler names in the shaders. Each diffuse texture should be named
// as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER>
// Same applies to other texture as the following list summarizes:
// diffuse: texture_diffuseN
// specular: texture_specularN
// normal: texture_normalN
// 1. diffuse maps
std::vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
// 2. specular maps
std::vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
// 3. normal maps
std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
// 4. height maps
std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());
// return a mesh object created from the extracted mesh data
return Mesh(vertices, indices, textures);
}
Model::Model(const std::string &path, bool gamma) : gammaCorrection(gamma)
{
loadModel(path);
}
void Model::Draw(Shader &shader) {
for(auto& mesh : meshes)
mesh.Draw(shader);
}
void Model::loadModel(const std::string &path) {
// read file via ASSIMP
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
return;
}
directory = path.substr(0, path.find_last_of('/'));
processNode(scene->mRootNode, scene);
}
void Model::processNode(aiNode *node, const aiScene *scene) {
// process each mesh located at the current node
for (uint i = 0; i < node->mNumMeshes; i++)
{
// the node object only contains indices to index the actual object in the scene.
// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene));
}
// after we've processed all of the meshes we then recursively process each of the children nodes
for (uint i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene);
}
}
std::vector<Texture> Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, std::string typeName) {
std::vector<Texture> textures;
// Next Texture
for (uint i = 0; i < mat->GetTextureCount(type); i++)
{
aiString str;
mat->GetTexture(type, i, &str);
// check if texture was loaded before and if so, skip to next iteration:
bool skip = false;
for (uint j = 0; j < textures_loaded.size(); j++)
{
if (std::strcmp(textures_loaded[j].Path.data(), str.C_Str()) == 0)
{
textures.push_back(textures_loaded[j]);
skip = true; // A texture with the same filepath has already been loaded, continue to next one.
break;
}
}
if (!skip)
{
Texture texture;
texture.ID = TextureFromFile(str.C_Str(), this->directory);
texture.Type = typeName;
texture.Path = str.C_Str();
// store it as a texture loaded for entire model,
// to ensure we won't unnecessarily load duplicate textures.
textures.push_back(texture);
textures_loaded.push_back(texture);
}
}
return textures;
}
void Model::ExtractBoneWeightForVertices(std::vector<Vertex> &vertices, aiMesh *mesh, const aiScene *scene) {
auto& boneInfoMap = BoneInfoMap;
int& boneCount = BoneCounter;
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) {
int boneID = -1;
std::string boneName = mesh->mBones[boneIndex]->mName.C_Str();
if (boneInfoMap.find(boneName) == boneInfoMap.end()) {
BoneInfo newBoneInfo;
newBoneInfo.ID = boneCount;
newBoneInfo.Offset = Matrix4x4(&mesh->mBones[boneIndex]->mOffsetMatrix.a1);
boneInfoMap[boneName] = newBoneInfo;
boneID = boneCount;
boneCount++;
} else {
boneID = boneInfoMap[boneName].ID;
}
assert(boneID != -1);
auto weights = mesh->mBones[boneIndex]->mWeights;
int numWeights = mesh->mBones[boneIndex]->mNumWeights;
for (int weightIndex = 0; weightIndex < numWeights; ++weightIndex)
{
int vertexID = weights[weightIndex].mVertexId;
float weight = weights[weightIndex].mWeight;
assert(vertexID < vertices.size());
SetVertexBoneData(vertices[vertexID], boneID, weight);
}
}
}
void Model::SetVertexBoneDataToDefault(Vertex &vertex) {
for (int i = 0; i < MAX_BONE_INFLUENCE; i++)
{
vertex.BoneIDs[i] = -1;
vertex.BoneWeights[i] = 0.0f;
}
}
void Model::SetVertexBoneData(Vertex &vertex, int boneID, float weight) {
for (int i = 0; i < MAX_BONE_INFLUENCE; ++i)
{
if (vertex.BoneIDs[i] < 0)
{
vertex.BoneWeights[i] = weight;
vertex.BoneIDs[i] = boneID;
break;
}
}
}
uint TextureFromFile(const char *path, const std::string &directory, bool gamma) {
std::string filename = std::string(path);
filename = directory + '/' + filename;
uint textureID;
glGenTextures(1, &textureID);
int width, height, numChannels;
//u8* data = stbi_load(filename.c_str(), &width, &height, &numChannels, 0);
u8* data = nullptr;
if (data)
{
GLenum format;
if (numChannels == 1)
format = GL_RED;
else if (numChannels == 3)
format = GL_RGB;
else if (numChannels == 4)
format = GL_RGBA;
//glBindTextures(GL_TEXTURE_2D, textureID);
//glTexImage2D(GL_TEXTURE2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
//glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
} else {
std::cout << "Texture failed to load at path: " << path << std::endl;
//stbi_image_free(data);
}
return textureID;
}
}

View File

@@ -0,0 +1,25 @@
#include <LearnOpenGL/RenderBuffer.h>
namespace LearnOpenGL
{
RenderBuffer::RenderBuffer(int width, int height) {
//glGenRenderbuffers(1, &BufferID);
//glBindRenderbuffer(GL_RENDERBUFFER, &BufferID);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
RenderBuffer::~RenderBuffer() {
//glDeleteRenderbuffers(1, &BufferID);
}
void RenderBuffer::Bind() const {
//glBindRenderbuffer(GL_RENDERBUFFER, BufferID);
}
void RenderBuffer::Unbind() const {
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
}

View File

@@ -46,4 +46,8 @@ namespace LearnOpenGL {
isBound = false;
}
Texture2D Texture2D::FromPNG() {
return Texture2D();
}
}

View File

@@ -0,0 +1 @@
#include <LearnOpenGL/Vertex.h>

View File

@@ -0,0 +1,185 @@
#include <LearnOpenGL/VertexArray.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include "J3ML/J3ML.h"
namespace LearnOpenGL
{
bool useVBO = false;
void VertexArray::load (const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Couldn't Load Model: " << filename << std::endl;
//engine->setError(ENGINE_ERROR_CODE::MODEL_NOT_FOUND, true);
}
std::vector<Vector3> positions;
std::vector<Vector2> uvs;
std::string line;
while (std::getline(file, line)) {
std::istringstream stream(line);
std::string prefix;
stream >> prefix;
if (prefix == "v") {
float x, y, z;
stream >> x >> y >> z;
positions.push_back(Vector3(x, y, z));
} else if (prefix == "vt") {
float u, v;
stream >> u >> v;
uvs.push_back(Vector2(u, v));
} else if (prefix == "f") {
unsigned int vertexIndex[3], texCoordIndex[3];
char slash;
for (int i = 0; i < 3; ++i) {
stream >> vertexIndex[i] >> slash >> texCoordIndex[i];
vertexIndex[i]--;
texCoordIndex[i]--;
}
for (int i = 0; i < 3; ++i) {
Vertex vertex;
vertex.Position = positions[vertexIndex[i]];
vertex.TexCoords = uvs[texCoordIndex[i]];
vertices.push_back(vertex);
indices.push_back(static_cast<unsigned int>(indices.size()));
}
}
}
file.close();
//Copy verts to vram.
if (useVBO) {
if (vbo == NULL) {
GLfloat verts[vertices.size() * 3];
size_t index = 0;
for (const Vertex &vertex: vertices) {
verts[index++] = vertex.Position.x;
verts[index++] = vertex.Position.y;
verts[index++] = vertex.Position.z;
}
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), &verts, GL_STATIC_DRAW); //GL_STATIC_DRAW means the VBO won't be changed atall.
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
// TODO: Re-implement BoundingVolume calculation
//boundingVolume = Collision::calculateBoundingBox(this, {0,0,0}).vertices;
}
//TODO: Render queue
void VertexArray::draw() {
if (!indices.empty()) {
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), vertices[0].TexCoords.ptr());
if (!useVBO)
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), vertices[0].Position.ptr());
if (useVBO) {
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexPointer(3, GL_FLOAT, 0, 0);
}
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, &indices[0]);
if (useVBO)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
}
//This only works for *really* simple shapes.
//Primarily for the bounding box.
if (indices.empty()) {
glBegin(GL_LINES);
for (int i = 0; i < 4; ++i) {
auto vertA = vertices[i];
auto vertB = vertices[(i + 1) % 4];
auto vertC = vertices[((i+1)%4) + 4];
auto vertD = vertices[i+4];
glVertex3f(vertA.Position.x, vertA.Position.y, vertA.Position.z);
glVertex3f(vertB.Position.x, vertB.Position.y, vertB.Position.z);
glVertex3f(vertD.Position.x, vertD.Position.y, vertD.Position.z);
glVertex3f(vertC.Position.x, vertC.Position.y, vertC.Position.z);
glVertex3f(vertA.Position.x, vertA.Position.y, vertA.Position.z);
glVertex3f(vertD.Position.x, vertD.Position.y, vertD.Position.z);
}
glEnd();
}
}
void VertexArray::drawWireframe() {
glBegin(GL_LINES);
for (size_t i = 0; i < indices.size(); i += 2) {
if (i + 1 < indices.size()) {
int index1 = static_cast<int>(indices[i]);
int index2 = static_cast<int>(indices[i + 1]);
if (index1 >= 0 && index1 < vertices.size() && index2 >= 0 && index2 < vertices.size()) {
glVertex3f(vertices[index1].Position.x, vertices[index1].Position.y, vertices[index1].Position.z);
glVertex3f(vertices[index2].Position.x, vertices[index2].Position.y, vertices[index2].Position.z);
}
}
}
glEnd();
}
void VertexArray::rotate(const Vector3& angle) {
float angleX = J3ML::Math::Radians(angle.x);
float angleY = J3ML::Math::Radians(angle.y);
float angleZ = J3ML::Math::Radians(angle.z);
for (Vertex &vertex: vertices) {
float newX = vertex.Position.x * std::cos(angleZ) - vertex.Position.y * std::sin(angleZ);
float newY = vertex.Position.x * std::sin(angleZ) + vertex.Position.y * std::cos(angleZ);
vertex.Position.x = newX;
vertex.Position.y = newY;
}
for (Vertex &vertex: vertices) {
float newX = vertex.Position.x * std::cos(angleY) + vertex.Position.z * std::sin(angleY);
float newZ = -vertex.Position.x * std::sin(angleY) + vertex.Position.z * std::cos(angleY);
vertex.Position.x = newX;
vertex.Position.z = newZ;
}
for (Vertex &vertex: vertices) {
float newY = vertex.Position.y * std::cos(angleX) - vertex.Position.z * std::sin(angleX);
float newZ = vertex.Position.y * std::sin(angleX) + vertex.Position.z * std::cos(angleX);
vertex.Position.y = newY;
vertex.Position.z = newZ;
}
}
VertexArray VertexArray::getBoundingVolume(Vector3 angle) {
VertexArray vArray;
vArray.vertices = boundingVolume;
vArray.rotate(angle);
return vArray;
}
void VertexArray::erase(const VertexArray& vArray) {
if (useVBO)
glDeleteBuffers(1, &vArray.vbo);
//for (int i = 0; i < engine->world->geometryList.size(); i++) {
//if (engine->world->geometryList[i].name == vArray.name)
//engine->world->geometryList.erase(engine->world->geometryList.begin() + i);
}
}

View File

@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.18..3.27)
project(SkeletalAnim
VERSION 1.5
LANGUAGES CXX)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed!")
endif()
set(CMAKE_CXX_STANDARD 20)
if (WIN32)
set(CMAKE_CXX_FLAGS "-municode")
endif()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}")
# Redacted Software's ReWindow Library
# One Class Header, Implemented for multiple platforms
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/vA0.2.23.zip
)
add_executable(SkeletalAnimDemo main.cpp)
target_include_directories(SkeletalAnimDemo PRIVATE ${ReWindow_SOURCE_DIR}/include)
target_link_libraries(SkeletalAnimDemo PRIVATE ReWindowLibrary)
target_link_libraries(SkeletalAnimDemo PRIVATE LearnOpenGL)

View File

@@ -0,0 +1,109 @@
#include <LearnOpenGL/LearnOpenGL.h>
#include <rewindow/types/window.h>
#include "LearnOpenGL/Grid.h"
Matrix4x4 animation_model_matrix = Matrix4x4(1.f);
Matrix4x4 projection_matrix = Matrix4x4::OpenGLPerspProjLH(-10, 10, 1, 1);
class SkeletalAnimationDemo : public ReWindow::RWindow
{
public:
Model ourModel;
Animation animation;
Animator animator;
Shader shader;
Shader skeletal_animation_shader;
Camera3D camera;
SkeletalAnimationDemo() : ReWindow::RWindow("Skeletal Animation Demo", 1024, 768, RenderingAPI::OPENGL)
{
}
void InitGraphics()
{
Matrix4x4 view_matrix = camera.GetViewMatrix();
//animation = Animation("");
shader = Shader(std::string("resources/shaders/basic.vert.glsl"), "resources/shaders/basic.frag.glsl");
//shader.use();
shader.setMat4("projection", projection_matrix);
shader.setMat4("view", view_matrix);
shader.setMat4("model", Matrix4x4(1.f));
// Setup skeletal animation shader
animation_model_matrix = animation_model_matrix.Translate({0.01f, 0.01f, 0.01f});
skeletal_animation_shader = Shader(std::string("resources/shaders/skeletal_animation.vert.glsl"), "resources/shaders/textured.frag.glsl");
skeletal_animation_shader.setMat4("projection", projection_matrix);
skeletal_animation_shader.setMat4("view", view_matrix);
skeletal_animation_shader.setMat4("model", animation_model_matrix);
Grid grid{};
// TODO: support loading fbx files
ourModel = Model("resources/entity/Wolf_One_dae.dae");
unsigned int texture_id;
auto res = Texture();
camera = Camera3D({0, 0, 3.f});
animation = Animation("resources/entity/Wolf_One_dae.dae", &ourModel);
animator = Animator(&animation);
}
void OnRefresh(float dt) override
{
std::cout << dt << std::endl;
CameraInputState state{
.MoveForward = this->isKeyDown(Keys::W),
.MoveBackward = this->isKeyDown(Keys::S),
.MoveLeft = this->isKeyDown(Keys::A),
.MoveRight = this->isKeyDown(Keys::D),
.MoveUp = this->isKeyDown(Keys::Q),
.MoveDown = this->isKeyDown(Keys::E)
};
camera.ProcessKeyboard(state, 1.f/60.f);
animator.UpdateAnimations(1.f/60.f);
glClearColor(0.1f, 0.05f, 0.05f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use();
//shader.setMat4("projection", Projection);
shader.setMat4("view", camera.GetViewMatrix());
auto transforms = animator.GetFinalBoneMatrices();
for (int i = 0; i < transforms.size(); ++i)
shader.setMat4("finalBoneMatrices[" + std::to_string(i) + "]", transforms[i]);
Matrix4x4 model = Matrix4x4(1.f);
model = model.Translate(Vector3(0.f, -0.0f, 0.f));
model = model.Scale({.5f, .5f, .5f});
shader.setMat4("model", model);
ourModel.Draw(shader);
glSwapBuffers();
}
protected:
private:
};
int main()
{
auto* window = new SkeletalAnimationDemo();
window->setRenderer(RenderingAPI::OPENGL);
window->Open();
gladLoadGL();
window->InitGraphics();
while (window->isAlive())
{
window->pollEvents();
window->refresh();
}
return 0;
}