First working animations, but lots of bugs and refactoring needed.
This commit is contained in:
parent
8b41591360
commit
a1c8208b47
158 changed files with 17271 additions and 0 deletions
311
45-animation/src/KHR/khrplatform.h
Normal file
311
45-animation/src/KHR/khrplatform.h
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
1
45-animation/src/animation.cpp
Normal file
1
45-animation/src/animation.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "animation.hpp"
|
||||
115
45-animation/src/animation.hpp
Normal file
115
45-animation/src/animation.hpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <glm/glm.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <functional>
|
||||
#include "animdata.hpp"
|
||||
#include "bone.hpp"
|
||||
#include "model.hpp"
|
||||
#include "assimp_glm_helpers.hpp"
|
||||
|
||||
struct Bone;
|
||||
|
||||
struct AssimpNodeData {
|
||||
glm::mat4 transformation;
|
||||
std::string name;
|
||||
int children_count;
|
||||
std::vector<AssimpNodeData> children;
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
float $duration{0.0f};
|
||||
int $ticks_per_sec{0};
|
||||
std::vector<Bone> $bones;
|
||||
AssimpNodeData $root_node;
|
||||
std::unordered_map<std::string, BoneInfo> $bone_info_map;
|
||||
|
||||
Animation() = default;
|
||||
|
||||
Animation(const std::string& animationPath, Model *model) {
|
||||
Assimp::Importer importer;
|
||||
|
||||
const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate);
|
||||
dbc::check(scene != nullptr, $F("Scene for animation {} failed to load.", animationPath));
|
||||
dbc::check(scene->mRootNode, $F("Scene {} doesn't have a root node.", animationPath));
|
||||
|
||||
auto animation = scene->mAnimations[0];
|
||||
$duration = animation->mDuration;
|
||||
$ticks_per_sec = animation->mTicksPerSecond;
|
||||
aiMatrix4x4 globalTransformation = scene->mRootNode->mTransformation;
|
||||
globalTransformation = globalTransformation.Inverse();
|
||||
ReadHierarchyData($root_node, scene->mRootNode);
|
||||
ReadMissingBones(animation, *model);
|
||||
}
|
||||
|
||||
~Animation() {
|
||||
}
|
||||
|
||||
|
||||
Bone* FindBone(const std::string& name) {
|
||||
auto iter = std::find_if($bones.begin(), $bones.end(),
|
||||
[&](const auto& bone) {
|
||||
return bone.GetBoneName() == name;
|
||||
}
|
||||
);
|
||||
|
||||
return iter == $bones.end() ? nullptr : &(*iter);
|
||||
}
|
||||
|
||||
float GetTicksPerSecond() {
|
||||
return $ticks_per_sec;
|
||||
}
|
||||
|
||||
float GetDuration() {
|
||||
return $duration;
|
||||
}
|
||||
|
||||
const AssimpNodeData& GetRootNode() {
|
||||
return $root_node;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, BoneInfo>& GetBoneIDMap()
|
||||
{
|
||||
return $bone_info_map;
|
||||
}
|
||||
|
||||
void ReadMissingBones(const aiAnimation* animation, Model& model) {
|
||||
// this code is weird as hell
|
||||
int size = animation->mNumChannels;
|
||||
auto& boneInfoMap = model.bone_info_map;
|
||||
int& boneCount = model.bone_counter;
|
||||
|
||||
for(int i = 0; i < size; i++) {
|
||||
auto channel = animation->mChannels[i];
|
||||
std::string boneName = channel->mNodeName.data;
|
||||
|
||||
if(!boneInfoMap.contains(boneName)) {
|
||||
boneInfoMap[boneName].id = boneCount;
|
||||
boneCount++;
|
||||
}
|
||||
|
||||
dbc::check(boneCount < MAX_BONES, "Too many bones!");
|
||||
|
||||
$bones.emplace_back(boneName,
|
||||
boneInfoMap[boneName].id, channel);
|
||||
}
|
||||
|
||||
$bone_info_map = boneInfoMap;
|
||||
}
|
||||
|
||||
void ReadHierarchyData(AssimpNodeData& dest, const aiNode* src) {
|
||||
dbc::check(src != nullptr, "aiNode src is null?!");
|
||||
|
||||
dest.name = src->mName.data;
|
||||
dest.transformation = AssimpGLMHelpers::ConvertMatrixToGLMFormat(src->mTransformation);
|
||||
dest.children_count = src->mNumChildren;
|
||||
|
||||
for(size_t i = 0; i < src->mNumChildren; i++) {
|
||||
AssimpNodeData newData;
|
||||
ReadHierarchyData(newData, src->mChildren[i]);
|
||||
dest.children.push_back(newData);
|
||||
}
|
||||
}
|
||||
};
|
||||
1
45-animation/src/animator.cpp
Normal file
1
45-animation/src/animator.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "animator.hpp"
|
||||
72
45-animation/src/animator.hpp
Normal file
72
45-animation/src/animator.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include "animation.hpp"
|
||||
#include "bone.hpp"
|
||||
|
||||
struct Bone;
|
||||
|
||||
struct Animator {
|
||||
std::vector<glm::mat4> $final_bone_matrices;
|
||||
Animation* $current_animation = nullptr;
|
||||
float $current_time = 0.0f;
|
||||
|
||||
Animator(Animation* animation) :
|
||||
$current_animation(animation)
|
||||
{
|
||||
$final_bone_matrices.reserve(MAX_BONES);
|
||||
|
||||
for(size_t i = 0; i < MAX_BONES; i++) {
|
||||
$final_bone_matrices.push_back(glm::mat4(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAnimation(float current_time) {
|
||||
if($current_animation) {
|
||||
$current_time = $current_animation->GetTicksPerSecond() * current_time;
|
||||
$current_time = fmod($current_time, $current_animation->GetDuration());
|
||||
CalculateBoneTransform(&$current_animation->GetRootNode(), glm::mat4(1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayAnimation(Animation* animation) {
|
||||
$current_animation = animation;
|
||||
$current_time = 0.0f;
|
||||
}
|
||||
|
||||
void CalculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform) {
|
||||
std::string node_name = node->name;
|
||||
glm::mat4 node_transform = node->transformation;
|
||||
|
||||
Bone *bone = $current_animation->FindBone(node_name);
|
||||
|
||||
if(bone) {
|
||||
bone->Update($current_time);
|
||||
node_transform = bone->GetLocalTransform();
|
||||
}
|
||||
|
||||
glm::mat4 global_transformation = parentTransform * node_transform;
|
||||
|
||||
auto bone_map = $current_animation->GetBoneIDMap();
|
||||
|
||||
if(bone_map.contains(node_name)) {
|
||||
auto& bone_info = bone_map[node_name];
|
||||
|
||||
$final_bone_matrices[bone_info.id] = global_transformation * bone_info.offset;
|
||||
}
|
||||
|
||||
for(int i = 0; i < node->children_count; i++) {
|
||||
CalculateBoneTransform(&node->children[i], global_transformation);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<glm::mat4>& GetFinalBoneMatrices() {
|
||||
return $final_bone_matrices;
|
||||
}
|
||||
|
||||
};
|
||||
13
45-animation/src/animdata.hpp
Normal file
13
45-animation/src/animdata.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include<glm/glm.hpp>
|
||||
|
||||
struct BoneInfo
|
||||
{
|
||||
/*id is index in finalBoneMatrices*/
|
||||
int id;
|
||||
|
||||
/*offset matrix transforms vertex from model space to bone space*/
|
||||
glm::mat4 offset;
|
||||
|
||||
};
|
||||
34
45-animation/src/assimp_glm_helpers.hpp
Normal file
34
45-animation/src/assimp_glm_helpers.hpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include<assimp/quaternion.h>
|
||||
#include<assimp/vector3.h>
|
||||
#include<assimp/matrix4x4.h>
|
||||
#include<glm/glm.hpp>
|
||||
#include<glm/gtc/quaternion.hpp>
|
||||
|
||||
|
||||
class AssimpGLMHelpers
|
||||
{
|
||||
public:
|
||||
|
||||
static inline glm::mat4 ConvertMatrixToGLMFormat(const aiMatrix4x4& from)
|
||||
{
|
||||
glm::mat4 to;
|
||||
//the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column
|
||||
to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4;
|
||||
to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4;
|
||||
to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4;
|
||||
to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4;
|
||||
return to;
|
||||
}
|
||||
|
||||
static inline glm::vec3 GetGLMVec(const aiVector3D& vec)
|
||||
{
|
||||
return glm::vec3(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
static inline glm::quat GetGLMQuat(const aiQuaternion& pOrientation)
|
||||
{
|
||||
return glm::quat(pOrientation.w, pOrientation.x, pOrientation.y, pOrientation.z);
|
||||
}
|
||||
};
|
||||
1
45-animation/src/bone.cpp
Normal file
1
45-animation/src/bone.cpp
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "bone.hpp"
|
||||
184
45-animation/src/bone.hpp
Normal file
184
45-animation/src/bone.hpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "model.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
#include "assimp_glm_helpers.hpp"
|
||||
#include <string>
|
||||
|
||||
struct KeyPosition {
|
||||
glm::vec3 position;
|
||||
float timeStamp;
|
||||
};
|
||||
|
||||
struct KeyRotation {
|
||||
glm::quat orientation;
|
||||
float timeStamp;
|
||||
};
|
||||
|
||||
struct KeyScale {
|
||||
glm::vec3 scale;
|
||||
float timeStamp;
|
||||
};
|
||||
|
||||
struct Bone
|
||||
{
|
||||
std::vector<KeyPosition> $positions;
|
||||
std::vector<KeyRotation> $rotations;
|
||||
std::vector<KeyScale> $scales;
|
||||
size_t $num_positions = 0;
|
||||
size_t $num_rotations = 0;
|
||||
size_t $num_scalings = 0;
|
||||
size_t $cur_position = 0;
|
||||
size_t $cur_rotation = 0;
|
||||
size_t $cur_scale = 0;
|
||||
|
||||
glm::mat4 $local_transform = 1.0f;
|
||||
std::string $name{};
|
||||
int $id = 0;
|
||||
|
||||
Bone(const std::string& name, int ID, const aiNodeAnim* channel) :
|
||||
$name(name),
|
||||
$id(ID)
|
||||
{
|
||||
$num_positions = channel->mNumPositionKeys;
|
||||
|
||||
for(size_t pos_i = 0; pos_i < $num_positions; pos_i++) {
|
||||
const auto& ai_position = channel->mPositionKeys[pos_i];
|
||||
auto position = AssimpGLMHelpers::GetGLMVec(ai_position.mValue);
|
||||
$positions.emplace_back(position, ai_position.mTime);
|
||||
}
|
||||
|
||||
$num_rotations = channel->mNumRotationKeys;
|
||||
for(size_t rot_i = 0; rot_i < $num_rotations; rot_i++) {
|
||||
const auto& ai_orientation = channel->mRotationKeys[rot_i];
|
||||
auto orientation = AssimpGLMHelpers::GetGLMQuat(ai_orientation.mValue);
|
||||
|
||||
$rotations.emplace_back(orientation, ai_orientation.mTime);
|
||||
}
|
||||
|
||||
$num_scalings = channel->mNumScalingKeys;
|
||||
for(size_t key_i = 0; key_i < $num_scalings; key_i++) {
|
||||
const auto& ai_scale = channel->mScalingKeys[key_i];
|
||||
auto scale = AssimpGLMHelpers::GetGLMVec(ai_scale.mValue);
|
||||
$scales.emplace_back(scale, ai_scale.mTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Update(float animationTime) {
|
||||
glm::mat4 translation = InterpolatPosition(animationTime);
|
||||
glm::mat4 rotation = InterpolateRotation(animationTime);
|
||||
glm::mat4 scale = InterpolateScaling(animationTime);
|
||||
$local_transform = translation * rotation * scale;
|
||||
}
|
||||
|
||||
glm::mat4 GetLocalTransform() {
|
||||
return $local_transform;
|
||||
}
|
||||
|
||||
std::string GetBoneName() const {
|
||||
return $name;
|
||||
}
|
||||
|
||||
int GetBoneId() {
|
||||
return $id;
|
||||
}
|
||||
|
||||
int GetPositionIndex(float animationTime) {
|
||||
for(size_t index = 0; index < $num_positions - 1; ++index) {
|
||||
auto ts = $positions[index + 1].timeStamp;
|
||||
|
||||
if(animationTime < ts) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
dbc::sentinel("Failed to find an animation position");
|
||||
}
|
||||
|
||||
int GetRotationIndex(float animationTime) {
|
||||
for(size_t index = 0; index < $num_rotations - 1; ++index) {
|
||||
auto ts = $rotations[index + 1].timeStamp;
|
||||
|
||||
if(animationTime < ts) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
dbc::sentinel("Failed to find an animation rotation");
|
||||
}
|
||||
|
||||
int GetScaleIndex(float animationTime) {
|
||||
for(size_t index = 0; index < $num_scalings - 1; ++index) {
|
||||
auto ts = $scales[index + 1].timeStamp;
|
||||
if(animationTime < ts) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
dbc::sentinel("Failed to find an animation scale");
|
||||
}
|
||||
|
||||
float GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime) {
|
||||
float scaleFactor = 0.0f;
|
||||
float midWayLength = animationTime - lastTimeStamp;
|
||||
float framesDiff = nextTimeStamp - lastTimeStamp;
|
||||
scaleFactor = midWayLength / framesDiff;
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
glm::mat4 InterpolatPosition(float animationTime) {
|
||||
if($num_positions == 1) {
|
||||
return glm::translate(glm::mat4(1.0f), $positions[0].position);
|
||||
}
|
||||
|
||||
int p0Index = GetPositionIndex(animationTime);
|
||||
auto& p0 = $positions[p0Index];
|
||||
auto& p1 = $positions[p0Index + 1];
|
||||
|
||||
float scaleFactor = GetScaleFactor(
|
||||
p0.timeStamp, p1.timeStamp, animationTime);
|
||||
|
||||
glm::vec3 finalPosition = glm::mix(p0.position,
|
||||
p1.position, scaleFactor);
|
||||
|
||||
return glm::translate(glm::mat4(1.0f), finalPosition);
|
||||
}
|
||||
|
||||
glm::mat4 InterpolateRotation(float animationTime) {
|
||||
if($num_rotations == 1) {
|
||||
auto rotation = glm::normalize($rotations[0].orientation);
|
||||
return glm::toMat4(rotation);
|
||||
}
|
||||
|
||||
int p0Index = GetRotationIndex(animationTime);
|
||||
auto& p0 = $rotations[p0Index];
|
||||
auto& p1 = $rotations[p0Index + 1];
|
||||
|
||||
float scaleFactor = GetScaleFactor(
|
||||
p0.timeStamp, p1.timeStamp, animationTime);
|
||||
|
||||
glm::quat finalRotation = glm::slerp(
|
||||
p0.orientation, p1.orientation, scaleFactor);
|
||||
|
||||
return glm::toMat4(finalRotation);
|
||||
}
|
||||
|
||||
glm::mat4 InterpolateScaling(float animationTime) {
|
||||
if($num_scalings == 1) {
|
||||
return glm::scale(glm::mat4(1.0f), $scales[0].scale);
|
||||
}
|
||||
|
||||
int p0Index = GetScaleIndex(animationTime);
|
||||
|
||||
auto& p0 = $scales[p0Index];
|
||||
auto& p1 = $scales[p0Index + 1];
|
||||
float scaleFactor = GetScaleFactor(
|
||||
p0.timeStamp, p1.timeStamp, animationTime);
|
||||
|
||||
glm::vec3 finalScale = glm::mix(p0.scale, p1.scale, scaleFactor);
|
||||
|
||||
return glm::scale(glm::mat4(1.0f), finalScale);
|
||||
}
|
||||
};
|
||||
70
45-animation/src/camera.cpp
Normal file
70
45-animation/src/camera.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "camera.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
glm::mat4 Camera::look_at() {
|
||||
return glm::lookAt(position, position + front, up);
|
||||
}
|
||||
|
||||
void Camera::forward() {
|
||||
dirty = true;
|
||||
position += speed * front;
|
||||
}
|
||||
|
||||
void Camera::back() {
|
||||
dirty = true;
|
||||
position -= speed * front;
|
||||
}
|
||||
|
||||
void Camera::left() {
|
||||
dirty = true;
|
||||
position -= glm::normalize(glm::cross(front, up)) * speed;
|
||||
}
|
||||
|
||||
void Camera::right() {
|
||||
dirty = true;
|
||||
position += glm::normalize(glm::cross(front, up)) * speed;
|
||||
}
|
||||
|
||||
void Camera::update(float deltaTime) {
|
||||
speed = movement_speed * deltaTime;
|
||||
}
|
||||
|
||||
void Camera::mouse_move(double xpos, double ypos) {
|
||||
dirty = true;
|
||||
|
||||
if(firstMouse) {
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
firstMouse = false;
|
||||
}
|
||||
|
||||
float xoffset = xpos - lastX;
|
||||
float yoffset = lastY - ypos;
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
|
||||
const float sensitivity = 0.1f;
|
||||
xoffset *= sensitivity;
|
||||
yoffset *= sensitivity;
|
||||
|
||||
yaw += xoffset;
|
||||
pitch += yoffset;
|
||||
|
||||
if(pitch > 89.0f) pitch = 89.0f;
|
||||
if(pitch < -89.0f) pitch = -89.0f;
|
||||
|
||||
direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
|
||||
direction.y = sin(glm::radians(pitch));
|
||||
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
|
||||
|
||||
front = glm::normalize(direction);
|
||||
}
|
||||
|
||||
void Camera::mouse_scroll(double xoffset, double yoffset) {
|
||||
dirty = true;
|
||||
fov -= (float)yoffset;
|
||||
|
||||
if(fov < 1.0f) fov = 1.0f;
|
||||
if(fov > 90.0f) fov = 90.0f;
|
||||
}
|
||||
28
45-animation/src/camera.hpp
Normal file
28
45-animation/src/camera.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct Camera {
|
||||
glm::vec3 position{0.0f, 0.0f, 3.0f};
|
||||
glm::vec3 front{0.0f, 0.0f, -1.0f};
|
||||
glm::vec3 up{0.0f, 1.0f, 0.0f};
|
||||
glm::vec3 direction{0.0f, 0.0f, 0.0f};
|
||||
float movement_speed = 20.0f;
|
||||
|
||||
float pitch = 0.0f;
|
||||
float yaw = -90.0f;
|
||||
float speed = 0.05f;
|
||||
float lastX = 400;
|
||||
float lastY = 300;
|
||||
float fov = 45.0f;
|
||||
bool firstMouse = true;
|
||||
bool dirty = true;
|
||||
|
||||
glm::mat4 look_at();
|
||||
void forward();
|
||||
void back();
|
||||
void left();
|
||||
void right();
|
||||
void update(float deltaTime);
|
||||
void mouse_move(double xpos, double ypos);
|
||||
void mouse_scroll(double xoffset, double yoffset);
|
||||
};
|
||||
113
45-animation/src/components.hpp
Normal file
113
45-animation/src/components.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "json.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
namespace components {
|
||||
template <typename T> struct NameOf;
|
||||
|
||||
struct Material {
|
||||
glm::vec3 ambient{0.1f,0.1f,0.1f};
|
||||
float shininess{32.0f};
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Material, ambient, shininess);
|
||||
|
||||
struct Light {
|
||||
glm::vec3 position{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 direction{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 ambient{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 diffuse{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 specular{0.0f, 0.0f, 0.0f};
|
||||
|
||||
float constant=0.0f;
|
||||
float linear=0.0f;
|
||||
float quadratic=0.0f;
|
||||
float cut_off=0.0f;
|
||||
float outer_cut_off=0.0f;
|
||||
bool on=true;
|
||||
|
||||
void adjust(float amount) {
|
||||
ambient += amount;
|
||||
diffuse += amount;
|
||||
specular += amount;
|
||||
}
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Light,
|
||||
position, direction,
|
||||
ambient, diffuse, specular,
|
||||
constant, linear, quadratic,
|
||||
cut_off, outer_cut_off, on);
|
||||
|
||||
struct Lighting {
|
||||
Light directional;
|
||||
std::vector<Light> positioned;
|
||||
std::vector<Light> spot;
|
||||
Light camera;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Lighting, directional, positioned, spot, camera);
|
||||
|
||||
struct Camera {
|
||||
glm::vec3 position{0.0f, 0.0f, 3.0f};
|
||||
glm::vec3 front{0.0f, 0.0f, -1.0f};
|
||||
glm::vec3 up{0.0f, 1.0f, 0.0f};
|
||||
glm::vec3 direction{0.0f, 0.0f, 0.0f};
|
||||
float movement_speed = 20.0f;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Camera, position, front, up, direction, movement_speed);
|
||||
|
||||
struct Model {
|
||||
std::string directory;
|
||||
std::string model_path;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Model, directory, model_path);
|
||||
|
||||
using Position = glm::vec3;
|
||||
struct Rotation {
|
||||
float angle = 0;
|
||||
glm::vec3 axes{1.0f, 0.0f, 0.0f};
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Rotation, angle, axes);
|
||||
|
||||
struct Thing {
|
||||
std::string model;
|
||||
std::string material;
|
||||
Position position;
|
||||
Rotation rotation;
|
||||
float scale;
|
||||
bool animate;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Thing, model, material, position, rotation, scale, animate);
|
||||
|
||||
struct Shader {
|
||||
std::string vertex_path;
|
||||
std::string frag_path;
|
||||
std::string geometry_path;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Shader, vertex_path, frag_path, geometry_path);
|
||||
|
||||
struct Scene {
|
||||
components::Shader shader;
|
||||
components::Shader reflect_shader;
|
||||
components::Shader screen_shader;
|
||||
components::Shader skybox_shader;
|
||||
std::vector<std::string> skybox_faces;
|
||||
std::unordered_map<std::string, Material> materials;
|
||||
std::unordered_map<std::string, Model> models;
|
||||
std::vector<Thing> things;
|
||||
Camera camera;
|
||||
Lighting light;
|
||||
};
|
||||
|
||||
ENROLL_COMPONENT(Scene, shader, reflect_shader, screen_shader, skybox_shader, skybox_faces, materials, models, things, camera, light);
|
||||
}
|
||||
9
45-animation/src/config.hpp
Normal file
9
45-animation/src/config.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#define INSTANCE_COUNT 5
|
||||
|
||||
const unsigned int SCR_WIDTH = 1792;
|
||||
const unsigned int SCR_HEIGHT = 1008;
|
||||
#define MAX_BONES 100
|
||||
|
||||
#define MAX_LIGHTS 4
|
||||
47
45-animation/src/dbc.cpp
Normal file
47
45-animation/src/dbc.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#include "dbc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
void dbc::log(const string &message, const std::source_location location) {
|
||||
std::cout << '[' << location.file_name() << ':'
|
||||
<< location.line() << "|"
|
||||
<< location.function_name() << "] "
|
||||
<< message << std::endl;
|
||||
}
|
||||
|
||||
void dbc::sentinel(const string &message, const std::source_location location) {
|
||||
string err = $F("[SENTINEL!] {}", message);
|
||||
dbc::log(err, location);
|
||||
throw dbc::SentinelError(err);
|
||||
}
|
||||
|
||||
void dbc::pre(const string &message, bool test, const std::source_location location) {
|
||||
if(!test) {
|
||||
string err = $F("[PRE!] {}", message);
|
||||
dbc::log(err, location);
|
||||
throw dbc::PreCondError(err);
|
||||
}
|
||||
}
|
||||
|
||||
void dbc::pre(const string &message, std::function<bool()> tester, const std::source_location location) {
|
||||
dbc::pre(message, tester(), location);
|
||||
}
|
||||
|
||||
void dbc::post(const string &message, bool test, const std::source_location location) {
|
||||
if(!test) {
|
||||
string err = $F("[POST!] {}", message);
|
||||
dbc::log(err, location);
|
||||
throw dbc::PostCondError(err);
|
||||
}
|
||||
}
|
||||
|
||||
void dbc::post(const string &message, std::function<bool()> tester, const std::source_location location) {
|
||||
dbc::post(message, tester(), location);
|
||||
}
|
||||
|
||||
void dbc::check(bool test, const string &message, const std::source_location location) {
|
||||
if(!test) {
|
||||
string err = $F("[CHECK!] {}\n", message);
|
||||
dbc::log(err, location);
|
||||
throw dbc::CheckError(err);
|
||||
}
|
||||
}
|
||||
46
45-animation/src/dbc.hpp
Normal file
46
45-animation/src/dbc.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fmt/core.h>
|
||||
#include <functional>
|
||||
#include <source_location>
|
||||
|
||||
// AKA the Fuckit macro
|
||||
#define $F(FMT, ...) fmt::format(FMT, ##__VA_ARGS__)
|
||||
|
||||
namespace dbc {
|
||||
using std::string;
|
||||
|
||||
using CheckError = std::runtime_error;
|
||||
using SentinelError = std::runtime_error;
|
||||
using PreCondError = std::runtime_error;
|
||||
using PostCondError = std::runtime_error;
|
||||
|
||||
void log(const string &message,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
[[noreturn]] void sentinel(const string &message,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
void pre(const string &message, bool test,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
void pre(const string &message, std::function<bool()> tester,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
void post(const string &message, bool test,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
void post(const string &message, std::function<bool()> tester,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
|
||||
void check(bool test, const string &message,
|
||||
const std::source_location location =
|
||||
std::source_location::current());
|
||||
}
|
||||
54
45-animation/src/framebuffer.cpp
Normal file
54
45-animation/src/framebuffer.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#include "framebuffer.hpp"
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
void FrameBuffer::init() {
|
||||
glGenFramebuffers(1, &target_fb);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, target_fb);
|
||||
glGenTextures(1, &texture_color_buffer);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture_color_buffer);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 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, GL_TEXTURE_2D, texture_color_buffer, 0);
|
||||
|
||||
glGenRenderbuffers(1, &target_rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, target_rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, target_rbo);
|
||||
|
||||
dbc::check(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Failed to init framebuffer.");
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
invariant();
|
||||
}
|
||||
|
||||
void FrameBuffer::begin() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, target_fb);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void FrameBuffer::commit() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glClearColor(0.0f, 0.0f, 1.0, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
void FrameBuffer::draw(Screen& screen) {
|
||||
screen.shader.use();
|
||||
glBindVertexArray(screen.target_VAO);
|
||||
glActiveTexture(GL_TEXTURE0 + screen.texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_color_buffer);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
void FrameBuffer::invariant() {
|
||||
dbc::check(target_fb != UINT_MAX, "target_fb not initialized");
|
||||
dbc::check(texture_color_buffer != UINT_MAX, "texture_color_buffer not initialized");
|
||||
}
|
||||
17
45-animation/src/framebuffer.hpp
Normal file
17
45-animation/src/framebuffer.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "shader.hpp"
|
||||
#include "screen.hpp"
|
||||
|
||||
struct FrameBuffer {
|
||||
unsigned int target_fb = UINT_MAX;
|
||||
unsigned int target_rbo = UINT_MAX;
|
||||
unsigned int texture_color_buffer = UINT_MAX;
|
||||
|
||||
|
||||
void init();
|
||||
void begin();
|
||||
void commit();
|
||||
void draw(Screen& screen);
|
||||
void invariant();
|
||||
};
|
||||
1140
45-animation/src/glad/glad.cpp
Normal file
1140
45-animation/src/glad/glad.cpp
Normal file
File diff suppressed because it is too large
Load diff
2129
45-animation/src/glad/glad.h
Normal file
2129
45-animation/src/glad/glad.h
Normal file
File diff suppressed because it is too large
Load diff
59
45-animation/src/json.hpp
Normal file
59
45-animation/src/json.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <optional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
|
||||
#define ENROLL_COMPONENT(COMPONENT, ...) \
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(COMPONENT, __VA_ARGS__); \
|
||||
template <> struct NameOf<COMPONENT> { \
|
||||
static constexpr const char *name = #COMPONENT; \
|
||||
};
|
||||
|
||||
// partial specialization (full specialization works too)
|
||||
namespace nlohmann {
|
||||
|
||||
template <>
|
||||
struct adl_serializer<glm::vec3> {
|
||||
static void to_json(json& j, const glm::vec3& opt) {
|
||||
|
||||
}
|
||||
|
||||
static void from_json(const json& j, glm::vec3& opt) {
|
||||
opt = glm::vec3(j[0], j[1], j[2]);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct adl_serializer<std::optional<T>> {
|
||||
static void to_json(json& j, const std::optional<T>& opt) {
|
||||
if (opt == std::nullopt) {
|
||||
j = nullptr;
|
||||
} else {
|
||||
j = *opt; // this will call adl_serializer<T>::to_json which will
|
||||
// find the free function to_json in T's namespace!
|
||||
}
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::optional<T>& opt) {
|
||||
if (j.is_null() || j == false) {
|
||||
opt = std::nullopt;
|
||||
} else {
|
||||
opt = std::make_optional<T>(j.template get<T>());
|
||||
// same as above, but with adl_serializer<T>::from_json
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct adl_serializer<std::chrono::milliseconds> {
|
||||
static void to_json(json& j, const std::chrono::milliseconds& opt) {
|
||||
j = opt.count();
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::chrono::milliseconds& opt) {
|
||||
opt = std::chrono::milliseconds{int(j)};
|
||||
}
|
||||
};
|
||||
}
|
||||
161
45-animation/src/main.cpp
Normal file
161
45-animation/src/main.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <stb_image.h>
|
||||
#include "scene.hpp"
|
||||
#include "utils.hpp"
|
||||
#include <print>
|
||||
#include <format>
|
||||
#include "text.hpp"
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
|
||||
|
||||
std::string text_content{""};
|
||||
|
||||
void init_glfw() {
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *, int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void mouse_callback(GLFWwindow *window, double xpos, double ypos) {
|
||||
Scene* scene = static_cast<Scene*>(glfwGetWindowUserPointer(window));
|
||||
scene->camera.mouse_move(xpos, ypos);
|
||||
}
|
||||
|
||||
void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) {
|
||||
Scene* scene = static_cast<Scene*>(glfwGetWindowUserPointer(window));
|
||||
scene->camera.mouse_scroll(xoffset, yoffset);
|
||||
}
|
||||
|
||||
GLFWwindow* create_window() {
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
|
||||
dbc::check(window != NULL, "failed to open window");
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
|
||||
auto good = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
|
||||
dbc::check(good, "failed to load GLAD");
|
||||
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
glfwSetScrollCallback(window, scroll_callback);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void process_input(GLFWwindow *window, std::shared_ptr<Scene> scene) {
|
||||
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
|
||||
scene->camera.forward();
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
|
||||
scene->camera.back();
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
|
||||
scene->camera.left();
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
|
||||
scene->camera.right();
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
|
||||
scene->reflect_on = !scene->reflect_on;
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_J) == GLFW_PRESS) {
|
||||
scene->debug_on = !scene->debug_on;
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_H) == GLFW_PRESS) {
|
||||
scene->spawn("human");
|
||||
glm::vec3 pos = scene->camera.position;
|
||||
text_content = std::format("Spawn human at {},{},{}", pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_L) == GLFW_PRESS) {
|
||||
scene->light.camera.on = !scene->light.camera.on;
|
||||
scene->shader.apply_lighting(scene->light);
|
||||
scene->shadows_rendered = false;
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_O) == GLFW_PRESS) {
|
||||
// BUG: this assumes the old shader with different light so broken
|
||||
scene->light.directional.adjust(-0.01f);
|
||||
scene->shader.apply_lighting(scene->light);
|
||||
scene->shadows_rendered = false;
|
||||
}
|
||||
|
||||
if(glfwGetKey(window, GLFW_KEY_P) == GLFW_PRESS) {
|
||||
// BUG: this assumes the old shader with different light so broken
|
||||
scene->light.directional.adjust(0.01f);
|
||||
scene->shader.apply_lighting(scene->light);
|
||||
scene->shadows_rendered = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Scene> setup() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
components::Scene config = utils::load_scene_config("config.json");
|
||||
|
||||
return std::make_shared<Scene>(config);
|
||||
}
|
||||
|
||||
void update(GLFWwindow* window, std::shared_ptr<Scene> scene) {
|
||||
glfwPollEvents();
|
||||
process_input(window, scene);
|
||||
scene->update();
|
||||
}
|
||||
|
||||
void render(GLFWwindow* window, std::shared_ptr<Scene> scene, Text &text) {
|
||||
scene->render();
|
||||
text.render(text_content, 25.0f, 25.0f, 1.0f, glm::vec3(1.0, 0.0f, 0.0f));
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
void quit(GLFWwindow* window, std::shared_ptr<Scene> scene) {
|
||||
scene->cleanup();
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
int main() {
|
||||
init_glfw();
|
||||
|
||||
auto window = create_window();
|
||||
std::shared_ptr<Scene> scene = setup();
|
||||
Text text;
|
||||
|
||||
glfwSetWindowUserPointer(window, scene.get());
|
||||
|
||||
while(!glfwWindowShouldClose(window)) {
|
||||
update(window, scene);
|
||||
render(window, scene, text);
|
||||
}
|
||||
|
||||
quit(window, scene);
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
45-animation/src/mesh.cpp
Normal file
73
45-animation/src/mesh.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#include "mesh.hpp"
|
||||
#include "dbc.hpp"
|
||||
#include "config.hpp"
|
||||
|
||||
void Mesh::draw(Shader &shader, bool with_textures) {
|
||||
if(with_textures) {
|
||||
apply_textures(shader);
|
||||
}
|
||||
|
||||
glBindVertexArray(VAO);
|
||||
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Mesh::apply_textures(const Shader& shader) {
|
||||
int texture_i = 1;
|
||||
bool has_height = false;
|
||||
|
||||
for(auto& texture : textures) {
|
||||
texture.uniform_id = glGetUniformLocation(shader.ID, texture.target.c_str());
|
||||
if(texture.uniform_id == -1) continue;
|
||||
|
||||
if(texture.target.starts_with("texture_height")) {
|
||||
has_height = true;
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + texture_i);
|
||||
glBindTexture(GL_TEXTURE_2D, texture.id);
|
||||
glUniform1i(texture.uniform_id, texture_i);
|
||||
|
||||
texture_i++;
|
||||
}
|
||||
|
||||
// quick hack to avoid applying height/normal to things without one
|
||||
shader.setBool("hasHeight", has_height);
|
||||
}
|
||||
|
||||
void Mesh::setup_mesh() {
|
||||
glGenVertexArrays(1, &VAO);
|
||||
glGenBuffers(1, &VBO);
|
||||
glGenBuffers(1, &EBO);
|
||||
|
||||
glBindVertexArray(VAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_STATIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
|
||||
|
||||
// this matches the locations in the vertex shader
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
|
||||
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
|
||||
|
||||
glEnableVertexAttribArray(2);
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tex_coords));
|
||||
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent));
|
||||
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, bitangent));
|
||||
|
||||
// bone ids
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, bone_ids));
|
||||
|
||||
// bone weights
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, weights));
|
||||
}
|
||||
80
45-animation/src/mesh.hpp
Normal file
80
45-animation/src/mesh.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "shader.hpp"
|
||||
#include "animdata.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define MAX_BONE_INFLUENCE 4
|
||||
|
||||
struct Vertex {
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 tex_coords;
|
||||
glm::vec3 tangent;
|
||||
glm::vec3 bitangent;
|
||||
int bone_ids[MAX_BONE_INFLUENCE]{0};
|
||||
float weights[MAX_BONE_INFLUENCE]{0.0f};
|
||||
size_t bone_count = 0;
|
||||
};
|
||||
|
||||
struct TextureCounts {
|
||||
unsigned int diffuseNr = 1;
|
||||
unsigned int specularNr = 1;
|
||||
unsigned int normalNr = 1;
|
||||
unsigned int heightNr = 1;
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
unsigned int id;
|
||||
std::string type;
|
||||
std::string path;
|
||||
std::string target;
|
||||
int uniform_id=-1;
|
||||
|
||||
Texture(unsigned int id, const std::string& type, const std::string& path, TextureCounts& count) :
|
||||
id(id), type(type)
|
||||
{
|
||||
unsigned int number = 0;
|
||||
|
||||
if(type == "texture_diffuse") {
|
||||
number = count.diffuseNr++;
|
||||
} else if(type == "texture_specular") {
|
||||
number = count.specularNr++;
|
||||
} else if(type == "texture_normal") {
|
||||
number = count.normalNr++;
|
||||
} else if(type == "texture_height") {
|
||||
number = count.heightNr++;
|
||||
} else {
|
||||
dbc::sentinel($F("Invalid texture type={} for file={}", type, path));
|
||||
}
|
||||
|
||||
target = std::format("{}{}", type, number);
|
||||
}
|
||||
};
|
||||
|
||||
struct Mesh {
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<Texture> textures;
|
||||
unsigned int VAO = 0;
|
||||
unsigned int VBO = 0;
|
||||
unsigned int EBO = 0;
|
||||
|
||||
Mesh(std::vector<Vertex>& vertices, std::vector<unsigned int>& indices, std::vector<Texture>& textures) :
|
||||
vertices(vertices),
|
||||
indices(indices),
|
||||
textures(textures)
|
||||
{
|
||||
setup_mesh();
|
||||
}
|
||||
|
||||
void draw(Shader &shader, bool with_textures);
|
||||
void setup_mesh();
|
||||
void apply_textures(const Shader& shader);
|
||||
};
|
||||
276
45-animation/src/model.cpp
Normal file
276
45-animation/src/model.cpp
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
#include "model.hpp"
|
||||
#include "dbc.hpp"
|
||||
#include <print>
|
||||
#include "assimp_glm_helpers.hpp"
|
||||
|
||||
unsigned int texture_from_file(const std::string& directory, const std::string& path);
|
||||
unsigned int texture_from_internal(const aiScene *scene, size_t index);
|
||||
|
||||
unsigned int image_to_texture(unsigned char *data, int width, int height, int nrComponents) {
|
||||
unsigned int textureID;
|
||||
glGenTextures(1, &textureID);
|
||||
|
||||
GLenum format = GL_RGB;
|
||||
|
||||
switch(nrComponents) {
|
||||
case 1:
|
||||
format = GL_RED;
|
||||
break;
|
||||
case 3:
|
||||
format = GL_RGB;
|
||||
break;
|
||||
case 4:
|
||||
format = GL_RGBA;
|
||||
break;
|
||||
default:
|
||||
dbc::sentinel($F("Impossible nrComponents={} when loading image", nrComponents));
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 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_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
unsigned int texture_from_internal(const aiScene *scene, size_t index) {
|
||||
const aiTexture *aiTex = scene->mTextures[index];
|
||||
unsigned int textureID = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int nrComponents = 0;
|
||||
unsigned char *data = nullptr;
|
||||
const unsigned char* dataBytes = (const unsigned char*)aiTex->pcData;
|
||||
|
||||
if(aiTex->mHeight == 0) {
|
||||
// compressed texture (PNG or JPG)
|
||||
size_t dataSize = aiTex->mWidth;
|
||||
data = stbi_load_from_memory(dataBytes, dataSize, &width, &height, &nrComponents, 0);
|
||||
textureID = image_to_texture(data, width, height, nrComponents);
|
||||
stbi_image_free(data);
|
||||
} else {
|
||||
data = (unsigned char*)aiTex->pcData;
|
||||
width = aiTex->mWidth;
|
||||
height = aiTex->mHeight;
|
||||
nrComponents = 4; // either BGRA8888 or RGBA8888
|
||||
textureID = image_to_texture(data, width, height, nrComponents);
|
||||
}
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
unsigned int texture_from_file(const std::string& directory, const std::string& path)
|
||||
{
|
||||
std::string filename = directory + "/" + path;
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int nrComponents = 0;
|
||||
|
||||
unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
|
||||
dbc::check(data != nullptr, $F("Failed to load texture {}", filename));
|
||||
|
||||
unsigned int textureID = image_to_texture(data, width, height, nrComponents);
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
void Model::draw(Shader &shader, bool with_textures) {
|
||||
for(auto& mesh : meshes) {
|
||||
mesh.draw(shader, with_textures);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::load_model() {
|
||||
std::string path = directory + "/" + model_path;
|
||||
|
||||
fmt::println("Loading model {}", path);
|
||||
|
||||
Assimp::Importer importer;
|
||||
|
||||
const aiScene* scene = importer.ReadFile(path,
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_GenSmoothNormals |
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_CalcTangentSpace);
|
||||
|
||||
dbc::check(scene != nullptr, "Assimp ReadFile return null");
|
||||
dbc::check(!(scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE), "Assimp says incomplete.");
|
||||
dbc::check(scene->mRootNode != nullptr, "Assimp loaded scene doesn't have a root node");
|
||||
|
||||
process_node(scene, scene->mRootNode);
|
||||
}
|
||||
|
||||
void Model::process_node(const aiScene *scene, aiNode *node) {
|
||||
for(unsigned int i = 0; i < node->mNumMeshes; i++) {
|
||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
meshes.push_back(process_mesh(scene, mesh));
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < node->mNumChildren; i++) {
|
||||
process_node(scene, node->mChildren[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::SetVertexBoneData(Vertex& vertex, int boneID, float weight)
|
||||
{
|
||||
// BUG: this will ignore bones after the max, but it seems to always be 4, so is there an off-by-one?
|
||||
if(vertex.bone_count < MAX_BONE_INFLUENCE) {
|
||||
vertex.bone_ids[vertex.bone_count] = boneID;
|
||||
vertex.weights[vertex.bone_count] = weight;
|
||||
vertex.bone_count++;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::ExtractBoneWeightForVertices(std::vector<Vertex>& vertices, aiMesh *mesh, const aiScene* scene) {
|
||||
|
||||
for(size_t boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex) {
|
||||
int boneID = -1;
|
||||
std::string bone_name = mesh->mBones[boneIndex]->mName.C_Str();
|
||||
|
||||
if(bone_info_map.contains(bone_name)) {
|
||||
boneID = bone_info_map.at(bone_name).id;
|
||||
} else {
|
||||
fmt::println("FOUND BONE: {}", bone_name);
|
||||
bone_info_map.try_emplace(bone_name,
|
||||
bone_counter,
|
||||
AssimpGLMHelpers::ConvertMatrixToGLMFormat(mesh->mBones[boneIndex]->mOffsetMatrix));
|
||||
boneID = bone_counter;
|
||||
bone_counter++;
|
||||
}
|
||||
|
||||
dbc::check(boneID != -1, "There's no bones!");
|
||||
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;
|
||||
dbc::check((size_t)vertexId <= vertices.size(), "vertexID is too large");
|
||||
SetVertexBoneData(vertices[vertexId], boneID, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mesh Model::process_mesh(const aiScene *scene, aiMesh *mesh) {
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
std::vector<Texture> textures;
|
||||
|
||||
for(unsigned int i = 0; i < mesh->mNumVertices; i++) {
|
||||
glm::vec3 position{
|
||||
mesh->mVertices[i].x,
|
||||
mesh->mVertices[i].y,
|
||||
mesh->mVertices[i].z};
|
||||
|
||||
glm::vec3 normal{};
|
||||
glm::vec2 tex_coords{0.0f, 0.0f};
|
||||
glm::vec3 tangent;
|
||||
glm::vec3 bitangent;
|
||||
|
||||
if(mesh->HasNormals()) {
|
||||
normal = glm::vec3(
|
||||
mesh->mNormals[i].x,
|
||||
mesh->mNormals[i].y,
|
||||
mesh->mNormals[i].z);
|
||||
}
|
||||
|
||||
if(mesh->mTextureCoords[0]) {
|
||||
tex_coords = glm::vec2(
|
||||
mesh->mTextureCoords[0][i].x,
|
||||
mesh->mTextureCoords[0][i].y
|
||||
);
|
||||
}
|
||||
|
||||
// if has height/normal map
|
||||
if(mesh->HasTangentsAndBitangents()) {
|
||||
tangent = glm::vec3(
|
||||
mesh->mTangents[i].x,
|
||||
mesh->mTangents[i].y,
|
||||
mesh->mTangents[i].z
|
||||
);
|
||||
|
||||
bitangent = glm::vec3(
|
||||
mesh->mBitangents[i].x,
|
||||
mesh->mBitangents[i].y,
|
||||
mesh->mBitangents[i].z
|
||||
);
|
||||
}
|
||||
|
||||
vertices.emplace_back(position, normal,
|
||||
tex_coords, tangent, bitangent);
|
||||
}
|
||||
|
||||
ExtractBoneWeightForVertices(vertices, mesh, scene);
|
||||
|
||||
for(unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
||||
aiFace& face = mesh->mFaces[i];
|
||||
|
||||
for(unsigned int j = 0; j < face.mNumIndices; j++) {
|
||||
indices.emplace_back(face.mIndices[j]);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
load_material_textures(scene, textures, material, aiTextureType_DIFFUSE, "texture_diffuse");
|
||||
|
||||
// 2. specular maps
|
||||
load_material_textures(scene, textures, material, aiTextureType_SPECULAR, "texture_specular");
|
||||
|
||||
// 3. normal maps, possibly wrong?
|
||||
load_material_textures(scene, textures, material, aiTextureType_NORMALS, "texture_normal");
|
||||
|
||||
// 4. height maps
|
||||
load_material_textures(scene, textures, material, aiTextureType_HEIGHT, "texture_height");
|
||||
|
||||
return Mesh(vertices, indices, textures);
|
||||
}
|
||||
|
||||
void Model::load_material_textures(const aiScene *scene, std::vector<Texture>& textures, aiMaterial *mat, aiTextureType type, std::string type_name)
|
||||
{
|
||||
for(unsigned int i = 0; i < mat->GetTextureCount(type); i++) {
|
||||
aiString str;
|
||||
|
||||
mat->GetTexture(type, i, &str);
|
||||
std::string tx_path{str.C_Str()};
|
||||
dbc::check(!tx_path.empty(), "Texture has empty path, should be impossible?");
|
||||
|
||||
if(textures_loaded.contains(tx_path)) {
|
||||
textures.push_back(textures_loaded.at(tx_path));
|
||||
} else {
|
||||
unsigned int tx_id = 0;
|
||||
|
||||
// if it's a *# style path then it's internal
|
||||
if(tx_path[0] == '*') {
|
||||
// get the texture from the internal version
|
||||
tx_id = texture_from_internal(scene, std::stoi(tx_path.substr(1)));
|
||||
} else {
|
||||
// else get it from a file
|
||||
tx_id = texture_from_file(directory, tx_path);
|
||||
}
|
||||
|
||||
// dubious code here, but seems to be right
|
||||
auto& result = textures.emplace_back(tx_id, type_name, tx_path, texture_counts);
|
||||
|
||||
textures_loaded.try_emplace(tx_path, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
45-animation/src/model.hpp
Normal file
46
45-animation/src/model.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <stb_image.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
#include <mesh.hpp>
|
||||
#include <shader.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct Model {
|
||||
std::string directory;
|
||||
std::string model_path;
|
||||
std::unordered_map<std::string, Texture> textures_loaded;
|
||||
std::vector<Mesh> meshes;
|
||||
TextureCounts texture_counts;
|
||||
std::unordered_map<std::string, BoneInfo> bone_info_map;
|
||||
int bone_counter = 0;
|
||||
|
||||
Model(const std::string& directory, const std::string& model_path) :
|
||||
directory(directory), model_path(model_path)
|
||||
{
|
||||
load_model();
|
||||
}
|
||||
|
||||
void draw(Shader &shader, bool with_textures);
|
||||
|
||||
void load_model();
|
||||
void process_node(const aiScene *scene, aiNode *node);
|
||||
Mesh process_mesh(const aiScene *scene, aiMesh *mesh);
|
||||
|
||||
void load_material_textures(const aiScene *scene, std::vector<Texture>& textures, aiMaterial *mat, aiTextureType type, std::string typeName);
|
||||
|
||||
void ExtractBoneWeightForVertices(std::vector<Vertex>& vertices, aiMesh *mesh, const aiScene* scene);
|
||||
void SetVertexBoneData(Vertex& vertex, int boneID, float weight);
|
||||
};
|
||||
185
45-animation/src/scene.cpp
Normal file
185
45-animation/src/scene.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "scene.hpp"
|
||||
#include "dbc.hpp"
|
||||
#include <math.h>
|
||||
#include "skybox.hpp"
|
||||
#include <print>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
void Scene::update() {
|
||||
float currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
|
||||
// for now just detect the camera moved and do spotlight update
|
||||
if(camera.dirty) {
|
||||
light.camera.position = camera.position;
|
||||
light.camera.direction = camera.front;
|
||||
// just update the camera's light
|
||||
shader.apply_spot_light(light.camera, 0);
|
||||
camera.dirty = false;
|
||||
}
|
||||
|
||||
camera.update(deltaTime);
|
||||
animator.UpdateAnimation(currentFrame);
|
||||
}
|
||||
|
||||
void Scene::draw_thing(Shader& with_shader, Thing& thing)
|
||||
{
|
||||
with_shader.apply_material(thing.material);
|
||||
|
||||
glm::mat4 model = glm::mat4(1.0f);
|
||||
model = glm::translate(model, thing.position);
|
||||
model = glm::rotate(model, glm::radians(thing.rotation.angle), thing.rotation.axes);
|
||||
model = glm::scale(model, glm::vec3(thing.scale));
|
||||
|
||||
with_shader.setMat4("model", model);
|
||||
|
||||
if(thing.animation) {
|
||||
auto& transforms = animator.GetFinalBoneMatrices();
|
||||
|
||||
for(size_t i = 0; i < transforms.size(); i++) {
|
||||
with_shader.setMat4(std::format("finalBonesMatrices[{}]", (int)i), transforms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
with_shader.setBool("hasBones", thing.animation != nullptr);
|
||||
|
||||
// !reflect_on turns it on? yeah weird
|
||||
thing.model.draw(with_shader, !reflect_on);
|
||||
}
|
||||
|
||||
void Scene::render_scene(Shader& with_shader) {
|
||||
glm::mat4 view = camera.look_at();
|
||||
|
||||
// fov, aspect, near plane, far plane
|
||||
projection = glm::perspective(glm::radians(camera.fov), (float)SCR_WIDTH / (float)SCR_HEIGHT, near_plane, far_plane);
|
||||
|
||||
// render the scene
|
||||
with_shader.use();
|
||||
with_shader.setMat4("view", view);
|
||||
with_shader.setMat4("projection", projection);
|
||||
|
||||
for(auto& thing : things) {
|
||||
draw_thing(with_shader, thing);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::render_shadows() {
|
||||
// BUG: right now there's only one directional light to simplify things
|
||||
// but I might update this to support point lights so don't refactor this out
|
||||
shadows.begin();
|
||||
|
||||
shadows.render_light(light.directional.position);
|
||||
|
||||
render_scene(shadows.depth_shader);
|
||||
|
||||
shadows.commit();
|
||||
}
|
||||
|
||||
void Scene::render() {
|
||||
auto& with_shader = reflect_on ? reflect_shader : shader;
|
||||
|
||||
if(!shadows_rendered) {
|
||||
shadows_rendered = true;
|
||||
render_shadows();
|
||||
}
|
||||
|
||||
shadows.use();
|
||||
|
||||
with_shader.use();
|
||||
with_shader.setInt("shadowMap", 0);
|
||||
// NOTE: in the shader is it viewPos or cameraPos?
|
||||
with_shader.setVec3("viewPos", camera.position);
|
||||
with_shader.setVec3("lightPos", light.directional.position);
|
||||
with_shader.setMat4("lightSpaceMatrix", shadows.light_space);
|
||||
|
||||
framebuffer.begin();
|
||||
|
||||
render_scene(with_shader);
|
||||
render_skybox();
|
||||
|
||||
framebuffer.commit();
|
||||
framebuffer.draw(screen);
|
||||
|
||||
if(debug_on) {
|
||||
shadows.render_debug();
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::cleanup() {
|
||||
shader.cleanup();
|
||||
reflect_shader.cleanup();
|
||||
skybox_shader.cleanup();
|
||||
}
|
||||
|
||||
void Scene::init_skybox() {
|
||||
glBindVertexArray(skyboxVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
|
||||
load_skybox_textures();
|
||||
}
|
||||
|
||||
// BUG: Use this to spawn in the constructor. Duh.
|
||||
void Scene::spawn(const std::string& name) {
|
||||
if(models.contains(name)) {
|
||||
components::Rotation rot{0.0, glm::vec3(1.0, 1.0, 1.0)};
|
||||
|
||||
things.emplace_back(
|
||||
name,
|
||||
models.at(name),
|
||||
materials.at("default"),
|
||||
camera.position,
|
||||
rot,
|
||||
1.0f);
|
||||
|
||||
// rebuild the shadows
|
||||
shadows_rendered = false;
|
||||
} else {
|
||||
dbc::log($F("No model named: {}", name));
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::load_skybox_textures() {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int nrChannels = 0;
|
||||
size_t face_i = 0;
|
||||
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_id);
|
||||
|
||||
for(const auto& face : skybox_faces) {
|
||||
unsigned char *data = stbi_load(face.c_str(), &width, &height, &nrChannels, 0);
|
||||
dbc::check(data != nullptr, $F("Failed to load skybox face {}", face));
|
||||
dbc::check(nrChannels == 3, $F("Skybox face {} must be RGB but you have {} channels", face, nrChannels));
|
||||
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face_i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
|
||||
face_i++;
|
||||
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
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);
|
||||
}
|
||||
|
||||
void Scene::render_skybox() {
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
skybox_shader.use();
|
||||
glm::mat4 view = glm::mat4(glm::mat3(camera.look_at()));
|
||||
skybox_shader.setMat4("view", view);
|
||||
skybox_shader.setMat4("projection", projection);
|
||||
|
||||
glBindVertexArray(skyboxVAO);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, skybox_texture_id);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
glBindVertexArray(0);
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
135
45-animation/src/scene.hpp
Normal file
135
45-animation/src/scene.hpp
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "shader.hpp"
|
||||
#include "model.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "camera.hpp"
|
||||
#include "components.hpp"
|
||||
#include "framebuffer.hpp"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "config.hpp"
|
||||
#include "shadows.hpp"
|
||||
#include "animation.hpp"
|
||||
#include "animator.hpp"
|
||||
|
||||
struct Animation;
|
||||
struct Animator;
|
||||
|
||||
struct Thing {
|
||||
std::string name;
|
||||
Model& model;
|
||||
Material& material;
|
||||
components::Position position;
|
||||
components::Rotation rotation;
|
||||
float scale;
|
||||
Animation* animation = nullptr;
|
||||
float distance = 0;
|
||||
};
|
||||
|
||||
struct Scene {
|
||||
Shader shader;
|
||||
Shader reflect_shader;
|
||||
Shader skybox_shader;
|
||||
std::vector<std::string> skybox_faces;
|
||||
Screen screen;
|
||||
FrameBuffer framebuffer;
|
||||
|
||||
std::unordered_map<std::string, components::Material> materials;
|
||||
Camera camera;
|
||||
components::Lighting light;
|
||||
std::unordered_map<std::string, Model> models;
|
||||
std::vector<Thing> things;
|
||||
float deltaTime = 0.0f;
|
||||
float lastFrame = 0.0f;
|
||||
unsigned int skyboxVAO = UINT_MAX;
|
||||
unsigned int skyboxVBO = UINT_MAX;
|
||||
unsigned int skybox_texture_id = UINT_MAX;
|
||||
glm::mat4 projection = glm::mat4(1.0f);
|
||||
bool reflect_on = false;
|
||||
Shadows shadows;
|
||||
Animator animator{nullptr};
|
||||
|
||||
float near_plane = 1.0f;
|
||||
float far_plane = 100.0f;
|
||||
|
||||
bool debug_on = false;
|
||||
|
||||
bool shadows_rendered = false;
|
||||
|
||||
Scene(components::Scene& config):
|
||||
shader{config.shader},
|
||||
reflect_shader{config.reflect_shader},
|
||||
skybox_shader{config.skybox_shader},
|
||||
skybox_faces{config.skybox_faces},
|
||||
screen{.shader={config.screen_shader}},
|
||||
materials{config.materials},
|
||||
camera{
|
||||
.position=config.camera.position,
|
||||
.front=config.camera.front,
|
||||
.up=config.camera.up,
|
||||
.direction=config.camera.direction,
|
||||
.movement_speed=config.camera.movement_speed,
|
||||
},
|
||||
light{config.light}
|
||||
{
|
||||
for(auto& [name, model] : config.models) {
|
||||
models.try_emplace(name, model.directory, model.model_path);
|
||||
}
|
||||
|
||||
for(auto& thing : config.things) {
|
||||
Animation *anim = nullptr;
|
||||
auto &model = models.at(thing.model);
|
||||
|
||||
if(thing.animate) {
|
||||
// LEAK! Just temporary until I can clean up the original code
|
||||
anim = new Animation(model.model_path, &model);
|
||||
// WARNING! Only works with one right now
|
||||
animator.PlayAnimation(anim);
|
||||
}
|
||||
|
||||
things.emplace_back(
|
||||
thing.model,
|
||||
model,
|
||||
materials.at(thing.material),
|
||||
thing.position,
|
||||
thing.rotation,
|
||||
thing.scale,
|
||||
anim);
|
||||
}
|
||||
|
||||
glGenVertexArrays(1, &skyboxVAO);
|
||||
glGenBuffers(1, &skyboxVBO);
|
||||
glGenTextures(1, &skybox_texture_id);
|
||||
|
||||
shader.use();
|
||||
shader.setInt("texture1", 0);
|
||||
shader.apply_lighting(light);
|
||||
|
||||
skybox_shader.use();
|
||||
skybox_shader.setInt("skybox", 0);
|
||||
|
||||
framebuffer.init();
|
||||
screen.init();
|
||||
shadows.init();
|
||||
init_skybox();
|
||||
}
|
||||
|
||||
void update();
|
||||
void draw_thing(Shader& with_shader, Thing& thing);
|
||||
void render();
|
||||
void cleanup();
|
||||
void init_skybox();
|
||||
void init_shadows();
|
||||
void init_debug();
|
||||
void load_skybox_textures();
|
||||
void render_skybox();
|
||||
void spawn(const std::string& name);
|
||||
void render_scene(Shader& with_shader);
|
||||
void render_shadows();
|
||||
};
|
||||
18
45-animation/src/screen.cpp
Normal file
18
45-animation/src/screen.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include "screen.hpp"
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
void Screen::init() {
|
||||
shader.use();
|
||||
shader.setInt("screenTexture", texture_id);
|
||||
|
||||
glGenVertexArrays(1, &target_VAO);
|
||||
glGenBuffers(1, &target_VBO);
|
||||
glBindVertexArray(target_VAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, target_VBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||
}
|
||||
23
45-animation/src/screen.hpp
Normal file
23
45-animation/src/screen.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "shader.hpp"
|
||||
|
||||
struct Screen {
|
||||
Shader shader;
|
||||
unsigned int target_VAO = 0;
|
||||
unsigned int target_VBO = 0;
|
||||
unsigned int texture_id = 10;
|
||||
|
||||
float vertices[24] = {
|
||||
// vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
|
||||
// positions // texCoords
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
|
||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f
|
||||
};
|
||||
|
||||
void init();
|
||||
};
|
||||
224
45-animation/src/shader.cpp
Normal file
224
45-animation/src/shader.cpp
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
#include "shader.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include "dbc.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
inline std::string read_file(const std::string& filename) {
|
||||
// load the file
|
||||
std::ifstream in_file{filename, std::ios::binary};
|
||||
|
||||
// get the size of the file
|
||||
std::stringstream in_str;
|
||||
in_str << in_file.rdbuf();
|
||||
return in_str.str();
|
||||
}
|
||||
|
||||
void check_error(const std::string& what, unsigned int thing, GLenum check_type) {
|
||||
int success = 0;
|
||||
char infoLog[512] = {0};
|
||||
|
||||
if(check_type == GL_LINK_STATUS) {
|
||||
glGetProgramiv(thing, check_type, &success);
|
||||
|
||||
if(!success) {
|
||||
glGetProgramInfoLog(thing, 512, NULL, infoLog);
|
||||
dbc::sentinel(std::format("ERROR: Program {} compile failed: {}", what, infoLog));
|
||||
}
|
||||
} else {
|
||||
glGetShaderiv(thing, check_type, &success);
|
||||
|
||||
if(!success) {
|
||||
glGetShaderInfoLog(thing, 512, NULL, infoLog);
|
||||
dbc::sentinel(std::format("ERROR: Shader {} compile failed: {}", what, infoLog));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Shader::load_shader(const std::string& filename, GLenum shader_type) {
|
||||
dbc::log($F("Loading shader {}", filename));
|
||||
dbc::check(fs::exists(filename),
|
||||
std::format("shader file {} does not exist", filename));
|
||||
|
||||
// create the shader
|
||||
std::string shader_code = read_file(filename);
|
||||
const char* shader_code_ptr = shader_code.c_str();
|
||||
|
||||
int shader_id = glCreateShader(shader_type);
|
||||
glShaderSource(shader_id, 1, &shader_code_ptr, NULL);
|
||||
glCompileShader(shader_id);
|
||||
|
||||
check_error(filename, shader_id, GL_COMPILE_STATUS);
|
||||
|
||||
// check compile error
|
||||
return shader_id;
|
||||
}
|
||||
|
||||
|
||||
Shader::Shader(components::Shader c) : config(c) {
|
||||
dbc::log($F("Loading shaders vert={}, frag={}, geom={}", config.vertex_path, config.frag_path, config.geometry_path));
|
||||
|
||||
unsigned int vertex = load_shader(config.vertex_path, GL_VERTEX_SHADER);
|
||||
unsigned int fragment = load_shader(config.frag_path, GL_FRAGMENT_SHADER);
|
||||
unsigned int geometry = UINT_MAX;
|
||||
|
||||
ID = glCreateProgram();
|
||||
dbc::log($F("Created shader program ID: {}", ID));
|
||||
glAttachShader(ID, vertex);
|
||||
glAttachShader(ID, fragment);
|
||||
|
||||
if(config.geometry_path != "") {
|
||||
geometry = load_shader(config.geometry_path, GL_GEOMETRY_SHADER);
|
||||
glAttachShader(ID, geometry);
|
||||
}
|
||||
|
||||
glLinkProgram(ID);
|
||||
check_error("link", ID, GL_LINK_STATUS);
|
||||
|
||||
glDeleteShader(vertex);
|
||||
glDeleteShader(fragment);
|
||||
|
||||
if(config.geometry_path != "") {
|
||||
glDeleteShader(geometry);
|
||||
}
|
||||
}
|
||||
|
||||
void Shader::use() const {
|
||||
glUseProgram(ID);
|
||||
}
|
||||
|
||||
void Shader::cleanup() {
|
||||
glDeleteProgram(ID);
|
||||
}
|
||||
|
||||
void Shader::setBool(const std::string &name, bool value) const {
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform1i(uniform, (int)value);
|
||||
}
|
||||
|
||||
void Shader::setInt(const std::string &name, int value) const {
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform1i(uniform, value);
|
||||
}
|
||||
|
||||
void Shader::setFloat(const std::string &name, float value) const {
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform1f(uniform, value);
|
||||
}
|
||||
|
||||
void Shader::setVec4(const std::string &name, float v1, float v2, float v3, float v4) const {
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform4f(uniform, v1, v2, v3, v4);
|
||||
}
|
||||
|
||||
void Shader::setVec4(const std::string &name, const glm::vec4& value) const
|
||||
{
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform4fv(uniform, 1, &value[0]);
|
||||
}
|
||||
|
||||
void Shader::setVec2(const std::string &name, const glm::vec2& value) const
|
||||
{
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform2fv(uniform, 1, &value[0]);
|
||||
}
|
||||
|
||||
void Shader::setVec3(const std::string &name, const glm::vec3& value) const
|
||||
{
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform3fv(uniform, 1, &value[0]);
|
||||
}
|
||||
|
||||
void Shader::setVec3(const std::string &name, float v1, float v2, float v3) const {
|
||||
auto uniform = glGetUniformLocation(ID, name.c_str());
|
||||
glUniform3f(uniform, v1, v2, v3);
|
||||
}
|
||||
|
||||
void Shader::setMat4(const std::string &name, const glm::mat4& mat) const {
|
||||
unsigned int loc = glGetUniformLocation(ID, name.c_str());
|
||||
glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(mat));
|
||||
}
|
||||
|
||||
void Shader::apply_material(const Material& material) {
|
||||
setVec3("material.ambient", material.ambient);
|
||||
setFloat("material.shininess", material.shininess);
|
||||
}
|
||||
|
||||
void Shader::apply_lighting(const Lighting& lighting) {
|
||||
setInt("pointLightCount", lighting.positioned.size());
|
||||
setInt("dirLightCount", 1); // always 1 directional light
|
||||
|
||||
apply_dir_light(lighting.directional, 0);
|
||||
|
||||
for(size_t i = 0; i < lighting.positioned.size(); i++) {
|
||||
apply_point_light(lighting.positioned[i], i);
|
||||
}
|
||||
|
||||
// set the first spotlight to the camera light if given
|
||||
size_t i = 0;
|
||||
|
||||
// camera is optional, so only add if used
|
||||
if(lighting.camera.on) {
|
||||
apply_spot_light(lighting.camera, 0);
|
||||
i++;
|
||||
}
|
||||
|
||||
// gross but that's fine for now
|
||||
for(; i < lighting.spot.size(); i++) {
|
||||
// need to be off by one because the camera is a spot light
|
||||
if(lighting.spot[i].on) {
|
||||
apply_spot_light(lighting.spot[i], i+1);
|
||||
}
|
||||
}
|
||||
|
||||
// need +1 for the camera spot light
|
||||
setInt("spotLightCount", (int)i);
|
||||
}
|
||||
|
||||
|
||||
void Shader::apply_dir_light(const Light& light, size_t index) {
|
||||
dbc::check(index < MAX_LIGHTS, "too many directional lights");
|
||||
|
||||
// nasty, this needs to go
|
||||
setVec3(std::format("dirLights[{}].direction", index), light.direction);
|
||||
setVec3(std::format("dirLights[{}].ambient", index), light.ambient);
|
||||
setVec3(std::format("dirLights[{}].diffuse", index), light.diffuse);
|
||||
setVec3(std::format("dirLights[{}].specular", index), light.specular);
|
||||
}
|
||||
|
||||
void Shader::apply_point_light(const Light& light, size_t index) {
|
||||
dbc::check(index < MAX_LIGHTS, "too many positioned lights");
|
||||
|
||||
// disgusting, crafting a string on every render?
|
||||
setVec3(std::format("pointLights[{}].position", index), light.position);
|
||||
setVec3(std::format("pointLights[{}].ambient", index), light.ambient);
|
||||
setVec3(std::format("pointLights[{}].diffuse", index), light.diffuse);
|
||||
setVec3(std::format("pointLights[{}].specular", index), light.specular);
|
||||
|
||||
setFloat(std::format("pointLights[{}].constant", index), light.constant);
|
||||
setFloat(std::format("pointLights[{}].linear", index), light.linear);
|
||||
setFloat(std::format("pointLights[{}].quadratic", index), light.quadratic);
|
||||
}
|
||||
|
||||
void Shader::apply_spot_light(const Light& light, size_t index) {
|
||||
dbc::check(index < MAX_LIGHTS, "too many spot lights");
|
||||
|
||||
// find a way to not make strings all the time
|
||||
setVec3(std::format("spotLights[{}].position", index), light.position);
|
||||
setVec3(std::format("spotLights[{}].direction", index), light.direction);
|
||||
setVec3(std::format("spotLights[{}].ambient", index), light.ambient);
|
||||
setVec3(std::format("spotLights[{}].diffuse", index), light.diffuse);
|
||||
setVec3(std::format("spotLights[{}].specular", index), light.specular);
|
||||
setFloat(std::format("spotLights[{}].constant", index), light.constant);
|
||||
setFloat(std::format("spotLights[{}].linear", index), light.linear);
|
||||
setFloat(std::format("spotLights[{}].quadratic", index), light.quadratic);
|
||||
setFloat(std::format("spotLights[{}].cut_off", index), glm::cos(glm::radians(light.cut_off)));
|
||||
setFloat(std::format("spotLights[{}].outer_cut_off", index), glm::cos(glm::radians(light.outer_cut_off)));
|
||||
}
|
||||
40
45-animation/src/shader.hpp
Normal file
40
45-animation/src/shader.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <string>
|
||||
#include <climits>
|
||||
#include <glm/glm.hpp>
|
||||
#include <vector>
|
||||
#include <format>
|
||||
#include "dbc.hpp"
|
||||
#include "components.hpp"
|
||||
|
||||
using components::Material, components::Lighting, components::Light;
|
||||
|
||||
class Shader
|
||||
{
|
||||
public:
|
||||
unsigned int ID = UINT_MAX;
|
||||
components::Shader config;
|
||||
|
||||
Shader(components::Shader config);
|
||||
|
||||
unsigned int load_shader(const std::string& filename, GLenum shader_type);
|
||||
void use() const;
|
||||
void setBool(const std::string &name, bool value) const;
|
||||
void setInt(const std::string &name, int value) const;
|
||||
void setFloat(const std::string &name, float value) const;
|
||||
void setVec4(const std::string &name, float v1, float v2, float v3, float v4) const;
|
||||
void setVec4(const std::string &name, const glm::vec4& value) const;
|
||||
void setVec3(const std::string &name, float v1, float v2, float v3) const;
|
||||
void setVec3(const std::string &name, const glm::vec3& value) const;
|
||||
void setVec2(const std::string &name, const glm::vec2& value) const;
|
||||
void setMat4(const std::string &name, const glm::mat4& what) const;
|
||||
void cleanup();
|
||||
|
||||
void apply_material(const Material& material);
|
||||
void apply_lighting(const Lighting& lighting);
|
||||
void apply_dir_light(const Light& light, size_t index);
|
||||
void apply_point_light(const Light& light, size_t index);
|
||||
void apply_spot_light(const Light& light, size_t index);
|
||||
};
|
||||
96
45-animation/src/shadows.cpp
Normal file
96
45-animation/src/shadows.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "shadows.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
void Shadows::init() {
|
||||
glGenFramebuffers(1, &target_fb);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, target_fb);
|
||||
glGenTextures(1, &depth_map);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, depth_map);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_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);
|
||||
// set to clamp border with a color of one to remove the frustrum culled part of the shadow
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
float border_color[4]{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_map, 0);
|
||||
glDrawBuffer(GL_NONE);
|
||||
glReadBuffer(GL_NONE);
|
||||
|
||||
dbc::check(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Frame buffer not complete.");
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
init_debug();
|
||||
}
|
||||
|
||||
|
||||
void Shadows::begin() {
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, target_fb);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glCullFace(GL_FRONT);
|
||||
}
|
||||
|
||||
void Shadows::commit() {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
|
||||
void Shadows::use() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_map);
|
||||
}
|
||||
|
||||
void Shadows::render_debug() {
|
||||
// set depth_map on TEXTURE0
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, depth_map);
|
||||
|
||||
depth_dbg_shader.use();
|
||||
depth_dbg_shader.setInt("shadowMap", 0);
|
||||
depth_dbg_shader.setFloat("near_plane", depth_near_plane);
|
||||
depth_dbg_shader.setFloat("far_plane", depth_far_plane);
|
||||
|
||||
glBindVertexArray(quadVAO);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
void Shadows::init_debug() {
|
||||
// setup plane VAO
|
||||
glGenVertexArrays(1, &quadVAO);
|
||||
glGenBuffers(1, &quadVBO);
|
||||
glBindVertexArray(quadVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
|
||||
}
|
||||
|
||||
void Shadows::render_light(glm::vec3& light_pos) {
|
||||
glm::mat4 proj = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, depth_near_plane, depth_far_plane);
|
||||
glm::mat4 view = glm::lookAt(light_pos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
light_space = proj * view;
|
||||
|
||||
depth_shader.use();
|
||||
depth_shader.setMat4("lightSpaceMatrix", light_space);
|
||||
depth_shader.setFloat("near_plane", depth_near_plane);
|
||||
depth_shader.setFloat("far_plane", depth_far_plane);
|
||||
}
|
||||
37
45-animation/src/shadows.hpp
Normal file
37
45-animation/src/shadows.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "shader.hpp"
|
||||
#include "screen.hpp"
|
||||
#include "components.hpp"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
struct Shadows {
|
||||
unsigned int target_fb = UINT_MAX;
|
||||
unsigned int depth_map = UINT_MAX;
|
||||
const unsigned int SHADOW_WIDTH = 1024 * 8, SHADOW_HEIGHT = 1024 * 8;
|
||||
float depth_near_plane = 1.0f;
|
||||
float depth_far_plane = 7.5f;
|
||||
float quadVertices[20] = {
|
||||
// positions // texture Coords
|
||||
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
|
||||
};
|
||||
|
||||
unsigned int quadVAO = UINT_MAX;
|
||||
unsigned int quadVBO = UINT_MAX;
|
||||
glm::mat4 light_space;
|
||||
|
||||
// shadow debug stuff
|
||||
Shader depth_shader{{"shaders/33-depth.vert.glsl", "shaders/33-depth.frag.glsl", ""}};
|
||||
Shader depth_dbg_shader{{"shaders/33-shadow_dbg.vert.glsl", "shaders/33-shadow_dbg.frag.glsl", ""}};
|
||||
|
||||
void init();
|
||||
void init_debug();
|
||||
void begin();
|
||||
void commit();
|
||||
void use();
|
||||
void render_debug();
|
||||
void render_light(glm::vec3& light_pos);
|
||||
};
|
||||
46
45-animation/src/skybox.hpp
Normal file
46
45-animation/src/skybox.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
float skyboxVertices[] = {
|
||||
// positions
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f
|
||||
};
|
||||
2
45-animation/src/stb_image.cpp
Normal file
2
45-animation/src/stb_image.cpp
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
7988
45-animation/src/stb_image.h
Normal file
7988
45-animation/src/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
114
45-animation/src/text.cpp
Normal file
114
45-animation/src/text.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include "text.hpp"
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include "config.hpp"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
void Text::render(const std::string& text, float x, float y, float scale, glm::vec3 color) {
|
||||
text_shader.use();
|
||||
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(SCR_WIDTH), 0.0f, static_cast<float>(SCR_HEIGHT));
|
||||
text_shader.setMat4("projection", projection);
|
||||
|
||||
glUniform3f(glGetUniformLocation(text_shader.ID, "textColor"), color.x, color.y, color.z);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindVertexArray(text_vao);
|
||||
|
||||
for(auto c : text) {
|
||||
auto& ch = characters.at(c);
|
||||
float xpos = x + ch.bearing.x * scale;
|
||||
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
|
||||
float w = ch.size.x * scale;
|
||||
float h = ch.size.y * scale;
|
||||
|
||||
float vertices[6][4] = {
|
||||
{ xpos, ypos + h, 0.0f, 0.0f },
|
||||
{ xpos, ypos, 0.0f, 1.0f },
|
||||
{ xpos + w, ypos, 1.0f, 1.0f },
|
||||
|
||||
{ xpos, ypos + h, 0.0f, 0.0f },
|
||||
{ xpos + w, ypos, 1.0f, 1.0f },
|
||||
{ xpos + w, ypos + h, 1.0f, 0.0f }
|
||||
};
|
||||
|
||||
// rander glyph texture over quad
|
||||
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
|
||||
|
||||
// update content of VBO memory
|
||||
glBindBuffer(GL_ARRAY_BUFFER, text_vbo);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
// render quad
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
|
||||
x += (ch.advance >> 6) * scale;
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Text::load_font() {
|
||||
FT_Library ft;
|
||||
int fail = FT_Init_FreeType(&ft);
|
||||
dbc::check(fail == 0, "FreeType failed.");
|
||||
|
||||
FT_Face face;
|
||||
fail = FT_New_Face(ft, font.c_str(), 0, &face);
|
||||
dbc::check(fail == 0, "Failed to load font.");
|
||||
|
||||
// load the font bitmaps
|
||||
FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
for(unsigned char c = 0; c < 255; c++) {
|
||||
fail = FT_Load_Char(face, c, FT_LOAD_RENDER);
|
||||
dbc::check(fail == 0, $F("Failed to load char {}", int(c)));
|
||||
|
||||
unsigned int texture = UINT_MAX;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
GL_RED,
|
||||
face->glyph->bitmap.width,
|
||||
face->glyph->bitmap.rows,
|
||||
0,
|
||||
GL_RED,
|
||||
GL_UNSIGNED_BYTE,
|
||||
face->glyph->bitmap.buffer);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
characters.try_emplace(c,
|
||||
texture,
|
||||
glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
|
||||
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
|
||||
static_cast<unsigned int>(face->glyph->advance.x));
|
||||
}
|
||||
|
||||
// unbind
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// cleanup
|
||||
FT_Done_Face(face);
|
||||
FT_Done_FreeType(ft);
|
||||
|
||||
// create teh vao/vbo
|
||||
glGenVertexArrays(1, &text_vao);
|
||||
glGenBuffers(1, &text_vbo);
|
||||
glBindVertexArray(text_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, text_vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
27
45-animation/src/text.hpp
Normal file
27
45-animation/src/text.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include "shader.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct Character {
|
||||
unsigned int texture_id;
|
||||
glm::ivec2 size;
|
||||
glm::ivec2 bearing;
|
||||
unsigned int advance;
|
||||
};
|
||||
|
||||
struct Text {
|
||||
std::string font{"assets/fonts/text.ttf"};
|
||||
Shader text_shader{{"shaders/32-text.vert.glsl", "shaders/32-text.frag.glsl", ""}};
|
||||
unsigned int text_vao = UINT_MAX;
|
||||
unsigned int text_vbo = UINT_MAX;
|
||||
std::unordered_map<unsigned char, Character> characters;
|
||||
|
||||
Text() {
|
||||
load_font();
|
||||
}
|
||||
|
||||
void render(const std::string& text, float x, float y, float scale, glm::vec3 color);
|
||||
void load_font();
|
||||
};
|
||||
11
45-animation/src/utils.cpp
Normal file
11
45-animation/src/utils.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include <fstream>
|
||||
#include "json.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace utils {
|
||||
components::Scene load_scene_config(const std::string& config_file) {
|
||||
std::ifstream f{"config.json"};
|
||||
auto j = json::parse(f);
|
||||
return j["scene"].template get<components::Scene>();
|
||||
}
|
||||
}
|
||||
9
45-animation/src/utils.hpp
Normal file
9
45-animation/src/utils.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "components.hpp"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace utils {
|
||||
components::Scene load_scene_config(const std::string& config_file);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue