Compare commits
15 Commits
Prerelease
...
master
Author | SHA1 | Date | |
---|---|---|---|
bcb105ecaf | |||
29dd5d12f5 | |||
8100d5b333 | |||
d12d8c8d65 | |||
39ae1fa494 | |||
76e6e7cec7 | |||
44c150b395 | |||
580ff0dd7f | |||
6d70019858 | |||
9bf2bb79c0 | |||
95a87d0d11 | |||
17861e17fa | |||
|
268abb6f18 | ||
|
4939dd9d23 | ||
77e7a4974a |
@@ -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)
|
49
include/LearnOpenGL/Animation.h
Normal file
49
include/LearnOpenGL/Animation.h
Normal 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);
|
||||
};
|
||||
}
|
32
include/LearnOpenGL/Animator.h
Normal file
32
include/LearnOpenGL/Animator.h
Normal 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;
|
||||
};
|
||||
}
|
61
include/LearnOpenGL/Bone.h
Normal file
61
include/LearnOpenGL/Bone.h
Normal 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;
|
||||
};
|
||||
}
|
20
include/LearnOpenGL/BoneInfo.h
Normal file
20
include/LearnOpenGL/BoneInfo.h
Normal 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;
|
||||
};
|
||||
}
|
@@ -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:
|
||||
|
||||
};
|
||||
}
|
10
include/LearnOpenGL/ComputeShader.h
Normal file
10
include/LearnOpenGL/ComputeShader.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace LearnOpenGL::BillPlsFix
|
||||
{
|
||||
class Coompoopchute
|
||||
{
|
||||
|
||||
};
|
||||
}
|
@@ -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);
|
||||
};
|
@@ -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);
|
||||
};
|
||||
}
|
65
include/LearnOpenGL/Grid.h
Normal file
65
include/LearnOpenGL/Grid.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
@@ -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;}
|
||||
};
|
||||
}
|
@@ -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;
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
};
|
||||
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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);
|
||||
};
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Texture2D.h"
|
||||
|
||||
namespace LearnOpenGL
|
||||
{
|
||||
// TODO: Migrate Re3D::Skybox into here
|
||||
|
@@ -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;
|
||||
|
||||
};
|
||||
}
|
@@ -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);
|
||||
|
||||
|
28
include/LearnOpenGL/Vertex.h
Normal file
28
include/LearnOpenGL/Vertex.h
Normal 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];
|
||||
};
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
@@ -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:
|
||||
|
||||
};
|
||||
}
|
15
main.cpp
15
main.cpp
@@ -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;
|
||||
}
|
||||
|
2252
resources/entity/Wolf_One_dae.dae
Normal file
2252
resources/entity/Wolf_One_dae.dae
Normal file
File diff suppressed because one or more lines are too long
2001
resources/entity/Wolf_dae.dae
Normal file
2001
resources/entity/Wolf_dae.dae
Normal file
File diff suppressed because one or more lines are too long
BIN
resources/entity/character.fbx
Normal file
BIN
resources/entity/character.fbx
Normal file
Binary file not shown.
7981
resources/entity/dancing_vampire.dae
Normal file
7981
resources/entity/dancing_vampire.dae
Normal file
File diff suppressed because one or more lines are too long
BIN
resources/entity/jump.fbx
Normal file
BIN
resources/entity/jump.fbx
Normal file
Binary file not shown.
BIN
resources/entity/run.fbx
Normal file
BIN
resources/entity/run.fbx
Normal file
Binary file not shown.
BIN
resources/entity/skin.png
Normal file
BIN
resources/entity/skin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
7
resources/shaders/basic.frag.glsl
Normal file
7
resources/shaders/basic.frag.glsl
Normal file
@@ -0,0 +1,7 @@
|
||||
#version 450 core
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
void main() {
|
||||
FragColor = vec4(0.9, 0.2, 0.2, 1.0);
|
||||
}
|
10
resources/shaders/basic.vert.glsl
Normal file
10
resources/shaders/basic.vert.glsl
Normal 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);
|
||||
}
|
37
resources/shaders/skeletal_animation.vert.glsl
Normal file
37
resources/shaders/skeletal_animation.vert.glsl
Normal 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;
|
||||
}
|
||||
|
||||
|
11
resources/shaders/textured.frag.glsl
Normal file
11
resources/shaders/textured.frag.glsl
Normal 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);
|
||||
}
|
68
src/LearnOpenGL/Animation.cpp
Normal file
68
src/LearnOpenGL/Animation.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
62
src/LearnOpenGL/Animator.cpp
Normal file
62
src/LearnOpenGL/Animator.cpp
Normal 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
139
src/LearnOpenGL/Bone.cpp
Normal 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);
|
||||
}
|
||||
}
|
1
src/LearnOpenGL/Camera3D.cpp
Normal file
1
src/LearnOpenGL/Camera3D.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <LearnOpenGL/Camera3D.h>
|
52
src/LearnOpenGL/CubeMap.cpp
Normal file
52
src/LearnOpenGL/CubeMap.cpp
Normal 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;
|
||||
}
|
67
src/LearnOpenGL/FrameBuffer.cpp
Normal file
67
src/LearnOpenGL/FrameBuffer.cpp
Normal 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
7
src/LearnOpenGL/Grid.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <LearnOpenGL/Grid.h>
|
||||
|
||||
|
||||
namespace LearnOpenGL
|
||||
{
|
||||
|
||||
}
|
5
src/LearnOpenGL/IndexBuffer.cpp
Normal file
5
src/LearnOpenGL/IndexBuffer.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
namespace LearnOpenGL
|
||||
{
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
25
src/LearnOpenGL/RenderBuffer.cpp
Normal file
25
src/LearnOpenGL/RenderBuffer.cpp
Normal 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);
|
||||
}
|
||||
}
|
@@ -46,4 +46,8 @@ namespace LearnOpenGL {
|
||||
isBound = false;
|
||||
}
|
||||
|
||||
Texture2D Texture2D::FromPNG() {
|
||||
return Texture2D();
|
||||
}
|
||||
|
||||
}
|
1
src/LearnOpenGL/Vertex.cpp
Normal file
1
src/LearnOpenGL/Vertex.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <LearnOpenGL/Vertex.h>
|
185
src/LearnOpenGL/VertexArray.cpp
Normal file
185
src/LearnOpenGL/VertexArray.cpp
Normal 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);
|
||||
}
|
||||
}
|
29
src/demos/SkeletalAnim/CMakeLists.txt
Normal file
29
src/demos/SkeletalAnim/CMakeLists.txt
Normal 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)
|
109
src/demos/SkeletalAnim/main.cpp
Normal file
109
src/demos/SkeletalAnim/main.cpp
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user