mirror of
https://github.com/joel16/gLib2D.git
synced 2025-04-06 22:31:40 +00:00
1087 lines
25 KiB
C
1087 lines
25 KiB
C
/*
|
|
* gLib2D - A simple, fast, light-weight 2D graphics library.
|
|
*
|
|
* Copyright 2012 Clément Guérin <geecko.dev@free.fr>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "glib2d.h"
|
|
|
|
#include <pspdisplay.h>
|
|
#include <pspkernel.h>
|
|
#include <pspgu.h>
|
|
#include <vram.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* Defines */
|
|
|
|
#define DLIST_SIZE (524288)
|
|
#define LINE_SIZE (512)
|
|
#define PIXEL_SIZE (4)
|
|
#define FRAMEBUFFER_SIZE (LINE_SIZE * G2D_SCR_H * PIXEL_SIZE)
|
|
#define MALLOC_STEP (128)
|
|
#define TSTACK_MAX (64)
|
|
#define SLICE_WIDTH (64.f)
|
|
#define M_180_PI (57.29578f)
|
|
#define M_PI_180 (0.017453292f)
|
|
|
|
#define DEFAULT_SIZE (10)
|
|
#define DEFAULT_COORD_MODE (G2D_UP_LEFT)
|
|
#define DEFAULT_X (0.f)
|
|
#define DEFAULT_Y (0.f)
|
|
#define DEFAULT_Z (0.f)
|
|
#define DEFAULT_COLOR (WHITE)
|
|
#define DEFAULT_ALPHA (0xFF)
|
|
|
|
#define OBJ rctx.obj[rctx.n - 1]
|
|
#define OBJ_I rctx.obj[i]
|
|
#define TRANSFORM tstack[tstack_size - 1]
|
|
|
|
/* Enumerations */
|
|
|
|
typedef enum {
|
|
RECTS, LINES, QUADS, POINTS
|
|
} Obj_Type;
|
|
|
|
/* Structures */
|
|
|
|
typedef struct {
|
|
float x, y, z;
|
|
float rot, rot_sin, rot_cos;
|
|
float scale_w, scale_h;
|
|
} Transform;
|
|
|
|
typedef struct {
|
|
float x, y, z;
|
|
float rot_x, rot_y; // Rotation center
|
|
float rot, rot_sin, rot_cos;
|
|
int crop_x, crop_y;
|
|
int crop_w, crop_h;
|
|
float scale_w, scale_h;
|
|
g2dColor color;
|
|
g2dAlpha alpha;
|
|
} Object;
|
|
|
|
typedef struct {
|
|
Object *obj;
|
|
Object cur_obj;
|
|
unsigned int n;
|
|
Obj_Type type;
|
|
g2dTexture *tex;
|
|
|
|
bool use_strip;
|
|
bool use_z;
|
|
bool use_vert_color;
|
|
bool use_rot;
|
|
bool use_tex_linear;
|
|
bool use_tex_repeat;
|
|
bool use_int;
|
|
unsigned int color_count;
|
|
g2dCoord_Mode coord_mode;
|
|
} RenderContext;
|
|
|
|
/* Local variables */
|
|
|
|
static int *dlist;
|
|
|
|
static RenderContext rctx;
|
|
|
|
static Transform tstack[TSTACK_MAX];
|
|
static unsigned int tstack_size;
|
|
|
|
static bool init = false;
|
|
static bool start = false;
|
|
static bool begin = false;
|
|
static bool zclear = true;
|
|
static bool scissor = false;
|
|
|
|
static float global_scale;
|
|
|
|
/* Global variables */
|
|
|
|
g2dTexture g2d_draw_buffer = {
|
|
512, 512,
|
|
G2D_SCR_W, G2D_SCR_H,
|
|
(float)G2D_SCR_W/G2D_SCR_H,
|
|
false,
|
|
(g2dColor *)FRAMEBUFFER_SIZE
|
|
};
|
|
|
|
g2dTexture g2d_disp_buffer = {
|
|
512, 512,
|
|
G2D_SCR_W, G2D_SCR_H,
|
|
(float)G2D_SCR_W/G2D_SCR_H,
|
|
false,
|
|
(g2dColor *)0
|
|
};
|
|
|
|
/* Internal functions */
|
|
|
|
static void _g2dStart(void) {
|
|
if (!init)
|
|
g2dInit();
|
|
|
|
sceKernelDcacheWritebackRange(dlist, DLIST_SIZE);
|
|
sceGuStart(GU_DIRECT, dlist);
|
|
start = true;
|
|
}
|
|
|
|
static void *_g2dSetVertex(void *vp, int i, float vx, float vy) {
|
|
// Vertex order: [texture uv] [color] [coord]
|
|
short *vp_short;
|
|
g2dColor *vp_color;
|
|
float *vp_float;
|
|
|
|
// Texture coordinates
|
|
vp_short = (short *)vp;
|
|
|
|
if (rctx.tex != NULL) {
|
|
*(vp_short++) = OBJ_I.crop_x + vx * OBJ_I.crop_w;
|
|
*(vp_short++) = OBJ_I.crop_y + vy * OBJ_I.crop_h;
|
|
}
|
|
|
|
// Color
|
|
vp_color = (g2dColor*)vp_short;
|
|
|
|
if (rctx.use_vert_color)
|
|
*(vp_color++) = OBJ_I.color;
|
|
|
|
// Coordinates
|
|
vp_float = (float *)vp_color;
|
|
|
|
vp_float[0] = OBJ_I.x;
|
|
vp_float[1] = OBJ_I.y;
|
|
|
|
if (rctx.type == RECTS) {
|
|
vp_float[0] += vx * OBJ_I.scale_w;
|
|
vp_float[1] += vy * OBJ_I.scale_h;
|
|
|
|
if (rctx.use_rot) {// Apply a rotation
|
|
float tx = vp_float[0] - OBJ_I.rot_x;
|
|
float ty = vp_float[1] - OBJ_I.rot_y;
|
|
|
|
vp_float[0] = OBJ_I.rot_x - OBJ_I.rot_sin*ty + OBJ_I.rot_cos*tx,
|
|
vp_float[1] = OBJ_I.rot_y + OBJ_I.rot_cos*ty + OBJ_I.rot_sin*tx;
|
|
}
|
|
}
|
|
|
|
if (rctx.use_int) {// Pixel perfect
|
|
vp_float[0] = floorf(vp_float[0]);
|
|
vp_float[1] = floorf(vp_float[1]);
|
|
}
|
|
vp_float[2] = OBJ_I.z;
|
|
|
|
return (void*)(vp_float + 3);
|
|
}
|
|
|
|
void vfpu_sincosf(float x, float *s, float *c) {
|
|
__asm__ volatile (
|
|
"mtv %2, s000\n" // s000 = x
|
|
"vcst.s s001, VFPU_2_PI\n" // s001 = 2/pi
|
|
"vmul.s s000, s000, s001\n" // s000 = s000*s001
|
|
"vrot.p c010, s000, [s, c]\n" // s010 = sinf(s000), s011 = cosf(s000)
|
|
"mfv %0, s010\n" // *s = s010
|
|
"mfv %1, S011\n" // *c = s011
|
|
: "=r"(*s), "=r"(*c) : "r"(x)
|
|
);
|
|
}
|
|
|
|
/* Main functions */
|
|
|
|
void g2dInit(void) {
|
|
if (init)
|
|
return;
|
|
|
|
// Display list allocation
|
|
dlist = malloc(DLIST_SIZE);
|
|
|
|
// Setup GU
|
|
sceGuInit();
|
|
sceGuStart(GU_DIRECT, dlist);
|
|
|
|
sceGuDrawBuffer(GU_PSM_8888, g2d_draw_buffer.data, LINE_SIZE);
|
|
sceGuDispBuffer(G2D_SCR_W, G2D_SCR_H, g2d_disp_buffer.data, LINE_SIZE);
|
|
sceGuDepthBuffer((void *)(FRAMEBUFFER_SIZE * 2), LINE_SIZE);
|
|
sceGuOffset(2048 - G2D_SCR_W / 2, 2048 - G2D_SCR_H / 2);
|
|
sceGuViewport(2048, 2048, G2D_SCR_W, G2D_SCR_H);
|
|
|
|
g2d_draw_buffer.data = vabsptr(g2d_draw_buffer.data);
|
|
g2d_disp_buffer.data = vabsptr(g2d_disp_buffer.data);
|
|
|
|
sceGuDepthRange(65535, 0);
|
|
sceGuClearDepth(65535);
|
|
sceGuAlphaFunc(GU_GREATER, 0, 255);
|
|
sceGuDepthFunc(GU_LEQUAL);
|
|
sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
|
|
sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
|
|
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
|
|
sceGuShadeModel(GU_SMOOTH);
|
|
|
|
sceGuDisable(GU_CULL_FACE);
|
|
sceGuDisable(GU_CLIP_PLANES);
|
|
sceGuDisable(GU_DITHER);
|
|
sceGuEnable(GU_ALPHA_TEST);
|
|
sceGuEnable(GU_SCISSOR_TEST);
|
|
sceGuEnable(GU_BLEND);
|
|
|
|
g2dResetGlobalScale();
|
|
g2dResetScissor();
|
|
|
|
sceGuFinish();
|
|
sceGuSync(0, 0);
|
|
sceDisplayWaitVblankStart();
|
|
sceGuDisplay(GU_TRUE);
|
|
|
|
init = true;
|
|
}
|
|
|
|
void g2dTerm(void) {
|
|
if (!init)
|
|
return;
|
|
|
|
sceGuTerm();
|
|
|
|
free(dlist);
|
|
|
|
init = false;
|
|
}
|
|
|
|
void g2dClear(g2dColor color) {
|
|
if (!start)
|
|
_g2dStart();
|
|
|
|
sceGuClearColor(color);
|
|
sceGuClear(GU_COLOR_BUFFER_BIT | GU_FAST_CLEAR_BIT | (zclear ? GU_DEPTH_BUFFER_BIT : 0));
|
|
|
|
zclear = false;
|
|
}
|
|
|
|
void g2dClearZ(void) {
|
|
if (!start)
|
|
_g2dStart();
|
|
|
|
sceGuClear(GU_DEPTH_BUFFER_BIT | GU_FAST_CLEAR_BIT);
|
|
zclear = true;
|
|
}
|
|
|
|
static void _g2dBeginCommon(Obj_Type type, g2dTexture *tex) {
|
|
if (begin)
|
|
return;
|
|
|
|
if (!start)
|
|
_g2dStart();
|
|
|
|
// Reset render context
|
|
rctx.obj = realloc(rctx.obj, MALLOC_STEP * sizeof(Object));
|
|
rctx.n = 0;
|
|
rctx.type = type;
|
|
rctx.tex = tex;
|
|
rctx.use_strip = false;
|
|
rctx.use_z = false;
|
|
rctx.use_vert_color = false;
|
|
rctx.use_rot = false;
|
|
rctx.use_tex_linear = true;
|
|
rctx.use_tex_repeat = false;
|
|
rctx.use_int = false;
|
|
rctx.color_count = 0;
|
|
rctx.coord_mode = DEFAULT_COORD_MODE;
|
|
|
|
// Reset current object
|
|
g2dReset();
|
|
|
|
begin = true;
|
|
}
|
|
|
|
void g2dBeginRects(g2dTexture *tex) {
|
|
_g2dBeginCommon(RECTS, tex);
|
|
}
|
|
|
|
void g2dBeginLines(g2dLine_Mode mode) {
|
|
_g2dBeginCommon(LINES, NULL);
|
|
rctx.use_strip = (mode & G2D_STRIP);
|
|
}
|
|
|
|
void g2dBeginQuads(g2dTexture *tex) {
|
|
_g2dBeginCommon(QUADS, tex);
|
|
}
|
|
|
|
void g2dBeginPoints(void) {
|
|
_g2dBeginCommon(POINTS, NULL);
|
|
}
|
|
|
|
static void _g2dEndRects(void) {
|
|
// Define vertices properties
|
|
int v_prim = (rctx.use_rot ? GU_TRIANGLES : GU_SPRITES);
|
|
int v_obj_nbr = (rctx.use_rot ? 6 : 2);
|
|
int v_nbr;
|
|
int v_coord_size = 3;
|
|
int v_tex_size = (rctx.tex != NULL ? 2 : 0);
|
|
int v_color_size = (rctx.use_vert_color ? 1 : 0);
|
|
int v_size = v_tex_size * sizeof(short) + v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
|
|
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
|
|
int i;
|
|
|
|
if (rctx.tex != NULL)
|
|
v_type |= GU_TEXTURE_16BIT;
|
|
if (rctx.use_vert_color)
|
|
v_type |= GU_COLOR_8888;
|
|
|
|
// Count how many vertices to allocate.
|
|
if (rctx.tex == NULL || rctx.use_rot) // No slicing
|
|
v_nbr = v_obj_nbr * rctx.n;
|
|
else { // Can use texture slicing for tremendous performance :)
|
|
v_nbr = 0;
|
|
|
|
for (i = 0; i < rctx.n; i++)
|
|
v_nbr += v_obj_nbr * ceilf(OBJ_I.crop_w/SLICE_WIDTH);
|
|
}
|
|
|
|
// Allocate vertex list memory
|
|
void *v = sceGuGetMemory(v_nbr * v_size);
|
|
void *vi = v;
|
|
|
|
// Build the vertex list
|
|
for (i = 0; i < rctx.n; i += 1) {
|
|
if (rctx.use_rot) { // Two triangles per object
|
|
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i, 1.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i, 0.f, 1.f);
|
|
vi = _g2dSetVertex(vi, i, 0.f, 1.f);
|
|
vi = _g2dSetVertex(vi, i, 1.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i, 1.f, 1.f);
|
|
}
|
|
else if (rctx.tex == NULL) { // One sprite per object
|
|
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i, 1.f, 1.f);
|
|
}
|
|
else { // Several sprites per object for a better texture cache use
|
|
float step = SLICE_WIDTH/OBJ_I.crop_w;
|
|
float u;
|
|
|
|
for (u = 0.f; u < 1.f; u += step) {
|
|
vi = _g2dSetVertex(vi, i, u, 0.f);
|
|
vi = _g2dSetVertex(vi, i, (u + step > 1.f ? 1.f : u+step), 1.f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then put it in the display list.
|
|
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
|
|
}
|
|
|
|
static void _g2dEndLines(void) {
|
|
// Define vertices properties
|
|
int v_prim = (rctx.use_strip ? GU_LINE_STRIP : GU_LINES);
|
|
int v_obj_nbr = (rctx.use_strip ? 1 : 2);
|
|
int v_nbr = v_obj_nbr * (rctx.use_strip ? rctx.n : rctx.n/2);
|
|
int v_coord_size = 3;
|
|
int v_color_size = (rctx.use_vert_color ? 1 : 0);
|
|
int v_size = v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
|
|
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
|
|
int i;
|
|
|
|
if (rctx.use_vert_color)
|
|
v_type |= GU_COLOR_8888;
|
|
|
|
// Allocate vertex list memory
|
|
void *v = sceGuGetMemory(v_nbr * v_size);
|
|
void *vi = v;
|
|
|
|
// Build the vertex list
|
|
if (rctx.use_strip) {
|
|
vi = _g2dSetVertex(vi, 0, 0.f, 0.f);
|
|
|
|
for (i = 1; i < rctx.n; i += 1)
|
|
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i + 1 < rctx.n; i += 2) {
|
|
vi = _g2dSetVertex(vi, i , 0.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i + 1, 0.f, 0.f);
|
|
}
|
|
}
|
|
|
|
// Then put it in the display list.
|
|
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
|
|
}
|
|
|
|
static void _g2dEndQuads(void) {
|
|
// Define vertices properties
|
|
int v_prim = GU_TRIANGLES;
|
|
int v_obj_nbr = 6;
|
|
int v_nbr = v_obj_nbr * (rctx.n / 4);
|
|
int v_coord_size = 3;
|
|
int v_tex_size = (rctx.tex != NULL ? 2 : 0);
|
|
int v_color_size = (rctx.use_vert_color ? 1 : 0);
|
|
int v_size = v_tex_size * sizeof(short) + v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
|
|
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
|
|
int i;
|
|
|
|
if (rctx.tex != NULL)
|
|
v_type |= GU_TEXTURE_16BIT;
|
|
if (rctx.use_vert_color)
|
|
v_type |= GU_COLOR_8888;
|
|
|
|
// Allocate vertex list memory
|
|
void *v = sceGuGetMemory(v_nbr * v_size);
|
|
void *vi = v;
|
|
|
|
// Build the vertex list
|
|
for (i = 0; i + 3 < rctx.n; i += 4) {
|
|
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i + 1, 1.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i + 3, 0.f, 1.f);
|
|
vi = _g2dSetVertex(vi, i + 3, 0.f, 1.f);
|
|
vi = _g2dSetVertex(vi, i + 1, 1.f, 0.f);
|
|
vi = _g2dSetVertex(vi, i + 2, 1.f, 1.f);
|
|
}
|
|
|
|
// Then put it in the display list.
|
|
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
|
|
}
|
|
|
|
static void _g2dEndPoints(void) {
|
|
// Define vertices properties
|
|
int v_prim = GU_POINTS;
|
|
int v_obj_nbr = 1;
|
|
int v_nbr = v_obj_nbr * rctx.n;
|
|
int v_coord_size = 3;
|
|
int v_color_size = (rctx.use_vert_color ? 1 : 0);
|
|
int v_size = v_color_size * sizeof(g2dColor) + v_coord_size * sizeof(float);
|
|
int v_type = GU_VERTEX_32BITF | GU_TRANSFORM_2D;
|
|
int i;
|
|
|
|
if (rctx.use_vert_color)
|
|
v_type |= GU_COLOR_8888;
|
|
|
|
// Allocate vertex list memory
|
|
void *v = sceGuGetMemory(v_nbr * v_size);
|
|
void *vi = v;
|
|
|
|
// Build the vertex list
|
|
for (i = 0; i < rctx.n; i += 1)
|
|
vi = _g2dSetVertex(vi, i, 0.f, 0.f);
|
|
|
|
// Then put it in the display list.
|
|
sceGuDrawArray(v_prim, v_type, v_nbr, NULL, v);
|
|
}
|
|
|
|
void g2dEnd(void) {
|
|
if (!begin || rctx.n == 0) {
|
|
begin = false;
|
|
return;
|
|
}
|
|
|
|
// Manage pspgu extensions
|
|
|
|
if (rctx.use_z)
|
|
sceGuEnable(GU_DEPTH_TEST);
|
|
else
|
|
sceGuDisable(GU_DEPTH_TEST);
|
|
|
|
if (rctx.use_vert_color)
|
|
sceGuColor(WHITE);
|
|
else
|
|
sceGuColor(rctx.cur_obj.color);
|
|
|
|
if (rctx.tex == NULL)
|
|
sceGuDisable(GU_TEXTURE_2D);
|
|
else {
|
|
sceGuEnable(GU_TEXTURE_2D);
|
|
|
|
if (rctx.use_tex_linear)
|
|
sceGuTexFilter(GU_LINEAR, GU_LINEAR);
|
|
else
|
|
sceGuTexFilter(GU_NEAREST, GU_NEAREST);
|
|
|
|
if (rctx.use_tex_repeat)
|
|
sceGuTexWrap(GU_REPEAT, GU_REPEAT);
|
|
else
|
|
sceGuTexWrap(GU_CLAMP, GU_CLAMP);
|
|
|
|
// Load texture
|
|
sceGuTexMode(GU_PSM_8888, 0, 0, rctx.tex->swizzled);
|
|
sceGuTexImage(0, rctx.tex->tw, rctx.tex->th, rctx.tex->tw, rctx.tex->data);
|
|
}
|
|
|
|
switch (rctx.type) {
|
|
case RECTS:
|
|
_g2dEndRects();
|
|
break;
|
|
|
|
case LINES:
|
|
_g2dEndLines();
|
|
break;
|
|
|
|
case QUADS:
|
|
_g2dEndQuads();
|
|
break;
|
|
|
|
case POINTS:
|
|
_g2dEndPoints();
|
|
break;
|
|
}
|
|
|
|
sceGuColor(WHITE);
|
|
|
|
if (rctx.use_z)
|
|
zclear = true;
|
|
|
|
begin = false;
|
|
}
|
|
|
|
void g2dReset(void) {
|
|
g2dResetCoord();
|
|
g2dResetScale();
|
|
g2dResetColor();
|
|
g2dResetAlpha();
|
|
g2dResetRotation();
|
|
g2dResetCrop();
|
|
g2dResetTex();
|
|
}
|
|
|
|
void g2dFlip(g2dFlip_Mode mode) {
|
|
if (scissor)
|
|
g2dResetScissor();
|
|
|
|
sceGuFinish();
|
|
sceGuSync(0, 0);
|
|
|
|
if (mode & G2D_VSYNC)
|
|
sceDisplayWaitVblankStart();
|
|
|
|
g2d_disp_buffer.data = g2d_draw_buffer.data;
|
|
g2d_draw_buffer.data = vabsptr(sceGuSwapBuffers());
|
|
|
|
start = false;
|
|
}
|
|
|
|
void g2dAdd(void) {
|
|
if (!begin || rctx.cur_obj.scale_w == 0.f || rctx.cur_obj.scale_h == 0.f)
|
|
return;
|
|
|
|
if (rctx.n % MALLOC_STEP == 0)
|
|
rctx.obj = realloc(rctx.obj, (rctx.n+MALLOC_STEP) * sizeof(Object));
|
|
|
|
rctx.n++;
|
|
OBJ = rctx.cur_obj;
|
|
|
|
// Coordinate mode stuff
|
|
OBJ.rot_x = OBJ.x;
|
|
OBJ.rot_y = OBJ.y;
|
|
|
|
switch (rctx.coord_mode) {
|
|
case G2D_UP_RIGHT:
|
|
OBJ.x -= OBJ.scale_w;
|
|
break;
|
|
|
|
case G2D_DOWN_RIGHT:
|
|
OBJ.x -= OBJ.scale_w;
|
|
OBJ.y -= OBJ.scale_h;
|
|
break;
|
|
|
|
case G2D_DOWN_LEFT:
|
|
OBJ.y -= OBJ.scale_h;
|
|
break;
|
|
|
|
case G2D_CENTER:
|
|
OBJ.x -= OBJ.scale_w / 2.f;
|
|
OBJ.y -= OBJ.scale_h / 2.f;
|
|
break;
|
|
|
|
case G2D_UP_LEFT:
|
|
default:
|
|
break;
|
|
};
|
|
|
|
// Alpha stuff
|
|
OBJ.color = G2D_MODULATE(OBJ.color, 255, rctx.cur_obj.alpha);
|
|
}
|
|
|
|
void g2dPush(void) {
|
|
if (tstack_size >= TSTACK_MAX)
|
|
return;
|
|
|
|
tstack_size++;
|
|
|
|
TRANSFORM.x = rctx.cur_obj.x;
|
|
TRANSFORM.y = rctx.cur_obj.y;
|
|
TRANSFORM.z = rctx.cur_obj.z;
|
|
TRANSFORM.rot = rctx.cur_obj.rot;
|
|
TRANSFORM.rot_sin = rctx.cur_obj.rot_sin;
|
|
TRANSFORM.rot_cos = rctx.cur_obj.rot_cos;
|
|
TRANSFORM.scale_w = rctx.cur_obj.scale_w;
|
|
TRANSFORM.scale_h = rctx.cur_obj.scale_h;
|
|
}
|
|
|
|
void g2dPop(void) {
|
|
if (tstack_size <= 0)
|
|
return;
|
|
|
|
rctx.cur_obj.x = TRANSFORM.x;
|
|
rctx.cur_obj.y = TRANSFORM.y;
|
|
rctx.cur_obj.z = TRANSFORM.z;
|
|
rctx.cur_obj.rot = TRANSFORM.rot;
|
|
rctx.cur_obj.rot_sin = TRANSFORM.rot_sin;
|
|
rctx.cur_obj.rot_cos = TRANSFORM.rot_cos;
|
|
rctx.cur_obj.scale_w = TRANSFORM.scale_w;
|
|
rctx.cur_obj.scale_h = TRANSFORM.scale_h;
|
|
|
|
tstack_size--;
|
|
|
|
if (rctx.cur_obj.rot != 0.f)
|
|
rctx.use_rot = true;
|
|
if (rctx.cur_obj.z != 0.f)
|
|
rctx.use_z = true;
|
|
}
|
|
|
|
/* Coord functions */
|
|
|
|
void g2dResetCoord(void) {
|
|
rctx.cur_obj.x = DEFAULT_X;
|
|
rctx.cur_obj.y = DEFAULT_Y;
|
|
rctx.cur_obj.z = DEFAULT_Z;
|
|
}
|
|
|
|
void g2dSetCoordMode(g2dCoord_Mode mode) {
|
|
if (mode > G2D_CENTER)
|
|
return;
|
|
|
|
rctx.coord_mode = mode;
|
|
}
|
|
|
|
void g2dGetCoordXYZ(float *x, float *y, float *z) {
|
|
if (x != NULL) *x = rctx.cur_obj.x;
|
|
if (y != NULL) *y = rctx.cur_obj.y;
|
|
if (z != NULL) *z = rctx.cur_obj.z;
|
|
}
|
|
|
|
void g2dSetCoordXY(float x, float y) {
|
|
rctx.cur_obj.x = x * global_scale;
|
|
rctx.cur_obj.y = y * global_scale;
|
|
rctx.cur_obj.z = 0.f;
|
|
}
|
|
|
|
void g2dSetCoordXYZ(float x, float y, float z) {
|
|
rctx.cur_obj.x = x * global_scale;
|
|
rctx.cur_obj.y = y * global_scale;
|
|
rctx.cur_obj.z = z * global_scale;
|
|
|
|
if (z != 0.f)
|
|
rctx.use_z = true;
|
|
}
|
|
|
|
void g2dSetCoordXYRelative(float x, float y) {
|
|
float inc_x = x;
|
|
float inc_y = y;
|
|
|
|
if (rctx.cur_obj.rot_cos != 1.f) {
|
|
inc_x = -rctx.cur_obj.rot_sin*y + rctx.cur_obj.rot_cos*x;
|
|
inc_y = rctx.cur_obj.rot_cos*y + rctx.cur_obj.rot_sin*x;
|
|
}
|
|
|
|
rctx.cur_obj.x += inc_x * global_scale;
|
|
rctx.cur_obj.y += inc_y * global_scale;
|
|
}
|
|
|
|
void g2dSetCoordXYZRelative(float x, float y, float z) {
|
|
g2dSetCoordXYRelative(x, y);
|
|
|
|
rctx.cur_obj.z += z * global_scale;
|
|
|
|
if (z != 0.f)
|
|
rctx.use_z = true;
|
|
}
|
|
|
|
void g2dSetCoordInteger(bool use) {
|
|
rctx.use_int = use;
|
|
}
|
|
|
|
/* Scale functions */
|
|
|
|
void g2dResetGlobalScale(void) {
|
|
global_scale = 1.f;
|
|
}
|
|
|
|
void g2dResetScale(void) {
|
|
if (rctx.tex == NULL) {
|
|
rctx.cur_obj.scale_w = DEFAULT_SIZE;
|
|
rctx.cur_obj.scale_h = DEFAULT_SIZE;
|
|
}
|
|
else {
|
|
rctx.cur_obj.scale_w = rctx.tex->w;
|
|
rctx.cur_obj.scale_h = rctx.tex->h;
|
|
}
|
|
|
|
rctx.cur_obj.scale_w *= global_scale;
|
|
rctx.cur_obj.scale_h *= global_scale;
|
|
}
|
|
|
|
void g2dGetGlobalScale(float *scale) {
|
|
if (scale != NULL)
|
|
*scale = global_scale;
|
|
}
|
|
|
|
void g2dGetScaleWH(float *w, float *h) {
|
|
if (w != NULL)
|
|
*w = rctx.cur_obj.scale_w;
|
|
if (h != NULL)
|
|
*h = rctx.cur_obj.scale_h;
|
|
}
|
|
|
|
void g2dSetGlobalScale(float scale) {
|
|
global_scale = scale;
|
|
}
|
|
|
|
void g2dSetScale(float w, float h) {
|
|
g2dResetScale();
|
|
g2dSetScaleRelative(w, h);
|
|
}
|
|
|
|
void g2dSetScaleWH(float w, float h) {
|
|
rctx.cur_obj.scale_w = w * global_scale;
|
|
rctx.cur_obj.scale_h = h * global_scale;
|
|
|
|
// A trick to prevent an unexpected behavior when mirroring with GU_SPRITES.
|
|
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
|
|
rctx.use_rot = true;
|
|
}
|
|
|
|
void g2dSetScaleRelative(float w, float h) {
|
|
rctx.cur_obj.scale_w *= w;
|
|
rctx.cur_obj.scale_h *= h;
|
|
|
|
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
|
|
rctx.use_rot = true;
|
|
}
|
|
|
|
void g2dSetScaleWHRelative(float w, float h) {
|
|
rctx.cur_obj.scale_w += w * global_scale;
|
|
rctx.cur_obj.scale_h += h * global_scale;
|
|
|
|
if (rctx.cur_obj.scale_w < 0 || rctx.cur_obj.scale_h < 0)
|
|
rctx.use_rot = true;
|
|
}
|
|
|
|
/* Color functions */
|
|
|
|
void g2dResetColor(void) {
|
|
rctx.cur_obj.color = DEFAULT_COLOR;
|
|
}
|
|
|
|
void g2dResetAlpha(void) {
|
|
rctx.cur_obj.alpha = DEFAULT_ALPHA;
|
|
}
|
|
|
|
void g2dGetAlpha(g2dAlpha *alpha) {
|
|
if (alpha != NULL)
|
|
*alpha = rctx.cur_obj.alpha;
|
|
}
|
|
|
|
void g2dSetColor(g2dColor color) {
|
|
rctx.cur_obj.color = color;
|
|
|
|
if (++rctx.color_count > 1)
|
|
rctx.use_vert_color = true;
|
|
}
|
|
|
|
void g2dSetAlpha(g2dAlpha alpha) {
|
|
if (alpha < 0)
|
|
alpha = 0;
|
|
if (alpha > 255)
|
|
alpha = 255;
|
|
|
|
rctx.cur_obj.alpha = alpha;
|
|
|
|
if (++rctx.color_count > 1)
|
|
rctx.use_vert_color = true;
|
|
}
|
|
|
|
void g2dSetAlphaRelative(int alpha) {
|
|
g2dSetAlpha(rctx.cur_obj.alpha + alpha);
|
|
}
|
|
|
|
/* Rotation functions */
|
|
|
|
void g2dResetRotation(void) {
|
|
rctx.cur_obj.rot = 0.f;
|
|
rctx.cur_obj.rot_sin = 0.f;
|
|
rctx.cur_obj.rot_cos = 1.f;
|
|
}
|
|
|
|
void g2dGetRotationRad(float *radians) {
|
|
if (radians != NULL)
|
|
*radians = rctx.cur_obj.rot;
|
|
}
|
|
|
|
void g2dGetRotation(float *degrees) {
|
|
if (degrees != NULL)
|
|
*degrees = rctx.cur_obj.rot * M_180_PI;
|
|
}
|
|
|
|
void g2dSetRotationRad(float radians) {
|
|
if (radians == rctx.cur_obj.rot)
|
|
return;
|
|
|
|
rctx.cur_obj.rot = radians;
|
|
vfpu_sincosf(radians, &rctx.cur_obj.rot_sin, &rctx.cur_obj.rot_cos);
|
|
|
|
if (radians != 0.f)
|
|
rctx.use_rot = true;
|
|
}
|
|
|
|
void g2dSetRotation(float degrees) {
|
|
g2dSetRotationRad(degrees * M_PI_180);
|
|
}
|
|
|
|
void g2dSetRotationRadRelative(float radians) {
|
|
g2dSetRotationRad(rctx.cur_obj.rot + radians);
|
|
}
|
|
|
|
void g2dSetRotationRelative(float degrees) {
|
|
g2dSetRotationRadRelative(degrees * M_PI_180);
|
|
}
|
|
|
|
/* Crop functions */
|
|
|
|
void g2dResetCrop(void) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.cur_obj.crop_x = 0;
|
|
rctx.cur_obj.crop_y = 0;
|
|
rctx.cur_obj.crop_w = rctx.tex->w;
|
|
rctx.cur_obj.crop_h = rctx.tex->h;
|
|
}
|
|
|
|
void g2dGetCropXY(int *x, int *y) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
if (x != NULL)
|
|
*x = rctx.cur_obj.crop_x;
|
|
if (y != NULL)
|
|
*y = rctx.cur_obj.crop_y;
|
|
}
|
|
|
|
void g2dGetCropWH(int *w, int *h) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
if (w != NULL)
|
|
*w = rctx.cur_obj.crop_w;
|
|
if (h != NULL)
|
|
*h = rctx.cur_obj.crop_h;
|
|
}
|
|
|
|
void g2dSetCropXY(int x, int y) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.cur_obj.crop_x = x;
|
|
rctx.cur_obj.crop_y = y;
|
|
}
|
|
|
|
void g2dSetCropWH(int w, int h) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.cur_obj.crop_w = w;
|
|
rctx.cur_obj.crop_h = h;
|
|
}
|
|
|
|
void g2dSetCropXYRelative(int x, int y) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
g2dSetCropXY(rctx.cur_obj.crop_x + x, rctx.cur_obj.crop_y + y);
|
|
}
|
|
|
|
|
|
void g2dSetCropWHRelative(int w, int h) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
g2dSetCropWH(rctx.cur_obj.crop_w + w, rctx.cur_obj.crop_h + h);
|
|
}
|
|
|
|
/* Texture functions */
|
|
|
|
void g2dResetTex(void) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.use_tex_repeat = false;
|
|
rctx.use_tex_linear = true;
|
|
}
|
|
|
|
void g2dSetTexRepeat(bool use) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.use_tex_repeat = use;
|
|
}
|
|
|
|
void g2dSetTexLinear(bool use) {
|
|
if (rctx.tex == NULL)
|
|
return;
|
|
|
|
rctx.use_tex_linear = use;
|
|
}
|
|
|
|
/* Texture management */
|
|
|
|
static unsigned int _getNextPower2(unsigned int n) {
|
|
n--;
|
|
n |= n >> 1;
|
|
n |= n >> 2;
|
|
n |= n >> 4;
|
|
n |= n >> 8;
|
|
n |= n >> 16;
|
|
|
|
return n + 1;
|
|
}
|
|
|
|
static void _swizzle(unsigned char *dest, unsigned char *source, int width, int height) {
|
|
int i, j;
|
|
int rowblocks = (width / 16);
|
|
int rowblocks_add = (rowblocks-1) * 128;
|
|
unsigned int block_address = 0;
|
|
unsigned int *img = (unsigned int*)source;
|
|
|
|
for (j = 0; j < height; j++) {
|
|
unsigned int *block = (unsigned int *)(dest + block_address);
|
|
|
|
for (i = 0; i < rowblocks; i++) {
|
|
*block++ = *img++;
|
|
*block++ = *img++;
|
|
*block++ = *img++;
|
|
*block++ = *img++;
|
|
|
|
block += 28;
|
|
}
|
|
|
|
if ((j & 0x7) == 0x7)
|
|
block_address += rowblocks_add;
|
|
|
|
block_address += 16;
|
|
}
|
|
}
|
|
|
|
g2dTexture *g2dTexCreate(int w, int h) {
|
|
g2dTexture *tex = malloc(sizeof(g2dTexture));
|
|
if (tex == NULL)
|
|
return NULL;
|
|
|
|
tex->tw = _getNextPower2(w);
|
|
tex->th = _getNextPower2(h);
|
|
tex->w = w;
|
|
tex->h = h;
|
|
tex->ratio = (float)w / h;
|
|
tex->swizzled = false;
|
|
|
|
tex->data = malloc(tex->tw * tex->th * sizeof(g2dColor));
|
|
if (tex->data == NULL) {
|
|
free(tex);
|
|
return NULL;
|
|
}
|
|
|
|
memset(tex->data, 0, tex->tw * tex->th * sizeof(g2dColor));
|
|
|
|
return tex;
|
|
}
|
|
|
|
void g2dTexFree(g2dTexture **tex) {
|
|
if (tex == NULL)
|
|
return;
|
|
if (*tex == NULL)
|
|
return;
|
|
|
|
free((*tex)->data);
|
|
free((*tex));
|
|
|
|
*tex = NULL;
|
|
}
|
|
|
|
static g2dTexture *_g2dTexLoadData(void *data, int width, int height) {
|
|
g2dTexture *tex = NULL;
|
|
g2dColor *line = NULL;
|
|
u32 row = 0, col = 0;
|
|
|
|
line = data;
|
|
tex = g2dTexCreate(width, height);
|
|
|
|
for (row = 0; row < tex->w; row++) {
|
|
for (col = 0; col < tex->h; col++)
|
|
tex->data[row + col * tex->tw] = line[(row + col * tex->w)];
|
|
}
|
|
|
|
return tex;
|
|
}
|
|
|
|
g2dTexture *g2dTexLoad(void *data, int width, int height, g2dTex_Mode mode) {
|
|
g2dTexture *tex = NULL;
|
|
|
|
if (data == NULL)
|
|
return NULL;
|
|
|
|
tex = _g2dTexLoadData(data, width, height);
|
|
|
|
if (tex == NULL)
|
|
goto error;
|
|
|
|
// The PSP can't draw 512*512+ textures.
|
|
if (tex->w > 512 || tex->h > 512)
|
|
goto error;
|
|
|
|
// Swizzling is useless with small textures.
|
|
if ((mode & G2D_SWIZZLE) && (tex->w >= 16 || tex->h >= 16)) {
|
|
u8 *tmp = malloc(tex->tw*tex->th*PIXEL_SIZE);
|
|
_swizzle(tmp, (u8*)tex->data, tex->tw*PIXEL_SIZE, tex->th);
|
|
free(tex->data);
|
|
tex->data = (g2dColor*)tmp;
|
|
tex->swizzled = true;
|
|
}
|
|
else
|
|
tex->swizzled = false;
|
|
|
|
sceKernelDcacheWritebackRange(tex->data, tex->tw * tex->th * PIXEL_SIZE);
|
|
|
|
return tex;
|
|
|
|
// Load failure... abort
|
|
error:
|
|
|
|
g2dTexFree(&tex);
|
|
return NULL;
|
|
}
|
|
|
|
/* Scissor functions */
|
|
|
|
void g2dResetScissor(void) {
|
|
g2dSetScissor(0, 0, G2D_SCR_W, G2D_SCR_H);
|
|
scissor = false;
|
|
}
|
|
|
|
void g2dSetScissor(int x, int y, int w, int h) {
|
|
sceGuScissor(x, y, x+w, y+h);
|
|
scissor = true;
|
|
}
|
|
|
|
// EOF
|