tutorial 04
This commit is contained in:
+2
-4
@@ -1,7 +1,5 @@
|
|||||||
------------ Tyra's v2.0 roadmap to publish on GitHub ------------
|
------------ Tyra's v2.0 roadmap to publish on GitHub ------------
|
||||||
|
|
||||||
- [Tutorials] 4. Mesh rendering - explain staticPipeline, render .obj file via Mesh class, explain that this is high-level
|
|
||||||
abstract 3D object class, and what it have (animation etc)
|
|
||||||
|
|
||||||
- [Tutorials] 5. Manual rendering via staticPipeline - via Render3DBag (core.render()), render simple poly,
|
- [Tutorials] 5. Manual rendering via staticPipeline - via Render3DBag (core.render()), render simple poly,
|
||||||
explain that this is lower-level abstract rendering, it have the all the features as Mesh rendering,
|
explain that this is lower-level abstract rendering, it have the all the features as Mesh rendering,
|
||||||
@@ -13,7 +11,9 @@ because Mesh rendering uses only core.render()
|
|||||||
|
|
||||||
- [Tutorials] 8. Render skybox, and show that stdPipOptions.noClipChecks must be set to false, to turn on clip algorithms (prevent glitches)
|
- [Tutorials] 8. Render skybox, and show that stdPipOptions.noClipChecks must be set to false, to turn on clip algorithms (prevent glitches)
|
||||||
|
|
||||||
|
- audio
|
||||||
- own game
|
- own game
|
||||||
|
- how to cook audio,textures,obj
|
||||||
- usb
|
- usb
|
||||||
|
|
||||||
- [General] Check all left TODOs -> Github issues
|
- [General] Check all left TODOs -> Github issues
|
||||||
@@ -25,14 +25,12 @@ because Mesh rendering uses only core.render()
|
|||||||
- [General] smart pointers and std::array instead of raw pointers
|
- [General] smart pointers and std::array instead of raw pointers
|
||||||
- [renderer] fog
|
- [renderer] fog
|
||||||
- [General] CI in Github via docker image
|
- [General] CI in Github via docker image
|
||||||
- [renderer] bbox parts optimization
|
|
||||||
- [md2] refactor
|
- [md2] refactor
|
||||||
- [2D] Add zbuffer support (z-index)
|
- [2D] Add zbuffer support (z-index)
|
||||||
- [3DUtility] Add zbuffer support (z-index)
|
- [3DUtility] Add zbuffer support (z-index)
|
||||||
- [2D] Fixed font support
|
- [2D] Fixed font support
|
||||||
- [File] Async file loading
|
- [File] Async file loading
|
||||||
- [obj] Add multicolor support to obj
|
- [obj] Add multicolor support to obj
|
||||||
- [Obj] Improve obj importing for multiple usemtl with same names
|
|
||||||
- [tyrdat] Custom 3D data file with precalculated bboxes with support of async loading
|
- [tyrdat] Custom 3D data file with precalculated bboxes with support of async loading
|
||||||
- [Renderer] Interlacing, a potem resolution 640x448
|
- [Renderer] Interlacing, a potem resolution 640x448
|
||||||
- [General] Add cpp lint checker in GitHub
|
- [General] Add cpp lint checker in GitHub
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "../../loader.hpp"
|
#include "../../loader.hpp"
|
||||||
#include "../builder/mesh_builder_data.hpp"
|
#include "../builder/mesh_builder_data.hpp"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
@@ -24,13 +25,12 @@ struct MD2LoaderOptions {
|
|||||||
/** Class responsible for loading & parsing Quake's II ".md2" 3D files */
|
/** Class responsible for loading & parsing Quake's II ".md2" 3D files */
|
||||||
class MD2Loader : public Loader {
|
class MD2Loader : public Loader {
|
||||||
public:
|
public:
|
||||||
MD2Loader();
|
static std::unique_ptr<MeshBuilderData> load(const char* fullpath);
|
||||||
~MD2Loader();
|
static std::unique_ptr<MeshBuilderData> load(const char* fullpath,
|
||||||
|
MD2LoaderOptions options);
|
||||||
MeshBuilderData* load(const char* fullpath);
|
static std::unique_ptr<MeshBuilderData> load(const std::string& fullpath);
|
||||||
MeshBuilderData* load(const char* fullpath, MD2LoaderOptions options);
|
static std::unique_ptr<MeshBuilderData> load(const std::string& fullpath,
|
||||||
MeshBuilderData* load(const std::string& fullpath);
|
MD2LoaderOptions options);
|
||||||
MeshBuilderData* load(const std::string& fullpath, MD2LoaderOptions options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tyra
|
} // namespace Tyra
|
||||||
|
|||||||
@@ -38,32 +38,30 @@ struct MaterialVertexCount {
|
|||||||
/** Class responsible for loading & parsing obj files */
|
/** Class responsible for loading & parsing obj files */
|
||||||
class ObjLoader : public Loader {
|
class ObjLoader : public Loader {
|
||||||
public:
|
public:
|
||||||
ObjLoader();
|
static std::unique_ptr<MeshBuilderData> load(const char* fullpath);
|
||||||
~ObjLoader();
|
static std::unique_ptr<MeshBuilderData> load(const char* fullpath,
|
||||||
|
const ObjLoaderOptions& options);
|
||||||
std::unique_ptr<MeshBuilderData> load(const char* fullpath);
|
static std::unique_ptr<MeshBuilderData> load(const std::string& fullpath);
|
||||||
std::unique_ptr<MeshBuilderData> load(const char* fullpath,
|
static std::unique_ptr<MeshBuilderData> load(const std::string& fullpath,
|
||||||
const ObjLoaderOptions& options);
|
const ObjLoaderOptions& options);
|
||||||
std::unique_ptr<MeshBuilderData> load(const std::string& fullpath);
|
|
||||||
std::unique_ptr<MeshBuilderData> load(const std::string& fullpath,
|
|
||||||
const ObjLoaderOptions& options);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addOutputMaterialsAndFrames(
|
static void addOutputMaterialsAndFrames(
|
||||||
MeshBuilderData* output, const tinyobj::attrib_t& attrib,
|
MeshBuilderData* output, const tinyobj::attrib_t& attrib,
|
||||||
const std::vector<tinyobj::shape_t>& shapes,
|
const std::vector<tinyobj::shape_t>& shapes,
|
||||||
const std::vector<tinyobj::material_t>& materials, const u16& framesCount,
|
const std::vector<tinyobj::material_t>& materials, const u16& framesCount,
|
||||||
std::vector<MaterialVertexCount>& materialVertexCounts);
|
std::vector<MaterialVertexCount>& materialVertexCounts);
|
||||||
|
|
||||||
std::vector<MaterialVertexCount> scan(
|
static std::vector<MaterialVertexCount> scan(
|
||||||
const std::vector<tinyobj::shape_t>& shapes,
|
const std::vector<tinyobj::shape_t>& shapes,
|
||||||
const std::vector<tinyobj::material_t>& materials);
|
const std::vector<tinyobj::material_t>& materials);
|
||||||
|
|
||||||
void importFrame(MeshBuilderData* output, const tinyobj::attrib_t& attrib,
|
static void importFrame(MeshBuilderData* output,
|
||||||
const std::vector<tinyobj::shape_t>& shapes,
|
const tinyobj::attrib_t& attrib,
|
||||||
const std::vector<tinyobj::material_t>& materials,
|
const std::vector<tinyobj::shape_t>& shapes,
|
||||||
const u16& frameIndex, const float& scale,
|
const std::vector<tinyobj::material_t>& materials,
|
||||||
const bool& invertY, const u16& count);
|
const u16& frameIndex, const float& scale,
|
||||||
|
const bool& invertY, const u16& count);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tyra
|
} // namespace Tyra
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ namespace Tyra {
|
|||||||
|
|
||||||
class Loader {
|
class Loader {
|
||||||
protected:
|
protected:
|
||||||
std::string getFilenameFromPath(const std::string& path);
|
static std::string getFilenameFromPath(const std::string& path);
|
||||||
std::string getPathFromFilename(const std::string& path);
|
static std::string getPathFromFilename(const std::string& path);
|
||||||
std::string getFilenameWithoutExtension(const std::string& filename);
|
static std::string getFilenameWithoutExtension(const std::string& filename);
|
||||||
std::string getExtensionOfFilename(const std::string& filename);
|
static std::string getExtensionOfFilename(const std::string& filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tyra
|
} // namespace Tyra
|
||||||
|
|||||||
@@ -39,13 +39,16 @@ class DynamicPipeline : public Renderer3DPipeline {
|
|||||||
|
|
||||||
void onUse();
|
void onUse();
|
||||||
|
|
||||||
|
void onFrameEnd();
|
||||||
|
|
||||||
void onUseEnd();
|
void onUseEnd();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render dynamic model.
|
* Render dynamic model.
|
||||||
* This render() method is a bridge to core.render() method.
|
* This render() method is a bridge to core.render() method.
|
||||||
*/
|
*/
|
||||||
void render(const DynamicMesh& mesh, const DynPipOptions* options = nullptr);
|
void render(const DynamicMesh* mesh);
|
||||||
|
void render(const DynamicMesh* mesh, const DynPipOptions& options);
|
||||||
void render(const DynamicMesh* mesh, const DynPipOptions* options = nullptr);
|
void render(const DynamicMesh* mesh, const DynPipOptions* options = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class MinecraftPipeline : public Renderer3DPipeline {
|
|||||||
|
|
||||||
void onUse();
|
void onUse();
|
||||||
void onUseEnd();
|
void onUseEnd();
|
||||||
|
void onFrameEnd();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Render voxels
|
* @brief Render voxels
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class Renderer3DPipeline {
|
|||||||
|
|
||||||
virtual void setRenderer(RendererCore* core) = 0;
|
virtual void setRenderer(RendererCore* core) = 0;
|
||||||
virtual void onUse() = 0;
|
virtual void onUse() = 0;
|
||||||
|
virtual void onFrameEnd() = 0;
|
||||||
virtual void onUseEnd() = 0;
|
virtual void onUseEnd() = 0;
|
||||||
std::function<void(Renderer3DPipeline*)> onDestroy;
|
std::function<void(Renderer3DPipeline*)> onDestroy;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class StaPipCore {
|
|||||||
void init(RendererCore* t_core);
|
void init(RendererCore* t_core);
|
||||||
|
|
||||||
/** Render 3D via "bags" */
|
/** Render 3D via "bags" */
|
||||||
void render(StaPipBag* bag, StaPipBagPackagesBBox* bbox = nullptr);
|
void render(StaPipBag* bag, StaPipBagPackagesBBox* bbox,
|
||||||
|
const u32& maxVertCount);
|
||||||
|
|
||||||
/** Get max vert count of VU1 qbuffer (for optimizations) */
|
/** Get max vert count of VU1 qbuffer (for optimizations) */
|
||||||
u32 getMaxVertCountByParams(const bool& isSingleColor,
|
u32 getMaxVertCountByParams(const bool& isSingleColor,
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
# _____ ____ ___
|
||||||
|
# | \/ ____| |___|
|
||||||
|
# | | | \ | |
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
# Copyright 2022, tyra - https://github.com/h4570/tyra
|
||||||
|
# Licensed under Apache License 2.0
|
||||||
|
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tamtypes.h>
|
||||||
|
#include "core/bag/packaging/stapip_bag_packages_bbox.hpp"
|
||||||
|
#include "renderer/3d/mesh/mesh.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Tyra {
|
||||||
|
|
||||||
|
struct StapipBagBBoxesCacheItem {
|
||||||
|
u32 vu1MaxVertCount;
|
||||||
|
u32 frameId;
|
||||||
|
std::unique_ptr<StaPipBagPackagesBBox> bboxes;
|
||||||
|
int framesLeftToDestroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StapipBagBBoxesCacher {
|
||||||
|
public:
|
||||||
|
StapipBagBBoxesCacher();
|
||||||
|
~StapipBagBBoxesCacher();
|
||||||
|
|
||||||
|
const int cacheFramesCount = 50;
|
||||||
|
const int cacheSecondsCount = 5;
|
||||||
|
|
||||||
|
void onFrameEnd();
|
||||||
|
|
||||||
|
StaPipBagPackagesBBox* getBBoxesByMesh(const MeshMaterialFrame* frame,
|
||||||
|
const u32& maxVertCount);
|
||||||
|
|
||||||
|
private:
|
||||||
|
StapipBagBBoxesCacheItem* getCache(const u32& maxVertCount,
|
||||||
|
const u32& frameId);
|
||||||
|
|
||||||
|
std::vector<StapipBagBBoxesCacheItem> storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Tyra
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "renderer/3d/mesh/static/static_mesh.hpp"
|
#include "renderer/3d/mesh/static/static_mesh.hpp"
|
||||||
#include "./core/stapip_core.hpp"
|
#include "./core/stapip_core.hpp"
|
||||||
#include "./stapip_options.hpp"
|
#include "./stapip_options.hpp"
|
||||||
|
#include "./stapip_bag_bboxes_cacher.hpp"
|
||||||
|
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
@@ -38,17 +39,19 @@ class StaticPipeline : public Renderer3DPipeline {
|
|||||||
void setRenderer(RendererCore* core);
|
void setRenderer(RendererCore* core);
|
||||||
|
|
||||||
void onUse();
|
void onUse();
|
||||||
|
|
||||||
void onUseEnd();
|
void onUseEnd();
|
||||||
|
void onFrameEnd();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render static model
|
* Render static model
|
||||||
* This render() method is a bridge to core.render() method.
|
* This render() method is a bridge to core.render() method.
|
||||||
*/
|
*/
|
||||||
void render(const StaticMesh& mesh, const StaPipOptions* options = nullptr);
|
void render(const StaticMesh* mesh);
|
||||||
|
void render(const StaticMesh* mesh, const StaPipOptions& options);
|
||||||
void render(const StaticMesh* mesh, const StaPipOptions* options = nullptr);
|
void render(const StaticMesh* mesh, const StaPipOptions* options = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
StapipBagBBoxesCacher cacher;
|
||||||
RendererCore* rendererCore;
|
RendererCore* rendererCore;
|
||||||
Vec4* colorsCache;
|
Vec4* colorsCache;
|
||||||
|
|
||||||
@@ -65,6 +68,7 @@ class StaticPipeline : public Renderer3DPipeline {
|
|||||||
const MeshMaterialFrame* materialFrame);
|
const MeshMaterialFrame* materialFrame);
|
||||||
|
|
||||||
StaPipLightingBag* getLightingBag(const MeshMaterialFrame* materialFrame,
|
StaPipLightingBag* getLightingBag(const MeshMaterialFrame* materialFrame,
|
||||||
|
PipelineDirLightsBag* dirLightsBag,
|
||||||
M4x4* model,
|
M4x4* model,
|
||||||
const StaPipOptions* options) const;
|
const StaPipOptions* options) const;
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,16 @@ class Renderer3D {
|
|||||||
|
|
||||||
Renderer3DUtility utility;
|
Renderer3DUtility utility;
|
||||||
|
|
||||||
void init(RendererCore* core);
|
|
||||||
|
|
||||||
/** Deinitialize previous pipeline and initialize new pipeline */
|
/** Deinitialize previous pipeline and initialize new pipeline */
|
||||||
void usePipeline(Renderer3DPipeline* pipeline);
|
void usePipeline(Renderer3DPipeline* pipeline);
|
||||||
void usePipeline(Renderer3DPipeline& pipeline);
|
void usePipeline(Renderer3DPipeline& pipeline);
|
||||||
|
|
||||||
|
/** Called by engine */
|
||||||
|
void init(RendererCore* core);
|
||||||
|
|
||||||
|
/** Called by engine */
|
||||||
|
void onFrameEnd();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Renderer3DPipeline* currentPipeline;
|
Renderer3DPipeline* currentPipeline;
|
||||||
void onDestroy(Renderer3DPipeline* pipeline);
|
void onDestroy(Renderer3DPipeline* pipeline);
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ class RendererSettings {
|
|||||||
height(448.0F),
|
height(448.0F),
|
||||||
interlacedHeightF(height / 2),
|
interlacedHeightF(height / 2),
|
||||||
near(0.1F),
|
near(0.1F),
|
||||||
far(4096.0F),
|
far(8192.0F),
|
||||||
projectionScale(4096.0F),
|
projectionScale(8192.0F),
|
||||||
aspectRatio(width / height),
|
aspectRatio(width / height),
|
||||||
interlacedHeightUI(static_cast<unsigned int>(interlacedHeightF)) {}
|
interlacedHeightUI(static_cast<unsigned int>(interlacedHeightF)) {}
|
||||||
~RendererSettings();
|
~RendererSettings();
|
||||||
|
|||||||
@@ -70,25 +70,21 @@ typedef struct {
|
|||||||
s16 t;
|
s16 t;
|
||||||
} texCoord_t;
|
} texCoord_t;
|
||||||
|
|
||||||
MD2Loader::MD2Loader() {}
|
std::unique_ptr<MeshBuilderData> MD2Loader::load(const char* fullpath) {
|
||||||
|
|
||||||
MD2Loader::~MD2Loader() {}
|
|
||||||
|
|
||||||
MeshBuilderData* MD2Loader::load(const char* fullpath) {
|
|
||||||
return load(fullpath, MD2LoaderOptions());
|
return load(fullpath, MD2LoaderOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshBuilderData* MD2Loader::load(const std::string& fullpath) {
|
std::unique_ptr<MeshBuilderData> MD2Loader::load(const std::string& fullpath) {
|
||||||
return load(fullpath.c_str(), MD2LoaderOptions());
|
return load(fullpath.c_str(), MD2LoaderOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshBuilderData* MD2Loader::load(const std::string& fullpath,
|
std::unique_ptr<MeshBuilderData> MD2Loader::load(const std::string& fullpath,
|
||||||
MD2LoaderOptions options) {
|
MD2LoaderOptions options) {
|
||||||
return load(fullpath.c_str(), options);
|
return load(fullpath.c_str(), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshBuilderData* MD2Loader::load(const char* fullpath,
|
std::unique_ptr<MeshBuilderData> MD2Loader::load(const char* fullpath,
|
||||||
MD2LoaderOptions options) {
|
MD2LoaderOptions options) {
|
||||||
std::string path = fullpath;
|
std::string path = fullpath;
|
||||||
TYRA_ASSERT(!path.empty(), "Provided path is empty!");
|
TYRA_ASSERT(!path.empty(), "Provided path is empty!");
|
||||||
|
|
||||||
@@ -123,7 +119,7 @@ MeshBuilderData* MD2Loader::load(const char* fullpath,
|
|||||||
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
auto result = new MeshBuilderData();
|
auto result = std::make_unique<MeshBuilderData>();
|
||||||
|
|
||||||
auto* material = new MeshBuilderMaterialData();
|
auto* material = new MeshBuilderMaterialData();
|
||||||
material->name = getFilenameWithoutExtension(filename);
|
material->name = getFilenameWithoutExtension(filename);
|
||||||
|
|||||||
@@ -24,10 +24,6 @@
|
|||||||
|
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
ObjLoader::ObjLoader() {}
|
|
||||||
|
|
||||||
ObjLoader::~ObjLoader() {}
|
|
||||||
|
|
||||||
std::unique_ptr<MeshBuilderData> ObjLoader::load(const char* fullpath) {
|
std::unique_ptr<MeshBuilderData> ObjLoader::load(const char* fullpath) {
|
||||||
return load(fullpath, ObjLoaderOptions());
|
return load(fullpath, ObjLoaderOptions());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,13 @@ void DynamicPipeline::onUseEnd() {
|
|||||||
core.deallocateOnUse();
|
core.deallocateOnUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicPipeline::render(const DynamicMesh& mesh,
|
void DynamicPipeline::onFrameEnd() {}
|
||||||
const DynPipOptions* options) {
|
|
||||||
render(&mesh, options);
|
void DynamicPipeline::render(const DynamicMesh* mesh) { render(mesh, nullptr); }
|
||||||
|
|
||||||
|
void DynamicPipeline::render(const DynamicMesh* mesh,
|
||||||
|
const DynPipOptions& options) {
|
||||||
|
render(mesh, &options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicPipeline::render(const DynamicMesh* mesh,
|
void DynamicPipeline::render(const DynamicMesh* mesh,
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ void MinecraftPipeline::onUseEnd() {
|
|||||||
manager.deallocateOnUse();
|
manager.deallocateOnUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MinecraftPipeline::onFrameEnd() {}
|
||||||
|
|
||||||
void MinecraftPipeline::initBBox() {
|
void MinecraftPipeline::initBBox() {
|
||||||
const auto& block = manager.getBlockData();
|
const auto& block = manager.getBlockData();
|
||||||
bbox = new RenderBBox(block.vertices, block.count);
|
bbox = new RenderBBox(block.vertices, block.count);
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ u32 StaPipCore::getMaxVertCountByParams(const bool& isSingleColor,
|
|||||||
->getMaxVertCount(isSingleColor, qbufferRenderer.getBufferSize());
|
->getMaxVertCount(isSingleColor, qbufferRenderer.getBufferSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaPipCore::render(StaPipBag* bag, StaPipBagPackagesBBox* bbox) {
|
void StaPipCore::render(StaPipBag* bag, StaPipBagPackagesBBox* bbox,
|
||||||
|
const u32& maxVertCount) {
|
||||||
if (bag->count <= 0) return;
|
if (bag->count <= 0) return;
|
||||||
|
|
||||||
bool frustumCull =
|
bool frustumCull =
|
||||||
@@ -105,32 +106,20 @@ void StaPipCore::render(StaPipBag* bag, StaPipBagPackagesBBox* bbox) {
|
|||||||
TYRA_ASSERT(!(!frustumCull && bag->info->fullClipChecks == true),
|
TYRA_ASSERT(!(!frustumCull && bag->info->fullClipChecks == true),
|
||||||
"Full clip checks are not supported with frustum culling = off!");
|
"Full clip checks are not supported with frustum culling = off!");
|
||||||
|
|
||||||
u32 maxVertCount = getMaxVertCountByBag(bag);
|
|
||||||
setMaxVertCount(maxVertCount);
|
setMaxVertCount(maxVertCount);
|
||||||
|
|
||||||
StaPipBagPackagesBBox* renderBbox = nullptr;
|
|
||||||
|
|
||||||
CoreBBoxFrustum frustumCheck = OUTSIDE_FRUSTUM;
|
CoreBBoxFrustum frustumCheck = OUTSIDE_FRUSTUM;
|
||||||
|
|
||||||
if (frustumCull) {
|
if (frustumCull) {
|
||||||
if (!bbox)
|
frustumCheck = bbox->getMainBBox()->clipFrustumCheck(
|
||||||
renderBbox =
|
|
||||||
new StaPipBagPackagesBBox(bag->vertices, bag->count, maxVertCount);
|
|
||||||
else
|
|
||||||
renderBbox = bbox;
|
|
||||||
|
|
||||||
frustumCheck = renderBbox->getMainBBox()->clipFrustumCheck(
|
|
||||||
rendererCore->renderer3D.frustumPlanes.getAll(), *bag->info->model);
|
rendererCore->renderer3D.frustumPlanes.getAll(), *bag->info->model);
|
||||||
|
|
||||||
if (frustumCheck == OUTSIDE_FRUSTUM) {
|
if (frustumCheck == OUTSIDE_FRUSTUM) {
|
||||||
if (!bbox) {
|
|
||||||
delete renderBbox;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packager.setRenderBBox(renderBbox);
|
packager.setRenderBBox(bbox);
|
||||||
|
|
||||||
M4x4 mvp;
|
M4x4 mvp;
|
||||||
|
|
||||||
@@ -199,10 +188,6 @@ void StaPipCore::render(StaPipBag* bag, StaPipBagPackagesBBox* bbox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frustumCull && !bbox) {
|
|
||||||
delete renderBbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texBuffers) delete texBuffers;
|
if (texBuffers) delete texBuffers;
|
||||||
|
|
||||||
qbufferRenderer.flushBuffers();
|
qbufferRenderer.flushBuffers();
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
# _____ ____ ___
|
||||||
|
# | \/ ____| |___|
|
||||||
|
# | | | \ | |
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
# Copyright 2022, tyra - https://github.com/h4570/tyra
|
||||||
|
# Licensed under Apache License 2.0
|
||||||
|
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "renderer/3d/pipeline/static/stapip_bag_bboxes_cacher.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace Tyra {
|
||||||
|
|
||||||
|
StapipBagBBoxesCacher::StapipBagBBoxesCacher() {}
|
||||||
|
|
||||||
|
StapipBagBBoxesCacher::~StapipBagBBoxesCacher() {}
|
||||||
|
|
||||||
|
void StapipBagBBoxesCacher::onFrameEnd() {
|
||||||
|
for (auto& item : storage) {
|
||||||
|
if (item.framesLeftToDestroy > 0) {
|
||||||
|
item.framesLeftToDestroy--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.erase(std::remove_if(storage.begin(), storage.end(),
|
||||||
|
[](const StapipBagBBoxesCacheItem& item) {
|
||||||
|
return item.framesLeftToDestroy <= 0;
|
||||||
|
}),
|
||||||
|
storage.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
StaPipBagPackagesBBox* StapipBagBBoxesCacher::getBBoxesByMesh(
|
||||||
|
const MeshMaterialFrame* frame, const u32& maxVertCount) {
|
||||||
|
auto* cache = getCache(maxVertCount, frame->id);
|
||||||
|
|
||||||
|
if (cache) {
|
||||||
|
cache->framesLeftToDestroy = cacheFramesCount * cacheSecondsCount;
|
||||||
|
return cache->bboxes.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bboxes = std::make_unique<StaPipBagPackagesBBox>(
|
||||||
|
frame->vertices, frame->count, maxVertCount);
|
||||||
|
|
||||||
|
storage.push_back(
|
||||||
|
StapipBagBBoxesCacheItem{maxVertCount, frame->id, std::move(bboxes),
|
||||||
|
cacheFramesCount * cacheSecondsCount});
|
||||||
|
|
||||||
|
return storage.back().bboxes.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
StapipBagBBoxesCacheItem* StapipBagBBoxesCacher::getCache(
|
||||||
|
const u32& maxVertCount, const u32& frameId) {
|
||||||
|
for (auto& item : storage) {
|
||||||
|
if (item.vu1MaxVertCount == maxVertCount && item.frameId == frameId) {
|
||||||
|
return &item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Tyra
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include "renderer/3d/pipeline/static/static_pipeline.hpp"
|
#include "renderer/3d/pipeline/static/static_pipeline.hpp"
|
||||||
#include "debug/debug.hpp"
|
#include "debug/debug.hpp"
|
||||||
#include "renderer/3d/pipeline/static/core/bag/stapip_info_bag.hpp"
|
#include "renderer/3d/pipeline/static/core/bag/stapip_info_bag.hpp"
|
||||||
|
#include <memory>
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
StaticPipeline::StaticPipeline() {}
|
StaticPipeline::StaticPipeline() {}
|
||||||
@@ -39,9 +39,13 @@ void StaticPipeline::onUseEnd() {
|
|||||||
core.deallocateOnUse();
|
core.deallocateOnUse();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaticPipeline::render(const StaticMesh& mesh,
|
void StaticPipeline::onFrameEnd() { cacher.onFrameEnd(); }
|
||||||
const StaPipOptions* options) {
|
|
||||||
render(&mesh, options);
|
void StaticPipeline::render(const StaticMesh* mesh) { render(mesh, nullptr); }
|
||||||
|
|
||||||
|
void StaticPipeline::render(const StaticMesh* mesh,
|
||||||
|
const StaPipOptions& options) {
|
||||||
|
render(mesh, &options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaticPipeline::render(const StaticMesh* mesh,
|
void StaticPipeline::render(const StaticMesh* mesh,
|
||||||
@@ -55,6 +59,7 @@ void StaticPipeline::render(const StaticMesh* mesh,
|
|||||||
|
|
||||||
auto model = mesh->getModelMatrix();
|
auto model = mesh->getModelMatrix();
|
||||||
auto* infoBag = getInfoBag(mesh, options, &model);
|
auto* infoBag = getInfoBag(mesh, options, &model);
|
||||||
|
auto dirLightsBag = std::make_unique<PipelineDirLightsBag>(true);
|
||||||
|
|
||||||
TYRA_ASSERT(
|
TYRA_ASSERT(
|
||||||
!(options->frustumCulling != PipelineFrustumCulling_Precise &&
|
!(options->frustumCulling != PipelineFrustumCulling_Precise &&
|
||||||
@@ -85,11 +90,21 @@ void StaticPipeline::render(const StaticMesh* mesh,
|
|||||||
bag.info = infoBag;
|
bag.info = infoBag;
|
||||||
bag.color = getColorBag(material, materialFrame);
|
bag.color = getColorBag(material, materialFrame);
|
||||||
bag.texture = getTextureBag(material, materialFrame);
|
bag.texture = getTextureBag(material, materialFrame);
|
||||||
bag.lighting = getLightingBag(materialFrame, &model, options);
|
bag.lighting =
|
||||||
core.render(&bag);
|
getLightingBag(materialFrame, dirLightsBag.get(), &model, options);
|
||||||
|
|
||||||
|
u32 maxVertCount = core.getMaxVertCountByBag(&bag);
|
||||||
|
|
||||||
|
StaPipBagPackagesBBox* bbox = nullptr;
|
||||||
|
if (bag.info->frustumCulling == PipelineFrustumCulling_Precise) {
|
||||||
|
bbox = cacher.getBBoxesByMesh(materialFrame, maxVertCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.render(&bag, bbox, maxVertCount);
|
||||||
|
|
||||||
deallocDrawBags(&bag, material);
|
deallocDrawBags(&bag, material);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete infoBag;
|
delete infoBag;
|
||||||
|
|
||||||
if (optionsManuallyAllocated) delete options;
|
if (optionsManuallyAllocated) delete options;
|
||||||
@@ -156,18 +171,15 @@ StaPipTextureBag* StaticPipeline::getTextureBag(
|
|||||||
}
|
}
|
||||||
|
|
||||||
StaPipLightingBag* StaticPipeline::getLightingBag(
|
StaPipLightingBag* StaticPipeline::getLightingBag(
|
||||||
const MeshMaterialFrame* materialFrame, M4x4* model,
|
const MeshMaterialFrame* materialFrame, PipelineDirLightsBag* dirLightsBag,
|
||||||
const StaPipOptions* options) const {
|
M4x4* model, const StaPipOptions* options) const {
|
||||||
if (!materialFrame->normals || options == nullptr ||
|
if (!materialFrame->normals || options == nullptr ||
|
||||||
options->lighting == nullptr)
|
options->lighting == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto* result = new StaPipLightingBag();
|
auto* result = new StaPipLightingBag();
|
||||||
|
|
||||||
// TODO: Make it once per rendering, very redundant
|
|
||||||
auto* dirLightsBag = new PipelineDirLightsBag(true);
|
|
||||||
result->dirLights = dirLightsBag;
|
result->dirLights = dirLightsBag;
|
||||||
|
|
||||||
result->lightMatrix = model;
|
result->lightMatrix = model;
|
||||||
|
|
||||||
result->dirLights->setLightsManually(
|
result->dirLights->setLightsManually(
|
||||||
@@ -194,7 +206,6 @@ void StaticPipeline::deallocDrawBags(StaPipBag* bag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bag->lighting) {
|
if (bag->lighting) {
|
||||||
delete bag->lighting->dirLights; // TODO
|
|
||||||
delete bag->lighting;
|
delete bag->lighting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ Renderer3D::~Renderer3D() {}
|
|||||||
|
|
||||||
void Renderer3D::init(RendererCore* core) { utility.init(core); }
|
void Renderer3D::init(RendererCore* core) { utility.init(core); }
|
||||||
|
|
||||||
|
void Renderer3D::onFrameEnd() {
|
||||||
|
if (currentPipeline != nullptr) {
|
||||||
|
currentPipeline->onFrameEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer3D::usePipeline(Renderer3DPipeline& pipeline) {
|
void Renderer3D::usePipeline(Renderer3DPipeline& pipeline) {
|
||||||
usePipeline(&pipeline);
|
usePipeline(&pipeline);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ void Renderer::beginFrame(const CameraInfo3D& cameraInfo) {
|
|||||||
core.beginFrame(cameraInfo);
|
core.beginFrame(cameraInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::endFrame() { core.endFrame(); }
|
void Renderer::endFrame() {
|
||||||
|
core.endFrame();
|
||||||
|
renderer3D.onFrameEnd();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Tyra
|
} // namespace Tyra
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class Tutorial03 : public Game {
|
|||||||
* - Minecraft (this one)
|
* - Minecraft (this one)
|
||||||
* - Static (for static 3D objects)
|
* - Static (for static 3D objects)
|
||||||
* - Dynamic (for dynamic 3D objects)
|
* - Dynamic (for dynamic 3D objects)
|
||||||
|
*
|
||||||
|
* You can create your own VU1 programs and create your custom pipelines!
|
||||||
*/
|
*/
|
||||||
MinecraftPipeline mcpip;
|
MinecraftPipeline mcpip;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
# _____ ____ ___
|
||||||
|
# | \/ ____| |___|
|
||||||
|
# | | | \ | |
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
# Copyright 2022, tyra - https://github.com/h4570/tyra
|
||||||
|
# Licensed under Apache License 2.0
|
||||||
|
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tyra>
|
||||||
|
|
||||||
|
namespace Tyra {
|
||||||
|
|
||||||
|
class Camera {
|
||||||
|
public:
|
||||||
|
Camera(Pad* pad);
|
||||||
|
~Camera();
|
||||||
|
|
||||||
|
Vec4 lookAt;
|
||||||
|
Vec4 position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result variable of unit circle calculation
|
||||||
|
* This is needed for rotation about the camera lookAt
|
||||||
|
* https://www.geogebra.org/m/cNEtsbvC
|
||||||
|
*/
|
||||||
|
Vec4 unitCircle;
|
||||||
|
|
||||||
|
CameraInfo3D getCameraInfo() { return CameraInfo3D(&position, &lookAt); }
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updatePosition();
|
||||||
|
void updateLookAt();
|
||||||
|
|
||||||
|
Pad* pad;
|
||||||
|
float circleRotation, circleLength, speed, lookAtHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Tyra
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <tyra>
|
#include <tyra>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
|
#include "./camera.hpp"
|
||||||
|
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
@@ -28,8 +29,16 @@ class Tutorial04 : public Game {
|
|||||||
|
|
||||||
Engine* engine;
|
Engine* engine;
|
||||||
|
|
||||||
std::unique_ptr<StaticMesh> mesh;
|
Camera camera;
|
||||||
|
|
||||||
|
/** 3D pipeline used for rendering static meshes */
|
||||||
|
StaticPipeline stapip;
|
||||||
|
|
||||||
|
/** Options for static pipeline */
|
||||||
StaPipOptions renderOptions;
|
StaPipOptions renderOptions;
|
||||||
|
|
||||||
|
/** Mesh with de_dust2 */
|
||||||
|
std::unique_ptr<StaticMesh> mesh;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tyra
|
} // namespace Tyra
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
# _____ ____ ___
|
||||||
|
# | \/ ____| |___|
|
||||||
|
# | | | \ | |
|
||||||
|
#-----------------------------------------------------------------------
|
||||||
|
# Copyright 2022, tyra - https://github.com/h4570/tyra
|
||||||
|
# Licensed under Apache License 2.0
|
||||||
|
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "camera.hpp"
|
||||||
|
|
||||||
|
namespace Tyra {
|
||||||
|
|
||||||
|
Camera::Camera(Pad* t_pad)
|
||||||
|
: lookAt(0.0F),
|
||||||
|
position(-2000.0F, 0.0F, -6000.0F),
|
||||||
|
pad(t_pad),
|
||||||
|
circleRotation(-5.0F),
|
||||||
|
circleLength(30.0F),
|
||||||
|
speed(20.0F),
|
||||||
|
lookAtHeight(-4.0F) {}
|
||||||
|
|
||||||
|
Camera::~Camera() {}
|
||||||
|
|
||||||
|
void Camera::update() {
|
||||||
|
updatePosition();
|
||||||
|
updateLookAt();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move camera position via left joystick
|
||||||
|
*/
|
||||||
|
void Camera::updatePosition() {
|
||||||
|
const auto& leftJoy = pad->getLeftJoyPad();
|
||||||
|
|
||||||
|
auto direction = Vec4(unitCircle).getNormalized() * speed;
|
||||||
|
|
||||||
|
if (leftJoy.v <= 100) {
|
||||||
|
position.x += direction.x;
|
||||||
|
position.z += direction.z;
|
||||||
|
} else if (leftJoy.v >= 200) {
|
||||||
|
position.x += -direction.x;
|
||||||
|
position.z += -direction.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftJoy.h <= 100) {
|
||||||
|
position.x += direction.z;
|
||||||
|
position.z += -direction.x;
|
||||||
|
} else if (leftJoy.h >= 200) {
|
||||||
|
position.x += -direction.z;
|
||||||
|
position.z += direction.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Camera height */
|
||||||
|
position.y = 700.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move camera lookAt position via right joystick
|
||||||
|
*/
|
||||||
|
void Camera::updateLookAt() {
|
||||||
|
const float rotationOffset = 0.045F;
|
||||||
|
const float heightOffset = 2.0F;
|
||||||
|
const auto& rightJoy = pad->getRightJoyPad();
|
||||||
|
|
||||||
|
if (rightJoy.h <= 100) {
|
||||||
|
circleRotation -= rotationOffset;
|
||||||
|
} else if (rightJoy.h >= 200) {
|
||||||
|
circleRotation += rotationOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rightJoy.v <= 100) {
|
||||||
|
lookAtHeight -= heightOffset;
|
||||||
|
} else if (rightJoy.v >= 200) {
|
||||||
|
lookAtHeight += heightOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
unitCircle.x = Math::sin(circleRotation) * circleLength;
|
||||||
|
unitCircle.y = lookAtHeight;
|
||||||
|
unitCircle.z = Math::cos(circleRotation) * circleLength;
|
||||||
|
|
||||||
|
lookAt = unitCircle + position;
|
||||||
|
}
|
||||||
|
} // namespace Tyra
|
||||||
@@ -13,25 +13,64 @@
|
|||||||
|
|
||||||
namespace Tyra {
|
namespace Tyra {
|
||||||
|
|
||||||
Tutorial04::Tutorial04(Engine* t_engine) : engine(t_engine) {}
|
Tutorial04::Tutorial04(Engine* t_engine)
|
||||||
|
: engine(t_engine), camera(&t_engine->pad) {}
|
||||||
|
|
||||||
Tutorial04::~Tutorial04() {
|
Tutorial04::~Tutorial04() {
|
||||||
engine->renderer.getTextureRepository().freeByMesh(mesh.get());
|
engine->renderer.getTextureRepository().freeByMesh(mesh.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tutorial04::init() { loadMesh(); }
|
void Tutorial04::init() {
|
||||||
|
stapip.setRenderer(&engine->renderer.core);
|
||||||
|
loadMesh();
|
||||||
|
}
|
||||||
|
|
||||||
void Tutorial04::loop() {}
|
void Tutorial04::loop() {
|
||||||
|
/** Update camera's lookAt and position */
|
||||||
|
camera.update();
|
||||||
|
|
||||||
|
engine->renderer.beginFrame(camera.getCameraInfo());
|
||||||
|
{
|
||||||
|
/** Upload static pipeline's VU1 programs */
|
||||||
|
engine->renderer.renderer3D.usePipeline(stapip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render de_dust2.
|
||||||
|
*
|
||||||
|
* As you see there are clipping issues (visual glitches).
|
||||||
|
* If you want to know how to fix it, please
|
||||||
|
* watch video tutorial.
|
||||||
|
*/
|
||||||
|
stapip.render(mesh.get(), renderOptions);
|
||||||
|
}
|
||||||
|
engine->renderer.endFrame();
|
||||||
|
}
|
||||||
|
|
||||||
void Tutorial04::loadMesh() {
|
void Tutorial04::loadMesh() {
|
||||||
ObjLoader loader;
|
|
||||||
ObjLoaderOptions options;
|
ObjLoaderOptions options;
|
||||||
options.flipUVs = true;
|
|
||||||
options.scale = 200.0F;
|
options.scale = 200.0F;
|
||||||
auto data = loader.load(FileUtils::fromCwd("de_dust2/de_dust2.obj"), options);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load mesh data
|
||||||
|
* Texture names are the material names "usemtl <material name>"
|
||||||
|
*/
|
||||||
|
auto data =
|
||||||
|
ObjLoader::load(FileUtils::fromCwd("de_dust2/de_dust2.obj"), options);
|
||||||
|
|
||||||
|
/** Create mesh with loaded data */
|
||||||
mesh = std::make_unique<StaticMesh>(data.get());
|
mesh = std::make_unique<StaticMesh>(data.get());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable frustum culling in render options,
|
||||||
|
* because it not make sense to perform
|
||||||
|
* frustum check on entire map (which will be always visible)
|
||||||
|
*/
|
||||||
|
renderOptions.frustumCulling = Tyra::PipelineFrustumCulling_None;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which load all textures into texture repository,
|
||||||
|
* based on material names.
|
||||||
|
*/
|
||||||
engine->renderer.getTextureRepository().addByMesh(
|
engine->renderer.getTextureRepository().addByMesh(
|
||||||
mesh.get(), FileUtils::fromCwd("de_dust2/"), "png");
|
mesh.get(), FileUtils::fromCwd("de_dust2/"), "png");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user