add miniaudio; add tilesheet; add audio test

This commit is contained in:
VisualGMQ 2022-02-05 20:52:01 +08:00
parent d683587d58
commit d1fbdb1898
36 changed files with 90872 additions and 77 deletions

View File

@ -11,8 +11,10 @@ set(ENGINE_NAME tinyengine)
aux_source_directory(src/tinyengine ENGINE_SRC) aux_source_directory(src/tinyengine ENGINE_SRC)
aux_source_directory(src/component ENGINE_SRC) aux_source_directory(src/component ENGINE_SRC)
aux_source_directory(src/tinyengine/ecs ENGINE_SRC)
aux_source_directory(libs/glad/src ENGINE_SRC) aux_source_directory(libs/glad/src ENGINE_SRC)
aux_source_directory(libs/stb_image ENGINE_SRC) aux_source_directory(libs/stb_image ENGINE_SRC)
aux_source_directory(libs/miniaudio/ ENGINE_SRC)
add_library(${ENGINE_NAME} STATIC ${ENGINE_SRC}) add_library(${ENGINE_NAME} STATIC ${ENGINE_SRC})
@ -24,7 +26,7 @@ add_subdirectory(libs/glfw)
target_include_directories( target_include_directories(
${ENGINE_NAME} ${ENGINE_NAME}
PRIVATE include/tinyengine PRIVATE include/tinyengine
PUBLIC libs/glad/include libs/stb_image # /usr/local/Cellar/glm/0.9.9.8/include PUBLIC libs/glad/include libs/stb_image libs/miniaudio # /usr/local/Cellar/glm/0.9.9.8/include
) )
target_link_libraries( target_link_libraries(
@ -51,6 +53,7 @@ target_compile_options(
# space sector # space sector
################ ################
aux_source_directory(src/game GAME_SRC) aux_source_directory(src/game GAME_SRC)
aux_source_directory(src/game/stages GAME_SRC)
add_executable(${PROJECT_NAME} ${GAME_SRC}) add_executable(${PROJECT_NAME} ${GAME_SRC})
target_include_directories( target_include_directories(

BIN
assets/1mgame_sound.wav Normal file

Binary file not shown.

BIN
assets/test/goodbyESAKA.wav Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/test/start.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

BIN
assets/test/tilesheet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

View File

@ -0,0 +1,16 @@
#pragma once
#include "tinyengine/tinyengine.hpp"
class GameLogoScence final: public Scence {
public:
void OnInit() override;
void OnRender() override;
private:
Camera camera;
float initTime_;
Unique<Sound> sound_;
bool soundPlayed_;
};

View File

@ -0,0 +1,28 @@
#pragma once
#include "pch.hpp"
#include "tool.hpp"
#include "log.hpp"
#include "libmath.hpp"
class Audio final {
public:
static void Init();
static void Shutdown();
};
class Sound final {
public:
Sound(const std::string& filename);
~Sound();
void Play();
void Pause();
void Stop();
void SetVolume(float volume);
void EnableLoop();
void DisableLoop();
private:
ma_sound sound_;
};

View File

@ -13,7 +13,7 @@ public:
private: private:
Mat44 viewMat_; Mat44 viewMat_;
Point position_; Point position_ = {0, 0};
Point scale_; Point scale_ = {1, 1};
bool dirty_ = false; bool dirty_ = false;
}; };

View File

@ -0,0 +1,38 @@
_Pragma("once")
#include "pch.hpp"
#include "pool.hpp"
using ComponentId = unsigned int;
class Component {
public:
friend class Pool<Component>;
virtual ~Component() = default;
inline bool IsAlive() const { return alive_; }
virtual void Release() = 0;
private:
bool alive_;
};
class ComponentIdx final {
public:
template <typename T>
static ComponentId Get() {
static_assert(std::is_base_of_v<Component, T> && !std::is_same_v<Component, T>);
static ComponentId id = count_ ++;
return id;
}
inline static ComponentId GetCount() { return count_; }
private:
static ComponentId count_;
};
struct ComponentPool{
};
extern std::unordered_map<ComponentId, Pool<Component>> gComponentPool;

View File

@ -0,0 +1,46 @@
_Pragma("once")
#include "pool.hpp"
#include "component.hpp"
class Entity;
class Context final {
public:
friend class Entity;
Entity* CreateEntity();
bool HasEntity(Entity* entity);
Pool<Entity>::ElemContainer& GetEntities();
void DestroyEntity(Entity* entity);
int GetEntityNum() const;
template <typename T, typename... Args>
T* CreateComponent(Args... args) {
ComponentId id = ComponentIdx::Get<T>();
auto it = gComponentPool.find(id);
if (it != gComponentPool.end()) {
return it->second.Create<T>(args...);
} else {
gComponentPool[id] = Pool<Component>();
return gComponentPool[id].Create<T>(args...);
}
}
template <typename T>
void DestroyComponent(T* elem) {
DestroyComponentById(elem, ComponentIdx::Get<T>());
}
inline void DestroyComponentById(Component* elem, int id) {
auto it = gComponentPool.find(id);
if (it != gComponentPool.end()) {
gComponentPool[id].Destroy(elem);
}
}
private:
Pool<Entity> entities_;
void onComponentUpdate(Entity*, ComponentId, Component*);
};

View File

@ -0,0 +1,5 @@
_Pragma("once")
#include "entity.hpp"
#include "component.hpp"
#include "context.hpp"

View File

@ -0,0 +1,135 @@
_Pragma("once")
#include "pch.hpp"
#include "pool.hpp"
#include "component.hpp"
#include "context.hpp"
class Entity final {
public:
friend class Pool<Entity>;
template <typename T, typename... Args>
void Add(Args... args) {
if (!alive_) {
std::cout << "Entity::Add(): entity is die, can't add component." << std::endl;
} else {
T* comp = context_->CreateComponent<T>(args...);
components_[ComponentIdx::Get<T>()] = comp;
}
}
inline void Init(Context* context) {
context_ = context;
}
inline void Release() {
components_.clear();
}
template <typename T>
const T* Get() const {
if constexpr (!std::is_base_of_v<Component, T>) {
std::cout << "Entity::Get() const: the type T is not inherit from Component" << std::endl;
return nullptr;
} else {
return (T*)Get(ComponentIdx::Get<T>());
}
}
Component* Get(ComponentId id) const {
if (!alive_) {
std::cout << "Entity::Get(id) const: entity is die, can't get anything from it." << std::endl;
return nullptr;
}
auto it = components_.find(id);
if (it == components_.end()) {
return nullptr;
} else {
return it->second;
}
}
template <typename T>
T* Use() {
if (!alive_) {
std::cout << "Entity::Use() const: entity is die, can't get anything from it." << std::endl;
return nullptr;
}
auto it = components_.find(ComponentIdx::Get<T>());
if (it == components_.end()) {
return nullptr;
} else {
return (T*)it->second;
}
}
template <typename T, typename... Args>
void Replace(Args... args) {
if (!alive_) {
std::cout << "Entity::Replace() const: entity is die, can't replace component." << std::endl;
} else {
auto it = components_.find(ComponentIdx::Get<T>());
if (it != components_.end()) {
T* c = (T*)it->second;
c->Init(args...);
} else {
Add<T>(args...);
}
}
}
template <typename T>
void Remove() {
Remove(ComponentIdx::Get<T>());
}
inline void Remove(ComponentId id) {
if (!alive_) {
std::cout << "Entity::Remove() const: entity is die, can't remove anything from it." << std::endl;
} else {
auto it = components_.find(id);
if (it != components_.end()) {
context_->DestroyComponentById(it->second, id);
components_.erase(it);
}
}
}
template <typename ... Types>
bool Has() const {
if (!alive_) {
std::cout << "Entity::Has() const: entity is die, can't query." << std::endl;
return false;
}
return hasIter<Types ...>();
}
inline bool Has(ComponentId id) const {
return components_.find(id) != components_.end();
}
inline void Reset() {
for (ComponentId i = 0; i < ComponentIdx::GetCount(); i++) {
auto it = components_.find(i);
if (it != components_.end()) {
components_.erase(it);
}
}
}
inline bool IsAlive() const { return alive_; }
private:
std::unordered_map<ComponentId, Component*> components_;
bool alive_;
Context* context_ = nullptr;
template <typename T, typename ... Types>
bool hasIter() const {
if constexpr (sizeof...(Types) == 0) {
return Get<T>();
} else {
return Get<T>() && hasIter<Types ...>();
}
}
};

View File

@ -6,6 +6,7 @@
#include "inner_bmpfont.hpp" #include "inner_bmpfont.hpp"
#include "event.hpp" #include "event.hpp"
#include "tool.hpp" #include "tool.hpp"
#include "audio.hpp"
class Engine final { class Engine final {
public: public:

View File

@ -143,3 +143,20 @@ inline Mat44 CreateOrthoMat(float left, float right,
0, 0, 0, 1, 0, 0, 0, 1,
}); });
} }
inline Point GetCenterAlign(const Rect& parent, const Size& child) {
return Point{parent.x + (parent.w - child.w) / 2.0f,
parent.y + (parent.h - child.h) / 2.0f};
}
inline Mat44 CreateSRT(const Point& pos, const Point& scale, float degree) {
float theta = Radians(degree);
float sinTheta = std::sin(theta),
cosTheta = std::cos(theta);
return Mat44({
scale.x * cosTheta, scale.y * sinTheta, 0, pos.x,
-scale.x * sinTheta, scale.y * cosTheta, 0, pos.y,
0, 0, 1, 0,
0, 0, 0, 1,
});
}

View File

@ -21,6 +21,8 @@
#include <limits> #include <limits>
#include <cassert> #include <cassert>
#include "miniaudio.h"
#ifdef USE_GLM #ifdef USE_GLM
#include "glm/glm.hpp" #include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp" #include "glm/gtc/matrix_transform.hpp"

View File

@ -0,0 +1,69 @@
_Pragma("once")
#include "pch.hpp"
/*
* Memory Pool.
* To use this pool, your class must have:
* * `bool alive_` member
* * let Pool be it's friend class
* * `void Init(...)` function to init your object
* * `void Release()` function to release your object
*/
template <typename T>
class Pool {
public:
using ElemContainer = std::vector<T*>;
Pool(size_t capacity = 20) {
datas_.reserve(capacity);
}
~Pool() {
for (size_t i = 0; i < datas_.size(); i++)
delete datas_[i];
while (!cache_.empty()) {
delete cache_.top();
cache_.pop();
}
}
template <typename U = T, typename... Args>
U* Create(Args... args) {
static_assert(std::is_base_of_v<T, U>);
if (GetReuseableNum() > 0) {
U* elem = (U*)cache_.top();
elem->Init(args ...);
elem->alive_ = true;
cache_.pop();
datas_.push_back(elem);
return elem;
} else {
U* elem = new U;
datas_.push_back(elem);
elem->Init(args ...);
elem->alive_ = true;
return (U*)datas_.back();
}
}
void Destroy(T* elem) {
auto it = std::find(datas_.begin(),
datas_.end(),
elem);
if (it != datas_.end()) {
(*it)->alive_ = false;
(*it)->Release();
cache_.push(*it);
datas_.erase(it);
}
}
std::vector<T*>& GetElems() { return datas_; }
int GetNum() const { return datas_.size(); }
int GetReuseableNum() const { return cache_.size(); }
private:
ElemContainer datas_;
std::stack<T*> cache_;
};

View File

@ -5,9 +5,22 @@
#include "glhelpfunc.hpp" #include "glhelpfunc.hpp"
#include "shader.hpp" #include "shader.hpp"
#include "camera.hpp" #include "camera.hpp"
#include "tilesheet.hpp"
struct Vertex {
Point pos;
Point texcoord;
};
class Renderer final { class Renderer final {
public: public:
enum FlipFlag {
NoFlip = 0,
Vertical = 0x01,
Horizontal = 0x02,
Both = 0x03,
};
static void Init(); static void Init();
static void Shutdown(); static void Shutdown();
static void SetClearColor(const Color&); static void SetClearColor(const Color&);
@ -16,7 +29,21 @@ public:
static void DrawLine(const Point& p1, const Point& p2); static void DrawLine(const Point& p1, const Point& p2);
static void DrawRect(const Rect& rect); static void DrawRect(const Rect& rect);
static void FillRect(const Rect& rect); static void FillRect(const Rect& rect);
static void DrawTexture(const Texture& texture, const Mat44& transform, const Color& color = Color{1, 1, 1, 1}); static void DrawTile(const Tile& tile,
const Point& pos,
const Size& size = {0, 0},
float degree = 0,
FlipFlag flip = NoFlip);
static void DrawTexture(const Texture* texture,
const Rect* srcrect,
const Point& pos,
const Size& size = Size{0, 0},
float degree = 0,
FlipFlag flip = NoFlip);
static void DrawTexture(const Texture* texture,
const Rect* rect,
const Mat44& transform,
const Color& color = Color{1, 1, 1, 1});
static void SetViewport(int x, int y, int w, int h); static void SetViewport(int x, int y, int w, int h);
static const Color& GetDrawColor(); static const Color& GetDrawColor();
static void SetCamera(Camera& camera); static void SetCamera(Camera& camera);
@ -24,4 +51,9 @@ public:
private: private:
static Color color_; static Color color_;
static void drawRectFrag(const Texture* texture,
const std::array<Vertex, 6>& vertices,
const Mat44& transform,
const Color& color);
}; };

View File

@ -0,0 +1,21 @@
#pragma once
#include "texture.hpp"
struct Tile {
Texture* texture;
Rect rect;
Size size;
};
class TileSheet final {
public:
TileSheet(const std::string& filename, int x, int y);
const Tile GetTile(int x, int y);
Texture* GetTexture() { return texture_.get(); }
private:
Unique<Texture> texture_;
Size size_;
};

View File

@ -9,3 +9,5 @@
#include "texture.hpp" #include "texture.hpp"
#include "tool.hpp" #include "tool.hpp"
#include "inner_bmpfont.hpp" #include "inner_bmpfont.hpp"
#include "audio.hpp"
#include "tilesheet.hpp"

View File

@ -13,3 +13,7 @@ using Ref = std::shared_ptr<T>;
#else #else
#define FATAL_ERROR(msg) #define FATAL_ERROR(msg)
#endif #endif
inline float GetTime() {
return glfwGetTime();
}

View File

@ -0,0 +1,11 @@
#ifndef GAME_DEBUG
#define MA_DEBUG_OUTPUT
#endif
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS
#define MA_ENABLE_COREAUDIO
#define MA_NO_FLAC
#define MA_NO_MP3
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"

90143
libs/miniaudio/miniaudio.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,4 @@ fi
mkdir output mkdir output
cp ./build/SpaceSector output cp ./build/SpaceSector output
cp -r ./assets output cp -r ./assets output
rm -rf ./output/assets/test

View File

@ -1,24 +1,3 @@
#include "tinyengine/engine.hpp" #include "game/stages/gamelogo.hpp"
#include "tinyengine/event.hpp"
#include "tinyengine/camera.hpp"
class WelcomeScene final: public Scence { RUN_WINDOW("Space Sector", 1024, 720, GameLogoScence)
public:
void OnInit() override {
Log("init scence");
camera.Move(Point{0, 0});
camera.Scale(Point{1, 1});
}
void OnUpdate(float dt) override {
}
void OnQuit() override {
Log("quit scence");
}
private:
Camera camera;
};
RUN_WINDOW("Space Sector", 1024, 720, WelcomeScene)

View File

@ -0,0 +1,39 @@
#include "game/stages/gamelogo.hpp"
constexpr float TitlePixel = 40.0f;
constexpr float AuthorInfoPixel = 20.0f;
const char* Title = "1M GAMES DEV";
const char* AuthorInfo = "MADE BY VISUALGMQ";
void GameLogoScence::OnInit() {
Renderer::SetClearColor(Color{0, 0, 0, 255});
initTime_ = GetTime();
sound_.reset(new Sound("assets/1mgame_sound.wav"));
soundPlayed_ = false;
}
void GameLogoScence::OnRender() {
float duration = GetTime() - initTime_;
if (duration > 2 && !soundPlayed_) {
sound_->Play();
soundPlayed_ = true;
}
if (duration < 3) {
engine.GetInnerBmpFont().Render(Title,
TitlePixel,
Point{0, -25} +
GetCenterAlign(Rect{0, 0,
engine.GetWindowSize().w,
engine.GetWindowSize().h},
Size{TitlePixel * strlen(Title), TitlePixel}),
Color{1, 1, 1, 1});
engine.GetInnerBmpFont().Render(AuthorInfo,
AuthorInfoPixel,
Point{0, 25} +
GetCenterAlign(Rect{0, 0,
engine.GetWindowSize().w,
engine.GetWindowSize().h},
Size{AuthorInfoPixel * strlen(AuthorInfo), AuthorInfoPixel}),
Color{0.5, 0.5, 0.5, 1});
}
}

55
src/tinyengine/audio.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "audio.hpp"
ma_engine AudioEngine;
void Audio::Init() {
ma_result result;
result = ma_engine_init(NULL, &AudioEngine);
if (result != MA_SUCCESS) {
FATAL_ERROR("sound system init failed");
}
ma_engine_start(&AudioEngine);
}
void Audio::Shutdown() {
ma_engine_stop(&AudioEngine);
}
Sound::Sound(const std::string& filename) {
ma_result result;
result = ma_sound_init_from_file(&AudioEngine,
filename.c_str(),
0,
NULL, NULL,
&sound_);
if (result != MA_SUCCESS) {
Log("%s sound load failed!", filename.c_str());
}
}
void Sound::Play() {
ma_sound_start(&sound_);
}
void Sound::Pause() {
}
void Sound::Stop() {
ma_sound_stop(&sound_);
}
Sound::~Sound() {
ma_sound_uninit(&sound_);
}
void Sound::SetVolume(float volume) {
ma_sound_set_volume(&sound_, Clamp(0.0f, 10.0f, volume));
}
void Sound::EnableLoop() {
ma_sound_set_looping(&sound_, true);
}
void Sound::DisableLoop() {
ma_sound_set_looping(&sound_, false);
}

View File

@ -0,0 +1,6 @@
#include "ecs/component.hpp"
#include "ecs/context.hpp"
ComponentId ComponentIdx::count_ = 0;
std::unordered_map<ComponentId, Pool<Component>> gComponentPool;

View File

@ -0,0 +1,29 @@
#include "ecs/context.hpp"
#include "ecs/entity.hpp"
Pool<Entity>::ElemContainer& Context::GetEntities() {
return entities_.GetElems();
}
void Context::DestroyEntity(Entity* entity) {
for (size_t i = 0; i < ComponentIdx::GetCount(); i++) {
if (entity->Has(i)) {
DestroyComponentById(entity->Get(i), i);
}
}
entities_.Destroy(entity);
}
int Context::GetEntityNum() const {
return entities_.GetNum();
}
bool Context::HasEntity(Entity* entity) {
auto& entities = entities_.GetElems();
return std::find(entities.begin(), entities.end(), entity) !=
entities.end();
}
Entity* Context::CreateEntity() {
return entities_.Create(this);
}

View File

@ -39,7 +39,11 @@ void Engine::Init(const std::string& title, const Size& size, Scence* scence) {
Renderer::SetViewport(0, 0, width, height); Renderer::SetViewport(0, 0, width, height);
Renderer::Init(); Renderer::Init();
Log("render system init OK");
Renderer::SetClearColor(Color{0.1, 0.1, 0.1, 1}); Renderer::SetClearColor(Color{0.1, 0.1, 0.1, 1});
Audio::Init();
Log("sound system init OK");
scence_.reset(scence); scence_.reset(scence);
scence_->OnInit(); scence_->OnInit();
@ -75,6 +79,7 @@ void Engine::PollEvent() {
} }
void Engine::Shutdown() { void Engine::Shutdown() {
Audio::Shutdown();
if (scence_) if (scence_)
scence_->OnQuit(); scence_->OnQuit();
glfwDestroyWindow(window_); glfwDestroyWindow(window_);

View File

@ -1,16 +1,8 @@
#include "renderer.hpp" #include "renderer.hpp"
#include "libmath.hpp"
#include "engine.hpp" #include "engine.hpp"
Color Renderer::color_ = {255, 255, 255, 255}; Color Renderer::color_ = {255, 255, 255, 255};
struct Vertex {
float x;
float y;
float texCoordX;
float texCoordY;
};
struct { struct {
GLuint vbo; GLuint vbo;
GLuint vao; GLuint vao;
@ -20,16 +12,15 @@ struct {
Unique<Texture> WhiteTexture; Unique<Texture> WhiteTexture;
Camera* camera = nullptr; Camera* camera = nullptr;
Vertex rectVertices[6] = { const std::array<Vertex, 6> vertices = {
{0.0f, 1.0f, 0.0f, 1.0f}, Vertex{-0.5f, 0.5f, 0.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 0.0f}, Vertex{ 0.5f,-0.5f, 1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 0.0f}, Vertex{-0.5f,-0.5f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f, 1.0f}, Vertex{-0.5f, 0.5f, 0.0f, 1.0f},
{1.0f, 1.0f, 1.0f, 1.0f}, Vertex{ 0.5f, 0.5f, 1.0f, 1.0f},
{1.0f, 0.0f, 1.0f, 0.0f} Vertex{ 0.5f,-0.5f, 1.0f, 0.0f}
}; };
} Context; } Context;
void createWhiteTexture() { void createWhiteTexture() {
@ -86,6 +77,8 @@ void setShaderOrtho() {
} }
void Renderer::Init() { void Renderer::Init() {
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
createWhiteTexture(); createWhiteTexture();
initRenderContext(); initRenderContext();
initShader("assets/shaders/vertex.shader", initShader("assets/shaders/vertex.shader",
@ -128,41 +121,93 @@ void Renderer::DrawRect(const Rect& rect) {
} }
void Renderer::FillRect(const Rect& rect) { void Renderer::FillRect(const Rect& rect) {
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, Context.vbo)); drawRectFrag(nullptr,
GL_CALL(glBindVertexArray(Context.vao)); Context.vertices,
GL_CALL(glBufferData(GL_ARRAY_BUFFER, CreateSRT(Point{rect.x + rect.w / 2, rect.y + rect.h / 2},
sizeof(Context.rectVertices), Point{rect.w, rect.h},
Context.rectVertices, 0),
GL_STATIC_DRAW)); Renderer::color_);
Context.shader->Use();
Context.WhiteTexture->Bind();
Context.shader->SetMat4("model", Mat44({
rect.w, 0, 0, rect.x,
0, rect.h, 0, rect.y,
0, 0, 1, 0,
0, 0, 0, 1,
}));
Context.shader->SetInt("Texture", 0);
GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 6));
} }
void Renderer::DrawTexture(const Texture& texture, void Renderer::DrawTile(const Tile& tile,
const Point& pos,
const Size& size,
float degree,
Renderer::FlipFlag flip) {
if (size.w == 0 && size.h == 0) {
DrawTexture(tile.texture, &tile.rect, pos, tile.size, degree, flip);
} else {
DrawTexture(tile.texture, &tile.rect, pos, size, degree, flip);
}
}
void Renderer::DrawTexture(const Texture* texture,
const Rect* srcrect,
const Point& pos,
const Size& size,
float degree,
FlipFlag flip) {
if (texture) {
auto scale = size;
if (size.w == 0 && size.h == 0) {
scale = texture->GetSize();
}
if (flip & Vertical) {
scale.h *= -1;
}
if (flip & Horizontal) {
scale.w *= -1;
}
DrawTexture(texture, srcrect, CreateSRT(pos, scale, degree));
}
}
void Renderer::DrawTexture(const Texture* texture,
const Rect* rect,
const Mat44& transform, const Mat44& transform,
const Color& color) { const Color& color) {
if (!texture) return;
auto& size = texture->GetSize();
if (!rect) {
drawRectFrag(texture, Context.vertices, transform, color);
} else {
auto vertices = Context.vertices;
vertices[0].texcoord = Point{rect->x / size.w,
(rect->y + rect->h) / size.h};
vertices[1].texcoord = Point{(rect->x + rect->w) / size.w,
rect->y / size.h};
vertices[2].texcoord = Point{rect->x / size.w, rect->y / size.h};
vertices[3].texcoord = vertices[0].texcoord;
vertices[4].texcoord = Point{(rect->x + rect->w) / size.w,
(rect->y + rect->h) / size.h};
vertices[5].texcoord = vertices[1].texcoord;
drawRectFrag(texture, vertices, transform, color);
}
}
void Renderer::drawRectFrag(const Texture* texture,
const std::array<Vertex, 6>& vertices,
const Mat44& transform,
const Color& color) {
Context.shader->Use(); Context.shader->Use();
Context.shader->SetInt("Texture", 0); Context.shader->SetInt("Texture", 0);
texture.Bind(0); if (texture) {
texture->Bind(0);
} else {
Context.WhiteTexture->Bind(0);
}
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, Context.vbo)); GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, Context.vbo));
GL_CALL(glBindVertexArray(Context.vao)); GL_CALL(glBindVertexArray(Context.vao));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, GL_CALL(glBufferData(GL_ARRAY_BUFFER,
sizeof(Context.rectVertices), sizeof(Vertex) * vertices.size(),
Context.rectVertices, vertices.data(),
GL_STATIC_DRAW)); GL_STATIC_DRAW));
Renderer::SetDrawColor(color); Renderer::SetDrawColor(color);
Context.shader->SetMat4("model", transform); Context.shader->SetMat4("model", transform);
GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 6)); GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 6));
} }
void Renderer::Clear() { void Renderer::Clear() {

View File

@ -3,17 +3,22 @@
Texture::Texture(const std::string& filename) { Texture::Texture(const std::string& filename) {
int w, h, channels; int w, h, channels;
unsigned char* data = stbi_load(filename.c_str(), &w, &h, &channels, 0); unsigned char* data = stbi_load(filename.c_str(), &w, &h, &channels, 0);
if (channels == 4) { if (!data) {
format_ = GL_RGBA; Log("%s load failed!", filename.c_str());
}else if (channels == 3) { texture_ = 0;
format_ = GL_RGB; } else {
if (channels == 4) {
format_ = GL_RGBA;
}else if (channels == 3) {
format_ = GL_RGB;
}
size_.x = w;
size_.y = h;
createFromPixels(data, w, h, format_);
stbi_image_free(data);
} }
size_.x = w;
size_.y = h;
createFromPixels(data, w, h, format_);
stbi_image_free(data);
} }
Texture::Texture(const unsigned char* data, int w, int h, GLenum format) { Texture::Texture(const unsigned char* data, int w, int h, GLenum format) {

View File

@ -0,0 +1,14 @@
#include "tilesheet.hpp"
TileSheet::TileSheet(const std::string& filename, int x, int y) {
texture_.reset(new Texture(filename));
size_.x = x;
size_.y = y;
}
const Tile TileSheet::GetTile(int x, int y) {
Size size = texture_->GetSize() / size_;
return {texture_.get(),
Rect{size.w * x, size.h * y, size.w, size.h},
size};
}

View File

@ -9,3 +9,4 @@ endmacro()
AddTest(math math.cpp ${ENGINE_NAME}) AddTest(math math.cpp ${ENGINE_NAME})
AddTest(renderer renderer.cpp ${ENGINE_NAME}) AddTest(renderer renderer.cpp ${ENGINE_NAME})
AddTest(innerBmpFont innerBmpFont.cpp ${ENGINE_NAME}) AddTest(innerBmpFont innerBmpFont.cpp ${ENGINE_NAME})
AddTest(audio audio.cpp ${ENGINE_NAME})

22
tests/audio.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "tinyengine/tinyengine.hpp"
class TestAudio : public Scence {
public:
void OnInit() override {
sound_.reset(new Sound("assets/test/goodbyESAKA.wav"));
sound_->SetVolume(10);
sound_->Play();
}
void OnRender() override {
}
void OnQuit() override {
sound_.reset();
}
private:
Unique<Sound> sound_;
};
RUN_WINDOW("test renderer", 1024, 720, TestAudio)

View File

@ -5,7 +5,16 @@ class TestRenderer: public Scence {
public: public:
void OnInit() override { void OnInit() override {
dirt_.reset(new Texture("assets/test/dirt.png")); dirt_.reset(new Texture("assets/test/dirt.png"));
start_.reset(new Texture("assets/test/start.bmp")); start_.reset(new Texture("assets/test/start.png"));
tilesheet_.reset(new TileSheet("assets/test/tilesheet.png", 8, 1));
degree_ = 0;
}
void OnUpdate(float second) override {
if (degree_ > 360) {
degree_ -= 360;
}
degree_ += 40 * second;
} }
void OnRender() override { void OnRender() override {
@ -15,14 +24,26 @@ public:
Renderer::SetDrawColor(Color{0, 1, 0}); Renderer::SetDrawColor(Color{0, 1, 0});
Renderer::FillRect(Rect{100, 100, 50, 75}); Renderer::FillRect(Rect{100, 100, 50, 75});
Renderer::DrawTexture(*dirt_, CreateTextureTransform(dirt_->GetSize(), Point{50, 50})); Renderer::DrawTexture(dirt_.get(), nullptr, CreateSRT(Point{50, 50}, dirt_->GetSize(), 0));
Renderer::DrawTexture(*start_, CreateTextureTransform(dirt_->GetSize(), Point{100, 50})); Renderer::DrawTexture(start_.get(), nullptr, Point{100, 50}, Size{0, 0}, degree_);
Renderer::DrawTexture(tilesheet_->GetTexture(), nullptr, Point{100, 100});
Renderer::DrawTexture(tilesheet_->GetTexture(), nullptr, Point{100, 116}, Size{0, 0}, 0, Renderer::Horizontal);
Renderer::DrawTexture(tilesheet_->GetTexture(), nullptr, Point{100, 132}, Size{0, 0}, 0, Renderer::Vertical);
Renderer::DrawTexture(tilesheet_->GetTexture(), nullptr, Point{100, 148}, Size{0, 0}, 0, Renderer::Both);
Size tileSize = {16, 16};
for (int i = 0; i < 8; i++) {
Rect rect{i * 16.0f, 0, 16, 16};
Renderer::DrawTile(tilesheet_->GetTile(i, 0), Point{50 + 16.0f * i, 200}, Size{0, 0}, degree_);
}
} }
private: private:
Unique<Texture> dirt_; Unique<Texture> dirt_;
Unique<Texture> start_; Unique<Texture> start_;
Unique<TileSheet> tilesheet_;
float degree_;
}; };
RUN_WINDOW("test renderer", 1024, 720, TestRenderer) RUN_WINDOW("test renderer", 1024, 720, TestRenderer)