Merge branch 'master' into working
This commit is contained in:
commit
a1f1010fad
@ -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 graphics | Minecraft-esque voxel rendering "engine" with many more features
|
||||
**stb_dxt.h** | 1.04 | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
|
||||
**stb_perlin.h** | 0.2 | 3D graphics | revised Perlin noise (3D input, 1D output)
|
||||
**stb_easy_font.h** | 0.5 | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
|
||||
|
176
docs/stb_voxel_render_interview.md
Normal file
176
docs/stb_voxel_render_interview.md
Normal 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!*
|
@ -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
3576
stb_voxel_render.h
Normal file
File diff suppressed because it is too large
Load Diff
85
tests/caveview/README.md
Normal file
85
tests/caveview/README.md
Normal 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
598
tests/caveview/cave_main.c
Normal 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;
|
||||
}
|
927
tests/caveview/cave_mesher.c
Normal file
927
tests/caveview/cave_mesher.c
Normal 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
632
tests/caveview/cave_parse.c
Normal 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
|
41
tests/caveview/cave_parse.h
Normal file
41
tests/caveview/cave_parse.h
Normal 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
|
951
tests/caveview/cave_render.c
Normal file
951
tests/caveview/cave_render.c
Normal 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
157
tests/caveview/caveview.dsp
Normal 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
|
29
tests/caveview/caveview.dsw
Normal file
29
tests/caveview/caveview.dsw
Normal 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
51
tests/caveview/caveview.h
Normal 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
11124
tests/caveview/glext.h
Normal file
File diff suppressed because it is too large
Load Diff
34
tests/caveview/glext_list.h
Normal file
34
tests/caveview/glext_list.h
Normal 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
0
tests/caveview/main.c
Normal file
1103
tests/caveview/stb_gl.h
Normal 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
504
tests/caveview/stb_glprog.h
Normal 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
|
224
tests/caveview/win32/SDL_windows_main.c
Normal file
224
tests/caveview/win32/SDL_windows_main.c
Normal 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: */
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user