Merge branch 'master' into working

pull/102/head
Sean Barrett 2015-04-01 16:47:02 -07:00
commit a1f1010fad
20 changed files with 20216 additions and 2 deletions

View File

@ -13,6 +13,7 @@ library | lastest version | category | description
**stb_rect_pack.h** | 0.05 | graphics | simple 2D rectangle packer with decent quality
**stretchy_buffer.h** | 1.01 | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
**stb_textedit.h** | 1.5 | UI | guts of a text editor for games etc implementing them from scratch
**stb_voxel_render.h** | 0.76 | 3D&nbsp;graphics | Minecraft-esque voxel rendering "engine" with many more features
**stb_dxt.h** | 1.04 | 3D&nbsp;graphics | Fabian "ryg" Giesen's real-time DXT compressor
**stb_perlin.h** | 0.2 | 3D&nbsp;graphics | revised Perlin noise (3D input, 1D output)
**stb_easy_font.h** | 0.5 | 3D&nbsp;graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc

View File

@ -0,0 +1,176 @@
# An interview with STB about stb_voxel_render.h
**Q:**
I suppose you really like Minecraft?
**A:**
Not really. I mean, I do own it and play it some, and
I do watch YouTube videos of other people playing it
once in a while, but I'm not saying it's that great.
But I do love voxels. I've been playing with voxel rendering
since the mid-late 90's when we were still doing software
rendering and thinking maybe polygons weren't the answer.
Once GPUs came along that kind of died off, at least until
Minecraft brought it back to attention.
**Q:**
Do you expect people will make a lot of Minecraft clones
with this?
**A:**
I hope not!
For one thing, it's a terrible idea for the
developer. Remember before Minecraft was on the Xbox 360,
there were a ton of "indie" clones (some maybe making
decent money even), but then the real Minecraft came out
and just crushed them (as far as I know). It's just not
something you really want to compete with.
The reason I made this library is because I'd like
to see more games with Minecraft's *art style*, not
necessary its *gameplay*.
I can understand the urge to clone the gameplay. When
you have a world made of voxels/blocks, there are a
few things that become incredibly easy to do that would
otherwise be very hard (at least for an indie) to do in 3D.
One thing is that procedural generation becomes much easier.
Another is that destructible environments are easy. Another
is that you have a world where your average user can build
stuff that they find satisfactory.
Minecraft is at a sort of local maximum, a sweet spot, where
it leverages all of those easy-to-dos. And so I'm sure it's
hard to look at the space of 'games using voxels' and move
away from that local maximum, to give up some of that.
But I think that's what people should do.
**Q:**
So what else can people do with stb_voxel_render?
**A:**
All of those benefits I mentioned above are still valid even
if you stay away from the sweet spot. You can make a 3D roguelike
without player-creation/destruction that uses procedural generation.
You could make a shooter with pre-designed maps but destructible
environments.
And I'm sure there are other possible benefits to using voxels/blocks.
Hopefully this will make it easier for people to explore the space.
The library has a pretty wide range of features to allow
people to come up with some distinctive looks. For example,
the art style of Continue?9876543210 was one of the inspirations
for trying to make the multitexturing capabilities flexible.
I'm terrible at art, so this isn't really something I can
come up with myself, but I tried to put in flexible
technology that could be used multiple ways.
One thing I did intentionally was try to make it possible to
make nicer looking ground terrain, using the half-height
slopes and "weird slopes". There are Minecraft mods with
drivable cars and they just go up these blocky slopes and,
like, what? So I wanted you to be able to make smoother
terrain, either just for the look, or for vehicles etc.
Also, you can cross-fade between two ground textures for
that classic bad dirt/grass transition that has shipped
in plenty of professional games. Of course, you could
just use a separate non-voxel ground renderer for all of
this. But this way, you can seamlessly integrate everything
else with it. E.g. in your authoring tool (or procedural
generation) you can make smooth ground and then cut a
sharp-edged hole in it for a building's basement or whatever.
Another thing you can do is work at a very different scale.
In Minecraft, a person is just under 2 blocks tall. In
Ace of Spades, a person is just under 3 blocks tall. Why
not 4 or 6? Well, partly because you just need a lot more
voxels; if a meter is 2 voxels in Mineraft and 4 voxels in
your game, and you draw the same number of voxels due to
hardware limits, then your game has half the view distance
of Minecraft. Since stb_voxel_render is designed to keep
the meshes small and render efficiently, you can push the
view distance out further than Minecraft--or use a similar
view distance and a higher voxel resolution. You could also
stop making infinite worlds and work at entirely different
scales; where Minecraft is 1 voxel per meter, you could
have 20 voxels per meter and make a small arena that's
50 meters wide and 5 meters tall.
Back when the voxel game Voxatron was announced, the weekend
after the trailer came out I wrote my own little GPU-accelerated
version of the engine and thought that was pretty cool. I've
been tempted many times to extract that and release it, but
I don't want to steal Voxatron's thunder so I've avoided
it. You could use this engine to do the same kind of thing,
although it won't be as efficient as an engine dedicated to
that style of thing would be. (For example, if you're building
the whole mesh from scratch every frame--which you should do
because you want to enable that worst case--you can skip
creating voxel faces that face away from the camera, since
they can never be seen.)
**Q:**
What one thing would you really like to see somebody do?
**A:**
Before Unity, 3D has seemed deeply problematic in the indie
space. Software like GameMaker has tried to support 3D but
it seems like little of note has been done with it.
Minecraft has shown that people can build worlds with that
toolset far more easily than we've ever seen from those
other tools. Obviously people have done great things with
Unity, but those people are much closer to professional
developers; typically they still need real 3D modelling
and all of that stuff.
So what I'd really like to see is someone build some kind
of voxel-game-construction-set. Start with stb_voxel_render,
maybe expose all the flexibility of stb_voxel_render (so
people can do different things). Thrown in lua or something
else for scripting, make some kind of editor that feels
at least as good as Minecraft and Infinifactory, and see
where that gets you.
**Q:**
Why'd you make this library?
**A:**
Mainly as a way of releasing this technology I've been working
on since 2011 and seemed unlikely to ever ship myself. In 2011
I was playing the voxel shooter Ace of Spades. One of the maps
that we played on was a partial port of Broville (which is the
first Minecraft map in stb_voxel_render release trailer). I'd
made a bunch of procedural level generators for the game, and
I started trying to make a city generator inspired by Broville.
But I realized it would be a lot of work, and of very little
value (most of my maps didn't get much play because people
preferred to play on maps where they could charge straight
at the enemies and shoot them as fast as possible). So I
wrote my own voxel engine and started working on a procedural
city game. But I got bogged down after I finally got the road
generator working and never got anywhere with building
generation or gameplay.
stb_voxel_render is actually a complete rewrite from scratch,
but it's based a lot on what I learned from that previous work.
**Q:**
About the release video... how long did that take to edit?
**A:**
About seven or eight hours. I had the first version done in
maybe six or seven hours, but then I realized I'd left out
one clip, and when I went back to add it I also gussied up
a couple other moments in the video. But there was something
basically identical to it that was done in around six.
**Q:**
Ok, that's it. Thanks, me.
**A:**
Thanks *me!*

View File

@ -209,12 +209,12 @@ static int stb_easy_font_print(float x, float y, char *text, unsigned char color
static int stb_easy_font_width(char *text)
{
int len = 0;
float len = 0;
while (*text) {
len += stb_easy_font_charinfo[*text-32].advance & 15;
len += stb_easy_font_spacing_val;
++text;
}
return len;
return (int) ceil(len);
}
#endif

3576
stb_voxel_render.h Normal file

File diff suppressed because it is too large Load Diff

85
tests/caveview/README.md Normal file
View File

@ -0,0 +1,85 @@
# FAQ
### How to run it?
There's no GUI. Find a directory with Minecraft Anvil files (.mca).
Copy a Minecraft "terrain.png" into that directory (do a google
image search). Run from that directory.
### How accurate is this as a Minecraft viewer?
Not very. Many Minecraft blocks are not handled correctly:
* No redstone, rails, or other "flat" blocks
* No signs, doors, fences, carpets, or other complicated geometry
* Stairs are turned into ramps
* Upper slabs turn into lower slabs
* Wood types only for blocks, not stairs, slabs, etc
* Colored glass becomes regular glass
* Glass panes become glass blocks
* Water is opaque
* Water level is incorrect
* No biome coloration
* Cactus is not shrunk, shows holes
* Chests are not shrunk
* Double-chests draw as two chests
* Pumpkins etc. are not rotated properly
* Torches are drawn hackily, do not attach to walls
* Incorrect textures for blocks that postdate terrain.png
* Transparent textures have black fringes due to non-premultiplied-alpha
* Skylight and block light are combined in a single value
* Only blocks at y=1..255 are shown (not y=0)
* If a 32x32x256 "quad-chunk" needs more than 800K quads, isn't handled (very unlikely)
Some of these are due to engine limitations, and some of
these are because I didn't make the effort since my
goal was to make a demo for stb_voxel_render.h, not
to make a proper Minecraft viewer.
### Could this be turned into a proper Minecraft viewer?
Yes and no. Yes, you could do it, but no, it wouldn't
really resemble this code that much anymore.
You could certainly use this engine to
render the parts of Minecraft it works for, but many
of the things it doesn't handle it can't handle at all
(stairs, water, fences, carpets, etc) because it uses
low-precision coordinates to store voxel data.
You would have to render all of the stuff it doesn't
handle through another rendering path. In a game (not
a viewer) you would need such a path for movable entities
like doors and carts anyway, so possibly handling other
things that way wouldn't be so bad.
Rails, ladders, and redstone lines could be implemented by
using tex2 to overlay those effects, but you can't rotate
tex1 and tex2 independently, so there may be cases where
the underlying texture needs a different rotation from the
overlaid texture, which would require separate rendering.
Handling redstone's brightness being different from underlying
block's brightness would require separate rendering.
You can use the face-color effect to do biome coloration,
but the change won't be smooth the way it is in Minecraft.
### Why isn't building the mesh data faster?
Partly because converting from minecraft data is expensive.
Here is the approximate breakdown of an older version
of this executable and lib that did the building single-threaded.
* 25% loading & parsing minecraft files (4/5ths of this is my crappy zlib)
* 18% converting from minecraft blockids & lighting to stb blockids & lighting
* 10% reordering from data[z][y]\[x] (minecraft-style) to data[y][x]\[z] (stb-style)
* 40% building mesh data
* 7% uploading mesh data to OpenGL
I did do significant optimizations after the above, so the
final breakdown is different, but it should give you some
sense of the costs.

598
tests/caveview/cave_main.c Normal file
View File

@ -0,0 +1,598 @@
#define _WIN32_WINNT 0x400
#include <assert.h>
#include <windows.h>
// stb.h
#define STB_DEFINE
#include "stb.h"
// stb_gl.h
#define STB_GL_IMPLEMENTATION
#define STB_GLEXT_DEFINE "glext_list.h"
#include "stb_gl.h"
// SDL
#include "sdl.h"
#include "SDL_opengl.h"
// stb_glprog.h
#define STB_GLPROG_IMPLEMENTATION
#define STB_GLPROG_ARB_DEFINE_EXTENSIONS
#include "stb_glprog.h"
// stb_image.h
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// stb_easy_font.h
#include "stb_easy_font.h" // doesn't require an IMPLEMENTATION
#include "caveview.h"
char *game_name = "caveview";
#define REVERSE_DEPTH
static 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);
}
static float text_color[3];
static float pos_x = 10;
static float pos_y = 10;
static 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;
}
float camang[3], camloc[3] = { 60,22,77 };
float player_zoom = 1.0;
float rotate_view = 0.0;
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 = (float) sin(camang[0]*3.141592/180);
c = (float) 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 = (float) sin(camang[2]*3.141592/180);
c = (float) 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.0f // blocks per second
#define ACCEL 6.0f
#define DECEL 3.0f
#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 = (float) 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 *= (float) pow(0.75, dt);
view_z_vel *= (float) 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] = (float) fmod(camang[2], 360);
}
void process_tick(float dt)
{
while (dt > 1.0f/60) {
process_tick_raw(1.0f/60);
dt -= 1.0f/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.0f;
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.6f,0.7f,0.9f,0.0f);
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();
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();
}
#pragma warning(disable:4244; disable:4305; disable:4018)
#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 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
float tex2_alpha = 1.0;
int raw_level_time;
float global_timer;
int global_hack;
int loopmode(float dt, int real, int in_client)
{
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) {
if (global_hack) {
tex2_alpha += global_hack / 60.0f;
if (tex2_alpha < 0) tex2_alpha = 0;
if (tex2_alpha > 1) tex2_alpha = 1;
}
//update_input();
// if the player is dead, stop the sim
carried_dt -= 1.0/TICKRATE;
}
process_tick(dt);
draw();
return 0;
}
static int quit;
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:
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_KEYDOWN: {
int k = e->key.keysym.sym;
int s = e->key.keysym.scancode;
SDL_Keymod mod;
mod = SDL_GetModState();
if (k == SDLK_ESCAPE)
quit = 1;
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 (k == '1') global_hack = !global_hack;
if (k == '2') global_hack = -1;
#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)
{
ods("%s\n", message);
}
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();
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_DEPTH_SIZE, 24);
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?
SDL_SetRelativeMouseMode(SDL_TRUE);
#if defined(_MSC_VER) && _MSC_VER < 1300
// work around broken behavior in VC6 debugging
if (IsDebuggerPresent())
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
#endif
stbgl_initExtensions();
#ifdef GL_DEBUG
if (glDebugMessageCallbackARB) {
glDebugMessageCallbackARB(gl_debug, NULL);
enable_synchronous();
}
#endif
SDL_GL_SetSwapInterval(1);
render_init();
mesh_init();
world_init();
initialized = 1;
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e))
process_event(&e);
loopmode(getTimestep(0.0166f/8), 1, 1);
}
return 0;
}

View File

@ -0,0 +1,927 @@
// This file takes minecraft chunks (decoded by cave_parse) and
// uses stb_voxel_render to turn them into vertex buffers.
#define STB_GLEXT_DECLARE "glext_list.h"
#include "stb_gl.h"
#include "stb_image.h"
#include "stb_glprog.h"
#include "caveview.h"
#include "cave_parse.h"
#include "stb.h"
#include "sdl.h"
#include "sdl_thread.h"
#include <math.h>
//#define VHEIGHT_TEST
//#define STBVOX_OPTIMIZED_VHEIGHT
#define STBVOX_CONFIG_MODE 1
#define STBVOX_CONFIG_OPENGL_MODELVIEW
#define STBVOX_CONFIG_PREFER_TEXBUFFER
//#define STBVOX_CONFIG_LIGHTING_SIMPLE
#define STBVOX_CONFIG_FOG_SMOOTHSTEP
//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // this doesn't work properly alpha test without next #define
//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better
//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
#define STBVOX_CONFIG_DISABLE_TEX2
#define STBVOX_ROTATION_IN_LIGHTING
#define STB_VOXEL_RENDER_IMPLEMENTATION
#include "stb_voxel_render.h"
extern void ods(char *fmt, ...);
//#define FANCY_LEAVES // nearly 2x the triangles when enabled (if underground is filled)
#define FAST_CHUNK
#define IN_PLACE
#define SKIP_TERRAIN 48 // use to avoid building underground stuff
// allows you to see what perf would be like if underground was efficiently culled,
// or if you were making a game without underground
enum
{
C_empty,
C_solid,
C_trans,
C_cross,
C_water,
C_slab,
C_stair,
C_force,
};
unsigned char geom_map[] =
{
STBVOX_GEOM_empty,
STBVOX_GEOM_solid,
STBVOX_GEOM_transp,
STBVOX_GEOM_crossed_pair,
STBVOX_GEOM_solid,
STBVOX_GEOM_slab_lower,
STBVOX_GEOM_floor_slope_north_is_top,
STBVOX_GEOM_force,
};
unsigned char minecraft_info[256][7] =
{
{ C_empty, 0,0,0,0,0,0 },
{ C_solid, 1,1,1,1,1,1 },
{ C_solid, 3,3,3,3,40,2 },
{ C_solid, 2,2,2,2,2,2 },
{ C_solid, 16,16,16,16,16,16 },
{ C_solid, 4,4,4,4,4,4 },
{ C_cross, 15,15,15,15 },
{ C_solid, 17,17,17,17,17,17 },
// 8
{ C_water, 223,223,223,223,223,223 },
{ C_water, 223,223,223,223,223,223 },
{ C_solid, 255,255,255,255,255,255 },
{ C_solid, 255,255,255,255,255,255 },
{ C_solid, 18,18,18,18,18,18 },
{ C_solid, 19,19,19,19,19,19 },
{ C_solid, 32,32,32,32,32,32 },
{ C_solid, 33,33,33,33,33,33 },
// 16
{ C_solid, 34,34,34,34,34,34 },
{ C_solid, 20,20,20,20,21,21 },
#ifdef FANCY_LEAVES
{ C_force, 52,52,52,52,52,52 }, // leaves
#else
{ C_solid, 53,53,53,53,53,53 }, // leaves
#endif
{ C_solid, 24,24,24,24,24,24 },
{ C_trans, 49,49,49,49,49,49 }, // glass
{ C_solid, 160,160,160,160,160,160 },
{ C_solid, 144,144,144,144,144,144 },
{ C_solid, 46,45,45,45,62,62 },
// 24
{ C_solid, 192,192,192,192, 176,176 },
{ C_solid, 74,74,74,74,74,74 },
{ C_empty }, // bed
{ C_empty }, // powered rail
{ C_empty }, // detector rail
{ C_solid, 106,108,109,108,108,108 },
{ C_empty }, // cobweb=11
{ C_cross, 39,39,39,39 },
// 32
{ C_cross, 55,55,55,55,0,0 },
{ C_solid, 107,108,109,108,108,108 },
{ C_empty }, // piston head
{ C_solid, 64,64,64,64,64,64 }, // various colors
{ C_empty }, // unused
{ C_cross, 13,13,13,13,0,0 },
{ C_cross, 12,12,12,12,0,0 },
{ C_cross, 29,29,29,29,0,0 },
// 40
{ C_cross, 28,28,28,28,0,0 },
{ C_solid, 23,23,23,23,23,23 },
{ C_solid, 22,22,22,22,22,22 },
{ C_solid, 5,5,5,5,6,6, },
{ C_slab , 5,5,5,5,6,6, },
{ C_solid, 7,7,7,7,7,7, },
{ C_solid, 8,8,8,8,9,10 },
{ C_solid, 35,35,35,35,4,4, },
// 48
{ C_solid, 36,36,36,36,36,36 },
{ C_solid, 37,37,37,37,37,37 },
{ C_cross, 80,80,80,80,80,80 }, // torch
{ C_empty }, // fire
{ C_trans, 65,65,65,65,65,65 },
{ C_stair, 4,4,4,4,4,4 },
{ C_solid, 26,26,26,27,25,25 },
{ C_empty }, // redstone
// 56
{ C_solid, 50,50,50,50,50,50 },
{ C_solid, 26,26,26,26,26,26 },
{ C_solid, 60,59,59,59,43,43 },
{ C_cross, 95,95,95,95 },
{ C_solid, 2,2,2,2,86,2 },
{ C_solid, 44,45,45,45,62,62 },
{ C_solid, 61,45,45,45,62,62 },
{ C_empty }, // sign
// 64
{ C_empty }, // door
{ C_empty }, // ladder
{ C_empty }, // rail
{ C_stair, 16,16,16,16,16,16 }, // cobblestone stairs
{ C_empty }, // sign
{ C_empty }, // lever
{ C_empty }, // stone pressure plate
{ C_empty }, // iron door
// 72
{ C_empty }, // wooden pressure
{ C_solid, 51,51,51,51,51,51 },
{ C_solid, 51,51,51,51,51,51 },
{ C_empty },
{ C_empty },
{ C_empty },
{ C_empty }, // snow on block below, do as half slab?
{ C_solid, 67,67,67,67,67,67 },
// 80
{ C_solid, 66,66,66,66,66,66 },
{ C_solid, 70,70,70,70,69,71 },
{ C_solid, 72,72,72,72,72,72 },
{ C_cross, 73,73,73,73,73,73 },
{ C_solid, 74,74,74,74,75,74 },
{ C_empty }, // fence
{ C_solid,119,118,118,118,102,102 },
{ C_solid,103,103,103,103,103,103 },
// 88
{ C_solid, 104,104,104,104,104,104 },
{ C_solid, 105,105,105,105,105,105 },
{ C_solid, 167,167,167,167,167,167 },
{ C_solid, 120,118,118,118,102,102 },
{ C_empty }, // cake
{ C_empty }, // repeater
{ C_empty }, // repeater
{ C_solid, 49,49,49,49,49,49 }, // colored glass
// 96
{ C_empty },
{ C_empty },
{ C_solid, 54,54,54,54,54,54 },
{ C_solid, 125,125,125,125,125,125 },
{ C_solid, 126,126,126,126,126,126 },
{ C_empty }, // bars
{ C_trans, 49,49,49,49,49,49 }, // glass pane
{ C_solid, 136,136,136,136,137,137 }, // melon
// 104
{ C_empty }, // pumpkin stem
{ C_empty }, // melon stem
{ C_empty }, // vines
{ C_empty }, // gate
{ C_stair, 7,7,7,7,7,7, }, // brick stairs
{ C_stair, 54,54,54,54,54,54 }, // stone brick stairs
{ C_empty }, // mycelium
{ C_empty }, // lily pad
// 112
{ C_solid, 224,224,224,224,224,224 },
{ C_empty }, // nether brick fence
{ C_stair, 224,224,224,224,224,224 }, // nether brick stairs
{ C_empty }, // nether wart
{ C_solid, 182,182,182,182,166,183 },
{ C_empty }, // brewing stand
{ C_empty }, // cauldron
{ C_empty }, // end portal
// 120
{ C_solid, 159,159,159,159,158,158 },
{ C_solid, 175,175,175,175,175,175 },
{ C_empty }, // dragon egg
{ C_solid, 211,211,211,211,211,211 },
{ C_solid, 212,212,212,212,212,212 },
{ C_solid, 4,4,4,4,4,4, }, // wood double-slab
{ C_slab , 4,4,4,4,4,4, }, // wood slab
{ C_empty }, // cocoa
// 128
{ C_solid, 192,192,192,192,176,176 }, // sandstone stairs
{ C_solid, 32,32,32,32,32,32 }, // emerald ore
{ C_solid, 26,26,26,27,25,25 }, // ender chest
{ C_empty },
{ C_empty },
{ C_solid, 23,23,23,23,23,23 }, // emerald block
{ C_solid, 198,198,198,198,198,198 }, // spruce stairs
{ C_solid, 214,214,214,214,214,214 }, // birch stairs
// 136
{ C_stair, 199,199,199,199,199,199 }, // jungle stairs
{ C_empty }, // command block
{ C_empty }, // beacon
{ C_slab, 16,16,16,16,16,16 }, // cobblestone wall
{ C_empty }, // flower pot
{ C_empty }, // carrot
{ C_empty }, // potatoes
{ C_empty }, // wooden button
// 144
{ C_empty }, // mob head
{ C_empty }, // anvil
{ C_solid, 26,26,26,27,25,25 }, // trapped chest
{ C_empty }, // weighted pressure plate light
{ C_empty }, // weighted pressure plat eheavy
{ C_empty }, // comparator inactive
{ C_empty }, // comparator active
{ C_empty }, // daylight sensor
// 152
{ C_solid, 135,135,135,135,135,135 }, // redstone block
{ C_solid, 0,0,0,0,0,0, }, // nether quartz ore
{ C_empty }, // hopper
{ C_solid, 22,22,22,22,22,22 }, // quartz block
{ C_stair, 22,22,22,22,22,22 }, // quartz stairs
{ C_empty }, // activator rail
{ C_solid, 46,45,45,45,62,62 }, // dropper
{ C_solid, 72,72,72,72,72,72 }, // stained clay
// 160
{ C_trans, 49,49,49,49,49,49 }, // stained glass pane
#ifdef FANCY_LEAVES
{ C_force, 52,52,52,52,52,52 }, // leaves
#else
{ C_solid, 53,53,53,53,53,53 }, // acacia leaves
#endif
{ C_solid, 20,20,20,20,21,21 }, // acacia tree
{ C_solid, 199,199,199,199,199,199 }, // acacia wood stairs
{ C_solid, 198,198,198,198,198,198 }, // dark oak stairs
{ C_solid, 146,146,146,146,146,146 }, // slime block
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
// 168
{ C_empty },
{ C_empty },
{ C_empty },
{ C_empty },
{ C_solid, 72,72,72,72,72,72 }, // hardened clay
{ C_empty },
{ C_empty },
{ C_empty },
// 176
{ C_empty },
{ C_empty },
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
};
unsigned char minecraft_tex1_for_blocktype[256][6];
unsigned char effective_blocktype[256];
unsigned char minecraft_color_for_blocktype[256][6];
unsigned char minecraft_geom_for_blocktype[256];
uint8 build_buffer[BUILD_BUFFER_SIZE];
uint8 face_buffer[FACE_BUFFER_SIZE];
//GLuint vbuf, fbuf, fbuf_tex;
//unsigned char tex1_for_blocktype[256][6];
//unsigned char blocktype[34][34][257];
//unsigned char lighting[34][34][257];
// a superchunk is 64x64x256, with the border blocks computed as well,
// which means we need 4x4 chunks plus 16 border chunks plus 4 corner chunks
#define SUPERCHUNK_X 4
#define SUPERCHUNK_Y 4
unsigned char remap_data[16][16];
unsigned char remap[256];
unsigned char rotate_data[4] = { 1,3,2,0 };
void convert_fastchunk_inplace(fast_chunk *fc)
{
int i;
int num_blocks=0, step=0;
unsigned char rot[4096];
#ifndef IN_PLACE
unsigned char *storage;
#endif
memset(rot, 0, 4096);
for (i=0; i < 16; ++i)
num_blocks += fc->blockdata[i] != NULL;
#ifndef IN_PLACE
storage = malloc(16*16*16*2 * num_blocks);
#endif
for (i=0; i < 16; ++i) {
if (fc->blockdata[i]) {
int o=0;
unsigned char *bd,*dd,*lt,*sky;
unsigned char *out, *outb;
// this ordering allows us to determine which data we can safely overwrite for in-place processing
bd = fc->blockdata[i];
dd = fc->data[i];
lt = fc->light[i];
sky = fc->skylight[i];
#ifdef IN_PLACE
out = bd;
#else
out = storage + 16*16*16*2*step;
#endif
// bd is written in place, but also reads from dd
for (o=0; o < 16*16*16/2; o += 1) {
unsigned char v1,v2;
unsigned char d = dd[o];
v1 = bd[o*2+0];
v2 = bd[o*2+1];
if (remap[v1])
{
//unsigned char d = bd[o] & 15;
v1 = remap_data[remap[v1]][d&15];
rot[o*2+0] = rotate_data[d&3];
} else
v1 = effective_blocktype[v1];
if (remap[v2])
{
//unsigned char d = bd[o] >> 4;
v2 = remap_data[remap[v2]][d>>4];
rot[o*2+1] = rotate_data[(d>>4)&3];
} else
v2 = effective_blocktype[v2];
out[o*2+0] = v1;
out[o*2+1] = v2;
}
// this reads from lt & sky
#ifndef IN_PLACE
outb = out + 16*16*16;
++step;
#endif
// MC used to write in this order and it makes it possible to compute in-place
if (dd < sky && sky < lt) {
// @TODO go this path always if !IN_PLACE
#ifdef IN_PLACE
outb = dd;
#endif
for (o=0; o < 16*16*16/2; ++o) {
int bright;
bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16;
if (bright > 255) bright = 255;
if (bright < 32) bright = 32;
outb[o*2+0] = STBVOX_MAKE_LIGHTING((unsigned char) bright, (rot[o*2+0]&3));
bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16;
if (bright > 255) bright = 255;
if (bright < 32) bright = 32;
outb[o*2+1] = STBVOX_MAKE_LIGHTING((unsigned char) bright, (rot[o*2+1]&3));
}
} else {
// @TODO: if blocktype is in between others, this breaks; need to find which side has two pointers, and use that
// overwrite rot[] array, then copy out
#ifdef IN_PLACE
outb = (dd < sky) ? dd : sky;
if (lt < outb) lt = outb;
#endif
for (o=0; o < 16*16*16/2; ++o) {
int bright;
bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16;
if (bright > 255) bright = 255;
if (bright < 32) bright = 32;
rot[o*2+0] = STBVOX_MAKE_LIGHTING((unsigned char) bright, (rot[o*2+0]&3));
bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16;
if (bright > 255) bright = 255;
if (bright < 32) bright = 32;
rot[o*2+1] = STBVOX_MAKE_LIGHTING((unsigned char) bright, (rot[o*2+1]&3));
}
memcpy(outb, rot, 4096);
fc->data[i] = outb;
}
#ifndef IN_PLACE
fc->blockdata[i] = out;
fc->data[i] = outb;
#endif
}
}
#ifndef IN_PLACE
free(fc->pointer_to_free);
fc->pointer_to_free = storage;
#endif
}
void make_converted_fastchunk(fast_chunk *fc, int x, int y, int segment, uint8 *sv_blocktype, uint8 *sv_lighting)
{
int z;
assert(fc == NULL || (fc->refcount > 0 && fc->refcount < 64));
if (fc == NULL || fc->blockdata[segment] == NULL) {
for (z=0; z < 16; ++z) {
sv_blocktype[z] = C_empty;
sv_lighting[z] = 255;
}
} else {
unsigned char *block = fc->blockdata[segment];
unsigned char *data = fc->data[segment];
y = 15-y;
for (z=0; z < 16; ++z) {
sv_blocktype[z] = block[z*256 + y*16 + x];
sv_lighting [z] = data [z*256 + y*16 + x];
}
}
}
#define CHUNK_CACHE 64
typedef struct
{
int valid;
int chunk_x, chunk_y;
fast_chunk *fc;
} cached_converted_chunk;
cached_converted_chunk chunk_cache[CHUNK_CACHE][CHUNK_CACHE];
int cache_size = CHUNK_CACHE;
void reset_cache_size(int size)
{
int i,j;
for (j=size; j < cache_size; ++j) {
for (i=size; i < cache_size; ++i) {
cached_converted_chunk *ccc = &chunk_cache[j][i];
if (ccc->valid) {
if (ccc->fc) {
free(ccc->fc->pointer_to_free);
free(ccc->fc);
ccc->fc = NULL;
}
ccc->valid = 0;
}
}
}
cache_size = size;
}
// this must be called inside mutex
void deref_fastchunk(fast_chunk *fc)
{
if (fc) {
assert(fc->refcount > 0);
--fc->refcount;
if (fc->refcount == 0) {
free(fc->pointer_to_free);
free(fc);
}
}
}
SDL_mutex * chunk_cache_mutex;
SDL_mutex * chunk_get_mutex;
void lock_chunk_get_mutex(void)
{
SDL_LockMutex(chunk_get_mutex);
}
void unlock_chunk_get_mutex(void)
{
SDL_UnlockMutex(chunk_get_mutex);
}
fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y)
{
int slot_x = (chunk_x & (cache_size-1));
int slot_y = (chunk_y & (cache_size-1));
fast_chunk *fc;
cached_converted_chunk *ccc;
SDL_LockMutex(chunk_cache_mutex);
ccc = &chunk_cache[slot_y][slot_x];
if (ccc->valid) {
if (ccc->chunk_x == chunk_x && ccc->chunk_y == chunk_y) {
fast_chunk *fc = ccc->fc;
if (fc)
++fc->refcount;
SDL_UnlockMutex(chunk_cache_mutex);
return fc;
}
if (ccc->fc) {
deref_fastchunk(ccc->fc);
ccc->fc = NULL;
ccc->valid = 0;
}
}
SDL_UnlockMutex(chunk_cache_mutex);
fc = get_decoded_fastchunk_uncached(chunk_x, -chunk_y);
if (fc)
convert_fastchunk_inplace(fc);
SDL_LockMutex(chunk_cache_mutex);
// another thread might have updated it, so before we overwrite it...
if (ccc->fc) {
deref_fastchunk(ccc->fc);
ccc->fc = NULL;
}
if (fc)
fc->refcount = 1; // 1 in the cache
ccc->chunk_x = chunk_x;
ccc->chunk_y = chunk_y;
ccc->valid = 1;
if (fc)
++fc->refcount;
ccc->fc = fc;
SDL_UnlockMutex(chunk_cache_mutex);
return fc;
}
void make_map_segment_for_superchunk_preconvert(int chunk_x, int chunk_y, int segment, fast_chunk *fc_table[4][4], uint8 sv_blocktype[34][34][18], uint8 sv_lighting[34][34][18])
{
int a,b;
assert((chunk_x & 1) == 0);
assert((chunk_y & 1) == 0);
for (b=-1; b < 3; ++b) {
for (a=-1; a < 3; ++a) {
int xo = a*16+1;
int yo = b*16+1;
int x,y;
fast_chunk *fc = fc_table[b+1][a+1];
for (y=0; y < 16; ++y)
for (x=0; x < 16; ++x)
if (xo+x >= 0 && xo+x < 34 && yo+y >= 0 && yo+y < 34)
make_converted_fastchunk(fc,x,y, segment, sv_blocktype[xo+x][yo+y], sv_lighting[xo+x][yo+y]);
}
}
}
// build 1 mesh covering 2x2 chunks
void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm)
{
int a,b,z;
stbvox_input_description *map;
#ifdef VHEIGHT_TEST
unsigned char vheight[34][34][18];
#endif
#ifndef STBVOX_CONFIG_DISABLE_TEX2
unsigned char tex2_choice[34][34][18];
#endif
assert((chunk_x & 1) == 0);
assert((chunk_y & 1) == 0);
rm->cx = chunk_x;
rm->cy = chunk_y;
stbvox_set_input_stride(&rm->mm, 34*18, 18);
assert(rm->mm.input.geometry == NULL);
map = stbvox_get_input_description(&rm->mm);
map->block_tex1_face = minecraft_tex1_for_blocktype;
map->block_color_face = minecraft_color_for_blocktype;
map->block_geometry = minecraft_geom_for_blocktype;
stbvox_reset_buffers(&rm->mm);
stbvox_set_buffer(&rm->mm, 0, 0, rm->build_buffer, BUILD_BUFFER_SIZE);
stbvox_set_buffer(&rm->mm, 0, 1, rm->face_buffer , FACE_BUFFER_SIZE);
map->blocktype = &rm->sv_blocktype[1][1][1]; // this is (0,0,0), but we need to be able to query off the edges
map->lighting = &rm->sv_lighting[1][1][1];
// fill in the top two rows of the buffer
for (a=0; a < 34; ++a) {
for (b=0; b < 34; ++b) {
rm->sv_blocktype[a][b][16] = 0;
rm->sv_lighting [a][b][16] = 255;
rm->sv_blocktype[a][b][17] = 0;
rm->sv_lighting [a][b][17] = 255;
}
}
#ifndef STBVOX_CONFIG_DISABLE_TEX2
for (a=0; a < 34; ++a) {
for (b=0; b < 34; ++b) {
int px = chunk_x*16 + a - 1;
int py = chunk_y*16 + b - 1;
float dist = (float) sqrt(px*px + py*py);
float s1 = (float) sin(dist / 16), s2, s3;
dist = (float) sqrt((px-80)*(px-80) + (py-50)*(py-50));
s2 = (float) sin(dist / 11);
for (z=0; z < 18; ++z) {
s3 = (float) sin(z * 3.141592 / 8);
s3 = s1*s2*s3;
tex2_choice[a][b][z] = 63 & (int) stb_linear_remap(s3,-1,1, -20,83);
}
}
}
#endif
for (z=256-16; z >= SKIP_TERRAIN; z -= 16)
{
int z0 = z;
int z1 = z+16;
if (z1 == 256) z1 = 255;
make_map_segment_for_superchunk_preconvert(chunk_x, chunk_y, z >> 4, fc_table, rm->sv_blocktype, rm->sv_lighting);
map->blocktype = &rm->sv_blocktype[1][1][1-z]; // specify location of 0,0,0 so that accessing z0..z1 gets right data
map->lighting = &rm->sv_lighting[1][1][1-z];
#ifndef STBVOX_CONFIG_DISABLE_TEX2
map->tex2 = &tex2_choice[1][1][1-z];
#endif
#ifdef VHEIGHT_TEST
// hacky test of vheight
for (a=0; a < 34; ++a) {
for (b=0; b < 34; ++b) {
int c;
for (c=0; c < 17; ++c) {
if (rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c+1] == 0) {
// topmost block
vheight[a][b][c] = rand() & 255;
rm->sv_blocktype[a][b][c] = 168;
} else if (c > 0 && rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c-1] == 0) {
// bottommost block
vheight[a][b][c] = ((rand() % 3) << 6) + ((rand() % 3) << 4) + ((rand() % 3) << 2) + (rand() % 3);
rm->sv_blocktype[a][b][c] = 169;
}
}
vheight[a][b][c] = STBVOX_MAKE_VHEIGHT(2,2,2,2); // flat top
}
}
map->vheight = &vheight[1][1][1-z];
#endif
{
stbvox_set_input_range(&rm->mm, 0,0,z0, 32,32,z1);
stbvox_set_default_mesh(&rm->mm, 0);
stbvox_make_mesh(&rm->mm);
}
// copy the bottom two rows of data up to the top
for (a=0; a < 34; ++a) {
for (b=0; b < 34; ++b) {
rm->sv_blocktype[a][b][16] = rm->sv_blocktype[a][b][0];
rm->sv_blocktype[a][b][17] = rm->sv_blocktype[a][b][1];
rm->sv_lighting [a][b][16] = rm->sv_lighting [a][b][0];
rm->sv_lighting [a][b][17] = rm->sv_lighting [a][b][1];
}
}
}
stbvox_set_mesh_coordinates(&rm->mm, chunk_x*16, chunk_y*16, 0);
stbvox_get_transform(&rm->mm, rm->transform);
stbvox_set_input_range(&rm->mm, 0,0,0, 32,32,255);
stbvox_get_bounds(&rm->mm, rm->bounds);
rm->num_quads = stbvox_get_quad_count(&rm->mm, 0);
}
int next_blocktype = 255;
unsigned char mc_rot[4] = { 1,3,2,0 };
// create blocktypes with rotation baked into type...
// @TODO we no longer need this now that we store rotations
// in lighting
void build_stair_rotations(int blocktype, unsigned char *map)
{
int i;
// use the existing block type for floor stairs; allocate a new type for ceil stairs
for (i=0; i < 6; ++i) {
minecraft_color_for_blocktype[next_blocktype][i] = minecraft_color_for_blocktype[blocktype][i];
minecraft_tex1_for_blocktype [next_blocktype][i] = minecraft_tex1_for_blocktype [blocktype][i];
}
minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_ceil_slope_north_is_bottom, 0, 0);
minecraft_geom_for_blocktype[ blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_floor_slope_north_is_top, 0, 0);
for (i=0; i < 4; ++i) {
map[0+i+8] = map[0+i] = blocktype;
map[4+i+8] = map[4+i] = next_blocktype;
}
--next_blocktype;
}
void build_wool_variations(int bt, unsigned char *map)
{
int i,k;
unsigned char tex[16] = { 64, 210, 194, 178, 162, 146, 130, 114, 225, 209, 193, 177, 161, 145, 129, 113 };
for (i=0; i < 16; ++i) {
if (i == 0)
map[i] = bt;
else {
map[i] = next_blocktype;
for (k=0; k < 6; ++k) {
minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i];
}
minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt];
--next_blocktype;
}
}
}
void build_wood_variations(int bt, unsigned char *map)
{
int i,k;
unsigned char tex[4] = { 5, 198, 214, 199 };
for (i=0; i < 4; ++i) {
if (i == 0)
map[i] = bt;
else {
map[i] = next_blocktype;
for (k=0; k < 6; ++k) {
minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i];
}
minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt];
--next_blocktype;
}
}
map[i] = map[i-1];
++i;
for (; i < 16; ++i)
map[i] = bt;
}
void remap_in_place(int bt, int rm)
{
int i;
remap[bt] = rm;
for (i=0; i < 16; ++i)
remap_data[rm][i] = bt;
}
void mesh_init(void)
{
int i;
chunk_cache_mutex = SDL_CreateMutex();
chunk_get_mutex = SDL_CreateMutex();
for (i=0; i < 256; ++i) {
memcpy(minecraft_tex1_for_blocktype[i], minecraft_info[i]+1, 6);
effective_blocktype[i] = (minecraft_info[i][0] == C_empty ? 0 : i);
minecraft_geom_for_blocktype[i] = geom_map[minecraft_info[i][0]];
}
//effective_blocktype[50] = 0; // delete torches
for (i=0; i < 6*256; ++i) {
if (minecraft_tex1_for_blocktype[0][i] == 40)
minecraft_color_for_blocktype[0][i] = 38 | 64; // apply to tex1
if (minecraft_tex1_for_blocktype[0][i] == 39)
minecraft_color_for_blocktype[0][i] = 39 | 64; // apply to tex1
if (minecraft_tex1_for_blocktype[0][i] == 105)
minecraft_color_for_blocktype[0][i] = 63; // emissive
if (minecraft_tex1_for_blocktype[0][i] == 212)
minecraft_color_for_blocktype[0][i] = 63; // emissive
if (minecraft_tex1_for_blocktype[0][i] == 80)
minecraft_color_for_blocktype[0][i] = 63; // emissive
}
for (i=0; i < 6; ++i) {
minecraft_color_for_blocktype[172][i] = 47 | 64; // apply to tex1
minecraft_color_for_blocktype[178][i] = 47 | 64; // apply to tex1
minecraft_color_for_blocktype[18][i] = 39 | 64; // green
minecraft_color_for_blocktype[161][i] = 37 | 64; // green
minecraft_color_for_blocktype[10][i] = 63; // emissive lava
minecraft_color_for_blocktype[11][i] = 63; // emissive
}
#ifdef VHEIGHT_TEST
effective_blocktype[168] = 168;
minecraft_tex1_for_blocktype[168][0] = 1;
minecraft_tex1_for_blocktype[168][1] = 1;
minecraft_tex1_for_blocktype[168][2] = 1;
minecraft_tex1_for_blocktype[168][3] = 1;
minecraft_tex1_for_blocktype[168][4] = 1;
minecraft_tex1_for_blocktype[168][5] = 1;
minecraft_geom_for_blocktype[168] = STBVOX_GEOM_floor_vheight_12;
effective_blocktype[169] = 169;
minecraft_tex1_for_blocktype[169][0] = 1;
minecraft_tex1_for_blocktype[169][1] = 1;
minecraft_tex1_for_blocktype[169][2] = 1;
minecraft_tex1_for_blocktype[169][3] = 1;
minecraft_tex1_for_blocktype[169][4] = 1;
minecraft_tex1_for_blocktype[169][5] = 1;
minecraft_geom_for_blocktype[169] = STBVOX_GEOM_ceil_vheight_03;
#endif
remap[53] = 1;
remap[67] = 2;
remap[108] = 3;
remap[109] = 4;
remap[114] = 5;
remap[136] = 6;
remap[156] = 7;
for (i=0; i < 256; ++i)
if (remap[i])
build_stair_rotations(i, remap_data[remap[i]]);
remap[35] = 8;
build_wool_variations(35, remap_data[remap[35]]);
remap[5] = 11;
build_wood_variations(5, remap_data[remap[5]]);
// set the remap flags for these so they write the rotation values
remap_in_place(54, 9);
remap_in_place(146, 10);
}
// Timing stats while optimizing the single-threaded builder
// 32..-32, 32..-32, SKIP_TERRAIN=0, !FANCY_LEAVES on 'mcrealm' data set
// 6.27s - reblocked to do 16 z at a time instead of 256 (still using 66x66x258), 4 meshes in parallel
// 5.96s - reblocked to use FAST_CHUNK (no intermediate data structure)
// 5.45s - unknown change, or previous measurement was wrong
// 6.12s - use preconverted data, not in-place
// 5.91s - use preconverted, in-place
// 5.34s - preconvert, in-place, avoid dependency chain (suggested by ryg)
// 5.34s - preconvert, in-place, avoid dependency chain, use bit-table instead of byte-table
// 5.50s - preconvert, in-place, branchless
// 6.42s - non-preconvert, avoid dependency chain (not an error)
// 5.40s - non-preconvert, w/dependency chain (same as earlier)
// 5.50s - non-FAST_CHUNK, reblocked outer loop for better cache reuse
// 4.73s - FAST_CHUNK non-preconvert, reblocked outer loop
// 4.25s - preconvert, in-place, reblocked outer loop
// 4.18s - preconvert, in-place, unrolled again
// 4.10s - 34x34 1 mesh instead of 66x66 and 4 meshes (will make it easier to do multiple threads)
// 4.83s - building bitmasks but not using them (2 bits per block, one if empty, one if solid)
// 5.16s - using empty bitmasks to early out
// 5.01s - using solid & empty bitmasks to early out - "foo"
// 4.64s - empty bitmask only, test 8 at a time, then test geom
// 4.72s - empty bitmask only, 8 at a time, then test bits
// 4.46s - split bitmask building into three loops (each byte is separate)
// 4.42s - further optimize computing bitmask
// 4.58s - using solid & empty bitmasks to early out, same as "foo" but faster bitmask building
// 4.12s - using solid & empty bitmasks to efficiently test neighbors
// 4.04s - using 16-bit fetches (not endian-independent)
// - note this is first place that beats previous best '4.10s - 34x34 1 mesh'
// 4.30s - current time with bitmasks disabled again (note was 4.10s earlier)
// 3.95s - bitmasks enabled again, no other changes
// 4.00s - current time with bitmasks disabled again, no other changes -- wide variation that is time dependent?
// (note that most of the numbers listed here are median of 3 values already)
// 3.98s - bitmasks enabled
// Bitmasks removed from the code as not worth the complexity increase
// Raw data for Q&A:
//
// 26% parsing & loading minecraft files (4/5ths of which is zlib decode)
// 39% building mesh from stb input format
// 18% converting from minecraft blocks to stb blocks
// 9% reordering from minecraft axis order to stb axis order
// 7% uploading vertex buffer to OpenGL

632
tests/caveview/cave_parse.c Normal file
View File

@ -0,0 +1,632 @@
#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"
#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);
#ifdef FAST_CHUNK
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;
}
#endif
#ifndef FAST_CHUNK
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;
}
#endif

View File

@ -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

View File

@ -0,0 +1,951 @@
// This file renders vertex buffers, converts raw meshes
// to GL meshes, and manages threads that do the raw-mesh
// building (found in cave_mesher.c)
#include "stb_voxel_render.h"
#define STB_GLEXT_DECLARE "glext_list.h"
#include "stb_gl.h"
#include "stb_image.h"
#include "stb_glprog.h"
#include "caveview.h"
#include "cave_parse.h"
#include "stb.h"
#include "sdl.h"
#include "sdl_thread.h"
#include <math.h>
#include <assert.h>
//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
// currently no dynamic way to set mesh cache size or view distance
//#define SHORTVIEW
stbvox_mesh_maker g_mesh_maker;
GLuint main_prog;
GLint uniform_locations[64];
//#define MAX_QUADS_PER_DRAW (65536 / 4) // assuming 16-bit indices, 4 verts per quad
//#define FIXED_INDEX_BUFFER_SIZE (MAX_QUADS_PER_DRAW * 6 * 2) // 16*1024 * 12 == ~192KB
// while uploading texture data, this holds our each texture
#define TEX_SIZE 64
uint32 texture[TEX_SIZE][TEX_SIZE];
GLuint voxel_tex[2];
// chunk state
enum
{
STATE_invalid,
STATE_needed,
STATE_requested,
STATE_abandoned,
STATE_valid,
};
// mesh is 32x32x255 ... this is hardcoded in that
// a mesh covers 2x2 minecraft chunks, no #defines for it
typedef struct
{
int state;
int chunk_x, chunk_y;
int num_quads;
float priority;
int vbuf_size, fbuf_size;
float transform[3][3];
float bounds[2][3];
GLuint vbuf;// vbuf_tex;
GLuint fbuf, fbuf_tex;
} chunk_mesh;
void scale_texture(unsigned char *src, int x, int y, int w, int h)
{
int i,j,k;
assert(w == 256 && h == 256);
for (j=0; j < TEX_SIZE; ++j) {
for (i=0; i < TEX_SIZE; ++i) {
uint32 val=0;
for (k=0; k < 4; ++k) {
val >>= 8;
val += src[ 4*(x+(i>>2)) + 4*w*(y+(j>>2)) + k]<<24;
}
texture[j][i] = val;
}
}
}
void build_base_texture(int n)
{
int x,y;
uint32 color = stb_rand() | 0x808080;
for (y=0; y<TEX_SIZE; ++y)
for (x=0; x<TEX_SIZE; ++x) {
texture[y][x] = (color + (stb_rand()&0x1f1f1f))|0xff000000;
}
}
void build_overlay_texture(int n)
{
int x,y;
uint32 color = stb_rand();
if (color & 16)
color = 0xff000000;
else
color = 0xffffffff;
for (y=0; y<TEX_SIZE; ++y)
for (x=0; x<TEX_SIZE; ++x) {
texture[y][x] = 0;
}
for (y=0; y < TEX_SIZE/8; ++y) {
for (x=0; x < TEX_SIZE; ++x) {
texture[y][x] = color;
texture[TEX_SIZE-1-y][x] = color;
texture[x][y] = color;
texture[x][TEX_SIZE-1-y] = color;
}
}
}
// view radius of about 1024 = 2048 columns / 32 columns-per-mesh = 2^11 / 2^5 = 64x64
// so we need bigger than 64x64 so we can precache, which means we have to be
// non-power-of-two, or we have to be pretty huge
#define CACHED_MESH_NUM_X 128
#define CACHED_MESH_NUM_Y 128
chunk_mesh cached_chunk_mesh[CACHED_MESH_NUM_Y][CACHED_MESH_NUM_X];
void free_chunk(int slot_x, int slot_y)
{
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
if (cm->state == STATE_valid) {
glDeleteTextures(1, &cm->fbuf_tex);
glDeleteBuffersARB(1, &cm->vbuf);
glDeleteBuffersARB(1, &cm->fbuf);
cached_chunk_mesh[slot_y][slot_x].state = STATE_invalid;
}
}
void upload_mesh(chunk_mesh *cm, uint8 *build_buffer, uint8 *face_buffer)
{
glGenBuffersARB(1, &cm->vbuf);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, cm->num_quads*4*sizeof(uint32), build_buffer, GL_STATIC_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glGenBuffersARB(1, &cm->fbuf);
glBindBufferARB(GL_TEXTURE_BUFFER_ARB, cm->fbuf);
glBufferDataARB(GL_TEXTURE_BUFFER_ARB, cm->num_quads*sizeof(uint32), face_buffer , GL_STATIC_DRAW_ARB);
glBindBufferARB(GL_TEXTURE_BUFFER_ARB, 0);
glGenTextures(1, &cm->fbuf_tex);
glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex);
glTexBufferARB(GL_TEXTURE_BUFFER_ARB, GL_RGBA8UI, cm->fbuf);
glBindTexture(GL_TEXTURE_BUFFER_ARB, 0);
}
static void upload_mesh_data(raw_mesh *rm)
{
int cx = rm->cx;
int cy = rm->cy;
int slot_x = (cx >> 1) & (CACHED_MESH_NUM_X-1);
int slot_y = (cy >> 1) & (CACHED_MESH_NUM_Y-1);
chunk_mesh *cm;
free_chunk(slot_x, slot_y);
cm = &cached_chunk_mesh[slot_y][slot_x];
cm->num_quads = rm->num_quads;
upload_mesh(cm, rm->build_buffer, rm->face_buffer);
cm->vbuf_size = rm->num_quads*4*sizeof(uint32);
cm->fbuf_size = rm->num_quads*sizeof(uint32);
cm->priority = 100000;
cm->chunk_x = cx;
cm->chunk_y = cy;
memcpy(cm->bounds, rm->bounds, sizeof(cm->bounds));
memcpy(cm->transform, rm->transform, sizeof(cm->transform));
// write barrier here
cm->state = STATE_valid;
}
GLint uniform_loc[16];
float table3[128][3];
float table4[64][4];
GLint tablei[2];
float step=0;
#ifdef SHORTVIEW
int view_dist_in_chunks = 50;
#else
int view_dist_in_chunks = 80;
#endif
void setup_uniforms(float pos[3])
{
int i,j;
step += 1.0f/60.0f;
for (i=0; i < STBVOX_UNIFORM_count; ++i) {
stbvox_uniform_info raw, *ui=&raw;
stbvox_get_uniform_info(&raw, i);
uniform_loc[i] = -1;
if (i == STBVOX_UNIFORM_texscale || i == STBVOX_UNIFORM_texgen || i == STBVOX_UNIFORM_color_table)
continue;
if (ui) {
void *data = ui->default_value;
uniform_loc[i] = stbgl_find_uniform(main_prog, ui->name);
switch (i) {
case STBVOX_UNIFORM_face_data:
tablei[0] = 2;
data = tablei;
break;
case STBVOX_UNIFORM_tex_array:
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]);
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
glActiveTextureARB(GL_TEXTURE0_ARB);
tablei[0] = 0;
tablei[1] = 1;
data = tablei;
break;
case STBVOX_UNIFORM_color_table:
data = ui->default_value;
((float *)data)[63*4+3] = 2.0f; // emissive
break;
case STBVOX_UNIFORM_camera_pos:
data = table3[0];
table3[0][0] = pos[0];
table3[0][1] = pos[1];
table3[0][2] = pos[2];
table3[0][3] = stb_max(0,(float)sin(step*2)*0.125f);
break;
case STBVOX_UNIFORM_ambient: {
float bright = 1.0;
//float bright = 0.75;
float amb[3][3];
// ambient direction is sky-colored upwards
// "ambient" lighting is from above
table4[0][0] = 0.3f;
table4[0][1] = -0.5f;
table4[0][2] = 0.9f;
amb[1][0] = 0.3f; amb[1][1] = 0.3f; amb[1][2] = 0.3f; // dark-grey
amb[2][0] = 1.0; amb[2][1] = 1.0; amb[2][2] = 1.0; // white
// convert so (table[1]*dot+table[2]) gives
// above interpolation
// lerp((dot+1)/2, amb[1], amb[2])
// amb[1] + (amb[2] - amb[1]) * (dot+1)/2
// amb[1] + (amb[2] - amb[1]) * dot/2 + (amb[2]-amb[1])/2
for (j=0; j < 3; ++j) {
table4[1][j] = (amb[2][j] - amb[1][j])/2 * bright;
table4[2][j] = (amb[1][j] + amb[2][j])/2 * bright;
}
// fog color
table4[3][0] = 0.6f, table4[3][1] = 0.7f, table4[3][2] = 0.9f;
table4[3][3] = 1.0f / (view_dist_in_chunks * 16);
table4[3][3] *= table4[3][3];
data = table4;
break;
}
}
switch (ui->type) {
case STBVOX_UNIFORM_TYPE_sampler: stbglUniform1iv(uniform_loc[i], ui->array_length, data); break;
case STBVOX_UNIFORM_TYPE_vec2: stbglUniform2fv(uniform_loc[i], ui->array_length, data); break;
case STBVOX_UNIFORM_TYPE_vec3: stbglUniform3fv(uniform_loc[i], ui->array_length, data); break;
case STBVOX_UNIFORM_TYPE_vec4: stbglUniform4fv(uniform_loc[i], ui->array_length, data); break;
}
}
}
}
GLuint unitex[64], unibuf[64];
void make_texture_buffer_for_uniform(int uniform, int slot)
{
GLenum type;
stbvox_uniform_info raw, *ui=&raw;
GLint uloc;
stbvox_get_uniform_info(ui, uniform);
uloc = stbgl_find_uniform(main_prog, ui->name);
if (uniform == STBVOX_UNIFORM_color_table)
((float *)ui->default_value)[63*4+3] = 2.0f; // emissive
glGenBuffersARB(1, &unibuf[uniform]);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, unibuf[uniform]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ui->array_length * ui->bytes_per_element, ui->default_value, GL_STATIC_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glGenTextures(1, &unitex[uniform]);
glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]);
switch (ui->type) {
case STBVOX_UNIFORM_TYPE_vec2: type = GL_RG32F; break;
case STBVOX_UNIFORM_TYPE_vec3: type = GL_RGB32F; break;
case STBVOX_UNIFORM_TYPE_vec4: type = GL_RGBA32F; break;
default: assert(0);
}
glTexBufferARB(GL_TEXTURE_BUFFER_ARB, type, unibuf[uniform]);
glBindTexture(GL_TEXTURE_BUFFER_ARB, 0);
glActiveTextureARB(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]);
glActiveTextureARB(GL_TEXTURE0);
stbglUseProgram(main_prog);
stbglUniform1i(uloc, slot);
}
#define MAX_MESH_WORKERS 8
#define MAX_CHUNK_LOAD_WORKERS 2
int num_mesh_workers;
int num_chunk_load_workers;
typedef struct
{
int state;
int request_cx;
int request_cy;
int padding[13];
SDL_sem * request_received;
SDL_sem * chunk_server_done_processing;
int chunk_action;
int chunk_request_x;
int chunk_request_y;
fast_chunk *chunks[4][4];
int padding2[16];
raw_mesh rm;
int padding3[16];
uint8 *build_buffer;
uint8 *face_buffer ;
} mesh_worker;
enum
{
WSTATE_idle,
WSTATE_requested,
WSTATE_running,
WSTATE_mesh_ready,
};
mesh_worker mesh_data[MAX_MESH_WORKERS];
int num_meshes_started; // stats
int request_chunk(int chunk_x, int chunk_y);
void update_meshes_from_render_thread(void);
unsigned char tex2_data[64][4];
void init_tex2_gradient(void)
{
int i;
for (i=0; i < 16; ++i) {
tex2_data[i+ 0][0] = 64 + 12*i;
tex2_data[i+ 0][1] = 32;
tex2_data[i+ 0][2] = 64;
tex2_data[i+16][0] = 255;
tex2_data[i+16][1] = 32 + 8*i;
tex2_data[i+16][2] = 64;
tex2_data[i+32][0] = 255;
tex2_data[i+32][1] = 160;
tex2_data[i+32][2] = 64 + 12*i;
tex2_data[i+48][0] = 255;
tex2_data[i+48][1] = 160 + 6*i;
tex2_data[i+48][2] = 255;
}
}
void set_tex2_alpha(float fa)
{
int i;
int a = (int) stb_lerp(fa, 0, 255);
if (a < 0) a = 0; else if (a > 255) a = 255;
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
for (i=0; i < 64; ++i) {
tex2_data[i][3] = a;
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, 1,1,1, GL_RGBA, GL_UNSIGNED_BYTE, tex2_data[i]);
}
}
void render_init(void)
{
int i;
char *binds[] = { "attr_vertex", "attr_face", NULL };
char *vertex;
char *fragment;
int w=0,h=0;
unsigned char *texdata = stbi_load("terrain.png", &w, &h, NULL, 4);
stbvox_init_mesh_maker(&g_mesh_maker);
for (i=0; i < num_mesh_workers; ++i) {
stbvox_init_mesh_maker(&mesh_data[i].rm.mm);
}
vertex = stbvox_get_vertex_shader();
fragment = stbvox_get_fragment_shader();
{
char error_buffer[1024];
char *main_vertex[] = { vertex, NULL };
char *main_fragment[] = { fragment, NULL };
main_prog = stbgl_create_program(main_vertex, main_fragment, binds, error_buffer, sizeof(error_buffer));
if (main_prog == 0) {
ods("Compile error for main shader: %s\n", error_buffer);
assert(0);
exit(1);
}
}
//init_index_buffer();
make_texture_buffer_for_uniform(STBVOX_UNIFORM_texscale , 3);
make_texture_buffer_for_uniform(STBVOX_UNIFORM_texgen , 4);
make_texture_buffer_for_uniform(STBVOX_UNIFORM_color_table , 5);
glGenTextures(2, voxel_tex);
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]);
glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA,
TEX_SIZE,TEX_SIZE,256,
0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
for (i=0; i < 256; ++i) {
if (texdata)
scale_texture(texdata, (i&15)*w/16, (h/16)*(i>>4), w,h);
else
build_base_texture(i);
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]);
}
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
#ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#endif
glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA,
1,1,64,
0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
init_tex2_gradient();
set_tex2_alpha(0.0);
#if 0
for (i=0; i < 128; ++i) {
//build_overlay_texture(i);
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]);
}
#endif
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
}
void world_init(void)
{
int a,b,x,y;
Uint64 start_time, end_time;
#ifdef NDEBUG
int range = 32;
#else
int range = 12;
#endif
start_time = SDL_GetPerformanceCounter();
// iterate in 8x8 clusters of qchunks at a time to get better converted-chunk-cache reuse
// than a purely row-by-row ordering is (single-threaded this is a bigger win than
// any of the above optimizations were, since it halves zlib/mc-conversion costs)
for (x=-range; x <= range; x += 16)
for (y=-range; y <= range; y += 16)
for (b=y; b < y+16 && b <= range; b += 2)
for (a=x; a < x+16 && a <= range; a += 2)
while (!request_chunk(a, b)) { // if request fails, all threads are busy
update_meshes_from_render_thread();
SDL_Delay(1);
}
// wait until all the workers are done,
// (this is only needed if we want to time
// when the build finishes, or when we want to reset the
// cache size; otherwise we could just go ahead and
// start rendering whatever we've got)
for(;;) {
int i;
update_meshes_from_render_thread();
for (i=0; i < num_mesh_workers; ++i)
if (mesh_data[i].state != WSTATE_idle)
break;
if (i == num_mesh_workers)
break;
SDL_Delay(3);
}
end_time = SDL_GetPerformanceCounter();
ods("Build time: %7.2fs\n", (end_time - start_time) / (float) SDL_GetPerformanceFrequency());
// don't waste lots of storage on chunk caches once it's finished starting-up;
// this was only needed to be this large because we worked in large blocks
// to maximize sharing
reset_cache_size(32);
}
extern SDL_mutex * chunk_cache_mutex;
int mesh_worker_handler(void *data)
{
mesh_worker *mw = data;
mw->face_buffer = malloc(FACE_BUFFER_SIZE);
mw->build_buffer = malloc(BUILD_BUFFER_SIZE);
// this loop only works because the compiler can't
// tell that the SDL_calls don't access mw->state;
// really we should barrier that stuff
for(;;) {
int i,j;
int cx,cy;
// wait for a chunk request
SDL_SemWait(mw->request_received);
// analyze the chunk request
assert(mw->state == WSTATE_requested);
cx = mw->request_cx;
cy = mw->request_cy;
// this is inaccurate as it can block while another thread has the cache locked
mw->state = WSTATE_running;
// get the chunks we need (this takes a lock and caches them)
for (j=0; j < 4; ++j)
for (i=0; i < 4; ++i)
mw->chunks[j][i] = get_converted_fastchunk(cx-1 + i, cy-1 + j);
// build the mesh based on the chunks
mw->rm.build_buffer = mw->build_buffer;
mw->rm.face_buffer = mw->face_buffer;
build_chunk(cx, cy, mw->chunks, &mw->rm);
mw->state = WSTATE_mesh_ready;
// don't need to notify of this, because it gets polled
// when done, free the chunks
// for efficiency we just take the mutex once around the whole thing,
// though this spreads the mutex logic over two files
SDL_LockMutex(chunk_cache_mutex);
for (j=0; j < 4; ++j)
for (i=0; i < 4; ++i) {
deref_fastchunk(mw->chunks[j][i]);
mw->chunks[j][i] = NULL;
}
SDL_UnlockMutex(chunk_cache_mutex);
}
return 0;
}
int request_chunk(int chunk_x, int chunk_y)
{
int i;
for (i=0; i < num_mesh_workers; ++i) {
mesh_worker *mw = &mesh_data[i];
if (mw->state == WSTATE_idle) {
mw->request_cx = chunk_x;
mw->request_cy = chunk_y;
mw->state = WSTATE_requested;
SDL_SemPost(mw->request_received);
++num_meshes_started;
return 1;
}
}
return 0;
}
void prepare_threads(void)
{
int i;
int num_proc = SDL_GetCPUCount();
if (num_proc > 6)
num_mesh_workers = num_proc/2;
else if (num_proc > 4)
num_mesh_workers = 4;
else
num_mesh_workers = num_proc-1;
// @TODO
// Thread usage is probably pretty terrible; need to make a
// separate queue of needed chunks, instead of just generating
// one request per thread per frame, and a separate queue of
// results. (E.g. If it takes 1.5 frames to build mesh, thread
// is idle for 0.5 frames.) To fake this for now, I've just
// doubled the number of threads to let those serve as a 'queue',
// but that's dumb.
num_mesh_workers *= 2; // try to get better thread usage
if (num_mesh_workers > MAX_MESH_WORKERS)
num_mesh_workers = MAX_MESH_WORKERS;
for (i=0; i < num_mesh_workers; ++i) {
mesh_worker *data = &mesh_data[i];
data->request_received = SDL_CreateSemaphore(0);
data->chunk_server_done_processing = SDL_CreateSemaphore(0);
SDL_CreateThread(mesh_worker_handler, "mesh worker", data);
}
}
// "better" buffer uploading
#if 0
if (glBufferStorage) {
glDeleteBuffersARB(1, &vb->vbuf);
glGenBuffersARB(1, &vb->vbuf);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf);
glBufferStorage(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, 0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
} else {
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, GL_STATIC_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}
#endif
typedef struct
{
float x,y,z,w;
} plane;
static plane frustum[6];
static void matd_mul(double out[4][4], double src1[4][4], double src2[4][4])
{
int i,j,k;
for (j=0; j < 4; ++j) {
for (i=0; i < 4; ++i) {
double t=0;
for (k=0; k < 4; ++k)
t += src1[k][i] * src2[j][k];
out[i][j] = t;
}
}
}
// https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/
static void compute_frustum(void)
{
int i;
GLdouble mv[4][4],proj[4][4], mvproj[4][4];
glGetDoublev(GL_MODELVIEW_MATRIX , mv[0]);
glGetDoublev(GL_PROJECTION_MATRIX, proj[0]);
matd_mul(mvproj, proj, mv);
for (i=0; i < 4; ++i) {
(&frustum[0].x)[i] = (float) (mvproj[3][i] + mvproj[0][i]);
(&frustum[1].x)[i] = (float) (mvproj[3][i] - mvproj[0][i]);
(&frustum[2].x)[i] = (float) (mvproj[3][i] + mvproj[1][i]);
(&frustum[3].x)[i] = (float) (mvproj[3][i] - mvproj[1][i]);
(&frustum[4].x)[i] = (float) (mvproj[3][i] + mvproj[2][i]);
(&frustum[5].x)[i] = (float) (mvproj[3][i] - mvproj[2][i]);
}
}
static int test_plane(plane *p, float x0, float y0, float z0, float x1, float y1, float z1)
{
// return false if the box is entirely behind the plane
float d=0;
assert(x0 <= x1 && y0 <= y1 && z0 <= z1);
if (p->x > 0) d += x1*p->x; else d += x0*p->x;
if (p->y > 0) d += y1*p->y; else d += y0*p->y;
if (p->z > 0) d += z1*p->z; else d += z0*p->z;
return d + p->w >= 0;
}
static int is_box_in_frustum(float *bmin, float *bmax)
{
int i;
for (i=0; i < 5; ++i)
if (!test_plane(&frustum[i], bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]))
return 0;
return 1;
}
float compute_priority(int cx, int cy, float x, float y)
{
float distx, disty, dist2;
distx = (cx*16+8) - x;
disty = (cy*16+8) - y;
dist2 = distx*distx + disty*disty;
return view_dist_in_chunks*view_dist_in_chunks * 16 * 16 - dist2;
}
int chunk_locations, chunks_considered, chunks_in_frustum;
int quads_considered, quads_rendered;
int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total;
int update_frustum = 1;
#ifdef SHORTVIEW
int max_chunk_storage = 450 << 20;
int min_chunk_storage = 350 << 20;
#else
int max_chunk_storage = 900 << 20;
int min_chunk_storage = 800 << 20;
#endif
float min_priority = -500; // this really wants to be in unit space, not squared space
int num_meshes_uploaded;
void update_meshes_from_render_thread(void)
{
int i;
for (i=0; i < num_mesh_workers; ++i) {
mesh_worker *mw = &mesh_data[i];
if (mw->state == WSTATE_mesh_ready) {
upload_mesh_data(&mw->rm);
++num_meshes_uploaded;
mw->state = WSTATE_idle;
}
}
}
extern float tex2_alpha;
extern int global_hack;
int num_threads_active;
float chunk_server_activity;
void render_caves(float campos[3])
{
float x = campos[0], y = campos[1];
int qchunk_x, qchunk_y;
int cam_x, cam_y;
int i,j, rad;
compute_frustum();
chunk_locations = chunks_considered = chunks_in_frustum = 0;
quads_considered = quads_rendered = 0;
chunk_storage_total = chunk_storage_considered = chunk_storage_rendered = 0;
cam_x = (int) floor(x+0.5);
cam_y = (int) floor(y+0.5);
qchunk_x = (((int) floor(x)+16) >> 5) << 1;
qchunk_y = (((int) floor(y)+16) >> 5) << 1;
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5);
stbglUseProgram(main_prog);
setup_uniforms(campos); // set uniforms to default values inefficiently
glActiveTextureARB(GL_TEXTURE2_ARB);
stbglEnableVertexAttribArray(0);
{
float lighting[2][3] = { { campos[0],campos[1],campos[2] }, { 0.75,0.75,0.65f } };
float bright = 8;
lighting[1][0] *= bright;
lighting[1][1] *= bright;
lighting[1][2] *= bright;
stbglUniform3fv(stbgl_find_uniform(main_prog, "light_source"), 2, lighting[0]);
}
if (global_hack)
set_tex2_alpha(tex2_alpha);
num_meshes_uploaded = 0;
update_meshes_from_render_thread();
// traverse all in-range chunks and analyze them
for (j=-view_dist_in_chunks; j <= view_dist_in_chunks; j += 2) {
for (i=-view_dist_in_chunks; i <= view_dist_in_chunks; i += 2) {
float priority;
int cx = qchunk_x + i;
int cy = qchunk_y + j;
priority = compute_priority(cx, cy, x, y);
if (priority >= min_priority) {
int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1);
int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1);
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
++chunk_locations;
if (cm->state == STATE_valid && priority >= 0) {
// check if chunk pos actually matches
if (cm->chunk_x != cx || cm->chunk_y != cy) {
// we have a stale chunk we need to recreate
free_chunk(slot_x, slot_y); // it probably will have already gotten freed, but just in case
}
}
if (cm->state == STATE_invalid) {
cm->chunk_x = cx;
cm->chunk_y = cy;
cm->state = STATE_needed;
}
cm->priority = priority;
}
}
}
// draw front-to-back
for (rad = 0; rad <= view_dist_in_chunks; rad += 2) {
for (j=-rad; j <= rad; j += 2) {
// if j is +- rad, then iterate i through all values
// if j isn't +-rad, then i should be only -rad & rad
int step = 2;
if (abs(j) != rad)
step = 2*rad;
for (i=-rad; i <= rad; i += step) {
int cx = qchunk_x + i;
int cy = qchunk_y + j;
int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1);
int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1);
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
if (cm->state == STATE_valid && cm->priority >= 0) {
++chunks_considered;
quads_considered += cm->num_quads;
if (is_box_in_frustum(cm->bounds[0], cm->bounds[1])) {
++chunks_in_frustum;
// @TODO if in range
stbglUniform3fv(uniform_loc[STBVOX_UNIFORM_transform], 3, cm->transform[0]);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf);
glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 4, (void*) 0);
glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex);
glDrawArrays(GL_QUADS, 0, cm->num_quads*4);
quads_rendered += cm->num_quads;
chunk_storage_rendered += cm->vbuf_size + cm->fbuf_size;
}
chunk_storage_considered += cm->vbuf_size + cm->fbuf_size;
}
}
}
}
stbglDisableVertexAttribArray(0);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glActiveTextureARB(GL_TEXTURE0_ARB);
stbglUseProgram(0);
num_meshes_started = 0;
{
#define MAX_QUEUE 8
float highest_priority[MAX_QUEUE];
int highest_i[MAX_QUEUE], highest_j[MAX_QUEUE];
float lowest_priority = view_dist_in_chunks * view_dist_in_chunks * 16 * 16.0f;
int lowest_i = -1, lowest_j = -1;
for (i=0; i < MAX_QUEUE; ++i) {
highest_priority[i] = min_priority;
highest_i[i] = -1;
highest_j[i] = -1;
}
for (j=0; j < CACHED_MESH_NUM_Y; ++j) {
for (i=0; i < CACHED_MESH_NUM_X; ++i) {
chunk_mesh *cm = &cached_chunk_mesh[j][i];
if (cm->state == STATE_valid) {
cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y);
chunk_storage_total += cm->vbuf_size + cm->fbuf_size;
if (cm->priority < lowest_priority) {
lowest_priority = cm->priority;
lowest_i = i;
lowest_j = j;
}
}
if (cm->state == STATE_needed) {
cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y);
if (cm->priority < min_priority)
cm->state = STATE_invalid;
else if (cm->priority > highest_priority[0]) {
int k;
highest_priority[0] = cm->priority;
highest_i[0] = i;
highest_j[0] = j;
// bubble this up to right place
for (k=0; k < MAX_QUEUE-1; ++k) {
if (highest_priority[k] > highest_priority[k+1]) {
highest_priority[k] = highest_priority[k+1];
highest_priority[k+1] = cm->priority;
highest_i[k] = highest_i[k+1];
highest_i[k+1] = i;
highest_j[k] = highest_j[k+1];
highest_j[k+1] = j;
} else {
break;
}
}
}
}
}
}
// I couldn't find any straightforward logic that avoids
// the hysteresis problem of continually creating & freeing
// a block on the margin, so I just don't free a block until
// it's out of range, but this doesn't actually correctly
// handle when the cache is too small for the given range
if (chunk_storage_total >= min_chunk_storage && lowest_i >= 0) {
if (cached_chunk_mesh[lowest_j][lowest_i].priority < -1200) // -1000? 0?
free_chunk(lowest_i, lowest_j);
}
if (chunk_storage_total < max_chunk_storage && highest_i[0] >= 0) {
for (j=MAX_QUEUE-1; j >= 0; --j) {
if (highest_j[0] >= 0) {
chunk_mesh *cm = &cached_chunk_mesh[highest_j[j]][highest_i[j]];
if (request_chunk(cm->chunk_x, cm->chunk_y)) {
cm->state = STATE_requested;
} else {
// if we couldn't queue this one, skip the remainder
break;
}
}
}
}
}
update_meshes_from_render_thread();
num_threads_active = 0;
for (i=0; i < num_mesh_workers; ++i) {
num_threads_active += (mesh_data[i].state == WSTATE_running);
}
}

157
tests/caveview/caveview.dsp Normal file
View File

@ -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
# 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_main.c
# End Source File
# Begin Source File
SOURCE=.\cave_mesher.c
# End Source File
# Begin Source File
SOURCE=.\cave_parse.c
# End Source File
# Begin Source File
SOURCE=.\cave_parse.h
# End Source File
# Begin Source File
SOURCE=.\cave_render.c
# End Source File
# Begin Source File
SOURCE=.\caveview.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=.\README.md
# 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_voxel_render.h
# End Source File
# End Target
# End Project

View File

@ -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>
{{{
}}}
###############################################################################

51
tests/caveview/caveview.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef INCLUDE_CAVEVIEW_H
#define INCLUDE_CAVEVIEW_H
#include "stb.h"
#define STBVOX_ROTATION_IN_LIGHTING
#include "stb_voxel_render.h"
typedef struct
{
int cx,cy;
stbvox_mesh_maker mm;
uint8 *build_buffer;
uint8 *face_buffer;
int num_quads;
float transform[3][3];
float bounds[2][3];
uint8 sv_blocktype[34][34][18];
uint8 sv_lighting [34][34][18];
} raw_mesh;
// a 3D checkerboard of empty,solid would be: 32x32x255x6/2 ~= 800000
// an all-leaf qchunk would be: 32 x 32 x 255 x 6 ~= 1,600,000
#define BUILD_QUAD_MAX 400000
#define BUILD_BUFFER_SIZE (4*4*BUILD_QUAD_MAX) // 4 bytes per vertex, 4 vertices per quad
#define FACE_BUFFER_SIZE ( 4*BUILD_QUAD_MAX) // 4 bytes per quad
extern void mesh_init(void);
extern void render_init(void);
extern void world_init(void);
extern void ods(char *fmt, ...); // output debug string
extern void reset_cache_size(int size);
extern void render_caves(float pos[3]);
#include "cave_parse.h" // fast_chunk
extern fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y);
extern void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm);
extern void reset_cache_size(int size);
extern void deref_fastchunk(fast_chunk *fc);
#endif

11124
tests/caveview/glext.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
GLARB(ActiveTexture,ACTIVETEXTURE)
GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE)
GLARB(MultiTexCoord2f,MULTITEXCOORD2F)
GLEXT(TexImage3D,TEXIMAGE3D)
GLEXT(TexSubImage3D,TEXSUBIMAGE3D)
GLEXT(GenerateMipmap,GENERATEMIPMAP)
GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK)
GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER)
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
tests/caveview/main.c Normal file
View File

1103
tests/caveview/stb_gl.h Normal file

File diff suppressed because it is too large Load Diff

504
tests/caveview/stb_glprog.h Normal file
View File

@ -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

View File

@ -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: */

View File

@ -6,6 +6,7 @@ stb_image_resize.h | graphics | resize images larger/smaller wi
stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality
stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
stb_textedit.h | UI | guts of a text editor for games etc implementing them from scratch
stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features
stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output)
stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc