Use assimp for loading 3d models

This commit is contained in:
SeanOMik 2022-09-21 22:52:06 -04:00
parent 96ac056a5d
commit 5a7e5cd10f
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
22 changed files with 863 additions and 302 deletions

178
.clang-format Normal file
View File

@ -0,0 +1,178 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

BIN
Box0.bin Normal file

Binary file not shown.

142
Box0.gltf Normal file
View File

@ -0,0 +1,142 @@
{
"asset": {
"generator": "COLLADA2GLTF",
"version": "2.0"
},
"scene": 0,
"scenes": [
{
"nodes": [
0
]
}
],
"nodes": [
{
"children": [
1
],
"matrix": [
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
-1.0,
0.0,
0.0,
1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0
]
},
{
"mesh": 0
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 2
},
"indices": 0,
"mode": 4,
"material": 0
}
],
"name": "Mesh"
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5123,
"count": 36,
"max": [
23
],
"min": [
0
],
"type": "SCALAR"
},
{
"bufferView": 1,
"byteOffset": 0,
"componentType": 5126,
"count": 24,
"max": [
1.0,
1.0,
1.0
],
"min": [
-1.0,
-1.0,
-1.0
],
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 288,
"componentType": 5126,
"count": 24,
"max": [
0.5,
0.5,
0.5
],
"min": [
-0.5,
-0.5,
-0.5
],
"type": "VEC3"
}
],
"materials": [
{
"pbrMetallicRoughness": {
"baseColorFactor": [
0.800000011920929,
0.0,
0.0,
1.0
],
"metallicFactor": 0.0
},
"name": "Red"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 576,
"byteLength": 72,
"target": 34963
},
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 576,
"byteStride": 12,
"target": 34962
}
],
"buffers": [
{
"byteLength": 648,
"uri": "Box0.bin"
}
]
}

16
CMake/GetTinygltf.cmake Normal file
View File

@ -0,0 +1,16 @@
set(TINYGLTF_HEADER_ONLY ON CACHE INTERNAL "" FORCE)
set(TINYGLTF_INSTALL OFF CACHE INTERNAL "" FORCE)
set(TINYGLTF_BUILD_LOADER_EXAMPLE OFF CACHE INTERNAL "" FORCE)
# Get the tinygltf header libraries.
include(FetchContent)
FetchContent_Declare(
tinygltf
GIT_REPOSITORY https://github.com/syoyo/tinygltf.git
GIT_TAG eec4c98862b7fb760b2fb70971d7b652e593af9f
)
FetchContent_MakeAvailable(tinygltf)
message("Downloaded tinygltf library to: ${tinygltf_SOURCE_DIR}")
#set(STB_INCLUDE_DIR ${stb_SOURCE_DIR})

View File

@ -3,7 +3,7 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(SimpleEngine)
include(cmrc/CMakeRC.cmake)
include (CMake/GetStbLibraries.cmake)
include(CMake/GetStbLibraries.cmake)
# Add some CMake options:
option(SIMPLE_ENGINE_BUILD_EXAMPLES "Build example projects" ON)
@ -20,16 +20,7 @@ find_package(GLEW REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(OpenGL REQUIRED)
# Get the stb header libraries.
#include(FetchContent)
#FetchContent_Declare(
# stb
# GIT_REPOSITORY https://github.com/nothings/stb.git
# GIT_TAG 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55
#)
#FetchContent_MakeAvailable(stb)
#message("Downloaded stb library to: ${stb_SOURCE_DIR}")
find_package(assimp REQUIRED)
# Link sources
file(GLOB_RECURSE source_list src/*.cpp)
@ -53,12 +44,14 @@ target_link_libraries(simpleengine PUBLIC GLEW::GLEW)
target_link_libraries(simpleengine PUBLIC glfw)
target_link_libraries(simpleengine PUBLIC ${GLM_LIBRARIES})
target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES})
target_link_libraries(simpleengine PUBLIC assimp)
target_link_libraries(simpleengine PRIVATE simpleengine_resources)
target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR})
# Include some dependencies' include directories
include_directories(${OPENGL_INCLUDE_DIR})
include_directories(${GLM_INCLUDE_DIRS})
target_include_directories(simpleengine PUBLIC ${OPENGL_INCLUDE_DIR})
target_include_directories(simpleengine PUBLIC ${GLM_INCLUDE_DIRS})
# Add examples as a target if the user has them enabled
if (SIMPLE_ENGINE_BUILD_EXAMPLES)

View File

@ -1,9 +1,11 @@
#include "simpleengine/camera.h"
#include "simpleengine/ecs/component/model_componenet.h"
#include "simpleengine/ecs/component/mesh_component.h"
#include <simpleengine/ecs/component/model_component.h>
#include "simpleengine/ecs/entity.h"
#include "simpleengine/entity_manager.h"
#include "simpleengine/gfx/light.h"
#include "simpleengine/gfx/material.h"
#include "simpleengine/gfx/mesh.h"
#include "simpleengine/gfx/model.h"
#include "simpleengine/gfx/renderer.h"
#include "simpleengine/gfx/texture.h"
@ -16,6 +18,7 @@
#include <simpleengine/game.h>
#include <simpleengine/vertex.h>
#include <simpleengine/gfx/shaders/core_3d_shader.h>
#include <simpleengine/gfx/model.h>
//#include <simpleengine/scene.h>
@ -166,13 +169,11 @@ int main(int argc, char *argv[]) {
// Create the entity and add the model component to it.
/* auto entity = std::make_shared<simpleengine::Entity>();
entity->add_component<se::ModelComponent>(cube_vertices, cube_indicies, material, true);
entity->add_component<se::MeshComponent>(cube_vertices, cube_indicies, material, true);
entity->translate(3.5f, 0.f, 0.f); */
//auto entity = std::make_shared<se::gfx::M>(game.get_window(), core_shader, white_texture, "examples/dev_testing/resources/dragon.obj");
auto entity = std::make_shared<simpleengine::Entity>();
se::gfx::Model model(material, "examples/dev_testing/resources/dragon.obj");
entity->add_component<se::ModelComponent>(model);
entity->add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
entity->translate(12.f, -4.f, 0.f);
// Create a renderer and submit the entity into it.

View File

@ -1,6 +1,6 @@
#pragma once
#include "../../gfx/model.h"
#include "../../gfx/mesh.h"
#include "../../event/event.h"
#include <iostream>

View File

@ -1,7 +1,7 @@
#pragma once
#include "component.h"
#include "../../gfx/model.h"
#include "../../gfx/mesh.h"
#include "../../gfx/material.h"
#include <iostream>
@ -12,16 +12,16 @@ namespace simpleengine {
* @brief A Model is a object that will be shown on the screen by a renderer.
*
*/
class ModelComponent : public simpleengine::Component {
class MeshComponent : public simpleengine::Component {
public:
gfx::Model model;
gfx::Mesh model;
//gfx::Material material;
ModelComponent(gfx::Model model) : model(model) {
MeshComponent(gfx::Mesh model) : model(model) {
}
ModelComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, gfx::Material material,
MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, gfx::Material material,
bool calculate_normals = false): model(vertices, indicies, material) {
if (calculate_normals) {
@ -29,7 +29,7 @@ namespace simpleengine {
}
}
ModelComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(),
MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(),
std::optional<gfx::Material> material = std::nullopt, bool calculate_normals = false) :
model(vertices, indicies, material) {
@ -39,7 +39,7 @@ namespace simpleengine {
}
virtual void update(const float& delta_time) override {
std::cout << "Model Component update" << std::endl;
}
};
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "component.h"
#include "../../gfx/model.h"
#include <iostream>
#include <vector>
namespace simpleengine {
/**
* @brief A Model is a object that will be shown on the screen by a renderer.
*
*/
class ModelComponent : public simpleengine::Component {
public:
gfx::Model model;
ModelComponent(gfx::Model model) : model(model) {
}
ModelComponent(std::string model_file_path) : model(model_file_path) {
}
virtual void update(const float& delta_time) override {
}
};
}

View File

@ -54,13 +54,11 @@ namespace simpleengine {
}
virtual void update(const float& delta_time) override {
std::cout << "Update entity" << std::endl;
for (auto& component : components) {
component->update(delta_time);
}
rotate_y(delta_time * 10);
rotate_y(delta_time * 10); // TODO: Remove
}
template<typename T>

View File

@ -15,7 +15,7 @@ namespace simpleengine::gfx {
float shine;
float reflectivity;
Material(Texture texture, float shine, float reflectivity, float specular_scalar, float ambient_scalar, float diffuse_scalar) :
Material(Texture texture, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) :
texture(texture), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar),
shine(shine), reflectivity(reflectivity) {

View File

@ -0,0 +1,55 @@
#pragma once
#include "shader.h"
#include "../event/event.h"
#include "vao.h"
#include "vbo.h"
#include "../vertex.h"
#include "../renderable.h"
#include "../transformable.h"
#include "material.h"
#include <optional>
#include <vector>
namespace simpleengine::gfx {
/**
* @brief A Mesh is a object that will be shown on the screen by a renderer.
*
*/
class Mesh : public simpleengine::Event, public simpleengine::Transformable {
public:
std::optional<Material> material;
std::vector<LitVertex> vertices;
std::vector<GLuint> indicies;
gfx::VBO ebo;
gfx::VBO vbo;
gfx::VAO vao;
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(), std::optional<Material> material = std::nullopt);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao);
Mesh(Material material, std::string filename);
Mesh(Material material, std::ifstream file_stream);
virtual void destroy() override;
virtual void update(const float& delta_time) override;
glm::vec3 compute_face_normal(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3);
/**
* @brief Calculate the normals of the model.
*
* @note This **will** overwrite the existing normals.
*
*/
void calculate_normals();
private:
void process_vertex(const std::vector<std::string>& vertex_data, const std::vector<glm::vec2>& in_textures,
const std::vector<glm::vec3>& in_normals, std::vector<GLuint>& out_indicies,
std::vector<glm::vec2>& out_textures, std::vector<glm::vec3>& out_normals);
};
}

View File

@ -1,55 +1,33 @@
#pragma once
#include "shader.h"
#include "../event/event.h"
#include "vao.h"
#include "vbo.h"
#include "../vertex.h"
#include "../renderable.h"
#include "../transformable.h"
#include "material.h"
#include "mesh.h"
#include "simpleengine/gfx/texture.h"
#include <optional>
#include <vector>
#include <assimp/material.h>
#include <assimp/mesh.h>
#include <assimp/scene.h>
//#include <assimp/mesh.h>
namespace simpleengine::gfx {
/**
* @brief A Model is a object that will be shown on the screen by a renderer.
* @brief A Model is a group of Meshes read from the 3D model file.
*
* The engine uses assimp, so all formats that it supports can be found here:
* https://github.com/assimp/assimp/blob/master/doc/Fileformats.md
*
*/
class Model : public simpleengine::Event, public simpleengine::Transformable {
class Model : public simpleengine::Transformable {
protected:
std::string model_directory; // May be needed
public:
std::optional<Material> material;
std::vector<LitVertex> vertices;
std::vector<GLuint> indicies;
std::vector<gfx::Mesh> meshes;
// Buffer objects
gfx::VBO ebo;
gfx::VBO vbo;
gfx::VAO vao;
Model(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material);
Model(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(), std::optional<Material> material = std::nullopt);
Model(Material material, std::string filename);
Model(Material material, std::ifstream file_stream);
virtual void destroy() override;
virtual void update(const float& delta_time) override;
glm::vec3 compute_face_normal(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3);
/**
* @brief Calculate the normals of the model.
*
* @note This **will** overwrite the existing normals.
*
*/
void calculate_normals();
private:
void process_vertex(const std::vector<std::string>& vertex_data, const std::vector<glm::vec2>& in_textures,
const std::vector<glm::vec3>& in_normals, std::vector<GLuint>& out_indicies,
std::vector<glm::vec2>& out_textures, std::vector<glm::vec3>& out_normals);
Model(std::string file_path);
void load_model(std::string path);
void process_node(aiNode* node, const aiScene* scene);
gfx::Mesh process_mesh(aiMesh* mesh, const aiScene* scene);
std::vector<Texture> load_material_textures(aiMaterial* material, aiTextureType* type, std::string type_name);
};
}

View File

@ -5,6 +5,7 @@
#include "texture.h"
#include "shader.h"
//#include "renderable.h"
#include "mesh.h"
#include "model.h"
#include <unordered_map>
@ -19,7 +20,7 @@ namespace simpleengine::gfx {
class RenderingModel {
public:
std::shared_ptr<simpleengine::Entity> entity;
std::unordered_map<uint32_t, gfx::Model&> component_models;
std::unordered_map<uint32_t, gfx::Mesh&> component_models;
RenderingModel(std::shared_ptr<simpleengine::Entity> entity) : entity(entity) {

View File

@ -19,10 +19,12 @@
namespace simpleengine::gfx {
class Texture {
private:
unsigned char* img_data;
unsigned char* img_data; // TODO Free this if its not used anymore
unsigned int texture_id;
unsigned int image_type;
unsigned int image_type_gl;
Texture() = default;
public:
/**
* @brief The type of the texture
@ -36,8 +38,6 @@ namespace simpleengine::gfx {
int height;
int width;
int channels;
float shine_damper = 1.f;
float reflectivity = 0.f;
Type type;
/**
@ -47,7 +47,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(const char* path, bool img_2d = true, bool mipmap = true);
Texture(const char* path, Type type = Type::TexT_DIFFUSE, bool img_2d = true, bool mipmap = true);
/**
* @brief Construct a new Texture object from the loaded file buffer.
@ -57,7 +57,7 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(const unsigned char *const buffer, int buffer_length, bool img_2d = true, bool mipmap = true);
Texture(const unsigned char *const buffer, int buffer_length, Type type = Type::TexT_DIFFUSE, bool img_2d = true, bool mipmap = true);
/**
* @brief Construct a new Texture object from the loaded file buffer.
@ -66,7 +66,9 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture(std::vector<unsigned char> buffer, bool img_2d = true, bool mipmap = true);
Texture(std::vector<unsigned char> buffer, Type type = Type::TexT_DIFFUSE, bool img_2d = true, bool mipmap = true);
static Texture white_texture();
void bind() const;
void unbind() const;

View File

@ -1,6 +1,6 @@
#pragma once
#include "gfx/model.h"
#include "gfx/mesh.h"
#include "entity.h"
#include "event/event.h"
#include "renderable.h"

View File

@ -37,7 +37,6 @@ void main() {
vec3 final_specular = calculate_specular(unit_normal, shine_damper, reflectivity);
fs_color = vec4(diffuse, 1.f) * texture(u_textures[id], vs_texcoord) + vec4(final_specular, 1.f);
//fs_color = texture(u_textures[1], vs_texcoord);
} else {
fs_color = vec4(vs_color, 1.f); // We don't add any reflectivity to solid colored vectors.
}

View File

@ -11,6 +11,7 @@ pkgs.mkShell {
glew
glfw
glm
assimp
(callPackage ./soil2.nix { })
];
}

167
src/gfx/mesh.cpp Normal file
View File

@ -0,0 +1,167 @@
#include "gfx/mesh.h"
#include <optional>
namespace simpleengine::gfx {
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material) :
material(std::make_optional(material)), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, std::optional<Material> material) :
material(material), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao) :
vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao) {
}
Mesh::Mesh(Material material, std::string filename) :
Mesh(material, std::ifstream(filename, std::ios::in | std::ios::binary)) {
}
std::vector<std::string> split_string(std::string str, const char delim) {
std::istringstream ss(str);
std::vector<std::string> tokens;
size_t pos = 0;
std::string token;
while ((pos = str.find(delim)) != std::string::npos) {
token = str.substr(0, pos);
tokens.push_back(token);
str.erase(0, pos + 1);
}
tokens.push_back(str);
return tokens;
}
Mesh::Mesh(Material material, std::ifstream file_stream) :
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()), material(material) {
if (!file_stream.is_open()) {
std::cerr << "File stream that was given to ObjModel::ObjModel is not open!" << std::endl;
throw std::runtime_error("Failed to open ObjModel model file");
}
// The vertices, texture coords, and normals that were read from the obj file
// these are not in a particular order.
std::vector<glm::vec3> obj_vertices;
std::vector<glm::vec2> obj_textures;
std::vector<glm::vec3> obj_normals;
// The texture coords and normals that have been sorted.
std::vector<glm::vec2> textures;
std::vector<glm::vec3> normals;
// Read the vertices, texture coords, and normals. Break when run into indices
std::string line;
while (std::getline(file_stream, line)) {
std::vector<std::string> line_tokens = split_string(line, ' ');
if (line_tokens.front() == "v") {
//glm::vec3 vertex(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
obj_vertices.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
} else if (line_tokens.front() == "vt") {
obj_textures.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]));
} else if (line_tokens.front() == "vn") {
obj_normals.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
} else if (line_tokens.front() == "f") {
auto size = obj_vertices.size();
textures.resize(size);
normals.resize(size);
break;
}
}
// Process the indicies. This will sort everything for storing inside of the Vertex list.
do {
if (!line.starts_with("f")) {
continue;
}
std::vector<std::string> line_tokens = split_string(line, ' ');
std::vector<std::string> vertex1 = split_string(line_tokens[1], '/');
std::vector<std::string> vertex2 = split_string(line_tokens[2], '/');
std::vector<std::string> vertex3 = split_string(line_tokens[3], '/');
process_vertex(vertex1, obj_textures, obj_normals, indicies, textures, normals);
process_vertex(vertex2, obj_textures, obj_normals, indicies, textures, normals);
process_vertex(vertex3, obj_textures, obj_normals, indicies, textures, normals);
} while (std::getline(file_stream, line));
file_stream.close();
const int texture_id = 0;
std::cout << "Texture ID: " << texture_id << std::endl;
// Insert everything into lit_vertices.
for (int i = 0; i < obj_vertices.size(); i++) {
vertices.emplace_back(simpleengine::Vectorf(obj_vertices.at(i)), glm::vec3(1.f), textures.at(i), normals.at(i), texture_id);
}
}
void Mesh::process_vertex(const std::vector<std::string>& vertex_data, const std::vector<glm::vec2>& in_textures,
const std::vector<glm::vec3>& in_normals, std::vector<GLuint>& out_indicies,
std::vector<glm::vec2>& out_textures, std::vector<glm::vec3>& out_normals) {
// Get the index the current vertex and put it in indicies
int currentVertexIndex = stoi(vertex_data[0]) - 1;
out_indicies.push_back(currentVertexIndex);
// Read texture coords
glm::vec2 current_tex = in_textures.at(stoi(vertex_data[1]) - 1);
current_tex.y = 1 - current_tex.y;
out_textures.at(currentVertexIndex) = current_tex;
// Read normals
glm::vec3 current_norm = in_normals.at(stoi(vertex_data[2]) - 1);
out_normals.at(currentVertexIndex) = current_norm;
}
void Mesh::destroy() {
this->ebo.destroy();
this->vbo.destroy();
this->vao.destroy();
}
void Mesh::update(const float& delta_time) {
this->rotate_y(1.f);
}
glm::vec3 Mesh::compute_face_normal(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3) {
// Uses p2 as a new origin for p1,p3
auto a = p3 - p2;
auto b = p1 - p2;
// Compute the cross product a X b to get the face normal
return glm::normalize(glm::cross(a, b));
}
void Mesh::calculate_normals() {
std::vector<glm::vec3> normals = std::vector<glm::vec3>(vertices.size());
for (int i = 0; i < indicies.size(); i+=3) {
const glm::vec3& a = vertices[indicies[i]].position;
const glm::vec3& b = vertices[indicies[i + 1]].position;
const glm::vec3& c = vertices[indicies[i + 2]].position;
glm::vec3 normal = compute_face_normal(a, b, c);
normals[indicies[i]] += normal;
normals[indicies[i + 1]] += normal;
normals[indicies[i + 2]] += normal;
}
for (int i = 0; i < normals.size(); i++) {
normals[i] = glm::normalize(normals[i]);
vertices[i].normal = normals[i];
}
}
}

View File

@ -1,163 +1,114 @@
#include "gfx/model.h"
#include "gfx/material.h"
#include "gfx/texture.h"
#include "vector.h"
#include <assimp/Importer.hpp>
#include <assimp/material.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <optional>
namespace simpleengine::gfx {
Model::Model(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material) :
material(std::make_optional(material)), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
Model::Model(std::string file_path) {
load_model(file_path);
}
Model::Model(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, std::optional<Material> material) :
material(material), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
void Model::load_model(std::string path) {
Assimp::Importer importer;
}
// assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs);
Model::Model(Material material,std::string filename) :
Model(material, std::ifstream(filename, std::ios::in | std::ios::binary)) {
}
std::vector<std::string> split_string(std::string str, const char delim) {
std::istringstream ss(str);
std::vector<std::string> tokens;
size_t pos = 0;
std::string token;
while ((pos = str.find(delim)) != std::string::npos) {
token = str.substr(0, pos);
tokens.push_back(token);
str.erase(0, pos + 1);
}
tokens.push_back(str);
return tokens;
}
Model::Model(Material material, std::ifstream file_stream) :
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()), material(material) {
if (!file_stream.is_open()) {
std::cerr << "File stream that was given to ObjModel::ObjModel is not open!" << std::endl;
throw std::runtime_error("Failed to open ObjModel model file");
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
return;
}
// The vertices, texture coords, and normals that were read from the obj file
// these are not in a particular order.
std::vector<glm::vec3> obj_vertices;
std::vector<glm::vec2> obj_textures;
std::vector<glm::vec3> obj_normals;
model_directory = path.substr(0, path.find_last_of('/'));
// The texture coords and normals that have been sorted.
std::vector<glm::vec2> textures;
std::vector<glm::vec3> normals;
process_node(scene->mRootNode, scene);
}
// Read the vertices, texture coords, and normals. Break when run into indices
std::string line;
while (std::getline(file_stream, line)) {
std::vector<std::string> line_tokens = split_string(line, ' ');
void Model::process_node(aiNode* node, const aiScene* scene) {
// process all the node's meshes (if any)
for (int i = 0; i < node->mNumMeshes; i++) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene));
}
if (line_tokens.front() == "v") {
//glm::vec3 vertex(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
obj_vertices.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
} else if (line_tokens.front() == "vt") {
obj_textures.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]));
} else if (line_tokens.front() == "vn") {
obj_normals.emplace_back(stof(line_tokens[1]), stof(line_tokens[2]), stof(line_tokens[3]));
} else if (line_tokens.front() == "f") {
auto size = obj_vertices.size();
textures.resize(size);
normals.resize(size);
break;
// then do the same for each of its children
for (int i = 0; i < node->mNumChildren; i++) {
process_node(node->mChildren[i], scene);
}
}
gfx::Mesh Model::process_mesh(aiMesh* mesh, const aiScene* scene) {
std::vector<LitVertex> vertices;
std::vector<unsigned int> indices;
std::vector<Texture> textures;
for(unsigned int i = 0; i < mesh->mNumVertices; i++) {
LitVertex vertex;
vertex.color = glm::vec3(1.f);
vertex.texture_id = 0;
simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.position = position;
glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
vertex.normal = normal;
if(mesh->mTextureCoords[0]) {
glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
vertex.tex_coord = tex_coord;
}
vertices.push_back(vertex);
}
// Process indices
for (int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++) {
indices.push_back(face.mIndices[j]);
}
}
// Process the indicies. This will sort everything for storing inside of the Vertex list.
do {
if (!line.starts_with("f")) {
continue;
}
std::vector<std::string> line_tokens = split_string(line, ' ');
std::vector<std::string> vertex1 = split_string(line_tokens[1], '/');
std::vector<std::string> vertex2 = split_string(line_tokens[2], '/');
std::vector<std::string> vertex3 = split_string(line_tokens[3], '/');
std::optional<gfx::Material> op_mat;
// TODO: Process material
if(mesh->mMaterialIndex >= 0) {
std::cout << "TODO: Process model materials!" << std::endl;
/* aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuseMaps = load_material_textures(material,
aiTextureType_DIFFUSE, "texture_diffuse");
textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
process_vertex(vertex1, obj_textures, obj_normals, indicies, textures, normals);
process_vertex(vertex2, obj_textures, obj_normals, indicies, textures, normals);
process_vertex(vertex3, obj_textures, obj_normals, indicies, textures, normals);
} while (std::getline(file_stream, line));
file_stream.close();
const int texture_id = 0;
std::cout << "Texture ID: " << texture_id << std::endl;
// Insert everything into lit_vertices.
for (int i = 0; i < obj_vertices.size(); i++) {
vertices.emplace_back(simpleengine::Vectorf(obj_vertices.at(i)), glm::vec3(1.f), textures.at(i), normals.at(i), texture_id);
}
}
void Model::process_vertex(const std::vector<std::string>& vertex_data, const std::vector<glm::vec2>& in_textures,
const std::vector<glm::vec3>& in_normals, std::vector<GLuint>& out_indicies,
std::vector<glm::vec2>& out_textures, std::vector<glm::vec3>& out_normals) {
// Get the index the current vertex and put it in indicies
int currentVertexIndex = stoi(vertex_data[0]) - 1;
out_indicies.push_back(currentVertexIndex);
// Read texture coords
glm::vec2 current_tex = in_textures.at(stoi(vertex_data[1]) - 1);
current_tex.y = 1 - current_tex.y;
out_textures.at(currentVertexIndex) = current_tex;
// Read normals
glm::vec3 current_norm = in_normals.at(stoi(vertex_data[2]) - 1);
out_normals.at(currentVertexIndex) = current_norm;
}
void Model::destroy() {
this->ebo.destroy();
this->vbo.destroy();
this->vao.destroy();
}
void Model::update(const float& delta_time) {
this->rotate_y(1.f);
}
glm::vec3 Model::compute_face_normal(const glm::vec3& p1, const glm::vec3& p2, const glm::vec3& p3) {
// Uses p2 as a new origin for p1,p3
auto a = p3 - p2;
auto b = p1 - p2;
// Compute the cross product a X b to get the face normal
return glm::normalize(glm::cross(a, b));
}
void Model::calculate_normals() {
std::vector<glm::vec3> normals = std::vector<glm::vec3>(vertices.size());
for (int i = 0; i < indicies.size(); i+=3) {
const glm::vec3& a = vertices[indicies[i]].position;
const glm::vec3& b = vertices[indicies[i + 1]].position;
const glm::vec3& c = vertices[indicies[i + 2]].position;
glm::vec3 normal = compute_face_normal(a, b, c);
normals[indicies[i]] += normal;
normals[indicies[i + 1]] += normal;
normals[indicies[i + 2]] += normal;
std::vector<Texture> specularMaps = load_material_textures(material,
aiTextureType_SPECULAR, "texture_specular");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); */
}
for (int i = 0; i < normals.size(); i++) {
normals[i] = glm::normalize(normals[i]);
// TODO: After we start processing materials, this can be put in the else statement as a fallback.
vertices[i].normal = normals[i];
}
auto white_texture = gfx::Texture::white_texture();
//gfx::Material mat(white_texture);
//gfx::Texture white_texture("examples/dev_testing/resources/white_texture.png");
gfx::Material mat(white_texture, 1.f, 0.f, 0.f, 0.f, 0.f);
op_mat = std::optional<gfx::Material>(mat);
//return Mesh(vertices, indices, textures);
return Mesh(vertices, indices, op_mat);
}
}
std::vector<Texture> Model::load_material_textures(aiMaterial* material, aiTextureType* type, std::string type_name) {
}
} // namespace simpleengine::gfx

View File

@ -1,52 +1,17 @@
#include "gfx/renderer.h"
#include "ecs/component/component.h"
#include "ecs/entity.h"
#include "gfx/model.h"
#include "gfx/mesh.h"
#include "gfx/vao.h"
#include "renderable.h"
#include "ecs/component/model_componenet.h"
#include "ecs/component/mesh_component.h"
#include "ecs/component/model_component.h"
#include <algorithm>
namespace simpleengine::gfx {
void Renderer::RenderingModel::update_buffers() {
if (std::shared_ptr<ModelComponent> comp = entity->get_component<simpleengine::ModelComponent>()) {
auto iter = component_models.find(comp->get_handle());
if (iter == component_models.end()) {
std::cout << "Enabling buffer attributes for ModelComponent (" << comp->get_handle() << ")..." << std::endl;
//iter->second = comp->model;
gfx::Model& model = comp->model;
gfx::VBO& vbo = model.vbo;
gfx::VBO& ebo = model.ebo;
gfx::VAO& vao = model.vao;
vao.bind();
vbo.buffer(model.vertices.data(), 0, sizeof(LitVertex) * model.vertices.size());
if (!model.indicies.empty()) {
ebo.buffer(model.indicies.data(), 0, model.indicies.size() * sizeof(GLuint));
}
// Enable VAO attributes
vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false);
vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false);
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
component_models.emplace(comp->get_handle(), model);
std::cout << "Enabled all buffer attributes for ModelComponent" << std::endl;
} else {
std::cout << "Already exists" << std::endl;
}
}
}
void create_mesh_buffers(std::shared_ptr<simpleengine::Component> comp, simpleengine::gfx::Mesh& mesh);
void Renderer::RenderingModel::destroy_buffers() {
std::cout << "Destroying entity models..." << std::endl;
@ -119,13 +84,13 @@ namespace simpleengine::gfx {
shader.use();
for (auto& [handle, rendering] : rendering_models) {
if (rendering.component_models.size() > 0) {
if (rendering.component_models.size() >= 0) {
std::shared_ptr<Entity>& entity = rendering.entity;
shader.set_uniform_matrix_4f("transform_matrix", entity->transform_matrix, false);
for (const auto& pair : rendering.component_models) {
Model& model = pair.second;
Mesh& model = pair.second;
std::optional<Material>& material = model.material;
shader.set_uniform_int("u_textures", 0, false);
@ -151,4 +116,64 @@ namespace simpleengine::gfx {
shader.unuse();
}
void Renderer::RenderingModel::update_buffers() {
if (entity->has_component<simpleengine::MeshComponent>()) {
std::shared_ptr<MeshComponent> comp = entity->get_component<simpleengine::MeshComponent>();
auto iter = component_models.find(comp->get_handle());
if (iter == component_models.end()) {
std::cout << "Enabling buffer attributes for MeshComponent (" << comp->get_handle() << ")..." << std::endl;
//iter->second = comp->model;
gfx::Mesh& mesh = comp->model;
create_mesh_buffers(comp, mesh);
component_models.emplace(comp->get_handle(), mesh);
std::cout << "Enabled all buffer attributes for MeshComponent" << std::endl;
} else {
std::cout << "Already exists" << std::endl;
}
} else if (entity->has_component<simpleengine::ModelComponent>()) {
std::shared_ptr<ModelComponent> comp = entity->get_component<simpleengine::ModelComponent>();
auto iter = component_models.find(comp->get_handle());
if (iter == component_models.end()) {
std::cout << "Enabling buffer attributes for ModelComponent (" << comp->get_handle() << ")..." << std::endl;
// Store all the model's meshes
for (auto& mesh : comp->model.meshes) {
create_mesh_buffers(comp, mesh);
component_models.emplace(comp->get_handle(), mesh);
}
std::cout << "Enabled all buffer attributes for ModelComponent" << std::endl;
} else {
std::cout << "Already exists" << std::endl;
}
}
}
void create_mesh_buffers(std::shared_ptr<Component> comp, gfx::Mesh& mesh) {
gfx::VBO& vbo = mesh.vbo;
gfx::VBO& ebo = mesh.ebo;
gfx::VAO& vao = mesh.vao;
vao.bind();
vbo.buffer(mesh.vertices.data(), 0, sizeof(LitVertex) * mesh.vertices.size());
if (!mesh.indicies.empty()) {
ebo.buffer(mesh.indicies.data(), 0, mesh.indicies.size() * sizeof(GLuint));
}
// Enable VAO attributes
vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false);
vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false);
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
}

View File

@ -4,16 +4,18 @@
#include <stb_image.h>
namespace simpleengine::gfx {
Texture::Texture(const char* path, bool img_2d, bool mipmap) {
image_type = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
Texture::Texture(const char* path, Type type, bool img_2d, bool mipmap): type(type) {
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id);
bind();
glTexParameteri(image_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(image_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(image_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(image_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int linear_param = mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
glTexParameteri(image_type_gl, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(image_type_gl, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
// Read 4 channels (RGBA)
img_data = stbi_load(path, &width, &height, &channels, 4);
@ -24,33 +26,27 @@ namespace simpleengine::gfx {
}
std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl;
glTexImage2D(image_type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
if (mipmap) {
glGenerateMipmap(image_type);
glGenerateMipmap(image_type_gl);
}
unbind();
}
/**
* @brief Construct a new Texture object from the loaded file buffer.
*
* @param buffer The bytes of the loaded file.
* @param buffer_length The length of the buffer.
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture::Texture(const unsigned char *const buffer, int buffer_length, bool img_2d, bool mipmap) {
image_type = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
Texture::Texture(const unsigned char *const buffer, int buffer_length, Type type, bool img_2d, bool mipmap): type(type) {
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id);
bind();
glTexParameteri(image_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(image_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(image_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(image_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int linear_param = mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
glTexParameteri(image_type_gl, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(image_type_gl, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
// Read 4 channels (RGBA)
img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4);
@ -61,33 +57,61 @@ namespace simpleengine::gfx {
}
std::cout << "Loaded image with a width of " << width << "px, a height of " << height << "px and " << channels << " channels" << std::endl;
glTexImage2D(image_type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
glTexImage2D(image_type_gl, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data);
if (mipmap) {
glGenerateMipmap(image_type);
glGenerateMipmap(image_type_gl);
}
unbind();
}
/**
* @brief Construct a new Texture object from the loaded file buffer.
*
* @param buffer The bytes of the loaded file.
* @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture.
*/
Texture::Texture(std::vector<unsigned char> buffer, bool img_2d, bool mipmap) :
Texture(buffer.data(), buffer.size(), img_2d, mipmap) {
Texture::Texture(std::vector<unsigned char> buffer, Type type, bool img_2d, bool mipmap) :
Texture(buffer.data(), buffer.size(), type, img_2d, mipmap) {
}
Texture Texture::white_texture() {
// Create the texture
int width = 128, height = 128;
int size = width * height * sizeof(unsigned char) * 4;
unsigned char* data = (unsigned char*) malloc(size);
for(int i = 0; i < size; i++) {
data[i] = 255;
}
Texture texture;
texture.image_type_gl = GL_TEXTURE_2D;
texture.width = width;
texture.height = height;
texture.channels = 4;
texture.type = Texture::Type::TexT_DIFFUSE;
texture.img_data = data;
glGenTextures(1, &texture.texture_id);
texture.bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.img_data);
texture.unbind();
return texture;
}
void Texture::bind() const {
glBindTexture(image_type, texture_id);
glBindTexture(image_type_gl, texture_id);
}
void Texture::unbind() const {
glBindTexture(image_type, 0);
glBindTexture(image_type_gl, 0);
}
unsigned int Texture::get_texture_id() const {