merge remote and local changes
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "FrameBuffer.h"
|
||||
#include "SDLDisplay.h"
|
||||
#include "SpriteAssetLoader.h"
|
||||
#include "SpriteRasterizer.h"
|
||||
#include "Image.h"
|
||||
#include "Sprite.h"
|
||||
#include "SpriteAnimator.h"
|
||||
#include "AnimationSystem.h"
|
||||
|
||||
static std::string FindAssetPath(const std::string& fileName)
|
||||
{
|
||||
const char* roots[] = {
|
||||
"game/assets/sprites/",
|
||||
"../game/assets/sprites/",
|
||||
"../../game/assets/sprites/",
|
||||
"../../../game/assets/sprites/"
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i)
|
||||
{
|
||||
const std::string path = std::string(roots[i]) + fileName;
|
||||
std::ifstream file(path.c_str(), std::ios::binary);
|
||||
if (file.good())
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string("game/assets/sprites/") + fileName;
|
||||
}
|
||||
|
||||
static bool LoadSpriteImage(const std::string& fileName, RenderData::Image& image)
|
||||
{
|
||||
const std::string path = FindAssetPath(fileName);
|
||||
if (!Asset::SpriteAssetLoader::Load(path, image))
|
||||
{
|
||||
std::cerr << "Load sprite failed: " << path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static RenderData::Image ResizeImageNearest(const RenderData::Image& source, int32_t width, int32_t height)
|
||||
{
|
||||
if (!source.is_valid() || width <= 0 || height <= 0)
|
||||
{
|
||||
return RenderData::Image();
|
||||
}
|
||||
|
||||
RenderData::Image result(width, height);
|
||||
for (int32_t y = 0; y < height; ++y)
|
||||
{
|
||||
const int32_t sourceY = y * source.get_height() / height;
|
||||
for (int32_t x = 0; x < width; ++x)
|
||||
{
|
||||
const int32_t sourceX = x * source.get_width() / width;
|
||||
result.set_pixel(x, y, source.get_pixel_fast(sourceX, sourceY));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static RenderData::Image ResizeImageToFit(const RenderData::Image& source, int32_t maxWidth, int32_t maxHeight)
|
||||
{
|
||||
if (!source.is_valid() || maxWidth <= 0 || maxHeight <= 0)
|
||||
{
|
||||
return RenderData::Image();
|
||||
}
|
||||
|
||||
const float scaleX = static_cast<float>(maxWidth) / source.get_width();
|
||||
const float scaleY = static_cast<float>(maxHeight) / source.get_height();
|
||||
const float scale = std::min(scaleX, scaleY);
|
||||
|
||||
const int32_t width = std::max(1, static_cast<int32_t>(source.get_width() * scale));
|
||||
const int32_t height = std::max(1, static_cast<int32_t>(source.get_height() * scale));
|
||||
return ResizeImageNearest(source, width, height);
|
||||
}
|
||||
|
||||
static bool LoadFrames(
|
||||
const std::vector<std::string>& fileNames,
|
||||
std::vector<RenderData::Image>& images,
|
||||
std::vector<RenderData::Sprite>& frames,
|
||||
int32_t maxFrameWidth,
|
||||
int32_t maxFrameHeight)
|
||||
{
|
||||
images.clear();
|
||||
frames.clear();
|
||||
images.reserve(fileNames.size());
|
||||
|
||||
for (size_t i = 0; i < fileNames.size(); ++i)
|
||||
{
|
||||
RenderData::Image image;
|
||||
if (!LoadSpriteImage(fileNames[i], image))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
images.push_back(ResizeImageToFit(image, maxFrameWidth, maxFrameHeight));
|
||||
}
|
||||
|
||||
frames.reserve(images.size());
|
||||
for (size_t i = 0; i < images.size(); ++i)
|
||||
{
|
||||
frames.push_back(RenderData::Sprite(&images[i]));
|
||||
}
|
||||
|
||||
return !frames.empty();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
const int32_t width = 800;
|
||||
const int32_t height = 480;
|
||||
|
||||
Platform::SDLDisplay display;
|
||||
if (!display.init(width, height))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
RenderData::Image originalBackground;
|
||||
if (!LoadSpriteImage("background.sprite", originalBackground))
|
||||
{
|
||||
display.shutdown();
|
||||
return -1;
|
||||
}
|
||||
RenderData::Image background = ResizeImageNearest(originalBackground, width, height);
|
||||
|
||||
std::vector<std::string> frameFiles;
|
||||
frameFiles.push_back("Tom-stand.sprite");
|
||||
frameFiles.push_back("Tom-listhen.sprite");
|
||||
frameFiles.push_back("Tom-openmouse1.sprite");
|
||||
frameFiles.push_back("Tom-openmouse2.sprite");
|
||||
frameFiles.push_back("Tom-say1.sprite");
|
||||
frameFiles.push_back("Tom-say2.sprite");
|
||||
frameFiles.push_back("Tom-say3.sprite");
|
||||
frameFiles.push_back("Tom-say4.sprite");
|
||||
|
||||
std::vector<RenderData::Image> frameImages;
|
||||
std::vector<RenderData::Sprite> frames;
|
||||
if (!LoadFrames(frameFiles, frameImages, frames, width * 7 / 10, height * 9 / 10))
|
||||
{
|
||||
display.shutdown();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Core::FrameBuffer frameBuffer(width, height);
|
||||
Rasterizer::SpriteRasterizer spriteRasterizer(&frameBuffer);
|
||||
|
||||
Game::SpriteAnimator tomAnimator(frames, 0.12f, true);
|
||||
Game::AnimationSystem animationSystem;
|
||||
|
||||
const RenderData::Sprite* firstFrame = tomAnimator.get_current_sprite();
|
||||
const int32_t tomX = firstFrame ? (width - firstFrame->width) / 2 : 0;
|
||||
const int32_t tomY = firstFrame ? height - firstFrame->height - 20 : 0;
|
||||
animationSystem.add(&tomAnimator, tomX, tomY);
|
||||
|
||||
std::cout << "Sprite animation test" << std::endl;
|
||||
std::cout << "Space: play/pause, R: reset, Esc: quit" << std::endl;
|
||||
std::cout << "Background: " << originalBackground.get_width() << "x" << originalBackground.get_height()
|
||||
<< " -> " << background.get_width() << "x" << background.get_height() << std::endl;
|
||||
std::cout << "Tom frame 0: " << frameImages[0].get_width() << "x" << frameImages[0].get_height() << std::endl;
|
||||
|
||||
bool shouldQuit = false;
|
||||
uint32_t lastTime = display.get_time_ms();
|
||||
while (!shouldQuit)
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
shouldQuit = true;
|
||||
}
|
||||
else if (event.type == SDL_KEYDOWN)
|
||||
{
|
||||
if (event.key.repeat != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
shouldQuit = true;
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_SPACE)
|
||||
{
|
||||
if (tomAnimator.is_playing())
|
||||
{
|
||||
tomAnimator.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
tomAnimator.play();
|
||||
}
|
||||
}
|
||||
else if (event.key.keysym.sym == SDLK_r)
|
||||
{
|
||||
tomAnimator.reset();
|
||||
tomAnimator.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t currentTime = display.get_time_ms();
|
||||
const float deltaTime = static_cast<float>(currentTime - lastTime) * 0.001f;
|
||||
lastTime = currentTime;
|
||||
|
||||
animationSystem.update(deltaTime);
|
||||
|
||||
frameBuffer.clear(RenderData::Color(18, 18, 24, 255));
|
||||
spriteRasterizer.DrawImage(background, 0, 0);
|
||||
animationSystem.draw(spriteRasterizer);
|
||||
display.present(&frameBuffer);
|
||||
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
display.shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user