finished tutorial 7

This commit is contained in:
h4570
2022-08-23 22:31:07 +02:00
parent 9f831c7a6f
commit 989d2702e8
24 changed files with 439 additions and 25 deletions
+1 -3
View File
@@ -1,11 +1,10 @@
------------ Tyra's v2.0 roadmap to publish on GitHub ------------ ------------ Tyra's v2.0 roadmap to publish on GitHub ------------
- [Tutorials] 5
- [Tutorials] 6
- [Tutorials] 7 - [Tutorials] 7
- [Tutorials] 8 - [Tutorials] 8
- [Tutorials] 9. - [Tutorials] 9.
- [Renderer] Check ee clipping on de_dust2, add decimate to de_dust2?
- [General] Check all left TODOs -> Github issues - [General] Check all left TODOs -> Github issues
- [General] Readme Credits: clipping, obj loader - [General] Readme Credits: clipping, obj loader
- [Video] How to setup environment - [Video] How to setup environment
@@ -19,7 +18,6 @@
- [Demo] BBox constructors - [Demo] BBox constructors
- [General] Add cpp lint checker in GitHub / static code analysis - [General] Add cpp lint checker in GitHub / static code analysis
- [General] Smart pointers and std::array instead of raw pointers - [General] Smart pointers and std::array instead of raw pointers
- [Renderer] Check ee clipping on de_dust2, add decimate to de_dust2?
- [General] Control generated id to avoid duplication. Maybe Just increment from 0? Add interface IIdentificable? - [General] Control generated id to avoid duplication. Maybe Just increment from 0? Add interface IIdentificable?
- [renderer] fog - [renderer] fog
- [General] CI in Github via docker image - [General] CI in Github via docker image
@@ -22,7 +22,7 @@ class MeshMaterial {
explicit MeshMaterial(const MeshMaterial& material); explicit MeshMaterial(const MeshMaterial& material);
~MeshMaterial(); ~MeshMaterial();
u8 isMother, lightmapFlag; bool isMother, lightmapFlag;
u32 id; u32 id;
@@ -49,7 +49,7 @@ class DynamicPipeline : public Renderer3DPipeline {
*/ */
void render(const DynamicMesh* mesh); void render(const DynamicMesh* mesh);
void render(const DynamicMesh* mesh, const DynPipOptions& options); 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);
private: private:
RendererCore* rendererCore; RendererCore* rendererCore;
@@ -48,7 +48,7 @@ class StaticPipeline : public Renderer3DPipeline {
*/ */
void render(const StaticMesh* mesh); void render(const StaticMesh* mesh);
void render(const StaticMesh* mesh, const StaPipOptions& options); 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);
private: private:
StapipBagBBoxesCacher cacher; StapipBagBBoxesCacher cacher;
+4
View File
@@ -53,6 +53,10 @@ class Color {
void operator-=(const float& v); void operator-=(const float& v);
void operator*=(const float& v); void operator*=(const float& v);
void operator/=(const float& v); void operator/=(const float& v);
Color operator+(const float& v) const;
Color operator-(const float& v) const;
Color operator*(const float& v) const;
Color operator/(const float& v) const;
inline void set(const Color& v) { copy(this, v.rgba); } inline void set(const Color& v) { copy(this, v.rgba); }
inline void set(const float* v) { copy(this, v); } inline void set(const float* v) { copy(this, v); }
@@ -30,11 +30,11 @@ MeshMaterial::MeshMaterial(const MeshBuilderData& data,
id = rand() % 1000000; id = rand() % 1000000;
if (data.lightMapEnabled) { if (data.lightMapEnabled) {
lightmapFlag = false; lightmapFlag = true;
TYRA_ASSERT(material->frames[0]->colors != nullptr, TYRA_ASSERT(material->frames[0]->colors != nullptr,
"Colors faces are required"); "Colors faces are required");
} else { } else {
lightmapFlag = true; lightmapFlag = false;
} }
ambient.set(material->ambient); ambient.set(material->ambient);
@@ -112,9 +112,10 @@ void DynamicPipeline::render(const DynamicMesh* mesh,
auto* texture = auto* texture =
rendererCore->texture.repository.getByMeshMaterialId(material->id); rendererCore->texture.repository.getByMeshMaterialId(material->id);
TYRA_ASSERT( TYRA_ASSERT(texture, "Texture for material: ", material->name,
texture, "Texture for material: ", material->name, "Id: ", material->id, "Id: ", material->id,
"Was not found in texture repository! Did you forget to add texture?"); "Was not found in texture repository! Did you forget to add "
"texture or disable texture coords loading in mesh?");
for (u32 k = 0; k < partsCount; k++) { for (u32 k = 0; k < partsCount; k++) {
auto& buffer = buffers[bufferIndex]; auto& buffer = buffers[bufferIndex];
@@ -96,7 +96,7 @@ void StaticPipeline::render(const StaticMesh* mesh,
u32 maxVertCount = core.getMaxVertCountByBag(&bag); u32 maxVertCount = core.getMaxVertCountByBag(&bag);
StaPipBagPackagesBBox* bbox = nullptr; StaPipBagPackagesBBox* bbox = nullptr;
if (bag.info->frustumCulling == PipelineFrustumCulling_Precise) { if (bag.info->frustumCulling == PipelineInfoBagFrustumCulling_Precise) {
bbox = cacher.getBBoxesByMesh(materialFrame, maxVertCount); bbox = cacher.getBBoxesByMesh(materialFrame, maxVertCount);
} }
@@ -143,9 +143,9 @@ StaPipColorBag* StaticPipeline::getColorBag(
auto* result = new StaPipColorBag(); auto* result = new StaPipColorBag();
if (material->lightmapFlag) { if (material->lightmapFlag) {
result->single = &material->ambient;
} else {
result->many = materialFrame->colors; result->many = materialFrame->colors;
} else {
result->single = &material->ambient;
} }
return result; return result;
@@ -160,10 +160,10 @@ StaPipTextureBag* StaticPipeline::getTextureBag(
result->texture = result->texture =
rendererCore->texture.repository.getByMeshMaterialId(material->id); rendererCore->texture.repository.getByMeshMaterialId(material->id);
TYRA_ASSERT( TYRA_ASSERT(result->texture, "Texture for material: ", material->name,
result->texture, "Texture for material: ", material->name, "Id: ", material->id,
"Id: ", material->id, "Was not found in texture repository! Did you forget to add "
"Was not found in texture repository! Did you forget to add texture?"); "texture or disable texture coords loading in mesh?");
result->coordinates = materialFrame->textureCoords; result->coordinates = materialFrame->textureCoords;
+24
View File
@@ -47,6 +47,30 @@ void Color::operator/=(const float& v) {
a /= v; a /= v;
} }
Color Color::operator+(const float& v) const {
Color result = *this;
result += v;
return result;
}
Color Color::operator-(const float& v) const {
Color result = *this;
result -= v;
return result;
}
Color Color::operator*(const float& v) const {
Color result = *this;
result *= v;
return result;
}
Color Color::operator/(const float& v) const {
Color result = *this;
result /= v;
return result;
}
void Color::set(const float& t_r, const float& t_g, const float& t_b, void Color::set(const float& t_r, const float& t_g, const float& t_b,
const float& t_a) { const float& t_a) {
r = t_r; r = t_r;
+24
View File
@@ -0,0 +1,24 @@
TARGET := tutorial_02.elf
ENGINEDIR := ../../engine
#The Directories, Source, Includes, Objects, Binary and Resources
SRCDIR := src
INCDIR := inc
BUILDDIR := obj
TARGETDIR := bin
RESDIR := res
SRCEXT := cpp
VSMEXT := vsm
VCLEXT := vcl
VCLPPEXT := vclpp
DEPEXT := d
OBJEXT := o
#Flags, Libraries and Includes
CFLAGS :=
LIB :=
LIBDIRS :=
INC := -I$(INCDIR)
INCDEP := -I$(INCDIR)
include ../Makefile.tutorials-base
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+34
View File
@@ -0,0 +1,34 @@
/*
# _____ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# 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 Tutorial02 : public Game {
public:
Tutorial02(Engine* engine);
~Tutorial02();
void init();
void loop();
private:
void loadTexture();
void loadSprite();
Engine* engine;
Sprite sprite;
};
} // namespace Tyra
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+4
View File
@@ -0,0 +1,4 @@
$ConfigFile = Join-Path $PSScriptRoot '../../windows-pcsx2.ps1'
. $ConfigFile
RunPCSX2
+26
View File
@@ -0,0 +1,26 @@
/*
# _____ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licensed under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include "engine.hpp"
#include "tutorial_02.hpp"
/**
* In this tutorial we will learn how to:
* - Load png file and create texture from it
* - Create sprite class
* - Clear screen and render sprite
*/
int main() {
Tyra::Engine engine;
Tyra::Tutorial02 game(&engine);
engine.run(&game);
return 0;
}
+112
View File
@@ -0,0 +1,112 @@
/*
# _____ ____ ___
# | \/ ____| |___|
# | | | \ | |
#-----------------------------------------------------------------------
# Copyright 2022, tyra - https://github.com/h4570/tyra
# Licensed under Apache License 2.0
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
*/
#include <tyra>
#include "tutorial_02.hpp"
namespace Tyra {
Tutorial02::Tutorial02(Engine* t_engine) : engine(t_engine) {}
Tutorial02::~Tutorial02() {
engine->renderer.getTextureRepository().freeBySprite(sprite);
}
void Tutorial02::init() {
engine->renderer.setClearScreenColor(Color(32.0F, 32.0F, 32.0F));
/** Sprite contains rectangle information. */
loadSprite();
/** Texture contains png image. */
loadTexture();
}
void Tutorial02::loop() {
auto& renderer = engine->renderer;
/** Begin frame will clear our screen. */
renderer.beginFrame();
/** Render sprite. */
renderer.renderer2D.render(sprite);
/** End frame will perform vsync. */
renderer.endFrame();
}
void Tutorial02::loadSprite() {
const auto& screenSettings = engine->renderer.core.getSettings();
sprite.mode = SpriteMode::MODE_STRETCH;
/** Let's scale it down */
sprite.size = Vec2(256.0F, 128.0F);
/** Set it in screen center */
sprite.position =
Vec2(screenSettings.getWidth() / 2.0F - sprite.size.x / 2.0F,
screenSettings.getHeight() / 2.0F - sprite.size.y / 2.0F);
TYRA_LOG("Sprite created!");
}
void Tutorial02::loadTexture() {
/**
* Renderer has high layer functions,
* which allows to render:
* - Sprite (2D)
* - Mesh (3D)
*
* It uses ONLY low layer functions which are in renderer.core
*/
auto& renderer = engine->renderer;
/**
* TextureRepository is a repository of textures.
* It is a singleton class, with all game textures.
* We are linking these textures with sprite's (2D) and mesh (3D) materials.
*/
auto& textureRepository = renderer.getTextureRepository();
/**
* Texture is stored in "res" directory.
* Content of "res" directory is automatically copied into
* "bin" directory, which contains our final game.
*
* File utils automatically add's device prefix to the path,
* based on current working directory.
*
* In PS2 world:
* - USB has a "mass:" prefix
* - Our PC in PS2Link has a "host:" prefix
* - Our PC in PCSX2 has a "host:" prefix
*/
auto filepath = FileUtils::fromCwd("tyra.png");
/**
* Tyra supports following PNG formats:
* 32bpp (RGBA)
* 24bpp (RGB)
* 8bpp, palletized (RGBA)
* 4bpp, palletized (RGBA)
*
* 8bpp and 4bpp are the fastest.
* All of these formats can be easily exported via GIMP.
*/
auto* texture = textureRepository.add(filepath);
/** Let's assign this texture to sprite. */
texture->addLink(sprite.id);
TYRA_LOG("Texture loaded!");
}
} // namespace Tyra
+1 -1
View File
@@ -36,7 +36,7 @@ void Tutorial01::init() {
* Every function has infinite number of various-typed arguments. * Every function has infinite number of various-typed arguments.
*/ */
short positiveNumber = -1; // Change it to -1 and see what happen! short positiveNumber = 1; // Change it to -1 and see what happen!
TYRA_LOG("Hello world!"); TYRA_LOG("Hello world!");
+1 -1
View File
@@ -35,7 +35,7 @@ void Tutorial05::loop() {
{ {
engine->renderer.renderer3D.usePipeline(dynpip); engine->renderer.renderer3D.usePipeline(dynpip);
dynpip.render(zombieMesh.get(), renderOptions); dynpip.render(zombieMesh.get());
dynpip.render(warriorMesh.get(), renderOptions); dynpip.render(warriorMesh.get(), renderOptions);
} }
engine->renderer.endFrame(); engine->renderer.endFrame();
+2
View File
@@ -24,6 +24,8 @@ class Tutorial06 : public Game {
private: private:
Engine* engine; Engine* engine;
audsrv_adpcm_t* sample;
}; };
} // namespace Tyra } // namespace Tyra
+25 -2
View File
@@ -17,8 +17,31 @@ Tutorial06::Tutorial06(Engine* t_engine) : engine(t_engine) {}
Tutorial06::~Tutorial06() {} Tutorial06::~Tutorial06() {}
void Tutorial06::init() {} void Tutorial06::init() {
/** 16bit 22kHz wav file */
engine->audio.song.load(FileUtils::fromCwd("kayah-supermenka.wav"));
engine->audio.song.inLoop = true;
engine->audio.song.setVolume(60);
engine->audio.song.play();
void Tutorial06::loop() {} /** 16bit 22kHz wav file converted to adpcm sample */
sample = engine->audio.adpcm.load(FileUtils::fromCwd("shoot.adpcm"));
/** Set volume for channel 0 */
engine->audio.adpcm.setVolume(30, 0);
}
void Tutorial06::loop() {
engine->renderer.beginFrame();
{
/** Play ADPCM if (X) button is clicked! */
if (engine->pad.getClicked().Cross) {
/** Try to play sample in channel 0 */
if (engine->audio.adpcm.tryPlay(sample, 0) == ADPCM_CHANNEL_USED) {
TYRA_WARN("Channel is busy, please use another channel!");
}
}
}
engine->renderer.endFrame();
}
} // namespace Tyra } // namespace Tyra
+18
View File
@@ -23,7 +23,25 @@ class Tutorial07 : public Game {
void loop(); void loop();
private: private:
void loadMeshWithLightmap();
void setDirectionalLightsOptions();
void setLightmap(MeshBuilderData* data);
void switchBetweenLightmapAndDirectional();
int counter, switcher;
Engine* engine; Engine* engine;
Vec4 cameraPosition, cameraLookAt;
StaticPipeline stapip;
StaPipOptions renderOptions;
std::unique_ptr<StaticMesh> mesh;
PipelineLightingOptions renderLightingOptions;
Color directionalAmbientColor;
std::array<Color, 3> directionalColors;
std::array<Vec4, 3> directionalDirections;
}; };
} // namespace Tyra } // namespace Tyra
+134 -3
View File
@@ -13,12 +13,143 @@
namespace Tyra { namespace Tyra {
Tutorial07::Tutorial07(Engine* t_engine) : engine(t_engine) {} Tutorial07::Tutorial07(Engine* t_engine) : engine(t_engine) {
counter = 101;
switcher = 0;
}
Tutorial07::~Tutorial07() {} Tutorial07::~Tutorial07() {}
void Tutorial07::init() {} void Tutorial07::init() {
engine->renderer.setClearScreenColor(Color(32.0F, 32.0F, 32.0F));
void Tutorial07::loop() {} stapip.setRenderer(&engine->renderer.core);
cameraPosition = Vec4(0.0F, 0.0F, -10.0F);
cameraLookAt.unit();
/**
* Directional lights are dynamic lights with infinite range.
*
* Lightmap is a fast, pregenerated lightmap for a mesh.
* It is very useful for meshes like terrain, buildings, etc..
*
* Lightmap in PS2 world is done by old school color-per-vertex.
*/
loadMeshWithLightmap();
setDirectionalLightsOptions();
}
void Tutorial07::loop() {
switchBetweenLightmapAndDirectional();
/** Rotate the mesh to see the effect static vs dynamic lights */
mesh->rotation.rotateY(0.05F);
engine->renderer.beginFrame(CameraInfo3D(&cameraPosition, &cameraLookAt));
{
engine->renderer.renderer3D.usePipeline(stapip);
/** Switch between dynamic directional lights / static lightmap */
if (switcher == 0) {
stapip.render(mesh.get(), renderOptions);
} else {
stapip.render(mesh.get());
}
}
engine->renderer.endFrame();
}
void Tutorial07::loadMeshWithLightmap() {
ObjLoaderOptions options;
options.scale = 2.0F;
auto data = ObjLoader::load(FileUtils::fromCwd("cup.obj"), options);
/**
* We don't want to load texture coordinates,
* because this obj file has no texture.
*
* Anyways you can use texture as well.
*/
data->textureCoordsEnabled = false;
/** Lets add lightmap manually */
setLightmap(data.get());
mesh = std::make_unique<StaticMesh>(data.get());
mesh->translation.translateY(-1.0F);
mesh->rotation.rotateX(1.0F);
}
void Tutorial07::setDirectionalLightsOptions() {
// Set ambient color
directionalAmbientColor.set(16.0F, 32.0F, 16.0F);
// Reset colors and directions
for (int i = 0; i < 3; i++) {
directionalColors[i].set(0.0F, 0.0F, 0.0F, 1.0F);
directionalDirections[i].set(1.0F, 1.0F, 1.0F, 1.0F);
}
// Set light and direction 1
directionalDirections[0].set(-1.0F, 0.3F, 0.3F);
directionalColors[0].set(16.0F, 16.0F, 96.0F);
// Set light and direction 2
directionalDirections[1].set(0.0F, -1.0F, 1.0F);
directionalColors[1].set(96.0F, 16.0F, 16.0F);
// Set render options
renderLightingOptions.ambientColor = &directionalAmbientColor;
renderLightingOptions.directionalColors = directionalColors.begin();
renderLightingOptions.directionalDirections = directionalDirections.begin();
renderOptions.shadingType = TyraShadingGouraud;
renderOptions.blendingEnabled = true;
renderOptions.lighting = &renderLightingOptions;
}
void Tutorial07::setLightmap(MeshBuilderData* data) {
/** Information for mesh, that data has lightmap */
data->lightMapEnabled = true;
/** This object file has only 1 material */
auto* material = data->materials[0];
/** Static mesh so 1 frame */
auto* frame = material->frames[0];
/**
* You can load your pregenerated lightmap here.
* In tutorial we will generate some random lightmap.
*
* Dont worry about dynamic allocation,
* it will be handled by Mesh class.
*/
frame->colors = new Color[frame->count];
for (u32 i = 0; i < frame->count; i++) {
auto randomLight = 0.1F + ((2.0F + frame->vertices[i].x) / 4.0F);
frame->colors[i] = material->ambient * randomLight;
frame->colors[i].a = 128.0F;
}
}
void Tutorial07::switchBetweenLightmapAndDirectional() {
if (counter++ > 100) {
counter = 0;
switcher = !switcher;
auto* material = mesh->materials[0];
if (switcher == 0) {
material->lightmapFlag = false;
TYRA_LOG("Directional lights!");
} else {
material->lightmapFlag = true;
TYRA_LOG("Lightmap!");
}
}
}
} // namespace Tyra } // namespace Tyra
+1
View File
@@ -15,6 +15,7 @@
* In this tutorial we will learn: * In this tutorial we will learn:
* - How to render skybox and what is "ALLPASS" rendering method * - How to render skybox and what is "ALLPASS" rendering method
* - How to render debug lines and bboxes * - How to render debug lines and bboxes
* - How to get available RAM and FPS count
*/ */
int main() { int main() {