parent
bdef693b7c
commit
2770c92148
14 changed files with 18473 additions and 0 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,644 @@ |
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
#include <limits.h> |
||||
#include <stdlib.h> |
||||
|
||||
#define FAST_CHUNK // disabling this enables the old, slower path that deblocks into a regular form
|
||||
|
||||
#include "cave_parse.h" |
||||
|
||||
#include "stb_image.h" |
||||
#include "stb.h" |
||||
|
||||
// @TODO: need to unload LRU compressed chunks
|
||||
|
||||
#define NUM_CHUNKS_PER_REGION 32 // only on one axis
|
||||
#define NUM_CHUNKS_PER_REGION_LOG2 5 |
||||
|
||||
#define NUM_COLUMNS_PER_CHUNK 16 |
||||
#define NUM_COLUMNS_PER_CHUNK_LOG2 4 |
||||
|
||||
uint32 read_uint32_be(FILE *f) |
||||
{ |
||||
unsigned char data[4]; |
||||
fread(data, 1, 4, f); |
||||
return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; |
||||
} |
||||
|
||||
typedef struct |
||||
{ |
||||
uint8 *data; |
||||
size_t len; |
||||
int x,z; // chunk index
|
||||
int refcount; // for multi-threading
|
||||
} compressed_chunk; |
||||
|
||||
typedef struct |
||||
{ |
||||
int x,z; |
||||
uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION]; |
||||
} region; |
||||
|
||||
size_t cached_compressed=0; |
||||
|
||||
FILE *last_region; |
||||
int last_region_x; |
||||
int last_region_z; |
||||
int opened=0; |
||||
|
||||
static void open_file(int reg_x, int reg_z) |
||||
{ |
||||
if (!opened || last_region_x != reg_x || last_region_z != reg_z) { |
||||
char filename[256]; |
||||
if (last_region != NULL) |
||||
fclose(last_region); |
||||
sprintf(filename, "r.%d.%d.mca", reg_x, reg_z); |
||||
last_region = fopen(filename, "rb"); |
||||
last_region_x = reg_x; |
||||
last_region_z = reg_z; |
||||
opened = 1; |
||||
} |
||||
} |
||||
|
||||
static region *load_region(int reg_x, int reg_z) |
||||
{ |
||||
region *r; |
||||
int x,z; |
||||
|
||||
open_file(reg_x, reg_z); |
||||
|
||||
r = malloc(sizeof(*r)); |
||||
|
||||
if (last_region == NULL) { |
||||
memset(r, 0, sizeof(*r)); |
||||
} else { |
||||
fseek(last_region, 0, SEEK_SET); |
||||
for (z=0; z < NUM_CHUNKS_PER_REGION; ++z) |
||||
for (x=0; x < NUM_CHUNKS_PER_REGION; ++x) |
||||
r->sector_data[z][x] = read_uint32_be(last_region); |
||||
} |
||||
r->x = reg_x; |
||||
r->z = reg_z; |
||||
|
||||
return r; |
||||
} |
||||
|
||||
void free_region(region *r) |
||||
{ |
||||
free(r); |
||||
} |
||||
|
||||
#define MAX_MAP_REGIONS 64 // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns
|
||||
region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS]; |
||||
|
||||
static region *get_region(int reg_x, int reg_z) |
||||
{ |
||||
int slot_x = reg_x & (MAX_MAP_REGIONS-1); |
||||
int slot_z = reg_z & (MAX_MAP_REGIONS-1); |
||||
region *r; |
||||
|
||||
r = regions[slot_z][slot_x]; |
||||
|
||||
if (r) { |
||||
if (r->x == reg_x && r->z == reg_z) |
||||
return r; |
||||
free_region(r); |
||||
} |
||||
|
||||
r = load_region(reg_x, reg_z); |
||||
regions[slot_z][slot_x] = r; |
||||
|
||||
return r; |
||||
} |
||||
|
||||
// about one region, so size should be ok
|
||||
#define NUM_CACHED_X 64 |
||||
#define NUM_CACHED_Z 64 |
||||
|
||||
// @TODO: is it really worth caching these? we probably can just
|
||||
// pull them from the disk cache nearly as efficiently.
|
||||
// Can test that by setting to 1x1?
|
||||
compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X]; |
||||
|
||||
static void deref_compressed_chunk(compressed_chunk *cc) |
||||
{ |
||||
assert(cc->refcount > 0); |
||||
--cc->refcount; |
||||
if (cc->refcount == 0) { |
||||
if (cc->data) |
||||
free(cc->data); |
||||
free(cc); |
||||
} |
||||
} |
||||
|
||||
static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z) |
||||
{ |
||||
int slot_x = chunk_x & (NUM_CACHED_X-1); |
||||
int slot_z = chunk_z & (NUM_CACHED_Z-1); |
||||
compressed_chunk *cc = cached_chunk[slot_z][slot_x]; |
||||
|
||||
if (cc && cc->x == chunk_x && cc->z == chunk_z) |
||||
return cc; |
||||
else { |
||||
int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2; |
||||
int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2; |
||||
region *r = get_region(reg_x, reg_z); |
||||
if (cc) { |
||||
deref_compressed_chunk(cc); |
||||
cached_chunk[slot_z][slot_x] = NULL; |
||||
} |
||||
cc = malloc(sizeof(*cc)); |
||||
cc->x = chunk_x; |
||||
cc->z = chunk_z; |
||||
{ |
||||
int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1); |
||||
int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1); |
||||
uint32 code = r->sector_data[subchunk_z][subchunk_x]; |
||||
|
||||
if (code & 255) { |
||||
open_file(reg_x, reg_z); |
||||
fseek(last_region, (code>>8)*4096, SEEK_SET); |
||||
cc->len = (code&255)*4096; |
||||
cc->data = malloc(cc->len); |
||||
fread(cc->data, 1, cc->len, last_region); |
||||
} else { |
||||
cc->len = 0; |
||||
cc->data = 0; |
||||
} |
||||
} |
||||
cc->refcount = 1; |
||||
cached_chunk[slot_z][slot_x] = cc; |
||||
return cc; |
||||
} |
||||
} |
||||
|
||||
|
||||
// NBT parser -- can automatically parse stuff we don't
|
||||
// have definitions for, but want to explicitly parse
|
||||
// stuff we do have definitions for.
|
||||
//
|
||||
// option 1: auto-parse everything into data structures,
|
||||
// then read those
|
||||
//
|
||||
// option 2: have a "parse next object" which
|
||||
// doesn't resolve whether it expands its children
|
||||
// yet, and then the user either says "expand" or
|
||||
// "skip" after looking at the name. Anything with
|
||||
// "children" without names can't go through this
|
||||
// interface.
|
||||
//
|
||||
// Let's try option 2.
|
||||
|
||||
|
||||
typedef struct |
||||
{ |
||||
unsigned char *buffer_start; |
||||
unsigned char *buffer_end; |
||||
unsigned char *cur; |
||||
int nesting; |
||||
char temp_buffer[256]; |
||||
} nbt; |
||||
|
||||
enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4, |
||||
TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8, |
||||
TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 }; |
||||
|
||||
static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize) |
||||
{ |
||||
int len = data[0]*256 + data[1]; |
||||
int i; |
||||
for (i=0; i < len && i+1 < (int) bufsize; ++i) |
||||
buffer[i] = (char) data[i+2]; |
||||
buffer[i] = 0; |
||||
} |
||||
|
||||
static char *nbt_peek(nbt *n) |
||||
{ |
||||
unsigned char type = *n->cur; |
||||
if (type == TAG_End) |
||||
return NULL; |
||||
nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer)); |
||||
return n->temp_buffer; |
||||
} |
||||
|
||||
static uint32 nbt_parse_uint32(unsigned char *buffer) |
||||
{ |
||||
return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3]; |
||||
} |
||||
|
||||
static void nbt_skip(nbt *n); |
||||
|
||||
// skip an item that doesn't have an id or name prefix (usable in lists)
|
||||
static void nbt_skip_raw(nbt *n, unsigned char type) |
||||
{ |
||||
switch (type) { |
||||
case TAG_Byte : n->cur += 1; break; |
||||
case TAG_Short : n->cur += 2; break; |
||||
case TAG_Int : n->cur += 4; break; |
||||
case TAG_Long : n->cur += 8; break; |
||||
case TAG_Float : n->cur += 4; break; |
||||
case TAG_Double: n->cur += 8; break; |
||||
case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break; |
||||
case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break; |
||||
case TAG_String : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break; |
||||
case TAG_List : { |
||||
unsigned char list_type = *n->cur++; |
||||
unsigned int list_len = nbt_parse_uint32(n->cur); |
||||
unsigned int i; |
||||
n->cur += 4; // list_len
|
||||
for (i=0; i < list_len; ++i) |
||||
nbt_skip_raw(n, list_type); |
||||
break; |
||||
} |
||||
case TAG_Compound : { |
||||
while (*n->cur != TAG_End) |
||||
nbt_skip(n); |
||||
nbt_skip(n); // skip the TAG_end
|
||||
break; |
||||
} |
||||
} |
||||
assert(n->cur <= n->buffer_end); |
||||
} |
||||
|
||||
static void nbt_skip(nbt *n) |
||||
{ |
||||
unsigned char type = *n->cur++; |
||||
if (type == TAG_End) |
||||
return; |
||||
// skip name
|
||||
n->cur += (n->cur[0]*256 + n->cur[1]) + 2; |
||||
nbt_skip_raw(n, type); |
||||
} |
||||
|
||||
// byteswap
|
||||
static void nbt_swap(unsigned char *ptr, int len) |
||||
{ |
||||
int i; |
||||
for (i=0; i < (len>>1); ++i) { |
||||
unsigned char t = ptr[i]; |
||||
ptr[i] = ptr[len-1-i]; |
||||
ptr[len-1-i] = t; |
||||
} |
||||
} |
||||
|
||||
// pass in the expected type, fail if doesn't match
|
||||
// returns a pointer to the data, byteswapped if appropriate
|
||||
static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len) |
||||
{ |
||||
unsigned char *ptr; |
||||
assert(type != TAG_Compound); |
||||
assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh
|
||||
if (len) *len = 1; |
||||
ptr = n->cur; |
||||
switch (type) { |
||||
case TAG_Byte : break; |
||||
|
||||
case TAG_Short : nbt_swap(ptr, 2); break; |
||||
case TAG_Int : nbt_swap(ptr, 4); break; |
||||
case TAG_Long : nbt_swap(ptr, 8); break; |
||||
case TAG_Float : nbt_swap(ptr, 4); break; |
||||
case TAG_Double: nbt_swap(ptr, 8); break; |
||||
|
||||
case TAG_Byte_Array: |
||||
*len = nbt_parse_uint32(ptr); |
||||
ptr += 4; |
||||
break; |
||||
case TAG_Int_Array: { |
||||
int i; |
||||
*len = nbt_parse_uint32(ptr); |
||||
ptr += 4; |
||||
for (i=0; i < *len; ++i) |
||||
nbt_swap(ptr + 4*i, 4); |
||||
break; |
||||
} |
||||
|
||||
default: assert(0); // unhandled case
|
||||
} |
||||
nbt_skip_raw(n, type); |
||||
return ptr; |
||||
} |
||||
|
||||
static void *nbt_get(nbt *n, unsigned char type, int *len) |
||||
{ |
||||
assert(n->cur[0] == type); |
||||
n->cur += 3 + (n->cur[1]*256+n->cur[2]); |
||||
return nbt_get_fromlist(n, type, len); |
||||
} |
||||
|
||||
static void nbt_begin_compound(nbt *n) // start a compound
|
||||
{ |
||||
assert(*n->cur == TAG_Compound); |
||||
// skip header
|
||||
n->cur += 3 + (n->cur[1]*256 + n->cur[2]); |
||||
++n->nesting; |
||||
} |
||||
|
||||
static void nbt_begin_compound_in_list(nbt *n) // start a compound
|
||||
{ |
||||
++n->nesting; |
||||
} |
||||
|
||||
static void nbt_end_compound(nbt *n) // end a compound
|
||||
{ |
||||
assert(*n->cur == TAG_End); |
||||
assert(n->nesting != 0); |
||||
++n->cur; |
||||
--n->nesting;
|
||||
} |
||||
|
||||
// @TODO no interface to get lists from lists
|
||||
static int nbt_begin_list(nbt *n, unsigned char type) |
||||
{ |
||||
uint32 len; |
||||
unsigned char *ptr; |
||||
|
||||
ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]); |
||||
if (ptr[0] != type) |
||||
return -1; |
||||
n->cur = ptr; |
||||
len = nbt_parse_uint32(n->cur+1); |
||||
assert(n->cur[0] == type); |
||||
// @TODO keep a stack with the count to make sure they do it right
|
||||
++n->nesting; |
||||
n->cur += 5; |
||||
return (int) len; |
||||
} |
||||
|
||||
static void nbt_end_list(nbt *n) |
||||
{ |
||||
--n->nesting; |
||||
} |
||||
|
||||
// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB
|
||||
//
|
||||
// if we want to process 64x64x256 at a time, that will be:
|
||||
// 4*4*256KB => 4MB per area in raw_block
|
||||
//
|
||||
// (plus we maybe need to decode adjacent regions)
|
||||
|
||||
|
||||
#ifdef FAST_CHUNK |
||||
typedef fast_chunk parse_chunk; |
||||
#else |
||||
typedef chunk parse_chunk; |
||||
#endif |
||||
|
||||
static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len) |
||||
{ |
||||
char *s; |
||||
parse_chunk *c = NULL; |
||||
|
||||
nbt n_store, *n = &n_store; |
||||
n->buffer_start = data; |
||||
n->buffer_end = data + len; |
||||
n->cur = n->buffer_start; |
||||
n->nesting = 0; |
||||
|
||||
nbt_begin_compound(n); |
||||
while ((s = nbt_peek(n)) != NULL) { |
||||
if (!strcmp(s, "Level")) { |
||||
int *height; |
||||
c = malloc(sizeof(*c)); |
||||
#ifdef FAST_CHUNK |
||||
memset(c, 0, sizeof(*c)); |
||||
c->pointer_to_free = data; |
||||
#else |
||||
c->rb[15][15][255].block = 0; |
||||
#endif |
||||
c->max_y = 0; |
||||
|
||||
nbt_begin_compound(n); |
||||
while ((s = nbt_peek(n)) != NULL) { |
||||
if (!strcmp(s, "xPos")) |
||||
c->xpos = *(int *) nbt_get(n, TAG_Int, 0); |
||||
else if (!strcmp(s, "zPos")) |
||||
c->zpos = *(int *) nbt_get(n, TAG_Int, 0); |
||||
else if (!strcmp(s, "Sections")) { |
||||
int count = nbt_begin_list(n, TAG_Compound), i; |
||||
if (count == -1) { |
||||
// this not-a-list case happens in The End and I'm not sure
|
||||
// what it means... possibly one of those silly encodings
|
||||
// where it's not encoded as a list if there's only one?
|
||||
// not worth figuring out
|
||||
nbt_skip(n); |
||||
count = -1; |
||||
} |
||||
for (i=0; i < count; ++i) { |
||||
int yi, len; |
||||
uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL; |
||||
nbt_begin_compound_in_list(n); |
||||
while ((s = nbt_peek(n)) != NULL) { |
||||
if (!strcmp(s, "Y")) |
||||
yi = * (uint8 *) nbt_get(n, TAG_Byte, 0); |
||||
else if (!strcmp(s, "BlockLight")) { |
||||
light = nbt_get(n, TAG_Byte_Array, &len); |
||||
assert(len == 2048); |
||||
} else if (!strcmp(s, "Blocks")) { |
||||
blocks = nbt_get(n, TAG_Byte_Array, &len); |
||||
assert(len == 4096); |
||||
} else if (!strcmp(s, "Data")) { |
||||
data = nbt_get(n, TAG_Byte_Array, &len); |
||||
assert(len == 2048); |
||||
} else if (!strcmp(s, "SkyLight")) { |
||||
skylight = nbt_get(n, TAG_Byte_Array, &len); |
||||
assert(len == 2048); |
||||
} |
||||
} |
||||
nbt_end_compound(n); |
||||
|
||||
assert(yi < 16); |
||||
|
||||
#ifndef FAST_CHUNK |
||||
|
||||
// clear data below current max_y
|
||||
{ |
||||
int x,z; |
||||
while (c->max_y < yi*16) { |
||||
for (x=0; x < 16; ++x) |
||||
for (z=0; z < 16; ++z) |
||||
c->rb[z][x][c->max_y].block = 0; |
||||
++c->max_y; |
||||
} |
||||
} |
||||
|
||||
// now assemble the data
|
||||
{ |
||||
int x,y,z, o2=0,o4=0; |
||||
for (y=0; y < 16; ++y) { |
||||
for (z=0; z < 16; ++z) { |
||||
for (x=0; x < 16; x += 2) { |
||||
raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis
|
||||
rb[0].block = blocks[o4]; |
||||
rb[0].light = light[o2] & 15; |
||||
rb[0].data = data[o2] & 15; |
||||
rb[0].skylight = skylight[o2] & 15; |
||||
|
||||
rb[256].block = blocks[o4+1]; |
||||
rb[256].light = light[o2] >> 4; |
||||
rb[256].data = data[o2] >> 4; |
||||
rb[256].skylight = skylight[o2] >> 4; |
||||
|
||||
o2 += 1; |
||||
o4 += 2; |
||||
} |
||||
} |
||||
} |
||||
c->max_y += 16; |
||||
} |
||||
#else |
||||
c->blockdata[yi] = blocks; |
||||
c->data [yi] = data; |
||||
c->light [yi] = light; |
||||
c->skylight [yi] = skylight; |
||||
#endif |
||||
} |
||||
//nbt_end_list(n);
|
||||
} else if (!strcmp(s, "HeightMap")) { |
||||
height = nbt_get(n, TAG_Int_Array, &len); |
||||
assert(len == 256); |
||||
} else |
||||
nbt_skip(n); |
||||
} |
||||
nbt_end_compound(n); |
||||
|
||||
} else |
||||
nbt_skip(n); |
||||
} |
||||
nbt_end_compound(n); |
||||
assert(n->cur == n->buffer_end); |
||||
return c; |
||||
} |
||||
|
||||
#define MAX_DECODED_CHUNK_X 64 |
||||
#define MAX_DECODED_CHUNK_Z 64 |
||||
|
||||
typedef struct |
||||
{ |
||||
int cx,cz; |
||||
fast_chunk *fc; |
||||
int valid; |
||||
} decoded_buffer; |
||||
|
||||
static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; |
||||
void lock_chunk_get_mutex(void); |
||||
void unlock_chunk_get_mutex(void); |
||||
|
||||
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z) |
||||
{ |
||||
unsigned char *decoded; |
||||
compressed_chunk *cc; |
||||
int inlen; |
||||
int len; |
||||
fast_chunk *fc; |
||||
|
||||
lock_chunk_get_mutex(); |
||||
cc = get_compressed_chunk(chunk_x, chunk_z); |
||||
if (cc->len != 0) |
||||
++cc->refcount; |
||||
unlock_chunk_get_mutex(); |
||||
|
||||
if (cc->len == 0) |
||||
return NULL; |
||||
|
||||
assert(cc != NULL); |
||||
|
||||
assert(cc->data[4] == 2); |
||||
|
||||
inlen = nbt_parse_uint32(cc->data); |
||||
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); |
||||
assert(decoded != NULL); |
||||
assert(len != 0); |
||||
|
||||
lock_chunk_get_mutex(); |
||||
deref_compressed_chunk(cc); |
||||
unlock_chunk_get_mutex(); |
||||
|
||||
#ifdef FAST_CHUNK |
||||
fc = minecraft_chunk_parse(decoded, len); |
||||
#else |
||||
fc = NULL; |
||||
#endif |
||||
if (fc == NULL) |
||||
free(decoded); |
||||
return fc; |
||||
} |
||||
|
||||
|
||||
decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z) |
||||
{ |
||||
decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; |
||||
if (db->valid) { |
||||
if (db->cx == chunk_x && db->cz == chunk_z) |
||||
return db; |
||||
if (db->fc) { |
||||
free(db->fc->pointer_to_free); |
||||
free(db->fc); |
||||
} |
||||
} |
||||
|
||||
db->cx = chunk_x; |
||||
db->cz = chunk_z; |
||||
db->valid = 1; |
||||
db->fc = 0; |
||||
|
||||
{ |
||||
db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z); |
||||
return db; |
||||
} |
||||
} |
||||
|
||||
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z) |
||||
{ |
||||
decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z); |
||||
return db->fc; |
||||
} |
||||
|
||||
chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z) |
||||
{ |
||||
unsigned char *decoded; |
||||
compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z); |
||||
assert(cc != NULL); |
||||
if (cc->len == 0) |
||||
return NULL; |
||||
else { |
||||
chunk *ch; |
||||
int inlen = nbt_parse_uint32(cc->data); |
||||
int len; |
||||
assert(cc->data[4] == 2); |
||||
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); |
||||
assert(decoded != NULL); |
||||
#ifdef FAST_CHUNK |
||||
ch = NULL; |
||||
#else |
||||
ch = minecraft_chunk_parse(decoded, len); |
||||
#endif |
||||
free(decoded); |
||||
return ch; |
||||
} |
||||
} |
||||
|
||||
static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; |
||||
chunk *get_decoded_chunk(int chunk_x, int chunk_z) |
||||
{ |
||||
chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; |
||||
if (c && c->xpos == chunk_x && c->zpos == chunk_z) |
||||
return c; |
||||
if (c) free(c); |
||||
c = get_decoded_chunk_raw(chunk_x, chunk_z); |
||||
decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c; |
||||
return c; |
||||
} |
||||
|
||||
#if 0 |
||||
chunk *map[257][257]; |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
int i,j; |
||||
for (j= -32; j <= 32; ++j) |
||||
for (i= -32; i <= 32; ++i) |
||||
map[j+128][i+128] = get_decoded_chunk(i,j); |
||||
return 0; |
||||
} |
||||
#endif |
||||
|
@ -0,0 +1,41 @@ |
||||
#ifndef INCLUDE_CAVE_PARSE_H |
||||
#define INCLUDE_CAVE_PARSE_H |
||||
|
||||
typedef struct |
||||
{ |
||||
unsigned char block; |
||||
unsigned char data; |
||||
unsigned char light:4; |
||||
unsigned char skylight:4; |
||||
} raw_block; |
||||
|
||||
// this is the old fully-decoded chunk
|
||||
typedef struct |
||||
{ |
||||
int xpos, zpos, max_y; |
||||
int height[16][16]; |
||||
raw_block rb[16][16][256]; // [z][x][y] which becomes [y][x][z] in stb
|
||||
} chunk; |
||||
|
||||
chunk *get_decoded_chunk(int chunk_x, int chunk_z); |
||||
|
||||
#define NUM_SEGMENTS 16 |
||||
typedef struct |
||||
{ |
||||
int max_y, xpos, zpos; |
||||
|
||||
unsigned char *blockdata[NUM_SEGMENTS]; |
||||
unsigned char *data[NUM_SEGMENTS]; |
||||
unsigned char *skylight[NUM_SEGMENTS]; |
||||
unsigned char *light[NUM_SEGMENTS]; |
||||
|
||||
void *pointer_to_free;
|
||||
|
||||
int refcount; // this allows multi-threaded building without wrapping in ANOTHER struct
|
||||
} fast_chunk; |
||||
|
||||
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z); // cache, never call free()
|
||||
|
||||
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z); |
||||
|
||||
#endif |
@ -0,0 +1,157 @@ |
||||
# Microsoft Developer Studio Project File - Name="caveview" - Package Owner=<4> |
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00 |
||||
# ** DO NOT EDIT ** |
||||
|
||||
# TARGTYPE "Win32 (x86) Application" 0x0101 |
||||
|
||||
CFG=caveview - Win32 Debug |
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE, |
||||
!MESSAGE use the Export Makefile command and run |
||||
!MESSAGE |
||||
!MESSAGE NMAKE /f "caveview.mak". |
||||
!MESSAGE |
||||
!MESSAGE You can specify a configuration when running NMAKE |
||||
!MESSAGE by defining the macro CFG on the command line. For example: |
||||
!MESSAGE |
||||
!MESSAGE NMAKE /f "caveview.mak" CFG="caveview - Win32 Debug" |
||||
!MESSAGE |
||||
!MESSAGE Possible choices for configuration are: |
||||
!MESSAGE |
||||
!MESSAGE "caveview - Win32 Release" (based on "Win32 (x86) Application") |
||||
!MESSAGE "caveview - Win32 Debug" (based on "Win32 (x86) Application") |
||||
!MESSAGE |
||||
|
||||
# Begin Project |
||||
# PROP AllowPerConfigDependencies 0 |
||||
# PROP Scc_ProjName "" |
||||
# PROP Scc_LocalPath "" |
||||
CPP=cl.exe |
||||
MTL=midl.exe |
||||
RSC=rc.exe |
||||
|
||||
!IF "$(CFG)" == "caveview - Win32 Release" |
||||
|
||||
# PROP BASE Use_MFC 0 |
||||
# PROP BASE Use_Debug_Libraries 0 |
||||
# PROP BASE Output_Dir "Release" |
||||
# PROP BASE Intermediate_Dir "Release" |
||||
# PROP BASE Target_Dir "" |
||||
# PROP Use_MFC 0 |
||||
# PROP Use_Debug_Libraries 0 |
||||
# PROP Output_Dir "Release" |
||||
# PROP Intermediate_Dir "Release" |
||||
# PROP Ignore_Export_Lib 0 |
||||
# PROP Target_Dir "" |
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c |
||||
# ADD CPP /nologo /MD /W3 /WX /GX /Zd /O2 /I "../.." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c |
||||
# SUBTRACT CPP /YX |
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 |
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 |
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG" |
||||
# ADD RSC /l 0x409 /d "NDEBUG" |
||||
BSC32=bscmake.exe |
||||
# ADD BASE BSC32 /nologo |
||||
# ADD BSC32 /nologo |
||||
LINK32=link.exe |
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 |
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Release/caveview.exe" |
||||
# SUBTRACT LINK32 /map |
||||
|
||||
!ELSEIF "$(CFG)" == "caveview - Win32 Debug" |
||||
|
||||
# PROP BASE Use_MFC 0 |
||||
# PROP BASE Use_Debug_Libraries 1 |
||||
# PROP BASE Output_Dir "Debug" |
||||
# PROP BASE Intermediate_Dir "Debug" |
||||
# PROP BASE Target_Dir "" |
||||
# PROP Use_MFC 0 |
||||
# PROP Use_Debug_Libraries 1 |
||||
# PROP Output_Dir "Debug" |
||||
# PROP Intermediate_Dir "Debug" |
||||
# PROP Ignore_Export_Lib 0 |
||||
# PROP Target_Dir "" |
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c |
||||
# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /Zi /Od /I "../.." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c |
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 |
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 |
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG" |
||||
# ADD RSC /l 0x409 /d "_DEBUG" |
||||
BSC32=bscmake.exe |
||||
# ADD BASE BSC32 /nologo |
||||
# ADD BSC32 /nologo |
||||
LINK32=link.exe |
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept |
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept |
||||
|
||||
!ENDIF |
||||
|
||||
# Begin Target |
||||
|
||||
# Name "caveview - Win32 Release" |
||||
# Name "caveview - Win32 Debug" |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\cave_parse.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\cave_parse.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\game.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\game.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\glext.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\glext_list.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\main.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\render.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\win32\SDL_windows_main.c |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_easy_font.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_gl.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_glprog.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_image.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_image_write.h |
||||
# End Source File |
||||
# Begin Source File |
||||
|
||||
SOURCE=.\stb_voxel_render.h |
||||
# End Source File |
||||
# End Target |
||||
# End Project |
@ -0,0 +1,29 @@ |
||||
Microsoft Developer Studio Workspace File, Format Version 6.00 |
||||
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! |
||||
|
||||
############################################################################### |
||||
|
||||
Project: "caveview"=.\caveview.dsp - Package Owner=<4> |
||||
|
||||
Package=<5> |
||||
{{{ |
||||
}}} |
||||
|
||||
Package=<4> |
||||
{{{ |
||||
}}} |
||||
|
||||
############################################################################### |
||||
|
||||
Global: |
||||
|
||||
Package=<5> |
||||
{{{ |
||||
}}} |
||||
|
||||
Package=<3> |
||||
{{{ |
||||
}}} |
||||
|
||||
############################################################################### |
||||
|
@ -0,0 +1,354 @@ |
||||
#include <windows.h> |
||||
#include "game.h" |
||||
#include "stb.h" |
||||
|
||||
#include "sdl.h" |
||||
#include "SDL_opengl.h" |
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION |
||||
#include "stb_image.h" |
||||
|
||||
#define STB_GL_IMPLEMENTATION |
||||
#include "stb_gl.h" |
||||
|
||||
#define STB_GLPROG_IMPLEMENTATION |
||||
#define STB_GLPROG_ARB_DEFINE_EXTENSIONS |
||||
#include "stb_glprog.h" |
||||
|
||||
#include "stb_easy_font.h" |
||||
|
||||
char *game_name = "caveview"; |
||||
|
||||
// assume only a single texture with all sprites
|
||||
float texture_s_scale; |
||||
float texture_t_scale; |
||||
GLuint interface_tex, logo_tex; |
||||
|
||||
extern int screen_x, screen_y; // main.c
|
||||
|
||||
int tex_w, tex_h; |
||||
void *tex_data; |
||||
|
||||
GLuint load_texture(char *filename, int keep) |
||||
{ |
||||
GLuint tex; |
||||
tex_data = stbi_load(filename, &tex_w, &tex_h, NULL, 4); |
||||
if (tex_data == NULL) { |
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, |
||||
game_name, |
||||
"Couldn't open image file.", |
||||
NULL); |
||||
exit(1); |
||||
} |
||||
|
||||
glGenTextures(1, &tex); |
||||
glBindTexture(GL_TEXTURE_2D, tex); |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_data); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
||||
|
||||
if (!keep) { |
||||
free(tex_data); |
||||
tex_data = NULL; |
||||
} |
||||
|
||||
return tex; |
||||
} |
||||
|
||||
static void init_graphics(void) |
||||
{ |
||||
//logo_tex = load_texture("sss_logo.png",0);
|
||||
//interface_tex = load_texture("game_art.png",1);
|
||||
texture_s_scale = 1.0f / tex_w; |
||||
texture_t_scale = 1.0f / tex_h; |
||||
} |
||||
|
||||
void print_string(float x, float y, char *text, float r, float g, float b) |
||||
{ |
||||
static char buffer[99999]; |
||||
int num_quads; |
||||
|
||||
num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer)); |
||||
|
||||
glColor3f(r,g,b); |
||||
glEnableClientState(GL_VERTEX_ARRAY); |
||||
glVertexPointer(2, GL_FLOAT, 16, buffer); |
||||
glDrawArrays(GL_QUADS, 0, num_quads*4); |
||||
glDisableClientState(GL_VERTEX_ARRAY); |
||||
} |
||||
|
||||
float text_color[3]; |
||||
float pos_x = 10; |
||||
float pos_y = 10; |
||||
|
||||
void print(char *text, ...) |
||||
{ |
||||
char buffer[999]; |
||||
va_list va; |
||||
va_start(va, text); |
||||
vsprintf(buffer, text, va); |
||||
va_end(va); |
||||
print_string(pos_x, pos_y, buffer, text_color[0], text_color[1], text_color[2]); |
||||
pos_y += 10; |
||||
} |
||||
|
||||
// mouse offsetting 'pixel to virtual'
|
||||
float xs_p2v, ys_p2v;// xoff_p2v, yoff_p2v;
|
||||
|
||||
// viewport offseting 'virtual to pixel'
|
||||
float xs_v2p, ys_v2p, xoff_v2p, yoff_v2p; |
||||
|
||||
float camang[3], camloc[3] = { 0,0,75 }; |
||||
float player_zoom = 1.0; |
||||
int third_person; |
||||
float rotate_view = 0.0; |
||||
|
||||
#define REVERSE_DEPTH |
||||
|
||||
|
||||
void init_game(void) |
||||
{ |
||||
init_graphics(); |
||||
} |
||||
|
||||
void render_caves(float pos[3]); |
||||
|
||||
void camera_to_worldspace(float world[3], float cam_x, float cam_y, float cam_z) |
||||
{ |
||||
float vec[3] = { cam_x, cam_y, cam_z }; |
||||
float t[3]; |
||||
float s,c; |
||||
s = sin(camang[0]*3.141592/180); |
||||
c = cos(camang[0]*3.141592/180); |
||||
|
||||
t[0] = vec[0]; |
||||
t[1] = c*vec[1] - s*vec[2]; |
||||
t[2] = s*vec[1] + c*vec[2]; |
||||
|
||||
s = sin(camang[2]*3.141592/180); |
||||
c = cos(camang[2]*3.141592/180); |
||||
world[0] = c*t[0] - s*t[1]; |
||||
world[1] = s*t[0] + c*t[1]; |
||||
world[2] = t[2]; |
||||
} |
||||
|
||||
// camera worldspace velocity
|
||||
float cam_vel[3]; |
||||
|
||||
int controls; |
||||
|
||||
#define MAX_VEL 150 // blocks per second
|
||||
#define ACCEL 6 |
||||
#define DECEL 3 |
||||
|
||||
#define STATIC_FRICTION DECEL |
||||
#define EFFECTIVE_ACCEL (ACCEL+DECEL) |
||||
|
||||
// dynamic friction:
|
||||
//
|
||||
// if going at MAX_VEL, ACCEL and friction must cancel
|
||||
// EFFECTIVE_ACCEL = DECEL + DYNAMIC_FRIC*MAX_VEL
|
||||
#define DYNAMIC_FRICTION (ACCEL/(float)MAX_VEL) |
||||
|
||||
float view_x_vel = 0; |
||||
float view_z_vel = 0; |
||||
float pending_view_x; |
||||
float pending_view_z; |
||||
float pending_view_x; |
||||
float pending_view_z; |
||||
|
||||
void process_tick_raw(float dt) |
||||
{ |
||||
int i; |
||||
float thrust[3] = { 0,0,0 }; |
||||
float world_thrust[3]; |
||||
|
||||
// choose direction to apply thrust
|
||||
|
||||
thrust[0] = (controls & 3)== 1 ? EFFECTIVE_ACCEL : (controls & 3)== 2 ? -EFFECTIVE_ACCEL : 0; |
||||
thrust[1] = (controls & 12)== 4 ? EFFECTIVE_ACCEL : (controls & 12)== 8 ? -EFFECTIVE_ACCEL : 0; |
||||
thrust[2] = (controls & 48)==16 ? EFFECTIVE_ACCEL : (controls & 48)==32 ? -EFFECTIVE_ACCEL : 0; |
||||
|
||||
// @TODO clamp thrust[0] & thrust[1] vector length to EFFECTIVE_ACCEL
|
||||
|
||||
camera_to_worldspace(world_thrust, thrust[0], thrust[1], 0); |
||||
world_thrust[2] += thrust[2]; |
||||
|
||||
for (i=0; i < 3; ++i) { |
||||
float acc = world_thrust[i]; |
||||
cam_vel[i] += acc*dt; |
||||
} |
||||
|
||||
if (cam_vel[0] || cam_vel[1] || cam_vel[2]) |
||||
{ |
||||
float vel = sqrt(cam_vel[0]*cam_vel[0] + cam_vel[1]*cam_vel[1] + cam_vel[2]*cam_vel[2]); |
||||
float newvel = vel; |
||||
float dec = STATIC_FRICTION + DYNAMIC_FRICTION*vel; |
||||
newvel = vel - dec*dt; |
||||
if (newvel < 0) |
||||
newvel = 0; |
||||
cam_vel[0] *= newvel/vel; |
||||
cam_vel[1] *= newvel/vel; |
||||
cam_vel[2] *= newvel/vel; |
||||
} |
||||
|
||||
camloc[0] += cam_vel[0] * dt; |
||||
camloc[1] += cam_vel[1] * dt; |
||||
camloc[2] += cam_vel[2] * dt; |
||||
|
||||
view_x_vel *= pow(0.75, dt); |
||||
view_z_vel *= pow(0.75, dt); |
||||
|
||||
view_x_vel += (pending_view_x - view_x_vel)*dt*60; |
||||
view_z_vel += (pending_view_z - view_z_vel)*dt*60; |
||||
|
||||
pending_view_x -= view_x_vel * dt; |
||||
pending_view_z -= view_z_vel * dt; |
||||
camang[0] += view_x_vel * dt; |
||||
camang[2] += view_z_vel * dt; |
||||
camang[0] = stb_clamp(camang[0], -90, 90); |
||||
camang[2] = fmod(camang[2], 360); |
||||
} |
||||
|
||||
void process_tick(float dt) |
||||
{ |
||||
while (dt > 1.0/60) { |
||||
process_tick_raw(1.0/60); |
||||
dt -= 1.0/60; |
||||
} |
||||
process_tick_raw(dt); |
||||
} |
||||
|
||||
void update_view(float dx, float dy) |
||||
{ |
||||
// hard-coded mouse sensitivity, not resolution independent?
|
||||
pending_view_z -= dx*300; |
||||
pending_view_x -= dy*700; |
||||
} |
||||
|
||||
extern int screen_x, screen_y; |
||||
extern int is_synchronous_debug; |
||||
float render_time; |
||||
|
||||
extern int chunk_locations, chunks_considered, chunks_in_frustum; |
||||
extern int quads_considered, quads_rendered; |
||||
extern int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total; |
||||
extern int view_dist_in_chunks; |
||||
extern int num_threads_active, num_meshes_started, num_meshes_uploaded; |
||||
extern float chunk_server_activity; |
||||
|
||||
static Uint64 start_time, end_time; // render time
|
||||
|
||||
float chunk_server_status[32]; |
||||
int chunk_server_pos; |
||||
|
||||
void draw_stats(void) |
||||
{ |
||||
int i; |
||||
|
||||
static Uint64 last_frame_time; |
||||
Uint64 cur_time = SDL_GetPerformanceCounter(); |
||||
float chunk_server=0; |
||||
float frame_time = (cur_time - last_frame_time) / (float) SDL_GetPerformanceFrequency(); |
||||
last_frame_time = cur_time; |
||||
|
||||
chunk_server_status[chunk_server_pos] = chunk_server_activity; |
||||
chunk_server_pos = (chunk_server_pos+1) %32; |
||||
|
||||
for (i=0; i < 32; ++i) |
||||
chunk_server += chunk_server_status[i] / 32.0; |
||||
|
||||
stb_easy_font_spacing(-0.75); |
||||
pos_y = 10; |
||||
text_color[0] = text_color[1] = text_color[2] = 1.0f; |
||||
print("Frame time: %6.2fms, CPU frame render time: %5.2fms", frame_time*1000, render_time*1000); |
||||
print("Tris: %4.1fM drawn of %4.1fM in range", 2*quads_rendered/1000000.0f, 2*quads_considered/1000000.0f); |
||||
print("Vbuf storage: %dMB in frustum of %dMB in range of %dMB in cache", chunk_storage_rendered>>20, chunk_storage_considered>>20, chunk_storage_total>>20); |
||||
print("Num mesh builds started this frame: %d; num uploaded this frame: %d\n", num_meshes_started, num_meshes_uploaded); |
||||
print("QChunks: %3d in frustum of %3d valid of %3d in range", chunks_in_frustum, chunks_considered, chunk_locations); |
||||
print("Mesh worker threads active: %d", num_threads_active); |
||||
print("View distance: %d blocks", view_dist_in_chunks*16); |
||||
print("%s", glGetString(GL_RENDERER)); |
||||
|
||||
if (is_synchronous_debug) { |
||||
text_color[0] = 1.0; |
||||
text_color[1] = 0.5; |
||||
text_color[2] = 0.5; |
||||
print("SLOWNESS: Synchronous debug output is enabled!"); |
||||
} |
||||
} |
||||
|
||||
void draw_main(void) |
||||
{ |
||||
glEnable(GL_CULL_FACE); |
||||
glDisable(GL_TEXTURE_2D); |
||||
glDisable(GL_LIGHTING); |
||||
glEnable(GL_DEPTH_TEST); |
||||
#ifdef REVERSE_DEPTH |
||||
glDepthFunc(GL_GREATER); |
||||
glClearDepth(0); |
||||
#else |
||||
glDepthFunc(GL_LESS); |
||||
glClearDepth(1); |
||||
#endif |
||||
glDepthMask(GL_TRUE); |
||||
glDisable(GL_SCISSOR_TEST); |
||||
glClearColor(0.6,0.7,0.9,0); |
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||
glColor3f(1,1,1); |
||||
glFrontFace(GL_CW); |
||||
glEnable(GL_TEXTURE_2D); |
||||
glDisable(GL_BLEND); |
||||
|
||||
|
||||
glMatrixMode(GL_PROJECTION); |
||||
glLoadIdentity(); |
||||
|
||||
#ifdef REVERSE_DEPTH |
||||
stbgl_Perspective(player_zoom, 90, 70, 3000, 1.0/16); |
||||
#else |
||||
stbgl_Perspective(player_zoom, 90, 70, 1.0/16, 3000); |
||||
#endif |
||||
|
||||
// now compute where the camera should be
|
||||
glMatrixMode(GL_MODELVIEW); |
||||
glLoadIdentity(); |
||||
stbgl_initCamera_zup_facing_y(); |
||||
|
||||
//glTranslatef(0,150,-5);
|
||||
// position the camera and render it
|
||||
|
||||
if (third_person) { |
||||
glTranslatef(0,2.5,0); |
||||
glRotatef(-camang[0],1,0,0); |
||||
|
||||
glTranslatef(0,2,0); |
||||
glRotatef(-camang[2]-rotate_view,0,0,1); |
||||
|
||||
//glTranslatef(0,0,1);
|
||||
//glTranslatef(0,0,-1);
|
||||
} else { |
||||
glRotatef(-camang[0],1,0,0); |
||||
glRotatef(-camang[2],0,0,1); |
||||
} |
||||
|
||||
glTranslatef(-camloc[0], -camloc[1], -camloc[2]); |
||||
|
||||
start_time = SDL_GetPerformanceCounter(); |
||||
render_caves(camloc); |
||||
end_time = SDL_GetPerformanceCounter(); |
||||
|
||||
render_time = (end_time - start_time) / (float) SDL_GetPerformanceFrequency(); |
||||
|
||||
glMatrixMode(GL_PROJECTION); |
||||
glLoadIdentity(); |
||||
gluOrtho2D(0,screen_x/2,screen_y/2,0); |
||||
glMatrixMode(GL_MODELVIEW); |
||||
glLoadIdentity(); |
||||
glDisable(GL_TEXTURE_2D); |
||||
glDisable(GL_BLEND); |
||||
glDisable(GL_CULL_FACE); |
||||
draw_stats(); |
||||
} |
@ -0,0 +1,43 @@ |
||||
#ifndef INCLUDED_GAME_H |
||||
#define INCLUDED_GAME_H |
||||
|
||||
#include <assert.h> |
||||
|
||||
typedef unsigned int uint32; |
||||
typedef signed int int32; |
||||
typedef unsigned short uint16; |
||||
typedef signed short int16; |
||||
typedef unsigned char uint8 ; |
||||
typedef signed char int8 ; |
||||
|
||||
|
||||
#pragma warning(disable:4244; disable:4305; disable:4018) |
||||
|
||||
// virtual screen size
|
||||
#define SIZE_X 480 |
||||
#define SIZE_Y 360 |
||||
|
||||
// main.c
|
||||
extern void error(char *s); |
||||
extern void ods(char *fmt, ...); |
||||
|
||||
|
||||
// game.c
|
||||
extern void init_game(void); |
||||
extern void draw_main(void); |
||||
extern void process_tick(float dt); |
||||
|
||||
|
||||
#define BUTTON_MAX 15 |
||||
typedef struct |
||||
{ |
||||
int id; |
||||
uint8 buttons[BUTTON_MAX]; |
||||
float axes[4]; |
||||
float triggers[2]; |
||||
} gamepad; |
||||
|
||||
extern gamepad pads[4]; |
||||
|
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,54 @@ |
||||
GLARB(ActiveTexture,ACTIVETEXTURE) |
||||
GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE) |
||||
GLARB(MultiTexCoord2f,MULTITEXCOORD2F) |
||||
GLEXT(TexImage3D,TEXIMAGE3D) |
||||
GLEXT(TexSubImage3D,TEXSUBIMAGE3D) |
||||
GLEXT(GenerateMipmap,GENERATEMIPMAP) |
||||
GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK) |
||||
|
||||
GLARB(DeleteObject,DELETEOBJECT) |
||||
GLARB(CreateProgramObject,CREATEPROGRAMOBJECT) |
||||
GLARB(UseProgramObject,USEPROGRAMOBJECT) |
||||
GLARB(CreateShaderObject,CREATESHADEROBJECT) |
||||
GLARB(ShaderSource,SHADERSOURCE) |
||||
GLARB(CompileShader,COMPILESHADER) |
||||
GLARB(GetInfoLog,GETINFOLOG) |
||||
GLARB(AttachObject,ATTACHOBJECT) |
||||
GLARB(LinkProgram,LINKPROGRAM) |
||||
GLARB(GetObjectParameteriv,GETOBJECTPARAMETERIV) |
||||
GLARB(GetUniformLocation,GETUNIFORMLOCATION) |
||||
GLARB(Uniform1i,UNIFORM1I) |
||||
GLARB(Uniform1f,UNIFORM1F) |
||||
GLARB(Uniform4f,UNIFORM4F) |
||||
GLARB(Uniform2fv,UNIFORM2FV) |
||||
GLARB(Uniform3fv,UNIFORM3FV) |
||||
GLARB(Uniform4fv,UNIFORM4FV) |
||||
GLARB(VertexAttribPointer,VERTEXATTRIBPOINTER) |
||||
GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER) |
||||
GLARB(EnableVertexAttribArray,ENABLEVERTEXATTRIBARRAY) |
||||
GLARB(DisableVertexAttribArray,DISABLEVERTEXATTRIBARRAY) |
||||
|
||||
GLEXT(BindFramebuffer,BINDFRAMEBUFFER) |
||||
GLEXT(DeleteFramebuffers,DELETEFRAMEBUFFERS) |
||||
GLEXT(GenFramebuffers,GENFRAMEBUFFERS) |
||||
GLEXT(CheckFramebufferStatus,CHECKFRAMEBUFFERSTATUS) |
||||
GLEXT(FramebufferTexture2D,FRAMEBUFFERTEXTURE2D) |
||||
GLEXT(BindRenderBuffer,BINDRENDERBUFFER) |
||||
GLEXT(RenderbufferStorage,RENDERBUFFERSTORAGE) |
||||
GLEXT(GenRenderbuffers,GENRENDERBUFFERS) |
||||
GLEXT(BindRenderbuffer,BINDRENDERBUFFER) |
||||
GLEXT(FramebufferRenderbuffer,FRAMEBUFFERRENDERBUFFER) |
||||
GLEXT(GenerateMipmap,GENERATEMIPMAP) |
||||
|
||||
GLARB(BindBuffer ,BINDBUFFER,) |
||||
GLARB(GenBuffers ,GENBUFFERS ) |
||||
GLARB(DeleteBuffers,DELETEBUFFERS) |
||||
GLARB(BufferData ,BUFFERDATA ) |
||||
GLARB(BufferSubData,BUFFERSUBDATA) |
||||
GLARB(MapBuffer ,MAPBUFFER ) |
||||
GLARB(UnmapBuffer ,UNMAPBUFFER ) |
||||
GLARB(TexBuffer ,TEXBUFFER ) |
||||
|
||||
GLEXT(NamedBufferStorage,NAMEDBUFFERSTORAGE) |
||||
GLE(BufferStorage,BUFFERSTORAGE) |
||||
GLE(GetStringi,GETSTRINGI) |
@ -0,0 +1,354 @@ |
||||
#pragma warning(disable:4244; disable:4305; disable:4018) |
||||
|
||||
#define _WIN32_WINNT 0x400 |
||||
|
||||
#include "sdl.h" |
||||
#include "sdl_mixer.h" |
||||
|
||||
#include <assert.h> |
||||
#include <ctype.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include "game.h" |
||||
|
||||
#include <windows.h> |
||||
|
||||
#define STB_GLEXT_DEFINE "glext_list.h" |
||||
#include "stb_gl.h" |
||||
|
||||
#define STB_DEFINE |
||||
#include "stb.h" |
||||
|
||||
#define SCALE 2 |
||||
|
||||
void error(char *s) |
||||
{ |
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", s, NULL); |
||||
exit(0); |
||||
} |
||||
|
||||
void ods(char *fmt, ...) |
||||
{ |
||||
char buffer[1000]; |
||||
va_list va; |
||||
va_start(va, fmt); |
||||
vsprintf(buffer, fmt, va); |
||||
va_end(va); |
||||
SDL_Log("%s", buffer); |
||||
} |
||||
|
||||
#define TICKS_PER_SECOND 60 |
||||
|
||||
static SDL_Window *window; |
||||
|
||||
extern void init_game(void); |
||||
extern void draw_main(void); |
||||
extern void process_tick(float dt); |
||||
extern void editor_init(void); |
||||
|
||||
void draw(void) |
||||
{ |
||||
draw_main(); |
||||
SDL_GL_SwapWindow(window); |
||||
} |
||||
|
||||
|
||||
static int initialized=0; |
||||
static float last_dt; |
||||
|
||||
int screen_x,screen_y; |
||||
|
||||
float carried_dt = 0; |
||||
#define TICKRATE 60 |
||||
|
||||
|
||||
int raw_level_time; |
||||
extern void init_game(void); |
||||
extern void draw_main(void); |
||||
|
||||
float global_timer; |
||||
|
||||
int loopmode(float dt, int real, int in_client) |
||||
{ |
||||
float actual_dt = dt; |
||||
|
||||
float jump_timer = dt; |
||||
|
||||
if (!initialized) return 0; |
||||
|
||||
if (!real) |
||||
return 0; |
||||
|
||||
// don't allow more than 6 frames to update at a time
|
||||
if (dt > 0.075) dt = 0.075; |
||||
|
||||
global_timer += dt; |
||||
|
||||
carried_dt += dt; |
||||
while (carried_dt > 1.0/TICKRATE) { |
||||
//update_input();
|
||||
// if the player is dead, stop the sim
|
||||
carried_dt -= 1.0/TICKRATE; |
||||
} |
||||
|
||||
process_tick(dt); |
||||
draw(); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int quit; |
||||
|
||||
//int winproc(void *data, stbwingraph_event *e)
|
||||
|
||||
extern int editor_scale; |
||||
extern void editor_key(enum stbte_action act); |
||||
extern int controls; |
||||
|
||||
void active_control_set(int key) |
||||
{ |
||||
controls |= 1 << key; |
||||
} |
||||
|
||||
void active_control_clear(int key) |
||||
{ |
||||
controls &= ~(1 << key); |
||||
} |
||||
|
||||
extern void update_view(float dx, float dy); |
||||
|
||||
void process_sdl_mouse(SDL_Event *e) |
||||
{ |
||||
update_view((float) e->motion.xrel / screen_x, (float) e->motion.yrel / screen_y); |
||||
} |
||||
|
||||
void process_event(SDL_Event *e) |
||||
{ |
||||
switch (e->type) { |
||||
case SDL_MOUSEMOTION: |
||||
process_sdl_mouse(e); |
||||
break; |
||||
case SDL_MOUSEBUTTONDOWN: |
||||
case SDL_MOUSEBUTTONUP: |
||||
case SDL_MOUSEWHEEL: |
||||
//stbte_mouse_sdl(edit_map, e, 1.0f/editor_scale,1.0f/editor_scale,0,0);
|
||||
break; |
||||
|
||||
case SDL_QUIT: |
||||
quit = 1; |
||||
break; |
||||
|
||||
case SDL_WINDOWEVENT: |
||||
switch (e->window.event) { |
||||
case SDL_WINDOWEVENT_SIZE_CHANGED: |
||||
screen_x = e->window.data1; |
||||
screen_y = e->window.data2; |
||||
loopmode(0,1,0); |
||||
break; |
||||
} |
||||
break; |
||||
|
||||
case SDL_TEXTINPUT: |
||||
switch(e->text.text[0]) { |
||||
#if 0 |
||||
case 27: { |
||||
#if 0 |
||||
int result; |
||||
SDL_ShowCursor(SDL_ENABLE); |
||||
if (MessageBox(e->handle, "Exit CLTT?", "CLTT", MB_OKCANCEL) == IDOK); |
||||
#endif |
||||
{ |
||||
quit = 1; |
||||
break; |
||||
} |
||||
//SDL_ShowCursor(SDL_DISABLE);
|
||||
break; |
||||
} |
||||
#endif |
||||
} |
||||
break; |
||||
|
||||
case SDL_KEYDOWN: { |
||||
int k = e->key.keysym.sym; |
||||
int s = e->key.keysym.scancode; |
||||
SDL_Keymod mod; |
||||
mod = SDL_GetModState(); |
||||
if (k == SDLK_ESCAPE) { |
||||
#ifndef DO_EDITOR |
||||
quit = 1; |
||||
#endif |
||||
} |
||||
|
||||
if (s == SDL_SCANCODE_D) active_control_set(0); |
||||
if (s == SDL_SCANCODE_A) active_control_set(1); |
||||
if (s == SDL_SCANCODE_W) active_control_set(2); |
||||
if (s == SDL_SCANCODE_S) active_control_set(3); |
||||
if (k == SDLK_SPACE) active_control_set(4);
|
||||
if (s == SDL_SCANCODE_LCTRL) active_control_set(5); |
||||
if (s == SDL_SCANCODE_S) active_control_set(6); |
||||
if (s == SDL_SCANCODE_D) active_control_set(7); |
||||
|
||||
#if 0 |
||||
if (game_mode == GAME_editor) { |
||||
switch (k) { |
||||
case SDLK_RIGHT: editor_key(STBTE_scroll_right); break; |
||||
case SDLK_LEFT : editor_key(STBTE_scroll_left ); break; |
||||
case SDLK_UP : editor_key(STBTE_scroll_up ); break; |
||||
case SDLK_DOWN : editor_key(STBTE_scroll_down ); break; |
||||
} |
||||
switch (s) { |
||||
case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break; |
||||
case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break; |
||||
case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break; |
||||
case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break; |
||||
case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break; |
||||
case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break; |
||||
case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break; |
||||
} |
||||
if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) { |
||||
switch (s) { |
||||
case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break; |
||||
case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break; |
||||
case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break; |
||||
case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break; |
||||
case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break; |
||||
} |
||||
} |
||||
} |
||||
#endif |
||||
break; |
||||
} |
||||
case SDL_KEYUP: { |
||||
int k = e->key.keysym.sym; |
||||
int s = e->key.keysym.scancode; |
||||
if (s == SDL_SCANCODE_D) active_control_clear(0); |
||||
if (s == SDL_SCANCODE_A) active_control_clear(1); |
||||
if (s == SDL_SCANCODE_W) active_control_clear(2); |
||||
if (s == SDL_SCANCODE_S) active_control_clear(3); |
||||
if (k == SDLK_SPACE) active_control_clear(4);
|
||||
if (s == SDL_SCANCODE_LCTRL) active_control_clear(5); |
||||
if (s == SDL_SCANCODE_S) active_control_clear(6); |
||||
if (s == SDL_SCANCODE_D) active_control_clear(7); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static SDL_GLContext *context; |
||||
|
||||
static float getTimestep(float minimum_time) |
||||
{ |
||||
float elapsedTime; |
||||
double thisTime; |
||||
static double lastTime = -1; |
||||
|
||||
if (lastTime == -1) |
||||
lastTime = SDL_GetTicks() / 1000.0 - minimum_time; |
||||
|
||||
for(;;) { |
||||
thisTime = SDL_GetTicks() / 1000.0; |
||||
elapsedTime = (float) (thisTime - lastTime); |
||||
if (elapsedTime >= minimum_time) { |
||||
lastTime = thisTime;
|
||||
return elapsedTime; |
||||
} |
||||
// @TODO: compute correct delay
|
||||
SDL_Delay(1); |
||||
} |
||||
} |
||||
|
||||
void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *param) |
||||
{ |
||||
#if 0 |
||||
if (severity == GL_DEBUG_SEVERITY_LOW_ARB) |
||||
return; |
||||
#endif |
||||
ods("%s\n", message); |
||||
} |
||||
|
||||
extern void do_music(int16 *stream, int samples); |
||||
|
||||
extern void foo(void); |
||||
|
||||
void render_init(void); |
||||
|
||||
int is_synchronous_debug; |
||||
void enable_synchronous(void) |
||||
{ |
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); |
||||
is_synchronous_debug = 1; |
||||
} |
||||
|
||||
void prepare_threads(void); |
||||
|
||||
//void stbwingraph_main(void)
|
||||
int SDL_main(int argc, char **argv) |
||||
{ |
||||
SDL_Init(SDL_INIT_VIDEO); |
||||
|
||||
prepare_threads(); |
||||
|
||||
Mix_Init(0); |
||||
Mix_OpenAudio(48000, AUDIO_S16SYS, 2, 1024); // 1024 bytes = 256 samples =
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8); |
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8); |
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); |
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); |
||||
|
||||
#ifdef GL_DEBUG |
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); |
||||
#endif |
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); |
||||
|
||||
screen_x = 1920; |
||||
screen_y = 1080; |
||||
|
||||
window = SDL_CreateWindow("caveview", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, |
||||
screen_x, screen_y, |
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE |
||||
); |
||||
if (!window) error("Couldn't create window"); |
||||
|
||||
context = SDL_GL_CreateContext(window); |
||||
if (!context) error("Couldn't create context"); |
||||
|
||||
SDL_GL_MakeCurrent(window, context); // is this true by default?
|
||||
|
||||
// if (!IsDebuggerPresent())
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE); |
||||
|
||||
stbgl_initExtensions(); |
||||
|
||||
#ifdef GL_DEBUG |
||||
if (glDebugMessageCallbackARB) { |
||||
glDebugMessageCallbackARB(gl_debug, NULL); |
||||
|
||||
enable_synchronous(); |
||||
} |
||||
#endif |
||||
|
||||
SDL_GL_SetSwapInterval(0); // only when profiling
|
||||
|
||||
render_init(); |
||||
init_game(); |
||||
//editor_init();
|
||||
initialized = 1; |
||||
|
||||
while (!quit) { |
||||
SDL_Event e; |
||||
while (SDL_PollEvent(&e)) |
||||
process_event(&e); |
||||
|
||||
loopmode(getTimestep(0.0166f/8), 1, 1); |
||||
quit=quit; |
||||
} |
||||
//music_end();
|
||||
|
||||
return 0; |
||||
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,504 @@ |
||||
// stb_glprog v0.02 public domain functions to reduce GLSL boilerplate
|
||||
// http://nothings.org/stb/stb_glprog.h especially with GL1 + ARB extensions
|
||||
//
|
||||
// Following defines *before* including have following effects:
|
||||
//
|
||||
// STB_GLPROG_IMPLEMENTATION
|
||||
// creates the implementation
|
||||
//
|
||||
// STB_GLPROG_STATIC
|
||||
// forces the implementation to be static (private to file that creates it)
|
||||
//
|
||||
// STB_GLPROG_ARB
|
||||
// uses ARB extension names for GLSL functions and enumerants instead of core names
|
||||
//
|
||||
// STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||
// instantiates function pointers needed, static to implementing file
|
||||
// to avoid collisions (but will collide if implementing file also
|
||||
// defines any; best to isolate this to its own file in this case).
|
||||
// This will try to automatically #include glext.h, but if it's not
|
||||
// in the default include directories you'll need to include it
|
||||
// yourself and define the next macro.
|
||||
//
|
||||
// STB_GLPROG_SUPPRESS_GLEXT_INCLUDE
|
||||
// disables the automatic #include of glext.h which is normally
|
||||
// forced by STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||
//
|
||||
// So, e.g., sample usage on an old Windows compiler:
|
||||
//
|
||||
// #define STB_GLPROG_IMPLEMENTATION
|
||||
// #define STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||
// #include <windows.h>
|
||||
// #include "gl/gl.h"
|
||||
// #include "stb_glprog.h"
|
||||
//
|
||||
// Note though that the header-file version of this (when you don't define
|
||||
// STB_GLPROG_IMPLEMENTATION) still uses GLint and such, so you basically
|
||||
// can only include it in places where you're already including GL, especially
|
||||
// on Windows where including "gl.h" requires (some of) "windows.h".
|
||||
//
|
||||
// See following comment blocks for function documentation.
|
||||
//
|
||||
// Version history:
|
||||
// 2013-12-08 v0.02 slightly simplified API and reduced GL resource usage (@rygorous)
|
||||
// 2013-12-08 v0.01 initial release
|
||||
|
||||
|
||||
// header file section starts here
|
||||
#if !defined(INCLUDE_STB_GLPROG_H) |
||||
#define INCLUDE_STB_GLPROG_H |
||||
|
||||
#ifndef STB_GLPROG_STATIC |
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////// SHADER CREATION
|
||||
|
||||
|
||||
/// EASY API
|
||||
|
||||
extern GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen); |
||||
// This function returns a compiled program or 0 if there's an error.
|
||||
// To free the created program, call stbgl_delete_program.
|
||||
//
|
||||
// stbgl_create_program(
|
||||
// char **vertex_source, // NULL or one or more strings with the vertex shader source, with a final NULL
|
||||
// char **frag_source, // NULL or one or more strings with the fragment shader source, with a final NULL
|
||||
// char **binds, // NULL or zero or more strings with attribute bind names, with a final NULL
|
||||
// char *error, // output location where compile error message is placed
|
||||
// int error_buflen) // length of error output buffer
|
||||
//
|
||||
// Returns a GLuint with the GL program object handle.
|
||||
//
|
||||
// If an individual bind string is "", no name is bound to that slot (this
|
||||
// allows you to create binds that aren't continuous integers starting at 0).
|
||||
//
|
||||
// If the vertex shader is NULL, then fixed-function vertex pipeline
|
||||
// is used, if that's legal in your version of GL.
|
||||
//
|
||||
// If the fragment shader is NULL, then fixed-function fragment pipeline
|
||||
// is used, if that's legal in your version of GL.
|
||||
|
||||
extern void stgbl_delete_program(GLuint program); |
||||
// deletes a program created by stbgl_create_program or stbgl_link_program
|
||||
|
||||
|
||||
/// FLEXIBLE API
|
||||
|
||||
extern GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen); |
||||
// compiles a shader. returns the shader on success or 0 on failure.
|
||||
//
|
||||
// type either: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
|
||||
// or GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB
|
||||
// or STBGL_VERTEX_SHADER or STBGL_FRAGMENT_SHADER
|
||||
// sources array of strings containing the shader source
|
||||
// num_sources number of string in sources, or -1 meaning sources is NULL-terminated
|
||||
// error string to output compiler error to
|
||||
// error_buflen length of error buffer in chars
|
||||
|
||||
extern GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen); |
||||
// links a shader. returns the linked program on success or 0 on failure.
|
||||
//
|
||||
// vertex_shader a compiled vertex shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
|
||||
// fragment_shader a compiled fragment shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
|
||||
//
|
||||
|
||||
extern void stbgl_delete_shader(GLuint shader); |
||||
// deletes a shader created by stbgl_compile_shader
|
||||
|
||||
|
||||
///////////// RENDERING WITH SHADERS
|
||||
|
||||
extern GLint stbgl_find_uniform(GLuint prog, char *uniform); |
||||
|
||||
extern void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms); |
||||
// Given the locations array that is num_uniforms long, fills out
|
||||
// the locations of each of those uniforms for the specified program.
|
||||
// If num_uniforms is -1, then uniforms[] must be NULL-terminated
|
||||
|
||||
// the following functions just wrap the difference in naming between GL2+ and ARB,
|
||||
// so you don't need them unless you're using both ARB and GL2+ in the same codebase,
|
||||
// or you're relying on this lib to provide the extensions
|
||||
extern void stbglUseProgram(GLuint program); |
||||
extern void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer); |
||||
extern void stbglEnableVertexAttribArray(GLuint index); |
||||
extern void stbglDisableVertexAttribArray(GLuint index); |
||||
extern void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v); |
||||
extern void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v); |
||||
extern void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v); |
||||
extern void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v); |
||||
extern void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v); |
||||
extern void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v); |
||||
extern void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v); |
||||
extern void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v); |
||||
extern void stbglUniform1f(GLint loc, float v0); |
||||
extern void stbglUniform2f(GLint loc, float v0, float v1); |
||||
extern void stbglUniform3f(GLint loc, float v0, float v1, float v2); |
||||
extern void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3); |
||||
extern void stbglUniform1i(GLint loc, GLint v0); |
||||
extern void stbglUniform2i(GLint loc, GLint v0, GLint v1); |
||||
extern void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2); |
||||
extern void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3); |
||||
|
||||
|
||||
////////////// END OF FUNCTIONS
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
#endif // STB_GLPROG_STATIC
|
||||
|
||||
#ifdef STB_GLPROG_ARB |
||||
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB |
||||
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB |
||||
#else |
||||
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER |
||||
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER |
||||
#endif |
||||
|
||||
#endif // INCLUDE_STB_GLPROG_H
|
||||
|
||||
|
||||
///////// header file section ends here
|
||||
|
||||
|
||||
#ifdef STB_GLPROG_IMPLEMENTATION |
||||
#include <string.h> // strncpy |
||||
|
||||
#ifdef STB_GLPROG_STATIC |
||||
#define STB_GLPROG_DECLARE static |
||||
#else |
||||
#define STB_GLPROG_DECLARE extern |
||||
#endif |
||||
|
||||
// check if user wants this file to define the GL extensions itself
|
||||
#ifdef STB_GLPROG_ARB_DEFINE_EXTENSIONS |
||||
#define STB_GLPROG_ARB // make sure later code uses the extensions
|
||||
|
||||
#ifndef STB_GLPROG_SUPPRESS_GLEXT_INCLUDE |
||||
#include "glext.h" |
||||
#endif |
||||
|
||||
#define STB_GLPROG_EXTENSIONS \ |
||||
STB_GLPROG_FUNC(ATTACHOBJECT , AttachObject ) \
|
||||
STB_GLPROG_FUNC(BINDATTRIBLOCATION , BindAttribLocation ) \
|
||||
STB_GLPROG_FUNC(COMPILESHADER , CompileShader ) \
|
||||
STB_GLPROG_FUNC(CREATEPROGRAMOBJECT , CreateProgramObject ) \
|
||||
STB_GLPROG_FUNC(CREATESHADEROBJECT , CreateShaderObject ) \
|
||||
STB_GLPROG_FUNC(DELETEOBJECT , DeleteObject ) \
|
||||
STB_GLPROG_FUNC(DETACHOBJECT , DetachObject ) \
|
||||
STB_GLPROG_FUNC(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray) \
|
||||
STB_GLPROG_FUNC(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray ) \
|
||||
STB_GLPROG_FUNC(GETATTACHEDOBJECTS , GetAttachedObjects ) \
|
||||
STB_GLPROG_FUNC(GETOBJECTPARAMETERIV, GetObjectParameteriv) \
|
||||
STB_GLPROG_FUNC(GETINFOLOG , GetInfoLog ) \
|
||||
STB_GLPROG_FUNC(GETUNIFORMLOCATION , GetUniformLocation ) \
|
||||
STB_GLPROG_FUNC(LINKPROGRAM , LinkProgram ) \
|
||||
STB_GLPROG_FUNC(SHADERSOURCE , ShaderSource ) \
|
||||
STB_GLPROG_FUNC(UNIFORM1F , Uniform1f ) \
|
||||
STB_GLPROG_FUNC(UNIFORM2F , Uniform2f ) \
|
||||
STB_GLPROG_FUNC(UNIFORM3F , Uniform3f ) \
|
||||
STB_GLPROG_FUNC(UNIFORM4F , Uniform4f ) \
|
||||
STB_GLPROG_FUNC(UNIFORM1I , Uniform1i ) \
|
||||
STB_GLPROG_FUNC(UNIFORM2I , Uniform2i ) \
|
||||
STB_GLPROG_FUNC(UNIFORM3I , Uniform3i ) \
|
||||
STB_GLPROG_FUNC(UNIFORM4I , Uniform4i ) \
|
||||
STB_GLPROG_FUNC(UNIFORM1FV , Uniform1fv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM2FV , Uniform2fv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM3FV , Uniform3fv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM4FV , Uniform4fv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM1IV , Uniform1iv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM2IV , Uniform2iv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM3IV , Uniform3iv ) \
|
||||
STB_GLPROG_FUNC(UNIFORM4IV , Uniform4iv ) \
|
||||
STB_GLPROG_FUNC(USEPROGRAMOBJECT , UseProgramObject ) \
|
||||
STB_GLPROG_FUNC(VERTEXATTRIBPOINTER , VertexAttribPointer ) |
||||
|
||||
// define the static function pointers
|
||||
|
||||
#define STB_GLPROG_FUNC(x,y) static PFNGL##x##ARBPROC gl##y##ARB; |
||||
STB_GLPROG_EXTENSIONS |
||||
#undef STB_GLPROG_FUNC |
||||
|
||||
// define the GetProcAddress
|
||||
|
||||
#ifdef _WIN32 |
||||
#ifndef WINGDIAPI |
||||
#ifndef STB__HAS_WGLPROC |
||||
typedef int (__stdcall *stbgl__voidfunc)(void); |
||||
static __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *); |
||||
#endif |
||||
#endif |
||||
#define STBGL__GET_FUNC(x) wglGetProcAddress(x) |
||||
#else |
||||
#error "need to define how this platform gets extensions" |
||||
#endif |
||||
|
||||
// create a function that fills out the function pointers
|
||||
|
||||
static void stb_glprog_init(void) |
||||
{ |
||||
static int initialized = 0; // not thread safe!
|
||||
if (initialized) return; |
||||
#define STB_GLPROG_FUNC(x,y) gl##y##ARB = (PFNGL##x##ARBPROC) STBGL__GET_FUNC("gl" #y "ARB"); |
||||
STB_GLPROG_EXTENSIONS |
||||
#undef STB_GLPROG_FUNC |
||||
} |
||||
#undef STB_GLPROG_EXTENSIONS |
||||
|
||||
#else |
||||
static void stb_glprog_init(void) |
||||
{ |
||||
} |
||||
#endif |
||||
|
||||
|
||||
// define generic names for many of the gl functions or extensions for later use;
|
||||
// note that in some cases there are two functions in core and one function in ARB
|
||||
#ifdef STB_GLPROG_ARB |
||||
#define stbglCreateShader glCreateShaderObjectARB |
||||
#define stbglDeleteShader glDeleteObjectARB |
||||
#define stbglAttachShader glAttachObjectARB |
||||
#define stbglDetachShader glDetachObjectARB |
||||
#define stbglShaderSource glShaderSourceARB |
||||
#define stbglCompileShader glCompileShaderARB |
||||
#define stbglGetShaderStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_COMPILE_STATUS_ARB, b) |
||||
#define stbglGetShaderInfoLog glGetInfoLogARB |
||||
#define stbglCreateProgram glCreateProgramObjectARB |
||||
#define stbglDeleteProgram glDeleteObjectARB |
||||
#define stbglLinkProgram glLinkProgramARB |
||||
#define stbglGetProgramStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_LINK_STATUS_ARB, b) |
||||
#define stbglGetProgramInfoLog glGetInfoLogARB |
||||
#define stbglGetAttachedShaders glGetAttachedObjectsARB |
||||
#define stbglBindAttribLocation glBindAttribLocationARB |
||||
#define stbglGetUniformLocation glGetUniformLocationARB |
||||
#define stbgl_UseProgram glUseProgramObjectARB |
||||
#else |
||||
#define stbglCreateShader glCreateShader |
||||
#define stbglDeleteShader glDeleteShader |
||||
#define stbglAttachShader glAttachShader |
||||
#define stbglDetachShader glDetachShader |
||||
#define stbglShaderSource glShaderSource |
||||
#define stbglCompileShader glCompileShader |
||||
#define stbglGetShaderStatus(a,b) glGetShaderiv(a, GL_COMPILE_STATUS, b) |
||||
#define stbglGetShaderInfoLog glGetShaderInfoLog |
||||
#define stbglCreateProgram glCreateProgram |
||||
#define stbglDeleteProgram glDeleteProgram |
||||
#define stbglLinkProgram glLinkProgram |
||||
#define stbglGetProgramStatus(a,b) glGetProgramiv(a, GL_LINK_STATUS, b) |
||||
#define stbglGetProgramInfoLog glGetProgramInfoLog |
||||
#define stbglGetAttachedShaders glGetAttachedShaders |
||||
#define stbglBindAttribLocation glBindAttribLocation |
||||
#define stbglGetUniformLocation glGetUniformLocation |
||||
#define stbgl_UseProgram glUseProgram |
||||
#endif |
||||
|
||||
|
||||
// perform a safe strcat of 3 strings, given that we can't rely on portable snprintf
|
||||
// if you need to break on error, this is the best place to place a breakpoint
|
||||
static void stb_glprog_error(char *error, int error_buflen, char *str1, char *str2, char *str3) |
||||
{ |
||||
int n = strlen(str1); |
||||
strncpy(error, str1, error_buflen); |
||||
if (n < error_buflen && str2) { |
||||
strncpy(error+n, str2, error_buflen - n); |
||||
n += strlen(str2); |
||||
if (n < error_buflen && str3) { |
||||
strncpy(error+n, str3, error_buflen - n); |
||||
} |
||||
} |
||||
error[error_buflen-1] = 0; |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen) |
||||
{ |
||||
char *typename = (type == STBGL_VERTEX_SHADER ? "vertex" : "fragment"); |
||||
int len; |
||||
GLint result; |
||||
GLuint shader; |
||||
|
||||
// initialize the extensions if we haven't already
|
||||
stb_glprog_init(); |
||||
|
||||
// allocate
|
||||
|
||||
shader = stbglCreateShader(type); |
||||
if (!shader) { |
||||
stb_glprog_error(error, error_buflen, "Couldn't allocate shader object in stbgl_compile_shader for ", typename, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
// compile
|
||||
|
||||
// if num_sources is negative, assume source is NULL-terminated and count the non-NULL ones
|
||||
if (num_sources < 0) |
||||
for (num_sources = 0; sources[num_sources] != NULL; ++num_sources) |
||||
; |
||||
stbglShaderSource(shader, num_sources, sources, NULL); |
||||
stbglCompileShader(shader); |
||||
stbglGetShaderStatus(shader, &result); |
||||
if (result) |
||||
return shader; |
||||
|
||||
// errors
|
||||
|
||||
stb_glprog_error(error, error_buflen, "Compile error for ", typename, " shader: "); |
||||
len = strlen(error); |
||||
if (len < error_buflen) |
||||
stbglGetShaderInfoLog(shader, error_buflen-len, NULL, error+len); |
||||
|
||||
stbglDeleteShader(shader); |
||||
return 0; |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen) |
||||
{ |
||||
int len; |
||||
GLint result; |
||||
|
||||
// allocate
|
||||
|
||||
GLuint prog = stbglCreateProgram(); |
||||
if (!prog) { |
||||
stb_glprog_error(error, error_buflen, "Couldn't allocate program object in stbgl_link_program", NULL, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
// attach
|
||||
|
||||
if (vertex_shader) |
||||
stbglAttachShader(prog, vertex_shader); |
||||
if (fragment_shader) |
||||
stbglAttachShader(prog, fragment_shader); |
||||
|
||||
// attribute binds
|
||||
|
||||
if (binds) { |
||||
int i; |
||||
// if num_binds is negative, then it is NULL terminated
|
||||
if (num_binds < 0) |
||||
for (num_binds=0; binds[num_binds]; ++num_binds) |
||||
; |
||||
for (i=0; i < num_binds; ++i) |
||||
if (binds[i] && binds[i][0]) // empty binds can be NULL or ""
|
||||
stbglBindAttribLocation(prog, i, binds[i]); |
||||
} |
||||
|
||||
// link
|
||||
|
||||
stbglLinkProgram(prog); |
||||
|
||||
// detach
|
||||
|
||||
if (vertex_shader) |
||||
stbglDetachShader(prog, vertex_shader); |
||||
if (fragment_shader) |
||||
stbglDetachShader(prog, fragment_shader); |
||||
|
||||
// errors
|
||||
|
||||
stbglGetProgramStatus(prog, &result); |
||||
if (result) |
||||
return prog; |
||||
|
||||
stb_glprog_error(error, error_buflen, "Link error: ", NULL, NULL); |
||||
len = strlen(error); |
||||
if (len < error_buflen) |
||||
stbglGetProgramInfoLog(prog, error_buflen-len, NULL, error+len); |
||||
|
||||
stbglDeleteProgram(prog); |
||||
return 0;
|
||||
} |
||||
|
||||
STB_GLPROG_DECLARE GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen) |
||||
{ |
||||
GLuint vertex, fragment, prog=0; |
||||
vertex = stbgl_compile_shader(STBGL_VERTEX_SHADER, vertex_source, -1, error, error_buflen); |
||||
if (vertex) { |
||||
fragment = stbgl_compile_shader(STBGL_FRAGMENT_SHADER, frag_source, -1, error, error_buflen); |
||||
if (fragment) |
||||
prog = stbgl_link_program(vertex, fragment, binds, -1, error, error_buflen); |
||||
if (fragment) |
||||
stbglDeleteShader(fragment); |
||||
stbglDeleteShader(vertex); |
||||
} |
||||
return prog; |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE void stbgl_delete_shader(GLuint shader) |
||||
{ |
||||
stbglDeleteShader(shader); |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE void stgbl_delete_program(GLuint program) |
||||
{ |
||||
stbglDeleteProgram(program); |
||||
} |
||||
|
||||
GLint stbgl_find_uniform(GLuint prog, char *uniform) |
||||
{ |
||||
return stbglGetUniformLocation(prog, uniform); |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms) |
||||
{ |
||||
int i; |
||||
if (num_uniforms < 0) |
||||
num_uniforms = 999999; |
||||
for (i=0; i < num_uniforms && uniforms[i]; ++i) |
||||
locations[i] = stbglGetUniformLocation(prog, uniforms[i]); |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE void stbglUseProgram(GLuint program) |
||||
{ |
||||
stbgl_UseProgram(program); |
||||
} |
||||
|
||||
#ifdef STB_GLPROG_ARB |
||||
#define STBGL_ARBIFY(name) name##ARB |
||||
#else |
||||
#define STBGL_ARBIFY(name) name |
||||
#endif |
||||
|
||||
STB_GLPROG_DECLARE void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer) |
||||
{ |
||||
STBGL_ARBIFY(glVertexAttribPointer)(index, size, type, normalized, stride, pointer); |
||||
} |
||||
|
||||
STB_GLPROG_DECLARE void stbglEnableVertexAttribArray (GLuint index) { STBGL_ARBIFY(glEnableVertexAttribArray )(index); } |
||||
STB_GLPROG_DECLARE void stbglDisableVertexAttribArray(GLuint index) { STBGL_ARBIFY(glDisableVertexAttribArray)(index); } |
||||
|
||||
STB_GLPROG_DECLARE void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform1fv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform2fv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform3fv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform4fv)(loc,count,v); } |
||||
|
||||
STB_GLPROG_DECLARE void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform1iv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform2iv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform3iv)(loc,count,v); } |
||||
STB_GLPROG_DECLARE void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform4iv)(loc,count,v); } |
||||
|
||||
STB_GLPROG_DECLARE void stbglUniform1f(GLint loc, float v0) |
||||
{ STBGL_ARBIFY(glUniform1f)(loc,v0); } |
||||
STB_GLPROG_DECLARE void stbglUniform2f(GLint loc, float v0, float v1) |
||||
{ STBGL_ARBIFY(glUniform2f)(loc,v0,v1); } |
||||
STB_GLPROG_DECLARE void stbglUniform3f(GLint loc, float v0, float v1, float v2) |
||||
{ STBGL_ARBIFY(glUniform3f)(loc,v0,v1,v2); } |
||||
STB_GLPROG_DECLARE void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3) |
||||
{ STBGL_ARBIFY(glUniform4f)(loc,v0,v1,v2,v3); } |
||||
|
||||
STB_GLPROG_DECLARE void stbglUniform1i(GLint loc, GLint v0) |
||||
{ STBGL_ARBIFY(glUniform1i)(loc,v0); } |
||||
STB_GLPROG_DECLARE void stbglUniform2i(GLint loc, GLint v0, GLint v1) |
||||
{ STBGL_ARBIFY(glUniform2i)(loc,v0,v1); } |
||||
STB_GLPROG_DECLARE void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2) |
||||
{ STBGL_ARBIFY(glUniform3i)(loc,v0,v1,v2); } |
||||
STB_GLPROG_DECLARE void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3) |
||||
{ STBGL_ARBIFY(glUniform4i)(loc,v0,v1,v2,v3); } |
||||
|
||||
#endif |
@ -0,0 +1,224 @@ |
||||
/*
|
||||
SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 |
||||
|
||||
The WinMain function -- calls your program's main() function |
||||
*/ |
||||
#include "SDL_config.h" |
||||
|
||||
#ifdef __WIN32__ |
||||
|
||||
//#include "../../core/windows/SDL_windows.h"
|
||||
|
||||
/* Include this so we define UNICODE properly */ |
||||
#if defined(__WIN32__) |
||||
#define WIN32_LEAN_AND_MEAN |
||||
#define STRICT |
||||
#ifndef UNICODE |
||||
#define UNICODE 1 |
||||
#endif |
||||
#undef _WIN32_WINNT |
||||
#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */ |
||||
#endif |
||||
|
||||
#include <windows.h> |
||||
|
||||
/* Routines to convert from UTF8 to native Windows text */ |
||||
#if UNICODE |
||||
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR)) |
||||
#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1) |
||||
#else |
||||
/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */ |
||||
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1)) |
||||
#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1) |
||||
#endif |
||||
|
||||
/* Sets an error message based on a given HRESULT */ |
||||
extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr); |
||||
|
||||
/* Sets an error message based on GetLastError(). Always return -1. */ |
||||
extern int WIN_SetError(const char *prefix); |
||||
|
||||
/* Wrap up the oddities of CoInitialize() into a common function. */ |
||||
extern HRESULT WIN_CoInitialize(void); |
||||
extern void WIN_CoUninitialize(void); |
||||
|
||||
/* Returns SDL_TRUE if we're running on Windows Vista and newer */ |
||||
extern BOOL WIN_IsWindowsVistaOrGreater(); |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
/* Include the SDL main definition header */ |
||||
#include "SDL.h" |
||||
#include "SDL_main.h" |
||||
|
||||
#ifdef main |
||||
# undef main |
||||
#endif /* main */ |
||||
|
||||
static void |
||||
UnEscapeQuotes(char *arg) |
||||
{ |
||||
char *last = NULL; |
||||
|
||||
while (*arg) { |
||||
if (*arg == '"' && (last != NULL && *last == '\\')) { |
||||
char *c_curr = arg; |
||||
char *c_last = last; |
||||
|
||||
while (*c_curr) { |
||||
*c_last = *c_curr; |
||||
c_last = c_curr; |
||||
c_curr++; |
||||
} |
||||
*c_last = '\0'; |
||||
} |
||||
last = arg; |
||||
arg++; |
||||
} |
||||
} |
||||
|
||||
/* Parse a command line buffer into arguments */ |
||||
static int |
||||
ParseCommandLine(char *cmdline, char **argv) |
||||
{ |
||||
char *bufp; |
||||
char *lastp = NULL; |
||||
int argc, last_argc; |
||||
|
||||
argc = last_argc = 0; |
||||
for (bufp = cmdline; *bufp;) { |
||||
/* Skip leading whitespace */ |
||||
while (SDL_isspace(*bufp)) { |
||||
++bufp; |
||||
} |
||||
/* Skip over argument */ |
||||
if (*bufp == '"') { |
||||
++bufp; |
||||
if (*bufp) { |
||||
if (argv) { |
||||
argv[argc] = bufp; |
||||
} |
||||
++argc; |
||||
} |
||||
/* Skip over word */ |
||||
lastp = bufp; |
||||
while (*bufp && (*bufp != '"' || *lastp == '\\')) { |
||||
lastp = bufp; |
||||
++bufp; |
||||
} |
||||
} else { |
||||
if (*bufp) { |
||||
if (argv) { |
||||
argv[argc] = bufp; |
||||
} |
||||
++argc; |
||||
} |
||||
/* Skip over word */ |
||||
while (*bufp && !SDL_isspace(*bufp)) { |
||||
++bufp; |
||||
} |
||||
} |
||||
if (*bufp) { |
||||
if (argv) { |
||||
*bufp = '\0'; |
||||
} |
||||
++bufp; |
||||
} |
||||
|
||||
/* Strip out \ from \" sequences */ |
||||
if (argv && last_argc != argc) { |
||||
UnEscapeQuotes(argv[last_argc]); |
||||
} |
||||
last_argc = argc; |
||||
} |
||||
if (argv) { |
||||
argv[argc] = NULL; |
||||
} |
||||
return (argc); |
||||
} |
||||
|
||||
/* Show an error message */ |
||||
static void |
||||
ShowError(const char *title, const char *message) |
||||
{ |
||||
/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */ |
||||
#ifdef USE_MESSAGEBOX |
||||
MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); |
||||
#else |
||||
fprintf(stderr, "%s: %s\n", title, message); |
||||
#endif |
||||
} |
||||
|
||||
/* Pop up an out of memory message, returns to Windows */ |
||||
static BOOL |
||||
OutOfMemory(void) |
||||
{ |
||||
ShowError("Fatal Error", "Out of memory - aborting"); |
||||
return FALSE; |
||||
} |
||||
|
||||
#if defined(_MSC_VER) |
||||
/* The VC++ compiler needs main defined */ |
||||
#define console_main main |
||||
#endif |
||||
|
||||
/* This is where execution begins [console apps] */ |
||||
int |
||||
console_main(int argc, char *argv[]) |
||||
{ |
||||
int status; |
||||
|
||||
SDL_SetMainReady(); |
||||
|
||||
/* Run the application main() code */ |
||||
status = SDL_main(argc, argv); |
||||
|
||||
/* Exit cleanly, calling atexit() functions */ |
||||
exit(status); |
||||
|
||||
/* Hush little compiler, don't you cry... */ |
||||
return 0; |
||||
} |
||||
|
||||
/* This is where execution begins [windowed apps] */ |
||||
int WINAPI |
||||
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) |
||||
{ |
||||
char **argv; |
||||
int argc; |
||||
char *cmdline; |
||||
|
||||
/* Grab the command line */ |
||||
TCHAR *text = GetCommandLine(); |
||||
#if UNICODE |
||||
cmdline = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)(text), (SDL_wcslen(text)+1)*sizeof(WCHAR)); |
||||
#else |
||||
cmdline = SDL_strdup(text); |
||||
#endif |
||||
if (cmdline == NULL) { |
||||
return OutOfMemory(); |
||||
} |
||||
|
||||
/* Parse it into argv and argc */ |
||||
argc = ParseCommandLine(cmdline, NULL); |
||||
argv = SDL_stack_alloc(char *, argc + 1); |
||||
if (argv == NULL) { |
||||
return OutOfMemory(); |
||||
} |
||||
ParseCommandLine(cmdline, argv); |
||||
|
||||
/* Run the main program */ |
||||
console_main(argc, argv); |
||||
|
||||
SDL_stack_free(argv); |
||||
|
||||
SDL_free(cmdline); |
||||
|
||||
/* Hush little compiler, don't you cry... */ |
||||
return 0; |
||||
} |
||||
|
||||
#endif /* __WIN32__ */ |
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */ |
Loading…
Reference in New Issue