Added buffer, reimplemented some drawing functions on cpu

This commit is contained in:
Bobby Lucero 2024-04-27 23:27:27 -04:00
parent b53fc267cb
commit 9e48025846
3 changed files with 175 additions and 24 deletions

View File

@ -5,6 +5,7 @@
#include <fstream> #include <fstream>
#include "Graphics.h" #include "Graphics.h"
#include "../Utilities.h" #include "../Utilities.h"
#include <raymath.h>
Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : m_screenWidth(screenWidth), m_screenHeight(screenHeight){ Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : m_screenWidth(screenWidth), m_screenHeight(screenHeight){
@ -16,10 +17,19 @@ Graphics::Graphics(int screenWidth, int screenHeight, int startupScale) : m_scre
SetConfigFlags(FLAG_WINDOW_RESIZABLE); SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(m_startupScreenWidth, m_startupScreenHeight, "test"); InitWindow(m_startupScreenWidth, m_startupScreenHeight, "test");
SetTargetFPS(60); SetTargetFPS(60);
m_virtualScreen = LoadRenderTexture(screenWidth, screenHeight); m_virtualScreen = LoadRenderTexture(screenWidth, screenHeight);
m_origin = {0,0}; m_origin = {0,0};
m_virtualScreenLocalBounds = {0.0f, 0.0f, (float)m_virtualScreen.texture.width, -(float)m_virtualScreen.texture.height }; m_virtualScreenLocalBounds = {0.0f, 0.0f, (float)m_virtualScreen.texture.width, (float)m_virtualScreen.texture.height };
m_virtualScreenWindowBounds = {0.0f, 0.0f, (float)m_windowWidth, (float)m_windowHeight}; m_virtualScreenWindowBounds = {0.0f, 0.0f, (float)m_windowWidth, (float)m_windowHeight};
m_virtualScreenImageBuffer = GenImageColor(m_screenWidth, m_screenHeight, BLACK);
m_virtualScreenColorBuffer = {};
for (int i = 0; i < screenWidth * screenHeight; ++i) {
m_virtualScreenColorBuffer.push_back(GetRandomValue(0, 5));
}
calculateScreenPositionInWindow(); calculateScreenPositionInWindow();
} }
@ -33,18 +43,26 @@ void Graphics::draw(StateManager* stateManager) {
calculateScreenPositionInWindow(); calculateScreenPositionInWindow();
} }
BeginTextureMode(m_virtualScreen);
stateManager->Draw(this); stateManager->Draw(this);
copyBufferToGPU();
EndTextureMode();
renderVirtualScreen(); renderVirtualScreen();
} }
void Graphics::copyBufferToGPU() {
Color* pixel_data = LoadImageColors(m_virtualScreenImageBuffer);
for (int i = 0; i < m_screenWidth * m_screenHeight; ++i) {
pixel_data[i] = GetColor(m_paletteByID[m_virtualScreenColorBuffer[i]]);
}
UpdateTexture(m_virtualScreen.texture, pixel_data);
UnloadImageColors(pixel_data);
}
void Graphics::renderVirtualScreen() { void Graphics::renderVirtualScreen() {
BeginDrawing(); BeginDrawing();
ClearBackground(BLACK); ClearBackground(BLACK);
DrawTexturePro(m_virtualScreen.texture, m_virtualScreenLocalBounds, m_virtualScreenWindowBounds, m_origin, 0.0f, WHITE); DrawTexturePro(m_virtualScreen.texture, m_virtualScreenLocalBounds, m_virtualScreenWindowBounds, m_origin, 0.0f, WHITE);
DrawText(std::to_string(GetFPS()).c_str(), 10, 10, 30, YELLOW);
EndDrawing(); EndDrawing();
} }
@ -164,32 +182,131 @@ void Graphics::bindMethods(pkpy::VM *vm) {
} }
void Graphics::Clear(int paletteIndex) { void Graphics::Clear(int paletteIndex) {
if(paletteIndex < 0 || paletteIndex >= m_paletteByID.size()) paletteIndex = 0; for (int y = 0; y < m_screenHeight; ++y) {
ClearBackground(GetColor(m_paletteByID[paletteIndex])); for (int x = 0; x < m_screenWidth; ++x) {
Pixel(x, y, paletteIndex);
}
}
} }
void Graphics::Pixel(int x, int y, int paletteIndex) { void Graphics::Pixel(int x, int y, int paletteIndex) {
DrawPixel(x, y, GetColor(m_paletteByID[paletteIndex])); paletteIndex = Clamp(paletteIndex, 0, m_paletteByID.size() - 1);
if(x < 0 || y < 0 || x >= m_screenWidth || y >= m_screenHeight) return;
m_virtualScreenColorBuffer[y * m_screenWidth + x] = paletteIndex;
} }
void Graphics::Circle(int x, int y, int radius, int paletteIndex) { void Graphics::Circle(int x, int y, int radius, int paletteIndex) {
DrawCircle(x, y, radius, GetColor(m_paletteByID[paletteIndex])); Ellipse(x, y, radius, radius, paletteIndex);
} }
void Graphics::Ellipse(int x, int y, int w, int h, int paletteIndex){
int x0 = x - w;
int y0 = y - h;
int x1 = x + w;
int y1 = y + h;
if(x0 > x1 || y0 > y1)
return;
int a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1;
int dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a;
int err = dx + dy + b1 * a * a, e2;
if (x0 > x1) { x0 = x1; x1 += a; }
if (y0 > y1) y0 = y1;
y0 += (b + 1) / 2; y1 = y0 - b1;
a *= 8 * a; b1 = 8 * b * b;
int prevY = y0;
do
{
Pixel( x1, y0, paletteIndex);
Pixel( x0, y0, paletteIndex);
Pixel( x0, y1, paletteIndex);
Pixel( x1, y1, paletteIndex);
if(y0 != prevY && (y0 - y) != h){
h_line(x1, y0, x - (x1 - x) + 1, paletteIndex);
h_line(x1, y - (y0 - y), x - (x1 - x) + 1, paletteIndex);
}
prevY = y0;
e2 = 2 * err;
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
} while (x0 <= x1);
while (y0-y1 < b)
{ /* too early stop of flat ellipses a=1 */
Pixel( x0 - 1, y0, paletteIndex); /* -> finish tip of ellipse */
Pixel( x1 + 1, y0++, paletteIndex);
Pixel( x0 - 1, y1, paletteIndex);
Pixel( x1 + 1, y1--, paletteIndex);
}
h_line(x - w, y, x + w, paletteIndex);
}
void Graphics::EllipseBorder(int x, int y, int w, int h, int paletteIndex){
int x0 = x - w;
int y0 = y - h;
int x1 = x + w;
int y1 = y + h;
if(x0 > x1 || y0 > y1)
return;
int a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1;
int dx = 4 * (1 - a) * b * b, dy = 4 * (b1 + 1) * a * a;
int err = dx + dy + b1 * a * a, e2;
if (x0 > x1) { x0 = x1; x1 += a; }
if (y0 > y1) y0 = y1;
y0 += (b + 1) / 2; y1 = y0 - b1;
a *= 8 * a; b1 = 8 * b * b;
do
{
Pixel( x1, y0, paletteIndex);
Pixel( x0, y0, paletteIndex);
Pixel( x0, y1, paletteIndex);
Pixel( x1, y1, paletteIndex);
e2 = 2 * err;
if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */
if (e2 >= dx || 2 * err > dy) { x0++; x1--; err += dx += b1; } /* x step */
} while (x0 <= x1);
while (y0-y1 < b)
{ /* too early stop of flat ellipses a=1 */
Pixel( x0 - 1, y0, paletteIndex); /* -> finish tip of ellipse */
Pixel( x1 + 1, y0++, paletteIndex);
Pixel( x0 - 1, y1, paletteIndex);
Pixel( x1 + 1, y1--, paletteIndex);
}
}
void Graphics::Rect(int x, int y, int width, int height, int paletteIndex) { void Graphics::Rect(int x, int y, int width, int height, int paletteIndex) {
DrawRectangle(x, y, width, height, GetColor(m_paletteByID[paletteIndex])); for (int i = 0; i < height; ++i) {
h_line(x, y + i, x + width - 1, paletteIndex);
}
}
void Graphics::RectBorder(int x, int y, int width, int height, int paletteIndex) {
h_line(x, y, x + width - 1, paletteIndex);
h_line(x, y + height - 1, x + width - 1, paletteIndex);
v_line(x, y + 1, y + height - 2, paletteIndex);
v_line(x + width - 1, y + 1, y + height - 2, paletteIndex);
} }
void Graphics::Text(std::string s, int x, int y, int paletteIndex) { void Graphics::Text(std::string s, int x, int y, int paletteIndex) {
DrawText(s.c_str(), x, y, 5, GetColor(m_paletteByID[paletteIndex]));
}
void Graphics::beginDraw() {
BeginTextureMode(m_virtualScreen);
}
void Graphics::endDraw() {
EndTextureMode();
} }
void Graphics::updateVMVars(pkpy::VM* vm) { void Graphics::updateVMVars(pkpy::VM* vm) {
@ -199,6 +316,35 @@ void Graphics::updateVMVars(pkpy::VM* vm) {
vm->builtins->attr().set("height", pkpy::py_var(vm, m_screenHeight)); vm->builtins->attr().set("height", pkpy::py_var(vm, m_screenHeight));
} }
void Graphics::h_line(int x1, int y, int x2, int paletteIndex) {
if(y < 0 || y >= m_screenHeight) return;
int startX = x1;
int endX = x2;
if(x1 > x2){
startX = x2;
endX = x1;
}
for (int i = startX; i <= endX; ++i) {
Pixel(i, y, paletteIndex);
}
}
void Graphics::v_line(int x, int y1, int y2, int paletteIndex) {
if(x < 0 || x >= m_screenWidth) return;
int startY = y1;
int endY = y2;
if(y1 > y2){
startY = y2;
endY = y1;
}
for (int i = startY; i <= endY; ++i) {
Pixel(x, i, paletteIndex);
}
}

View File

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <array>
class StateManager; class StateManager;
@ -21,13 +22,18 @@ private:
Rectangle m_virtualScreenWindowBounds; // size of rect texture on window Rectangle m_virtualScreenWindowBounds; // size of rect texture on window
Vector2 m_origin; // position of rect texture on window Vector2 m_origin; // position of rect texture on window
Rectangle m_virtualScreenLocalBounds; // virtual screen bounds Rectangle m_virtualScreenLocalBounds; // virtual screen bounds
RenderTexture2D m_virtualScreen; // actual pixel screen RenderTexture2D m_virtualScreen;
std::vector<uint8_t> m_virtualScreenColorBuffer;
Image m_virtualScreenImageBuffer;
private: private:
void renderVirtualScreen(); void renderVirtualScreen();
void calculateScreenPositionInWindow(); void calculateScreenPositionInWindow();
void h_line(int x1, int y, int x2, int paletteIndex);
void v_line(int x, int y1, int y2, int paletteIndex);
public: public:
// virtual screen // virtual screen
int m_screenWidth = 0; int m_screenWidth = 0;
@ -46,8 +52,7 @@ public:
void draw(StateManager* stateManager); void draw(StateManager* stateManager);
void beginDraw(); void copyBufferToGPU();
void endDraw();
void updateVMVars(pkpy::VM* vm); void updateVMVars(pkpy::VM* vm);
@ -61,7 +66,10 @@ public:
void Clear(int paletteIndex); void Clear(int paletteIndex);
void Pixel(int x, int y, int paletteIndex); void Pixel(int x, int y, int paletteIndex);
void Circle(int x, int y, int radius, int paletteIndex); void Circle(int x, int y, int radius, int paletteIndex);
void Ellipse(int x, int y, int width, int height, int paletteIndex);
void EllipseBorder(int x, int y, int width, int height, int paletteIndex);
void Rect(int x, int y, int width, int height, int paletteIndex); void Rect(int x, int y, int width, int height, int paletteIndex);
void RectBorder(int x, int y, int width, int height, int paletteIndex);
void Text(std::string s, int x, int y, int paletteIndex); void Text(std::string s, int x, int y, int paletteIndex);

View File

@ -20,14 +20,11 @@ void StateManager::RequestStateChange(StateManager::StateType state) {
} }
if(m_currentState){ if(m_currentState){
// Game state needs ability to draw during code loading
if(m_currentState == m_gameState){ if(m_currentState == m_gameState){
m_pycron->m_graphics->beginDraw();
m_currentState->OnEnter(); m_currentState->OnEnter();
if(m_gameState->m_errorThrown){ if(m_gameState->m_errorThrown){
m_pycron->m_graphics->Text(m_gameState->m_previousError, 2, 2, 59); m_pycron->m_graphics->Text(m_gameState->m_previousError, 2, 2, 59);
} }
m_pycron->m_graphics->endDraw();
}else{ }else{
m_currentState->OnEnter(); m_currentState->OnEnter();
} }