235 lines
5.9 KiB
C++
235 lines
5.9 KiB
C++
#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;
|
|
}
|