|
|
8535a3 |
|
|
|
f63775 |
#include <sdl_image.h></sdl_image.h>
|
|
|
8535a3 |
|
|
|
8535a3 |
#include "private.h"
|
|
|
8935bc |
#include "world.h"
|
|
|
dba3fc |
#include "animation.h"
|
|
|
8535a3 |
|
|
|
8535a3 |
static HeliArray cache;
|
|
|
dba3fc |
static Animation first, last;
|
|
|
dba3fc |
|
|
|
dba3fc |
typedef struct _HeliTexture {
|
|
|
dba3fc |
const char *key;
|
|
|
dba3fc |
unsigned int id;
|
|
|
0cbd57 |
int own;
|
|
|
dba3fc |
int refcount;
|
|
|
dba3fc |
} HeliTexture;
|
|
|
dba3fc |
|
|
|
dba3fc |
struct _Animation {
|
|
|
dba3fc |
int playing;
|
|
|
dba3fc |
int loop;
|
|
|
dba3fc |
double fps;
|
|
|
dba3fc |
double pos;
|
|
|
dba3fc |
HeliArray frames;
|
|
|
dba3fc |
Animation prev, next;
|
|
|
dba3fc |
};
|
|
|
8535a3 |
|
|
|
f63775 |
|
|
|
f63775 |
|
|
|
f63775 |
static int fixsize(int x) {
|
|
|
f63775 |
int p = (int)ceil(log2(x > 1 ? x : 1) - 0.25);
|
|
|
f63775 |
if (p > 30) p = 30;
|
|
|
f63775 |
return 1 << p;
|
|
|
f63775 |
}
|
|
|
f63775 |
|
|
|
f63775 |
|
|
|
f63775 |
static float colorclamp(float x)
|
|
|
f63775 |
{ return x > 0.f ? (x < 1.f ? x : 1.f) : 0.f; }
|
|
|
f63775 |
|
|
|
f63775 |
|
|
|
f63775 |
static void mipx(float *buffer, int *w, int *h) {
|
|
|
f63775 |
*w /= 2;
|
|
|
f63775 |
if (*w <= 0) return;
|
|
|
a20939 |
for(float *d = buffer, *s = buffer, *end = d + (*w)*(*h)*4; d < end; d += 4, s += 8)
|
|
|
f63775 |
for(int i = 0; i < 4; ++i)
|
|
|
f63775 |
d[i] = (s[i] + s[i + 4])*0.5f;
|
|
|
f63775 |
}
|
|
|
f63775 |
|
|
|
f63775 |
|
|
|
f63775 |
static void mipy(float *buffer, int *w, int *h) {
|
|
|
f63775 |
*h /= 2;
|
|
|
f63775 |
if (*h <= 0) return;
|
|
|
a20939 |
int rstep = (*w)*4;
|
|
|
a20939 |
float *d = buffer, *s0 = buffer, *s1 = s0 + rstep;
|
|
|
a20939 |
for(float *end = d + (*h)*rstep; d < end; s0 += rstep, s1 += rstep)
|
|
|
a20939 |
for(float *rend = d + rstep; d < rend; ++d, ++s0, ++s1)
|
|
|
f63775 |
*d = (*s0 + *s1)*0.5f;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
ca6bde |
int imageLoadFromSDL(SDL_Surface *surface, int *outWidth, int *outHeight, unsigned char **outPixels) {
|
|
|
dba3fc |
*outWidth = 0;
|
|
|
dba3fc |
*outHeight = 0;
|
|
|
dba3fc |
*outPixels = NULL;
|
|
|
ca6bde |
if (!surface) return FALSE;
|
|
|
f63775 |
|
|
|
f63775 |
// convert to RGBA
|
|
|
f63775 |
if (surface->format->format != SDL_PIXELFORMAT_RGBA8888) {
|
|
|
f63775 |
SDL_PixelFormat *format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA8888);
|
|
|
f63775 |
SDL_Surface *converted = SDL_ConvertSurface(surface, format, 0);
|
|
|
f63775 |
SDL_FreeFormat(format);
|
|
|
f63775 |
surface = converted;
|
|
|
f63775 |
}
|
|
|
f63775 |
|
|
|
dba3fc |
// copy
|
|
|
f63775 |
SDL_LockSurface(surface);
|
|
|
f63775 |
int w = surface->w;
|
|
|
f63775 |
int h = surface->h;
|
|
|
dba3fc |
assert(w > 0 && h > 0);
|
|
|
dba3fc |
size_t rsize = (size_t)w*4;
|
|
|
dba3fc |
unsigned char *pixels = malloc(h*rsize);
|
|
|
d4e89f |
for(int i = 0; i < h; ++i) {
|
|
|
d4e89f |
const unsigned char *pp = surface->pixels + surface->pitch*i;
|
|
|
d4e89f |
for(unsigned char *p = pixels + i*rsize, *end = p + rsize; p < end; p += 4, pp += 4) {
|
|
|
d4e89f |
p[0] = pp[3];
|
|
|
d4e89f |
p[1] = pp[2];
|
|
|
d4e89f |
p[2] = pp[1];
|
|
|
d4e89f |
p[3] = pp[0];
|
|
|
d4e89f |
}
|
|
|
d4e89f |
}
|
|
|
dba3fc |
SDL_UnlockSurface(surface);
|
|
|
dba3fc |
|
|
|
dba3fc |
*outPixels = pixels;
|
|
|
dba3fc |
*outWidth = w;
|
|
|
dba3fc |
*outHeight = h;
|
|
|
dba3fc |
return TRUE;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
ca6bde |
int imageLoadFromMemory(const void *data, int size, int *outWidth, int *outHeight, unsigned char **outPixels) {
|
|
|
ca6bde |
SDL_RWops *rw = data && size > 0 ? SDL_RWFromMem((void*)data, size) : NULL;
|
|
|
ca6bde |
SDL_Surface *surface = rw ? IMG_Load_RW(rw, SDL_TRUE) : NULL;
|
|
|
ca6bde |
int res = imageLoadFromSDL(surface, outWidth, outHeight, outPixels);
|
|
|
ca6bde |
if (surface) {
|
|
|
ca6bde |
SDL_FreeSurface(surface);
|
|
|
ca6bde |
} else {
|
|
|
ca6bde |
fprintf(stderr, "helianthus: cannot load image from memory\n");
|
|
|
ca6bde |
}
|
|
|
ca6bde |
return res;
|
|
|
ca6bde |
}
|
|
|
ca6bde |
|
|
|
ca6bde |
int imageLoad(const char *path, int *outWidth, int *outHeight, unsigned char **outPixels) {
|
|
|
ca6bde |
SDL_Surface *surface = IMG_Load(path);
|
|
|
ca6bde |
int res = imageLoadFromSDL(surface, outWidth, outHeight, outPixels);
|
|
|
ca6bde |
if (surface) {
|
|
|
ca6bde |
SDL_FreeSurface(surface);
|
|
|
ca6bde |
} else {
|
|
|
ca6bde |
fprintf(stderr, "helianthus: cannot load image: %s\n", path);
|
|
|
ca6bde |
}
|
|
|
ca6bde |
return res;
|
|
|
ca6bde |
}
|
|
|
ca6bde |
|
|
|
dba3fc |
|
|
|
0cbd57 |
int imageSave(const char *path, int width, int height, const void *pixels) {
|
|
|
0cbd57 |
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
|
0cbd57 |
Uint32 rm = 0xff000000,
|
|
|
0cbd57 |
gm = 0x00ff0000,
|
|
|
0cbd57 |
bm = 0x0000ff00,
|
|
|
0cbd57 |
am = 0x000000ff;
|
|
|
0cbd57 |
#else
|
|
|
0cbd57 |
Uint32 rm = 0x000000ff,
|
|
|
0cbd57 |
gm = 0x0000ff00,
|
|
|
0cbd57 |
bm = 0x00ff0000,
|
|
|
0cbd57 |
am = 0xff000000;
|
|
|
0cbd57 |
#endif
|
|
|
0cbd57 |
|
|
|
0cbd57 |
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(SDL_const_cast(void*, pixels), width, height, 32, width*4, rm, gm, bm, am);
|
|
|
0cbd57 |
|
|
|
0cbd57 |
if (!surface) {
|
|
|
0cbd57 |
fprintf(stderr, "helianthus: cannot create SDL surface to save image: %s\n", SDL_GetError());
|
|
|
0cbd57 |
return FALSE;
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
|
|
|
0cbd57 |
if (IMG_SavePNG(surface, path) != 0) {
|
|
|
0cbd57 |
fprintf(stderr, "helianthus: cannot save image to file: %s, : %s\n", path, SDL_GetError());
|
|
|
0cbd57 |
SDL_FreeSurface(surface);
|
|
|
0cbd57 |
return FALSE;
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
|
|
|
0cbd57 |
SDL_FreeSurface(surface);
|
|
|
0cbd57 |
return TRUE;
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
|
|
|
0cbd57 |
|
|
|
ba5c91 |
unsigned int imageToGLTexture(int width, int height, const void *pixels, int wrap)
|
|
|
ba5c91 |
{ return imageToGLTextureEx(width, height, pixels, wrap, wrap, TRUE, TRUE); }
|
|
|
ba5c91 |
|
|
|
ba5c91 |
|
|
|
1d641c |
int imageToExistingGLTexture(unsigned int texid, int width, int height, const void *pixels) {
|
|
|
1d641c |
if (!texid || width <= 0 || height <= 0 || !pixels) return FALSE;
|
|
|
1d641c |
|
|
|
dba3fc |
// convert to float and premult alpha
|
|
|
dba3fc |
size_t count = (size_t)width*height*4;
|
|
|
f63775 |
float *buffer = (float*)malloc(count*sizeof(float));
|
|
|
ba5c91 |
const unsigned char *pp = (const unsigned char *)pixels;
|
|
|
dba3fc |
for(float *p = buffer, *end = p + count; p < end; p += 4, pp += 4) {
|
|
|
d4e89f |
if (pp[3]) {
|
|
|
d4e89f |
float a = pp[3]/255.f;
|
|
|
d4e89f |
p[0] = pp[0]/255.f*a;
|
|
|
d4e89f |
p[1] = pp[1]/255.f*a;
|
|
|
d4e89f |
p[2] = pp[2]/255.f*a;
|
|
|
dba3fc |
p[3] = a;
|
|
|
dba3fc |
} else {
|
|
|
dba3fc |
p[0] = p[1] = p[2] = p[3] = 0;
|
|
|
f63775 |
}
|
|
|
f63775 |
}
|
|
|
f63775 |
|
|
|
f63775 |
// resample to power of two
|
|
|
dba3fc |
int w = width, h = height;
|
|
|
f63775 |
int fw = fixsize(w);
|
|
|
f63775 |
int fh = fixsize(h);
|
|
|
f63775 |
if (fw != w || fh != h) {
|
|
|
f63775 |
float *fbuffer = (float*)malloc((size_t)fw*fh*4*sizeof(float));
|
|
|
59dae5 |
double kr = (double)h/fh;
|
|
|
59dae5 |
double kc = (double)w/fw;
|
|
|
f63775 |
for(int r = 0; r < fh; ++r) {
|
|
|
f63775 |
for(int c = 0; c < fw; ++c) {
|
|
|
f63775 |
double pr1 = r*kr;
|
|
|
f63775 |
int r0 = (int)floor(pr1 + HELI_PRECISION);
|
|
|
f63775 |
pr1 -= r0;
|
|
|
f63775 |
int r1 = r0 + 1;
|
|
|
502515 |
if (r1 >= h) r1 = h - 1;
|
|
|
f63775 |
if (r0 > r1) r0 = r1;
|
|
|
f63775 |
double pr0 = 1 - pr1;
|
|
|
f63775 |
|
|
|
f63775 |
double pc1 = c*kc;
|
|
|
f63775 |
int c0 = (int)floor(pc1 + HELI_PRECISION);
|
|
|
f63775 |
pc1 -= c0;
|
|
|
f63775 |
int c1 = c0 + 1;
|
|
|
59dae5 |
if (c1 >= w) c1 = w - 1;
|
|
|
f63775 |
if (c0 > c1) c0 = c1;
|
|
|
f63775 |
double pc0 = 1 - pc1;
|
|
|
f63775 |
|
|
|
f63775 |
float *p = fbuffer + (r*fw + c)*4;
|
|
|
f63775 |
const float *p00 = buffer + (r0*w + c0)*4;
|
|
|
f63775 |
const float *p01 = buffer + (r0*w + c1)*4;
|
|
|
f63775 |
const float *p10 = buffer + (r1*w + c0)*4;
|
|
|
f63775 |
const float *p11 = buffer + (r1*w + c1)*4;
|
|
|
f63775 |
for(int i = 0; i < 4; ++i)
|
|
|
f63775 |
p[i] = p00[i]*pr0*pc0
|
|
|
f63775 |
+ p01[i]*pr0*pc1
|
|
|
f63775 |
+ p10[i]*pr1*pc0
|
|
|
f63775 |
+ p11[i]*pr1*pc1;
|
|
|
f63775 |
}
|
|
|
f63775 |
}
|
|
|
f63775 |
free(buffer);
|
|
|
f63775 |
buffer = fbuffer;
|
|
|
f63775 |
w = fw;
|
|
|
f63775 |
h = fh;
|
|
|
f63775 |
}
|
|
|
f63775 |
|
|
|
f63775 |
// fix max texture size
|
|
|
f63775 |
int maxSize = 0;
|
|
|
f63775 |
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
|
|
|
f63775 |
if (maxSize < 64) maxSize = 64;
|
|
|
f63775 |
while(w > maxSize) mipx(buffer, &w, &h);
|
|
|
f63775 |
while(h > maxSize) mipy(buffer, &w, &h);
|
|
|
f63775 |
|
|
|
1d641c |
|
|
|
1d641c |
// upload to OpenGL texture
|
|
|
1d641c |
unsigned int prevtex = 0;
|
|
|
1d641c |
int minFilter = 0;
|
|
|
0cbd57 |
glGetIntegerv(GL_TEXTURE_BINDING_2D, (int*)&prevtex);
|
|
|
f63775 |
glBindTexture(GL_TEXTURE_2D, texid);
|
|
|
1d641c |
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &minFilter);
|
|
|
1d641c |
|
|
|
1d641c |
int mipMap = minFilter == GL_NEAREST_MIPMAP_NEAREST
|
|
|
1d641c |
|| minFilter == GL_NEAREST_MIPMAP_LINEAR
|
|
|
1d641c |
|| minFilter == GL_LINEAR_MIPMAP_NEAREST
|
|
|
1d641c |
|| minFilter == GL_LINEAR_MIPMAP_LINEAR;
|
|
|
a20939 |
|
|
|
f63775 |
// build mip levels
|
|
|
f63775 |
unsigned char *ibuffer = malloc((size_t)w*h*4);
|
|
|
a20939 |
int level = 0;
|
|
|
f63775 |
while(1) {
|
|
|
f63775 |
float *pp = buffer;
|
|
|
f63775 |
for(unsigned char *p = ibuffer, *end = p + (size_t)w*h*4; p < end; p += 4, pp += 4) {
|
|
|
f63775 |
if (pp[3] > 1e-5) {
|
|
|
f63775 |
float k = 1/pp[3];
|
|
|
f63775 |
p[0] = (unsigned char)floor(colorclamp(pp[0]*k)*255.99);
|
|
|
f63775 |
p[1] = (unsigned char)floor(colorclamp(pp[1]*k)*255.99);
|
|
|
f63775 |
p[2] = (unsigned char)floor(colorclamp(pp[2]*k)*255.99);
|
|
|
f63775 |
p[3] = (unsigned char)floor(colorclamp(pp[3])*255.99);
|
|
|
f63775 |
} else {
|
|
|
f63775 |
p[0] = (unsigned char)floor(colorclamp(pp[0])*255.99);
|
|
|
f63775 |
p[1] = (unsigned char)floor(colorclamp(pp[1])*255.99);
|
|
|
f63775 |
p[2] = (unsigned char)floor(colorclamp(pp[2])*255.99);
|
|
|
f63775 |
p[3] = 0;
|
|
|
f63775 |
}
|
|
|
f63775 |
}
|
|
|
a20939 |
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ibuffer);
|
|
|
a20939 |
++level;
|
|
|
f63775 |
|
|
|
ba5c91 |
if (!mipMap) break;
|
|
|
f63775 |
if (w <= 1 && h <= 1) break;
|
|
|
f63775 |
if (w > 1) mipx(buffer, &w, &h);
|
|
|
f63775 |
if (h > 1) mipy(buffer, &w, &h);
|
|
|
8535a3 |
}
|
|
|
f63775 |
|
|
|
f63775 |
// done
|
|
|
f63775 |
free(ibuffer);
|
|
|
f63775 |
free(buffer);
|
|
|
0cbd57 |
glBindTexture(GL_TEXTURE_2D, prevtex);
|
|
|
dba3fc |
|
|
|
1d641c |
return TRUE;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
unsigned int imageToGLTextureEx(int width, int height, const void *pixels, int horWrap, int vertWrap, int smooth, int mipMap) {
|
|
|
1d641c |
// create OpenGL texture
|
|
|
1d641c |
unsigned int texid = 0, prevtex = 0;
|
|
|
1d641c |
glGetIntegerv(GL_TEXTURE_BINDING_2D, (int*)&prevtex);
|
|
|
1d641c |
glGenTextures(1, &texid);
|
|
|
1d641c |
glBindTexture(GL_TEXTURE_2D, texid);
|
|
|
1d641c |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
|
|
1d641c |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? (mipMap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR): (mipMap ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST));
|
|
|
1d641c |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, horWrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
1d641c |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vertWrap ? GL_REPEAT : GL_CLAMP_TO_EDGE);
|
|
|
1d641c |
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.7f);
|
|
|
1d641c |
imageToExistingGLTexture(texid, width, height, pixels);
|
|
|
1d641c |
glBindTexture(GL_TEXTURE_2D, prevtex);
|
|
|
f63775 |
return texid;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
0cbd57 |
int imageFromGLTexture(unsigned int texid, int *outWidth, int *outHeight, unsigned char **outPixels) {
|
|
|
0cbd57 |
*outWidth = *outHeight = 0; *outPixels = NULL;
|
|
|
0cbd57 |
|
|
|
0cbd57 |
int w = 0, h = 0;
|
|
|
0cbd57 |
unsigned int prevtex = 0;
|
|
|
0cbd57 |
glGetIntegerv(GL_TEXTURE_BINDING_2D, (int*)&prevtex);
|
|
|
0cbd57 |
glBindTexture(GL_TEXTURE_2D, texid);
|
|
|
173ebc |
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
|
|
173ebc |
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &h);
|
|
|
0cbd57 |
if (w > 0 && h > 0) {
|
|
|
0cbd57 |
*outWidth = w;
|
|
|
0cbd57 |
*outHeight = h;
|
|
|
0cbd57 |
*outPixels = malloc(sizeof(**outPixels)*w*h*4);
|
|
|
0cbd57 |
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, *outPixels);
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
glBindTexture(GL_TEXTURE_2D, prevtex);
|
|
|
0cbd57 |
|
|
|
0cbd57 |
return outPixels != NULL;
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
|
|
|
1d641c |
int viewportSave(const char *path) {
|
|
|
1d641c |
int width = 0;
|
|
|
1d641c |
int height = 0;
|
|
|
1d641c |
unsigned char *pixels = NULL;
|
|
|
1d641c |
if (!imageFromViewport(&width, &height, &pixels)) return FALSE;
|
|
|
3e56ed |
int result = imageSave(path, width, height, pixels);
|
|
|
3e56ed |
free(pixels);
|
|
|
3e56ed |
return result;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
unsigned int pixelGet(const void *pixel) {
|
|
|
1d641c |
const unsigned char *p = (const unsigned char *)pixel;
|
|
|
3e7c5f |
return (((unsigned int)p[0]) << 24)
|
|
|
3e7c5f |
| (((unsigned int)p[1]) << 16)
|
|
|
3e7c5f |
| (((unsigned int)p[2]) << 8)
|
|
|
3e7c5f |
| (((unsigned int)p[3]) << 0);
|
|
|
3e7c5f |
}
|
|
|
3e7c5f |
|
|
|
d6f40c |
void pixelSet(void *pixel, unsigned int colorCode) {
|
|
|
1d641c |
unsigned char *p = (unsigned char *)pixel;
|
|
|
d6f40c |
p[0] = (colorCode >> 24);
|
|
|
d6f40c |
p[1] = (colorCode >> 16) & 0xFF;
|
|
|
d6f40c |
p[2] = (colorCode >> 8) & 0xFF;
|
|
|
d6f40c |
p[3] = (colorCode ) & 0xFF;
|
|
|
3e7c5f |
}
|
|
|
3e7c5f |
|
|
|
1d641c |
unsigned int imageGetPixel(int width, int height, const void *pixels, int x, int y) {
|
|
|
1d641c |
if (x < 0 || x >= width || y < 0 || y >= height || !pixels) return 0;
|
|
|
1d641c |
return pixelGet((const unsigned char *)pixels + 4*((size_t)width*y + x));
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
d6f40c |
void imageSetPixel(int width, int height, void *pixels, int x, int y, unsigned int colorCode) {
|
|
|
1d641c |
if (x < 0 || x >= width || y < 0 || y >= height || !pixels) return;
|
|
|
d6f40c |
pixelSet((unsigned char *)pixels + 4*((size_t)width*y + x), colorCode);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
3e7c5f |
|
|
|
f63775 |
|
|
|
dba3fc |
static void unloadTexture(HeliTexture *texture) {
|
|
|
dba3fc |
assert(!texture->refcount);
|
|
|
0cbd57 |
if (texture->own && texture->id) {
|
|
|
0cbd57 |
unsigned int prevtex = 0;
|
|
|
0cbd57 |
glGetIntegerv(GL_TEXTURE_BINDING_2D, (int*)&prevtex);
|
|
|
0cbd57 |
if (prevtex == texture->id) glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
0cbd57 |
glDeleteTextures(1, &texture->id);
|
|
|
0cbd57 |
}
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
static HeliTexture* loadTexture(const char *key) {
|
|
|
dba3fc |
HeliTexture *texture = calloc(1, sizeof(*texture));
|
|
|
dba3fc |
texture->key = heliStringCopy(key);
|
|
|
0cbd57 |
texture->own = TRUE;
|
|
|
f8dca4 |
if (key[0] == 'T') {
|
|
|
f8dca4 |
texture->id = (unsigned int)atoll(key + 3);
|
|
|
f8dca4 |
} else {
|
|
|
f8dca4 |
int w = 0, h = 0;
|
|
|
f8dca4 |
unsigned char *pixels = NULL;
|
|
|
f8dca4 |
if (imageLoad(key+3, &w, &h, &pixels)) {
|
|
|
ba5c91 |
texture->id = imageToGLTextureEx(w, h, pixels, key[1] == 'W', key[2] == 'W', key[0] == 'L', TRUE);
|
|
|
f8dca4 |
free(pixels);
|
|
|
f8dca4 |
}
|
|
|
dba3fc |
}
|
|
|
dba3fc |
return texture;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
static HeliTexture *getTexture(const char *key) {
|
|
|
dba3fc |
HeliPair *item = heliStringmapGet(&cache, key);
|
|
|
dba3fc |
if (!item) item = heliStringmapAdd(&cache, key, loadTexture(key), (HeliFreeCallback)&unloadTexture);
|
|
|
dba3fc |
HeliTexture *texture = (HeliTexture*)item->value;
|
|
|
dba3fc |
++texture->refcount;
|
|
|
dba3fc |
return texture;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
static void unrefTexture(HeliTexture *texture) {
|
|
|
dba3fc |
if (--texture->refcount <= 0)
|
|
|
dba3fc |
heliStringmapRemove(&cache, texture->key);
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
static void heliAnimationFixPos(Animation animation) {
|
|
|
dba3fc |
double maxPos = animation->frames.count - HELI_PRECISION_SQR;
|
|
|
dba3fc |
if (!(animation->pos < maxPos)) animation->pos = maxPos;
|
|
|
dba3fc |
if (!(animation->pos > 0)) animation->pos = 0;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
Animation createAnimationEmpty() {
|
|
|
dba3fc |
Animation animation = calloc(1, sizeof(*animation));
|
|
|
dba3fc |
animation->prev = last;
|
|
|
dba3fc |
*(animation->prev ? &animation->prev->next : &first) = last = animation;
|
|
|
dba3fc |
|
|
|
dba3fc |
double minFps = worldGetMinFrameRate();
|
|
|
dba3fc |
double maxFps = worldGetMaxFrameRate();
|
|
|
dba3fc |
animation->fps = HELI_DEFAULT_FPS;
|
|
|
dba3fc |
if (animation->fps > maxFps) animation->fps = maxFps;
|
|
|
dba3fc |
if (animation->fps < minFps) animation->fps = minFps;
|
|
|
dba3fc |
animation->loop = TRUE;
|
|
|
dba3fc |
|
|
|
dba3fc |
heliObjectRegister(animation, (HeliFreeCallback)&animationDestroy);
|
|
|
dba3fc |
return animation;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
f8dca4 |
Animation createAnimationEx(const char* path, int smooth, int horWrap, int vertWrap) {
|
|
|
dba3fc |
Animation animation = createAnimationEmpty();
|
|
|
f8dca4 |
char *key = heliStringConcat("NCC", path);
|
|
|
f8dca4 |
if (smooth) key[0] = 'L';
|
|
|
f8dca4 |
if (horWrap) key[1] = 'W';
|
|
|
f8dca4 |
if (vertWrap) key[2] = 'W';
|
|
|
8535a3 |
|
|
|
f8dca4 |
Directory d = openDirectory(key + 3);
|
|
|
1015c5 |
if (d) {
|
|
|
1015c5 |
int count = directoryGetCount(d);
|
|
|
1015c5 |
for(int i = 0; i < count; ++i) {
|
|
|
dba3fc |
const char *file = directoryGet(d, i);
|
|
|
dba3fc |
if (heliStringEndsWithLowcase(file, ".png")) {
|
|
|
dba3fc |
char *k = heliStringConcat3(key, "/", file);
|
|
|
dba3fc |
heliArrayInsert(&animation->frames, -1, getTexture(k), (HeliFreeCallback)&unrefTexture);
|
|
|
dba3fc |
free(k);
|
|
|
dba3fc |
}
|
|
|
8535a3 |
}
|
|
|
1015c5 |
closeDirectory(d);
|
|
|
8535a3 |
} else {
|
|
|
dba3fc |
heliArrayInsert(&animation->frames, -1, getTexture(key), (HeliFreeCallback)&unrefTexture);
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
dba3fc |
return animation;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
f8dca4 |
Animation createAnimationFromGLTexId(unsigned int texid) {
|
|
|
f8dca4 |
Animation animation = createAnimationEmpty();
|
|
|
f8dca4 |
char key[256] = "TTT";
|
|
|
f8dca4 |
sprintf(key+3, "%d", texid);
|
|
|
f8dca4 |
heliArrayInsert(&animation->frames, -1, getTexture(key), (HeliFreeCallback)&unrefTexture);
|
|
|
f8dca4 |
return animation;
|
|
|
f8dca4 |
}
|
|
|
f8dca4 |
|
|
|
0cbd57 |
void animationGLTexIdSetOwnership(unsigned int texid, int own) {
|
|
|
0cbd57 |
char key[256] = "TTT";
|
|
|
0cbd57 |
sprintf(key+3, "%d", texid);
|
|
|
0cbd57 |
HeliPair *item = heliStringmapGet(&cache, key);
|
|
|
0cbd57 |
if (item) {
|
|
|
0cbd57 |
HeliTexture *texture = (HeliTexture*)item->value;
|
|
|
0cbd57 |
texture->own = own != 0;
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
}
|
|
|
0cbd57 |
|
|
|
3e7c5f |
Animation createAnimationFromImageEx(int width, int height, const void *pixels, int horWrap, int vertWrap, int smooth, int mipMap)
|
|
|
3e7c5f |
{ return createAnimationFromGLTexId(imageToGLTextureEx(width, height, pixels, horWrap, vertWrap, smooth, mipMap)); }
|
|
|
3e7c5f |
|
|
|
3e7c5f |
Animation createAnimationFromImage(int width, int height, const void *pixels, int wrap)
|
|
|
3e7c5f |
{ return createAnimationFromGLTexId(imageToGLTexture(width, height, pixels, wrap)); }
|
|
|
f8dca4 |
|
|
|
1d641c |
Animation createAnimationFromFramebuffer(Framebuffer framebuffer) {
|
|
|
1d641c |
unsigned int texid = framebufferGetGLTexId(framebuffer);
|
|
|
1d641c |
Animation animation = createAnimationFromGLTexId(texid);
|
|
|
909bc2 |
animationGLTexIdSetOwnership(texid, FALSE);
|
|
|
1d641c |
return animation;
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
Animation createAnimationFromViewportEx(int horWrap, int vertWrap, int smooth, int mipMap) {
|
|
|
1d641c |
int width = 0;
|
|
|
1d641c |
int height = 0;
|
|
|
1d641c |
unsigned char *pixels = NULL;
|
|
|
1d641c |
if (!imageFromViewport(&width, &height, &pixels)) return createAnimationEmpty();
|
|
|
1d641c |
return createAnimationFromImageEx(width, height, pixels, horWrap, vertWrap, smooth, mipMap);
|
|
|
1d641c |
}
|
|
|
1d641c |
|
|
|
1d641c |
Animation createAnimationFromViewport()
|
|
|
1d641c |
{ return createAnimationFromViewportEx(FALSE, FALSE, TRUE, TRUE); }
|
|
|
1d641c |
|
|
|
1d641c |
|
|
|
dba3fc |
Animation createAnimation(const char *path)
|
|
|
f8dca4 |
{ return createAnimationEx(path, TRUE, FALSE, FALSE); }
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationDestroy(Animation animation) {
|
|
|
44355f |
for(int i = worldGetSpriteCount() - 1; i >= 0; --i) {
|
|
|
44355f |
Sprite s = worldGetSprite(i);
|
|
|
44355f |
if (spriteGetAnimation(s) == animation)
|
|
|
44355f |
spriteSetNoAnimation(s);
|
|
|
44355f |
}
|
|
|
44355f |
|
|
|
53e18e |
HeliDrawingState *ss = heliDrawingGetState();
|
|
|
53e18e |
for(HeliDrawingState *s = heliDrawingGetStateStack(); s <= ss; ++s) {
|
|
|
53e18e |
if (ss->fillTexture.animation == animation) ss->fillTexture.animation = NULL;
|
|
|
53e18e |
if (ss->strokeTexture.animation == animation) ss->strokeTexture.animation = NULL;
|
|
|
53e18e |
}
|
|
|
44355f |
|
|
|
dba3fc |
*(animation->prev ? &animation->prev->next : &first) = animation->next;
|
|
|
dba3fc |
*(animation->next ? &animation->next->prev : &last ) = animation->prev;
|
|
|
dba3fc |
heliArrayDestroy(&animation->frames);
|
|
|
dba3fc |
heliObjectUnregister(animation);
|
|
|
dba3fc |
free(animation);
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
Animation animationCloneEx(Animation animation, int from, int to) {
|
|
|
dba3fc |
Animation a = createAnimationEmpty();
|
|
|
dba3fc |
animationInsertEx(a, 0, animation, from, to);
|
|
|
dba3fc |
animationSetFps(a, animationGetFps(animation));
|
|
|
dba3fc |
animationSetPos(a, animationGetPos(animation));
|
|
|
dba3fc |
if (animationIsPlaying(animation)) animationPlay(a); else animationPause(a);
|
|
|
8535a3 |
return a;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
dba3fc |
Animation animationClone(Animation animation)
|
|
|
dba3fc |
{ return animationCloneEx(animation, 0, animationGetFramesCount(animation)); }
|
|
|
f63775 |
|
|
|
3e7c5f |
unsigned int animationGetFrameGLTexId(Animation animation, int frame) {
|
|
|
3e7c5f |
int count = animationGetFramesCount(animation);
|
|
|
3e7c5f |
if (frame < 0 || frame >= count) return 0;
|
|
|
3e7c5f |
HeliTexture *texture = (HeliTexture*)animation->frames.items[frame].value;
|
|
|
dba3fc |
return texture->id;
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
3e7c5f |
unsigned int animationGetGLTexId(Animation animation)
|
|
|
3e7c5f |
{ return animationGetFrameGLTexId(animation, animationGetFrame(animation)); }
|
|
|
3e7c5f |
|
|
|
dba3fc |
int animationGetFramesCount(Animation animation)
|
|
|
dba3fc |
{ return animation->frames.count; }
|
|
|
f63775 |
|
|
|
dba3fc |
void animationInsertEx(Animation animation, int index, Animation other, int start, int count) {
|
|
|
dba3fc |
int dcnt = animationGetFramesCount(animation);
|
|
|
dba3fc |
int scnt = animationGetFramesCount(other);
|
|
|
dba3fc |
int i0 = start;
|
|
|
dba3fc |
int i1 = i0 + count;
|
|
|
dba3fc |
if (i0 < 0) i0 = 0;
|
|
|
dba3fc |
if (i1 > scnt) i1 = scnt;
|
|
|
dba3fc |
if (index < 0 || index > dcnt) index = dcnt;
|
|
|
dba3fc |
for(int i = i0; i < i1; ++i, ++index) {
|
|
|
dba3fc |
HeliTexture *texture = (HeliTexture*)other->frames.items[i].value;
|
|
|
dba3fc |
++texture->refcount;
|
|
|
dba3fc |
heliArrayInsert(&animation->frames, index, texture, (HeliFreeCallback)&unrefTexture);
|
|
|
dba3fc |
}
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationInsert(Animation animation, int index, Animation other)
|
|
|
dba3fc |
{ animationInsertEx(animation, index, other, 0, animationGetFramesCount(other)); }
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationRemove(Animation animation, int start, int count) {
|
|
|
dba3fc |
int cnt = animationGetFramesCount(animation);
|
|
|
dba3fc |
int i0 = start;
|
|
|
dba3fc |
int i1 = i0 + count;
|
|
|
dba3fc |
if (i0 < 0) i0 = 0;
|
|
|
dba3fc |
if (i1 > cnt) i1 = cnt;
|
|
|
dba3fc |
for(int i = i1 - 1; i >= i0; --i)
|
|
|
dba3fc |
heliArrayRemove(&animation->frames, i);
|
|
|
dba3fc |
heliAnimationFixPos(animation);
|
|
|
8535a3 |
}
|
|
|
8535a3 |
|
|
|
dba3fc |
void animationClear(Animation animation)
|
|
|
dba3fc |
{ animationRemove(animation, 0, animationGetFramesCount(animation)); }
|
|
|
f63775 |
|
|
|
dba3fc |
double animationGetFps(Animation animation)
|
|
|
dba3fc |
{ return animation->fps; }
|
|
|
dba3fc |
void animationSetFps(Animation animation, double fps) {
|
|
|
dba3fc |
animation->fps = !(fps > HELI_MIN_FPS) ? HELI_MIN_FPS
|
|
|
dba3fc |
: !(fps > HELI_MAX_FPS) ? HELI_MAX_FPS : fps;
|
|
|
8935bc |
}
|
|
|
8535a3 |
|
|
|
dba3fc |
int animationIsPlaying(Animation animation)
|
|
|
dba3fc |
{ return animation->playing; }
|
|
|
dba3fc |
void animationPlay(Animation animation)
|
|
|
dba3fc |
{ animation->playing = TRUE; }
|
|
|
dba3fc |
void animationPause(Animation animation)
|
|
|
dba3fc |
{ animation->playing = FALSE; }
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationAddTime(Animation animation, double time) {
|
|
|
dba3fc |
int cnt = animationGetFramesCount(animation);
|
|
|
dba3fc |
if (cnt <= 0) return;
|
|
|
dba3fc |
double t = animation->fps * time;
|
|
|
dba3fc |
if (animation->loop) {
|
|
|
dba3fc |
t /= cnt; t -= floor(t); t *= cnt;
|
|
|
dba3fc |
animation->pos += t;
|
|
|
dba3fc |
if (animation->pos > cnt) animation->pos -= cnt;
|
|
|
dba3fc |
} else {
|
|
|
dba3fc |
animation->pos += t;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
heliAnimationFixPos(animation);
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
int animationGetLoop(Animation animation)
|
|
|
dba3fc |
{ return animation->loop; }
|
|
|
dba3fc |
void animationSetLoop(Animation animation, int loop)
|
|
|
dba3fc |
{ animation->loop = loop != FALSE; }
|
|
|
dba3fc |
|
|
|
dba3fc |
double animationGetPos(Animation animation)
|
|
|
dba3fc |
{ return animation->pos; }
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationSetPos(Animation animation, double pos)
|
|
|
dba3fc |
{ animation->pos = pos; heliAnimationFixPos(animation); }
|
|
|
f63775 |
|
|
|
dba3fc |
int animationGetFrame(Animation animation) {
|
|
|
dba3fc |
int cnt = animationGetFramesCount(animation);
|
|
|
dba3fc |
int i = floor(animation->pos + HELI_PRECISION);
|
|
|
53e18e |
if (i >= cnt) i = cnt - 1;
|
|
|
dba3fc |
if (i < 0) i = 0;
|
|
|
dba3fc |
return i;
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationSetFrame(Animation animation, int frame)
|
|
|
dba3fc |
{ animationSetPos(animation, frame); }
|
|
|
dba3fc |
|
|
|
dba3fc |
void animationNextFrame(Animation animation)
|
|
|
dba3fc |
{ animationAddTime(animation, 1/animationGetFps(animation)); }
|
|
|
dba3fc |
|
|
|
dba3fc |
|
|
|
dba3fc |
void heliAnimationUpdate(double dt) {
|
|
|
dba3fc |
for(Animation animation = first; animation; animation = animation->next)
|
|
|
dba3fc |
if (animationIsPlaying(animation))
|
|
|
dba3fc |
animationAddTime(animation, dt);
|
|
|
dba3fc |
}
|
|
|
dba3fc |
|
|
|
dba3fc |
void heliAnimationFinish() {
|
|
|
dba3fc |
assert(!cache.count);
|
|
|
dba3fc |
heliArrayDestroy(&cache);
|
|
|
dba3fc |
}
|
|
|
07b70f |
|