Merge commit '3cd551098b8dc5a2b55d45a2a63bb9a219d31f2e' as 'external/stb/stb'

This commit is contained in:
2023-08-01 20:11:22 +02:00
422 changed files with 96140 additions and 0 deletions

12
external/stb/stb/tests/Makefile vendored Normal file
View File

@ -0,0 +1,12 @@
INCLUDES = -I..
CFLAGS = -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DSTB_DIVIDE_TEST
CPPFLAGS = -Wno-write-strings -DSTB_DIVIDE_TEST
# Uncomment this line for reproducing OSS-Fuzz bugs with image_fuzzer
#CFLAGS += -O -fsanitize=address
all:
$(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c test_c_lexer.c test_dxt.c test_easyfont.c test_image.c test_image_write.c test_perlin.c test_sprintf.c test_truetype.c test_voxel.c -lm
$(CC) $(INCLUDES) $(CPPFLAGS) -std=c++0x test_cpp_compilation.cpp -lm -lstdc++
$(CC) $(INCLUDES) $(CFLAGS) -DIWT_TEST image_write_test.c -lm -o image_write_test
$(CC) $(INCLUDES) $(CFLAGS) fuzz_main.c stbi_read_fuzzer.c -lm -o image_fuzzer

50
external/stb/stb/tests/c_lexer_test.c vendored Normal file
View File

@ -0,0 +1,50 @@
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit
#define STB_C_LEX_C99_HEX_FLOATS Y // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
#define STB_C_LEX_C_SQ_STRINGS Y // single-quote-delimited strings with escapes CLEX_ssstring
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
#define STB_C_LEX_EQUAL_ARROW Y // "=>" CLEX_eqarrow
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
// "<<=" CLEX_shleq ">>=" CLEX_shreq
#define STB_C_LEX_PARSE_SUFFIXES Y // letters after numbers are parsed as part of those numbers, and must be in suffix list below
#define STB_C_LEX_DECIMAL_SUFFIXES "uUlL" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
#define STB_C_LEX_HEX_SUFFIXES "lL" // e.g. "uUlL"
#define STB_C_LEX_OCTAL_SUFFIXES "lL" // e.g. "uUlL"
#define STB_C_LEX_FLOAT_SUFFIXES "uulL" //
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
#define STB_C_LEX_MULTILINE_DSTRINGS Y // allow newlines in double-quoted strings
#define STB_C_LEX_MULTILINE_SSTRINGS Y // allow newlines in single-quoted strings
#define STB_C_LEX_USE_STDLIB N // use strtod,strtol for parsing #s; otherwise inaccurate hack
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES Y // if Y, all CLEX_ token names are defined, even if never returned
// leaving it as N should help you catch config bugs
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
// still have #line, #pragma, etc)
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
#define STB_C_LEXER_IMPLEMENTATION
#define STB_C_LEXER_SELF_TEST
#include "../stb_c_lexer.h"

89
external/stb/stb/tests/c_lexer_test.dsp vendored Normal file
View File

@ -0,0 +1,89 @@
# Microsoft Developer Studio Project File - Name="c_lexer_test" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=c_lexer_test - 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 "c_lexer_test.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 "c_lexer_test.mak" CFG="c_lexer_test - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "c_lexer_test - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "c_lexer_test - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "c_lexer_test - 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 Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# 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 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:console /machine:I386
# ADD 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 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:console /machine:I386
!ELSEIF "$(CFG)" == "c_lexer_test - 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\c_lexer_test"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
# SUBTRACT CPP /YX
# 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 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:console /debug /machine:I386 /pdbtype:sept
# ADD 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 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:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "c_lexer_test - Win32 Release"
# Name "c_lexer_test - Win32 Debug"
# Begin Source File
SOURCE=.\c_lexer_test.c
# End Source File
# End Target
# End Project

View File

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

View File

@ -0,0 +1,598 @@
#define _WIN32_WINNT 0x400
#include <assert.h>
#include <windows.h>
// stb.h
#define STB_DEFINE
#include "stb.h"
// stb_gl.h
#define STB_GL_IMPLEMENTATION
#define STB_GLEXT_DEFINE "glext_list.h"
#include "stb_gl.h"
// SDL
#include "sdl.h"
#include "SDL_opengl.h"
// stb_glprog.h
#define STB_GLPROG_IMPLEMENTATION
#define STB_GLPROG_ARB_DEFINE_EXTENSIONS
#include "stb_glprog.h"
// stb_image.h
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// stb_easy_font.h
#include "stb_easy_font.h" // doesn't require an IMPLEMENTATION
#include "caveview.h"
char *game_name = "caveview";
#define REVERSE_DEPTH
static void print_string(float x, float y, char *text, float r, float g, float b)
{
static char buffer[99999];
int num_quads;
num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer));
glColor3f(r,g,b);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 16, buffer);
glDrawArrays(GL_QUADS, 0, num_quads*4);
glDisableClientState(GL_VERTEX_ARRAY);
}
static float text_color[3];
static float pos_x = 10;
static float pos_y = 10;
static void print(char *text, ...)
{
char buffer[999];
va_list va;
va_start(va, text);
vsprintf(buffer, text, va);
va_end(va);
print_string(pos_x, pos_y, buffer, text_color[0], text_color[1], text_color[2]);
pos_y += 10;
}
float camang[3], camloc[3] = { 60,22,77 };
float player_zoom = 1.0;
float rotate_view = 0.0;
void camera_to_worldspace(float world[3], float cam_x, float cam_y, float cam_z)
{
float vec[3] = { cam_x, cam_y, cam_z };
float t[3];
float s,c;
s = (float) sin(camang[0]*3.141592/180);
c = (float) cos(camang[0]*3.141592/180);
t[0] = vec[0];
t[1] = c*vec[1] - s*vec[2];
t[2] = s*vec[1] + c*vec[2];
s = (float) sin(camang[2]*3.141592/180);
c = (float) cos(camang[2]*3.141592/180);
world[0] = c*t[0] - s*t[1];
world[1] = s*t[0] + c*t[1];
world[2] = t[2];
}
// camera worldspace velocity
float cam_vel[3];
int controls;
#define MAX_VEL 150.0f // blocks per second
#define ACCEL 6.0f
#define DECEL 3.0f
#define STATIC_FRICTION DECEL
#define EFFECTIVE_ACCEL (ACCEL+DECEL)
// dynamic friction:
//
// if going at MAX_VEL, ACCEL and friction must cancel
// EFFECTIVE_ACCEL = DECEL + DYNAMIC_FRIC*MAX_VEL
#define DYNAMIC_FRICTION (ACCEL/(float)MAX_VEL)
float view_x_vel = 0;
float view_z_vel = 0;
float pending_view_x;
float pending_view_z;
float pending_view_x;
float pending_view_z;
void process_tick_raw(float dt)
{
int i;
float thrust[3] = { 0,0,0 };
float world_thrust[3];
// choose direction to apply thrust
thrust[0] = (controls & 3)== 1 ? EFFECTIVE_ACCEL : (controls & 3)== 2 ? -EFFECTIVE_ACCEL : 0;
thrust[1] = (controls & 12)== 4 ? EFFECTIVE_ACCEL : (controls & 12)== 8 ? -EFFECTIVE_ACCEL : 0;
thrust[2] = (controls & 48)==16 ? EFFECTIVE_ACCEL : (controls & 48)==32 ? -EFFECTIVE_ACCEL : 0;
// @TODO clamp thrust[0] & thrust[1] vector length to EFFECTIVE_ACCEL
camera_to_worldspace(world_thrust, thrust[0], thrust[1], 0);
world_thrust[2] += thrust[2];
for (i=0; i < 3; ++i) {
float acc = world_thrust[i];
cam_vel[i] += acc*dt;
}
if (cam_vel[0] || cam_vel[1] || cam_vel[2])
{
float vel = (float) sqrt(cam_vel[0]*cam_vel[0] + cam_vel[1]*cam_vel[1] + cam_vel[2]*cam_vel[2]);
float newvel = vel;
float dec = STATIC_FRICTION + DYNAMIC_FRICTION*vel;
newvel = vel - dec*dt;
if (newvel < 0)
newvel = 0;
cam_vel[0] *= newvel/vel;
cam_vel[1] *= newvel/vel;
cam_vel[2] *= newvel/vel;
}
camloc[0] += cam_vel[0] * dt;
camloc[1] += cam_vel[1] * dt;
camloc[2] += cam_vel[2] * dt;
view_x_vel *= (float) pow(0.75, dt);
view_z_vel *= (float) pow(0.75, dt);
view_x_vel += (pending_view_x - view_x_vel)*dt*60;
view_z_vel += (pending_view_z - view_z_vel)*dt*60;
pending_view_x -= view_x_vel * dt;
pending_view_z -= view_z_vel * dt;
camang[0] += view_x_vel * dt;
camang[2] += view_z_vel * dt;
camang[0] = stb_clamp(camang[0], -90, 90);
camang[2] = (float) fmod(camang[2], 360);
}
void process_tick(float dt)
{
while (dt > 1.0f/60) {
process_tick_raw(1.0f/60);
dt -= 1.0f/60;
}
process_tick_raw(dt);
}
void update_view(float dx, float dy)
{
// hard-coded mouse sensitivity, not resolution independent?
pending_view_z -= dx*300;
pending_view_x -= dy*700;
}
extern int screen_x, screen_y;
extern int is_synchronous_debug;
float render_time;
extern int chunk_locations, chunks_considered, chunks_in_frustum;
extern int quads_considered, quads_rendered;
extern int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total;
extern int view_dist_in_chunks;
extern int num_threads_active, num_meshes_started, num_meshes_uploaded;
extern float chunk_server_activity;
static Uint64 start_time, end_time; // render time
float chunk_server_status[32];
int chunk_server_pos;
void draw_stats(void)
{
int i;
static Uint64 last_frame_time;
Uint64 cur_time = SDL_GetPerformanceCounter();
float chunk_server=0;
float frame_time = (cur_time - last_frame_time) / (float) SDL_GetPerformanceFrequency();
last_frame_time = cur_time;
chunk_server_status[chunk_server_pos] = chunk_server_activity;
chunk_server_pos = (chunk_server_pos+1) %32;
for (i=0; i < 32; ++i)
chunk_server += chunk_server_status[i] / 32.0f;
stb_easy_font_spacing(-0.75);
pos_y = 10;
text_color[0] = text_color[1] = text_color[2] = 1.0f;
print("Frame time: %6.2fms, CPU frame render time: %5.2fms", frame_time*1000, render_time*1000);
print("Tris: %4.1fM drawn of %4.1fM in range", 2*quads_rendered/1000000.0f, 2*quads_considered/1000000.0f);
print("Vbuf storage: %dMB in frustum of %dMB in range of %dMB in cache", chunk_storage_rendered>>20, chunk_storage_considered>>20, chunk_storage_total>>20);
print("Num mesh builds started this frame: %d; num uploaded this frame: %d\n", num_meshes_started, num_meshes_uploaded);
print("QChunks: %3d in frustum of %3d valid of %3d in range", chunks_in_frustum, chunks_considered, chunk_locations);
print("Mesh worker threads active: %d", num_threads_active);
print("View distance: %d blocks", view_dist_in_chunks*16);
print("%s", glGetString(GL_RENDERER));
if (is_synchronous_debug) {
text_color[0] = 1.0;
text_color[1] = 0.5;
text_color[2] = 0.5;
print("SLOWNESS: Synchronous debug output is enabled!");
}
}
void draw_main(void)
{
glEnable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
#ifdef REVERSE_DEPTH
glDepthFunc(GL_GREATER);
glClearDepth(0);
#else
glDepthFunc(GL_LESS);
glClearDepth(1);
#endif
glDepthMask(GL_TRUE);
glDisable(GL_SCISSOR_TEST);
glClearColor(0.6f,0.7f,0.9f,0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor3f(1,1,1);
glFrontFace(GL_CW);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
#ifdef REVERSE_DEPTH
stbgl_Perspective(player_zoom, 90, 70, 3000, 1.0/16);
#else
stbgl_Perspective(player_zoom, 90, 70, 1.0/16, 3000);
#endif
// now compute where the camera should be
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
stbgl_initCamera_zup_facing_y();
glRotatef(-camang[0],1,0,0);
glRotatef(-camang[2],0,0,1);
glTranslatef(-camloc[0], -camloc[1], -camloc[2]);
start_time = SDL_GetPerformanceCounter();
render_caves(camloc);
end_time = SDL_GetPerformanceCounter();
render_time = (end_time - start_time) / (float) SDL_GetPerformanceFrequency();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,screen_x/2,screen_y/2,0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
draw_stats();
}
#pragma warning(disable:4244; disable:4305; disable:4018)
#define SCALE 2
void error(char *s)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", s, NULL);
exit(0);
}
void ods(char *fmt, ...)
{
char buffer[1000];
va_list va;
va_start(va, fmt);
vsprintf(buffer, fmt, va);
va_end(va);
SDL_Log("%s", buffer);
}
#define TICKS_PER_SECOND 60
static SDL_Window *window;
extern void draw_main(void);
extern void process_tick(float dt);
extern void editor_init(void);
void draw(void)
{
draw_main();
SDL_GL_SwapWindow(window);
}
static int initialized=0;
static float last_dt;
int screen_x,screen_y;
float carried_dt = 0;
#define TICKRATE 60
float tex2_alpha = 1.0;
int raw_level_time;
float global_timer;
int global_hack;
int loopmode(float dt, int real, int in_client)
{
if (!initialized) return 0;
if (!real)
return 0;
// don't allow more than 6 frames to update at a time
if (dt > 0.075) dt = 0.075;
global_timer += dt;
carried_dt += dt;
while (carried_dt > 1.0/TICKRATE) {
if (global_hack) {
tex2_alpha += global_hack / 60.0f;
if (tex2_alpha < 0) tex2_alpha = 0;
if (tex2_alpha > 1) tex2_alpha = 1;
}
//update_input();
// if the player is dead, stop the sim
carried_dt -= 1.0/TICKRATE;
}
process_tick(dt);
draw();
return 0;
}
static int quit;
extern int controls;
void active_control_set(int key)
{
controls |= 1 << key;
}
void active_control_clear(int key)
{
controls &= ~(1 << key);
}
extern void update_view(float dx, float dy);
void process_sdl_mouse(SDL_Event *e)
{
update_view((float) e->motion.xrel / screen_x, (float) e->motion.yrel / screen_y);
}
void process_event(SDL_Event *e)
{
switch (e->type) {
case SDL_MOUSEMOTION:
process_sdl_mouse(e);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
break;
case SDL_QUIT:
quit = 1;
break;
case SDL_WINDOWEVENT:
switch (e->window.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED:
screen_x = e->window.data1;
screen_y = e->window.data2;
loopmode(0,1,0);
break;
}
break;
case SDL_KEYDOWN: {
int k = e->key.keysym.sym;
int s = e->key.keysym.scancode;
SDL_Keymod mod;
mod = SDL_GetModState();
if (k == SDLK_ESCAPE)
quit = 1;
if (s == SDL_SCANCODE_D) active_control_set(0);
if (s == SDL_SCANCODE_A) active_control_set(1);
if (s == SDL_SCANCODE_W) active_control_set(2);
if (s == SDL_SCANCODE_S) active_control_set(3);
if (k == SDLK_SPACE) active_control_set(4);
if (s == SDL_SCANCODE_LCTRL) active_control_set(5);
if (s == SDL_SCANCODE_S) active_control_set(6);
if (s == SDL_SCANCODE_D) active_control_set(7);
if (k == '1') global_hack = !global_hack;
if (k == '2') global_hack = -1;
#if 0
if (game_mode == GAME_editor) {
switch (k) {
case SDLK_RIGHT: editor_key(STBTE_scroll_right); break;
case SDLK_LEFT : editor_key(STBTE_scroll_left ); break;
case SDLK_UP : editor_key(STBTE_scroll_up ); break;
case SDLK_DOWN : editor_key(STBTE_scroll_down ); break;
}
switch (s) {
case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break;
case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break;
case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break;
case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break;
case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break;
case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break;
case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break;
}
if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) {
switch (s) {
case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break;
case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break;
case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break;
case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break;
case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break;
}
}
}
#endif
break;
}
case SDL_KEYUP: {
int k = e->key.keysym.sym;
int s = e->key.keysym.scancode;
if (s == SDL_SCANCODE_D) active_control_clear(0);
if (s == SDL_SCANCODE_A) active_control_clear(1);
if (s == SDL_SCANCODE_W) active_control_clear(2);
if (s == SDL_SCANCODE_S) active_control_clear(3);
if (k == SDLK_SPACE) active_control_clear(4);
if (s == SDL_SCANCODE_LCTRL) active_control_clear(5);
if (s == SDL_SCANCODE_S) active_control_clear(6);
if (s == SDL_SCANCODE_D) active_control_clear(7);
break;
}
}
}
static SDL_GLContext *context;
static float getTimestep(float minimum_time)
{
float elapsedTime;
double thisTime;
static double lastTime = -1;
if (lastTime == -1)
lastTime = SDL_GetTicks() / 1000.0 - minimum_time;
for(;;) {
thisTime = SDL_GetTicks() / 1000.0;
elapsedTime = (float) (thisTime - lastTime);
if (elapsedTime >= minimum_time) {
lastTime = thisTime;
return elapsedTime;
}
// @TODO: compute correct delay
SDL_Delay(1);
}
}
void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *param)
{
ods("%s\n", message);
}
int is_synchronous_debug;
void enable_synchronous(void)
{
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
is_synchronous_debug = 1;
}
void prepare_threads(void);
//void stbwingraph_main(void)
int SDL_main(int argc, char **argv)
{
SDL_Init(SDL_INIT_VIDEO);
prepare_threads();
SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
#ifdef GL_DEBUG
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
screen_x = 1920;
screen_y = 1080;
window = SDL_CreateWindow("caveview", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_x, screen_y,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
);
if (!window) error("Couldn't create window");
context = SDL_GL_CreateContext(window);
if (!context) error("Couldn't create context");
SDL_GL_MakeCurrent(window, context); // is this true by default?
SDL_SetRelativeMouseMode(SDL_TRUE);
#if defined(_MSC_VER) && _MSC_VER < 1300
// work around broken behavior in VC6 debugging
if (IsDebuggerPresent())
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
#endif
stbgl_initExtensions();
#ifdef GL_DEBUG
if (glDebugMessageCallbackARB) {
glDebugMessageCallbackARB(gl_debug, NULL);
enable_synchronous();
}
#endif
SDL_GL_SetSwapInterval(1);
render_init();
mesh_init();
world_init();
initialized = 1;
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e))
process_event(&e);
loopmode(getTimestep(0.0166f/8), 1, 1);
}
return 0;
}

View File

@ -0,0 +1,933 @@
// 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_CONFIG_DOWN_TEXLERP_PACKED
#define STBVOX_CONFIG_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 0
//#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_force, 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_force, 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_EXT((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_EXT((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_EXT((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_EXT((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
//minecraft_color_for_blocktype[56][i] = 63; // emissive diamond
minecraft_color_for_blocktype[48][i] = 63; // emissive dungeon
}
#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

View File

@ -0,0 +1,632 @@
#include <assert.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#define FAST_CHUNK // disabling this enables the old, slower path that deblocks into a regular form
#include "cave_parse.h"
#include "stb_image.h"
#include "stb.h"
#define NUM_CHUNKS_PER_REGION 32 // only on one axis
#define NUM_CHUNKS_PER_REGION_LOG2 5
#define NUM_COLUMNS_PER_CHUNK 16
#define NUM_COLUMNS_PER_CHUNK_LOG2 4
uint32 read_uint32_be(FILE *f)
{
unsigned char data[4];
fread(data, 1, 4, f);
return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3];
}
typedef struct
{
uint8 *data;
size_t len;
int x,z; // chunk index
int refcount; // for multi-threading
} compressed_chunk;
typedef struct
{
int x,z;
uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION];
} region;
size_t cached_compressed=0;
FILE *last_region;
int last_region_x;
int last_region_z;
int opened=0;
static void open_file(int reg_x, int reg_z)
{
if (!opened || last_region_x != reg_x || last_region_z != reg_z) {
char filename[256];
if (last_region != NULL)
fclose(last_region);
sprintf(filename, "r.%d.%d.mca", reg_x, reg_z);
last_region = fopen(filename, "rb");
last_region_x = reg_x;
last_region_z = reg_z;
opened = 1;
}
}
static region *load_region(int reg_x, int reg_z)
{
region *r;
int x,z;
open_file(reg_x, reg_z);
r = malloc(sizeof(*r));
if (last_region == NULL) {
memset(r, 0, sizeof(*r));
} else {
fseek(last_region, 0, SEEK_SET);
for (z=0; z < NUM_CHUNKS_PER_REGION; ++z)
for (x=0; x < NUM_CHUNKS_PER_REGION; ++x)
r->sector_data[z][x] = read_uint32_be(last_region);
}
r->x = reg_x;
r->z = reg_z;
return r;
}
void free_region(region *r)
{
free(r);
}
#define MAX_MAP_REGIONS 64 // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns
region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS];
static region *get_region(int reg_x, int reg_z)
{
int slot_x = reg_x & (MAX_MAP_REGIONS-1);
int slot_z = reg_z & (MAX_MAP_REGIONS-1);
region *r;
r = regions[slot_z][slot_x];
if (r) {
if (r->x == reg_x && r->z == reg_z)
return r;
free_region(r);
}
r = load_region(reg_x, reg_z);
regions[slot_z][slot_x] = r;
return r;
}
// about one region, so size should be ok
#define NUM_CACHED_X 64
#define NUM_CACHED_Z 64
// @TODO: is it really worth caching these? we probably can just
// pull them from the disk cache nearly as efficiently.
// Can test that by setting to 1x1?
compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X];
static void deref_compressed_chunk(compressed_chunk *cc)
{
assert(cc->refcount > 0);
--cc->refcount;
if (cc->refcount == 0) {
if (cc->data)
free(cc->data);
free(cc);
}
}
static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z)
{
int slot_x = chunk_x & (NUM_CACHED_X-1);
int slot_z = chunk_z & (NUM_CACHED_Z-1);
compressed_chunk *cc = cached_chunk[slot_z][slot_x];
if (cc && cc->x == chunk_x && cc->z == chunk_z)
return cc;
else {
int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2;
int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2;
region *r = get_region(reg_x, reg_z);
if (cc) {
deref_compressed_chunk(cc);
cached_chunk[slot_z][slot_x] = NULL;
}
cc = malloc(sizeof(*cc));
cc->x = chunk_x;
cc->z = chunk_z;
{
int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1);
int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1);
uint32 code = r->sector_data[subchunk_z][subchunk_x];
if (code & 255) {
open_file(reg_x, reg_z);
fseek(last_region, (code>>8)*4096, SEEK_SET);
cc->len = (code&255)*4096;
cc->data = malloc(cc->len);
fread(cc->data, 1, cc->len, last_region);
} else {
cc->len = 0;
cc->data = 0;
}
}
cc->refcount = 1;
cached_chunk[slot_z][slot_x] = cc;
return cc;
}
}
// NBT parser -- can automatically parse stuff we don't
// have definitions for, but want to explicitly parse
// stuff we do have definitions for.
//
// option 1: auto-parse everything into data structures,
// then read those
//
// option 2: have a "parse next object" which
// doesn't resolve whether it expands its children
// yet, and then the user either says "expand" or
// "skip" after looking at the name. Anything with
// "children" without names can't go through this
// interface.
//
// Let's try option 2.
typedef struct
{
unsigned char *buffer_start;
unsigned char *buffer_end;
unsigned char *cur;
int nesting;
char temp_buffer[256];
} nbt;
enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4,
TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8,
TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 };
static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize)
{
int len = data[0]*256 + data[1];
int i;
for (i=0; i < len && i+1 < (int) bufsize; ++i)
buffer[i] = (char) data[i+2];
buffer[i] = 0;
}
static char *nbt_peek(nbt *n)
{
unsigned char type = *n->cur;
if (type == TAG_End)
return NULL;
nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer));
return n->temp_buffer;
}
static uint32 nbt_parse_uint32(unsigned char *buffer)
{
return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3];
}
static void nbt_skip(nbt *n);
// skip an item that doesn't have an id or name prefix (usable in lists)
static void nbt_skip_raw(nbt *n, unsigned char type)
{
switch (type) {
case TAG_Byte : n->cur += 1; break;
case TAG_Short : n->cur += 2; break;
case TAG_Int : n->cur += 4; break;
case TAG_Long : n->cur += 8; break;
case TAG_Float : n->cur += 4; break;
case TAG_Double: n->cur += 8; break;
case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break;
case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break;
case TAG_String : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break;
case TAG_List : {
unsigned char list_type = *n->cur++;
unsigned int list_len = nbt_parse_uint32(n->cur);
unsigned int i;
n->cur += 4; // list_len
for (i=0; i < list_len; ++i)
nbt_skip_raw(n, list_type);
break;
}
case TAG_Compound : {
while (*n->cur != TAG_End)
nbt_skip(n);
nbt_skip(n); // skip the TAG_end
break;
}
}
assert(n->cur <= n->buffer_end);
}
static void nbt_skip(nbt *n)
{
unsigned char type = *n->cur++;
if (type == TAG_End)
return;
// skip name
n->cur += (n->cur[0]*256 + n->cur[1]) + 2;
nbt_skip_raw(n, type);
}
// byteswap
static void nbt_swap(unsigned char *ptr, int len)
{
int i;
for (i=0; i < (len>>1); ++i) {
unsigned char t = ptr[i];
ptr[i] = ptr[len-1-i];
ptr[len-1-i] = t;
}
}
// pass in the expected type, fail if doesn't match
// returns a pointer to the data, byteswapped if appropriate
static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len)
{
unsigned char *ptr;
assert(type != TAG_Compound);
assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh
if (len) *len = 1;
ptr = n->cur;
switch (type) {
case TAG_Byte : break;
case TAG_Short : nbt_swap(ptr, 2); break;
case TAG_Int : nbt_swap(ptr, 4); break;
case TAG_Long : nbt_swap(ptr, 8); break;
case TAG_Float : nbt_swap(ptr, 4); break;
case TAG_Double: nbt_swap(ptr, 8); break;
case TAG_Byte_Array:
*len = nbt_parse_uint32(ptr);
ptr += 4;
break;
case TAG_Int_Array: {
int i;
*len = nbt_parse_uint32(ptr);
ptr += 4;
for (i=0; i < *len; ++i)
nbt_swap(ptr + 4*i, 4);
break;
}
default: assert(0); // unhandled case
}
nbt_skip_raw(n, type);
return ptr;
}
static void *nbt_get(nbt *n, unsigned char type, int *len)
{
assert(n->cur[0] == type);
n->cur += 3 + (n->cur[1]*256+n->cur[2]);
return nbt_get_fromlist(n, type, len);
}
static void nbt_begin_compound(nbt *n) // start a compound
{
assert(*n->cur == TAG_Compound);
// skip header
n->cur += 3 + (n->cur[1]*256 + n->cur[2]);
++n->nesting;
}
static void nbt_begin_compound_in_list(nbt *n) // start a compound
{
++n->nesting;
}
static void nbt_end_compound(nbt *n) // end a compound
{
assert(*n->cur == TAG_End);
assert(n->nesting != 0);
++n->cur;
--n->nesting;
}
// @TODO no interface to get lists from lists
static int nbt_begin_list(nbt *n, unsigned char type)
{
uint32 len;
unsigned char *ptr;
ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]);
if (ptr[0] != type)
return -1;
n->cur = ptr;
len = nbt_parse_uint32(n->cur+1);
assert(n->cur[0] == type);
// @TODO keep a stack with the count to make sure they do it right
++n->nesting;
n->cur += 5;
return (int) len;
}
static void nbt_end_list(nbt *n)
{
--n->nesting;
}
// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB
//
// if we want to process 64x64x256 at a time, that will be:
// 4*4*256KB => 4MB per area in raw_block
//
// (plus we maybe need to decode adjacent regions)
#ifdef FAST_CHUNK
typedef fast_chunk parse_chunk;
#else
typedef chunk parse_chunk;
#endif
static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len)
{
char *s;
parse_chunk *c = NULL;
nbt n_store, *n = &n_store;
n->buffer_start = data;
n->buffer_end = data + len;
n->cur = n->buffer_start;
n->nesting = 0;
nbt_begin_compound(n);
while ((s = nbt_peek(n)) != NULL) {
if (!strcmp(s, "Level")) {
int *height;
c = malloc(sizeof(*c));
#ifdef FAST_CHUNK
memset(c, 0, sizeof(*c));
c->pointer_to_free = data;
#else
c->rb[15][15][255].block = 0;
#endif
c->max_y = 0;
nbt_begin_compound(n);
while ((s = nbt_peek(n)) != NULL) {
if (!strcmp(s, "xPos"))
c->xpos = *(int *) nbt_get(n, TAG_Int, 0);
else if (!strcmp(s, "zPos"))
c->zpos = *(int *) nbt_get(n, TAG_Int, 0);
else if (!strcmp(s, "Sections")) {
int count = nbt_begin_list(n, TAG_Compound), i;
if (count == -1) {
// this not-a-list case happens in The End and I'm not sure
// what it means... possibly one of those silly encodings
// where it's not encoded as a list if there's only one?
// not worth figuring out
nbt_skip(n);
count = -1;
}
for (i=0; i < count; ++i) {
int yi, len;
uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL;
nbt_begin_compound_in_list(n);
while ((s = nbt_peek(n)) != NULL) {
if (!strcmp(s, "Y"))
yi = * (uint8 *) nbt_get(n, TAG_Byte, 0);
else if (!strcmp(s, "BlockLight")) {
light = nbt_get(n, TAG_Byte_Array, &len);
assert(len == 2048);
} else if (!strcmp(s, "Blocks")) {
blocks = nbt_get(n, TAG_Byte_Array, &len);
assert(len == 4096);
} else if (!strcmp(s, "Data")) {
data = nbt_get(n, TAG_Byte_Array, &len);
assert(len == 2048);
} else if (!strcmp(s, "SkyLight")) {
skylight = nbt_get(n, TAG_Byte_Array, &len);
assert(len == 2048);
}
}
nbt_end_compound(n);
assert(yi < 16);
#ifndef FAST_CHUNK
// clear data below current max_y
{
int x,z;
while (c->max_y < yi*16) {
for (x=0; x < 16; ++x)
for (z=0; z < 16; ++z)
c->rb[z][x][c->max_y].block = 0;
++c->max_y;
}
}
// now assemble the data
{
int x,y,z, o2=0,o4=0;
for (y=0; y < 16; ++y) {
for (z=0; z < 16; ++z) {
for (x=0; x < 16; x += 2) {
raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis
rb[0].block = blocks[o4];
rb[0].light = light[o2] & 15;
rb[0].data = data[o2] & 15;
rb[0].skylight = skylight[o2] & 15;
rb[256].block = blocks[o4+1];
rb[256].light = light[o2] >> 4;
rb[256].data = data[o2] >> 4;
rb[256].skylight = skylight[o2] >> 4;
o2 += 1;
o4 += 2;
}
}
}
c->max_y += 16;
}
#else
c->blockdata[yi] = blocks;
c->data [yi] = data;
c->light [yi] = light;
c->skylight [yi] = skylight;
#endif
}
//nbt_end_list(n);
} else if (!strcmp(s, "HeightMap")) {
height = nbt_get(n, TAG_Int_Array, &len);
assert(len == 256);
} else
nbt_skip(n);
}
nbt_end_compound(n);
} else
nbt_skip(n);
}
nbt_end_compound(n);
assert(n->cur == n->buffer_end);
return c;
}
#define MAX_DECODED_CHUNK_X 64
#define MAX_DECODED_CHUNK_Z 64
typedef struct
{
int cx,cz;
fast_chunk *fc;
int valid;
} decoded_buffer;
static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X];
void lock_chunk_get_mutex(void);
void unlock_chunk_get_mutex(void);
#ifdef FAST_CHUNK
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z)
{
unsigned char *decoded;
compressed_chunk *cc;
int inlen;
int len;
fast_chunk *fc;
lock_chunk_get_mutex();
cc = get_compressed_chunk(chunk_x, chunk_z);
if (cc->len != 0)
++cc->refcount;
unlock_chunk_get_mutex();
if (cc->len == 0)
return NULL;
assert(cc != NULL);
assert(cc->data[4] == 2);
inlen = nbt_parse_uint32(cc->data);
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len);
assert(decoded != NULL);
assert(len != 0);
lock_chunk_get_mutex();
deref_compressed_chunk(cc);
unlock_chunk_get_mutex();
#ifdef FAST_CHUNK
fc = minecraft_chunk_parse(decoded, len);
#else
fc = NULL;
#endif
if (fc == NULL)
free(decoded);
return fc;
}
decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z)
{
decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)];
if (db->valid) {
if (db->cx == chunk_x && db->cz == chunk_z)
return db;
if (db->fc) {
free(db->fc->pointer_to_free);
free(db->fc);
}
}
db->cx = chunk_x;
db->cz = chunk_z;
db->valid = 1;
db->fc = 0;
{
db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z);
return db;
}
}
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z)
{
decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z);
return db->fc;
}
#endif
#ifndef FAST_CHUNK
chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z)
{
unsigned char *decoded;
compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z);
assert(cc != NULL);
if (cc->len == 0)
return NULL;
else {
chunk *ch;
int inlen = nbt_parse_uint32(cc->data);
int len;
assert(cc->data[4] == 2);
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len);
assert(decoded != NULL);
#ifdef FAST_CHUNK
ch = NULL;
#else
ch = minecraft_chunk_parse(decoded, len);
#endif
free(decoded);
return ch;
}
}
static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X];
chunk *get_decoded_chunk(int chunk_x, int chunk_z)
{
chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)];
if (c && c->xpos == chunk_x && c->zpos == chunk_z)
return c;
if (c) free(c);
c = get_decoded_chunk_raw(chunk_x, chunk_z);
decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c;
return c;
}
#endif

View File

@ -0,0 +1,41 @@
#ifndef INCLUDE_CAVE_PARSE_H
#define INCLUDE_CAVE_PARSE_H
typedef struct
{
unsigned char block;
unsigned char data;
unsigned char light:4;
unsigned char skylight:4;
} raw_block;
// this is the old fully-decoded chunk
typedef struct
{
int xpos, zpos, max_y;
int height[16][16];
raw_block rb[16][16][256]; // [z][x][y] which becomes [y][x][z] in stb
} chunk;
chunk *get_decoded_chunk(int chunk_x, int chunk_z);
#define NUM_SEGMENTS 16
typedef struct
{
int max_y, xpos, zpos;
unsigned char *blockdata[NUM_SEGMENTS];
unsigned char *data[NUM_SEGMENTS];
unsigned char *skylight[NUM_SEGMENTS];
unsigned char *light[NUM_SEGMENTS];
void *pointer_to_free;
int refcount; // this allows multi-threaded building without wrapping in ANOTHER struct
} fast_chunk;
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z); // cache, never call free()
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z);
#endif

View File

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

View File

@ -0,0 +1,157 @@
# Microsoft Developer Studio Project File - Name="caveview" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Application" 0x0101
CFG=caveview - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "caveview.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "caveview.mak" CFG="caveview - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "caveview - Win32 Release" (based on "Win32 (x86) Application")
!MESSAGE "caveview - Win32 Debug" (based on "Win32 (x86) Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "caveview - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /WX /GX /Zd /O2 /I "../.." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
# SUBTRACT CPP /YX
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386
# SUBTRACT LINK32 /map
!ELSEIF "$(CFG)" == "caveview - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /Zi /Od /I "../.." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "caveview - Win32 Release"
# Name "caveview - Win32 Debug"
# Begin Source File
SOURCE=.\cave_main.c
# End Source File
# Begin Source File
SOURCE=.\cave_mesher.c
# End Source File
# Begin Source File
SOURCE=.\cave_parse.c
# End Source File
# Begin Source File
SOURCE=.\cave_parse.h
# End Source File
# Begin Source File
SOURCE=.\cave_render.c
# End Source File
# Begin Source File
SOURCE=.\caveview.h
# End Source File
# Begin Source File
SOURCE=.\glext.h
# End Source File
# Begin Source File
SOURCE=.\glext_list.h
# End Source File
# Begin Source File
SOURCE=.\README.md
# End Source File
# Begin Source File
SOURCE=.\win32\SDL_windows_main.c
# End Source File
# Begin Source File
SOURCE=..\..\stb.h
# End Source File
# Begin Source File
SOURCE=..\..\stb_easy_font.h
# End Source File
# Begin Source File
SOURCE=.\stb_gl.h
# End Source File
# Begin Source File
SOURCE=.\stb_glprog.h
# End Source File
# Begin Source File
SOURCE=..\..\stb_image.h
# End Source File
# Begin Source File
SOURCE=..\..\stb_voxel_render.h
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "caveview"=.\caveview.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,50 @@
#ifndef INCLUDE_CAVEVIEW_H
#define INCLUDE_CAVEVIEW_H
#include "stb.h"
#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
external/stb/stb/tests/caveview/glext.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
GLARB(ActiveTexture,ACTIVETEXTURE)
GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE)
GLARB(MultiTexCoord2f,MULTITEXCOORD2F)
GLEXT(TexImage3D,TEXIMAGE3D)
GLEXT(TexSubImage3D,TEXSUBIMAGE3D)
GLEXT(GenerateMipmap,GENERATEMIPMAP)
GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK)
GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER)
GLEXT(BindFramebuffer,BINDFRAMEBUFFER)
GLEXT(DeleteFramebuffers,DELETEFRAMEBUFFERS)
GLEXT(GenFramebuffers,GENFRAMEBUFFERS)
GLEXT(CheckFramebufferStatus,CHECKFRAMEBUFFERSTATUS)
GLEXT(FramebufferTexture2D,FRAMEBUFFERTEXTURE2D)
GLEXT(BindRenderBuffer,BINDRENDERBUFFER)
GLEXT(RenderbufferStorage,RENDERBUFFERSTORAGE)
GLEXT(GenRenderbuffers,GENRENDERBUFFERS)
GLEXT(BindRenderbuffer,BINDRENDERBUFFER)
GLEXT(FramebufferRenderbuffer,FRAMEBUFFERRENDERBUFFER)
GLEXT(GenerateMipmap,GENERATEMIPMAP)
GLARB(BindBuffer ,BINDBUFFER,)
GLARB(GenBuffers ,GENBUFFERS )
GLARB(DeleteBuffers,DELETEBUFFERS)
GLARB(BufferData ,BUFFERDATA )
GLARB(BufferSubData,BUFFERSUBDATA)
GLARB(MapBuffer ,MAPBUFFER )
GLARB(UnmapBuffer ,UNMAPBUFFER )
GLARB(TexBuffer ,TEXBUFFER )
GLEXT(NamedBufferStorage,NAMEDBUFFERSTORAGE)
GLE(BufferStorage,BUFFERSTORAGE)
GLE(GetStringi,GETSTRINGI)

View File

1103
external/stb/stb/tests/caveview/stb_gl.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,504 @@
// stb_glprog v0.02 public domain functions to reduce GLSL boilerplate
// http://nothings.org/stb/stb_glprog.h especially with GL1 + ARB extensions
//
// Following defines *before* including have following effects:
//
// STB_GLPROG_IMPLEMENTATION
// creates the implementation
//
// STB_GLPROG_STATIC
// forces the implementation to be static (private to file that creates it)
//
// STB_GLPROG_ARB
// uses ARB extension names for GLSL functions and enumerants instead of core names
//
// STB_GLPROG_ARB_DEFINE_EXTENSIONS
// instantiates function pointers needed, static to implementing file
// to avoid collisions (but will collide if implementing file also
// defines any; best to isolate this to its own file in this case).
// This will try to automatically #include glext.h, but if it's not
// in the default include directories you'll need to include it
// yourself and define the next macro.
//
// STB_GLPROG_SUPPRESS_GLEXT_INCLUDE
// disables the automatic #include of glext.h which is normally
// forced by STB_GLPROG_ARB_DEFINE_EXTENSIONS
//
// So, e.g., sample usage on an old Windows compiler:
//
// #define STB_GLPROG_IMPLEMENTATION
// #define STB_GLPROG_ARB_DEFINE_EXTENSIONS
// #include <windows.h>
// #include "gl/gl.h"
// #include "stb_glprog.h"
//
// Note though that the header-file version of this (when you don't define
// STB_GLPROG_IMPLEMENTATION) still uses GLint and such, so you basically
// can only include it in places where you're already including GL, especially
// on Windows where including "gl.h" requires (some of) "windows.h".
//
// See following comment blocks for function documentation.
//
// Version history:
// 2013-12-08 v0.02 slightly simplified API and reduced GL resource usage (@rygorous)
// 2013-12-08 v0.01 initial release
// header file section starts here
#if !defined(INCLUDE_STB_GLPROG_H)
#define INCLUDE_STB_GLPROG_H
#ifndef STB_GLPROG_STATIC
#ifdef __cplusplus
extern "C" {
#endif
//////////////////////////////////////////////////////////////////////////////
///////////// SHADER CREATION
/// EASY API
extern GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen);
// This function returns a compiled program or 0 if there's an error.
// To free the created program, call stbgl_delete_program.
//
// stbgl_create_program(
// char **vertex_source, // NULL or one or more strings with the vertex shader source, with a final NULL
// char **frag_source, // NULL or one or more strings with the fragment shader source, with a final NULL
// char **binds, // NULL or zero or more strings with attribute bind names, with a final NULL
// char *error, // output location where compile error message is placed
// int error_buflen) // length of error output buffer
//
// Returns a GLuint with the GL program object handle.
//
// If an individual bind string is "", no name is bound to that slot (this
// allows you to create binds that aren't continuous integers starting at 0).
//
// If the vertex shader is NULL, then fixed-function vertex pipeline
// is used, if that's legal in your version of GL.
//
// If the fragment shader is NULL, then fixed-function fragment pipeline
// is used, if that's legal in your version of GL.
extern void stgbl_delete_program(GLuint program);
// deletes a program created by stbgl_create_program or stbgl_link_program
/// FLEXIBLE API
extern GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen);
// compiles a shader. returns the shader on success or 0 on failure.
//
// type either: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
// or GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB
// or STBGL_VERTEX_SHADER or STBGL_FRAGMENT_SHADER
// sources array of strings containing the shader source
// num_sources number of string in sources, or -1 meaning sources is NULL-terminated
// error string to output compiler error to
// error_buflen length of error buffer in chars
extern GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen);
// links a shader. returns the linked program on success or 0 on failure.
//
// vertex_shader a compiled vertex shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
// fragment_shader a compiled fragment shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
//
extern void stbgl_delete_shader(GLuint shader);
// deletes a shader created by stbgl_compile_shader
///////////// RENDERING WITH SHADERS
extern GLint stbgl_find_uniform(GLuint prog, char *uniform);
extern void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms);
// Given the locations array that is num_uniforms long, fills out
// the locations of each of those uniforms for the specified program.
// If num_uniforms is -1, then uniforms[] must be NULL-terminated
// the following functions just wrap the difference in naming between GL2+ and ARB,
// so you don't need them unless you're using both ARB and GL2+ in the same codebase,
// or you're relying on this lib to provide the extensions
extern void stbglUseProgram(GLuint program);
extern void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
extern void stbglEnableVertexAttribArray(GLuint index);
extern void stbglDisableVertexAttribArray(GLuint index);
extern void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v);
extern void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v);
extern void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v);
extern void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v);
extern void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v);
extern void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v);
extern void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v);
extern void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v);
extern void stbglUniform1f(GLint loc, float v0);
extern void stbglUniform2f(GLint loc, float v0, float v1);
extern void stbglUniform3f(GLint loc, float v0, float v1, float v2);
extern void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3);
extern void stbglUniform1i(GLint loc, GLint v0);
extern void stbglUniform2i(GLint loc, GLint v0, GLint v1);
extern void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2);
extern void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3);
////////////// END OF FUNCTIONS
//////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif
#endif // STB_GLPROG_STATIC
#ifdef STB_GLPROG_ARB
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB
#else
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER
#endif
#endif // INCLUDE_STB_GLPROG_H
///////// header file section ends here
#ifdef STB_GLPROG_IMPLEMENTATION
#include <string.h> // strncpy
#ifdef STB_GLPROG_STATIC
#define STB_GLPROG_DECLARE static
#else
#define STB_GLPROG_DECLARE extern
#endif
// check if user wants this file to define the GL extensions itself
#ifdef STB_GLPROG_ARB_DEFINE_EXTENSIONS
#define STB_GLPROG_ARB // make sure later code uses the extensions
#ifndef STB_GLPROG_SUPPRESS_GLEXT_INCLUDE
#include "glext.h"
#endif
#define STB_GLPROG_EXTENSIONS \
STB_GLPROG_FUNC(ATTACHOBJECT , AttachObject ) \
STB_GLPROG_FUNC(BINDATTRIBLOCATION , BindAttribLocation ) \
STB_GLPROG_FUNC(COMPILESHADER , CompileShader ) \
STB_GLPROG_FUNC(CREATEPROGRAMOBJECT , CreateProgramObject ) \
STB_GLPROG_FUNC(CREATESHADEROBJECT , CreateShaderObject ) \
STB_GLPROG_FUNC(DELETEOBJECT , DeleteObject ) \
STB_GLPROG_FUNC(DETACHOBJECT , DetachObject ) \
STB_GLPROG_FUNC(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray) \
STB_GLPROG_FUNC(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray ) \
STB_GLPROG_FUNC(GETATTACHEDOBJECTS , GetAttachedObjects ) \
STB_GLPROG_FUNC(GETOBJECTPARAMETERIV, GetObjectParameteriv) \
STB_GLPROG_FUNC(GETINFOLOG , GetInfoLog ) \
STB_GLPROG_FUNC(GETUNIFORMLOCATION , GetUniformLocation ) \
STB_GLPROG_FUNC(LINKPROGRAM , LinkProgram ) \
STB_GLPROG_FUNC(SHADERSOURCE , ShaderSource ) \
STB_GLPROG_FUNC(UNIFORM1F , Uniform1f ) \
STB_GLPROG_FUNC(UNIFORM2F , Uniform2f ) \
STB_GLPROG_FUNC(UNIFORM3F , Uniform3f ) \
STB_GLPROG_FUNC(UNIFORM4F , Uniform4f ) \
STB_GLPROG_FUNC(UNIFORM1I , Uniform1i ) \
STB_GLPROG_FUNC(UNIFORM2I , Uniform2i ) \
STB_GLPROG_FUNC(UNIFORM3I , Uniform3i ) \
STB_GLPROG_FUNC(UNIFORM4I , Uniform4i ) \
STB_GLPROG_FUNC(UNIFORM1FV , Uniform1fv ) \
STB_GLPROG_FUNC(UNIFORM2FV , Uniform2fv ) \
STB_GLPROG_FUNC(UNIFORM3FV , Uniform3fv ) \
STB_GLPROG_FUNC(UNIFORM4FV , Uniform4fv ) \
STB_GLPROG_FUNC(UNIFORM1IV , Uniform1iv ) \
STB_GLPROG_FUNC(UNIFORM2IV , Uniform2iv ) \
STB_GLPROG_FUNC(UNIFORM3IV , Uniform3iv ) \
STB_GLPROG_FUNC(UNIFORM4IV , Uniform4iv ) \
STB_GLPROG_FUNC(USEPROGRAMOBJECT , UseProgramObject ) \
STB_GLPROG_FUNC(VERTEXATTRIBPOINTER , VertexAttribPointer )
// define the static function pointers
#define STB_GLPROG_FUNC(x,y) static PFNGL##x##ARBPROC gl##y##ARB;
STB_GLPROG_EXTENSIONS
#undef STB_GLPROG_FUNC
// define the GetProcAddress
#ifdef _WIN32
#ifndef WINGDIAPI
#ifndef STB__HAS_WGLPROC
typedef int (__stdcall *stbgl__voidfunc)(void);
static __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *);
#endif
#endif
#define STBGL__GET_FUNC(x) wglGetProcAddress(x)
#else
#error "need to define how this platform gets extensions"
#endif
// create a function that fills out the function pointers
static void stb_glprog_init(void)
{
static int initialized = 0; // not thread safe!
if (initialized) return;
#define STB_GLPROG_FUNC(x,y) gl##y##ARB = (PFNGL##x##ARBPROC) STBGL__GET_FUNC("gl" #y "ARB");
STB_GLPROG_EXTENSIONS
#undef STB_GLPROG_FUNC
}
#undef STB_GLPROG_EXTENSIONS
#else
static void stb_glprog_init(void)
{
}
#endif
// define generic names for many of the gl functions or extensions for later use;
// note that in some cases there are two functions in core and one function in ARB
#ifdef STB_GLPROG_ARB
#define stbglCreateShader glCreateShaderObjectARB
#define stbglDeleteShader glDeleteObjectARB
#define stbglAttachShader glAttachObjectARB
#define stbglDetachShader glDetachObjectARB
#define stbglShaderSource glShaderSourceARB
#define stbglCompileShader glCompileShaderARB
#define stbglGetShaderStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_COMPILE_STATUS_ARB, b)
#define stbglGetShaderInfoLog glGetInfoLogARB
#define stbglCreateProgram glCreateProgramObjectARB
#define stbglDeleteProgram glDeleteObjectARB
#define stbglLinkProgram glLinkProgramARB
#define stbglGetProgramStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_LINK_STATUS_ARB, b)
#define stbglGetProgramInfoLog glGetInfoLogARB
#define stbglGetAttachedShaders glGetAttachedObjectsARB
#define stbglBindAttribLocation glBindAttribLocationARB
#define stbglGetUniformLocation glGetUniformLocationARB
#define stbgl_UseProgram glUseProgramObjectARB
#else
#define stbglCreateShader glCreateShader
#define stbglDeleteShader glDeleteShader
#define stbglAttachShader glAttachShader
#define stbglDetachShader glDetachShader
#define stbglShaderSource glShaderSource
#define stbglCompileShader glCompileShader
#define stbglGetShaderStatus(a,b) glGetShaderiv(a, GL_COMPILE_STATUS, b)
#define stbglGetShaderInfoLog glGetShaderInfoLog
#define stbglCreateProgram glCreateProgram
#define stbglDeleteProgram glDeleteProgram
#define stbglLinkProgram glLinkProgram
#define stbglGetProgramStatus(a,b) glGetProgramiv(a, GL_LINK_STATUS, b)
#define stbglGetProgramInfoLog glGetProgramInfoLog
#define stbglGetAttachedShaders glGetAttachedShaders
#define stbglBindAttribLocation glBindAttribLocation
#define stbglGetUniformLocation glGetUniformLocation
#define stbgl_UseProgram glUseProgram
#endif
// perform a safe strcat of 3 strings, given that we can't rely on portable snprintf
// if you need to break on error, this is the best place to place a breakpoint
static void stb_glprog_error(char *error, int error_buflen, char *str1, char *str2, char *str3)
{
int n = strlen(str1);
strncpy(error, str1, error_buflen);
if (n < error_buflen && str2) {
strncpy(error+n, str2, error_buflen - n);
n += strlen(str2);
if (n < error_buflen && str3) {
strncpy(error+n, str3, error_buflen - n);
}
}
error[error_buflen-1] = 0;
}
STB_GLPROG_DECLARE GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen)
{
char *typename = (type == STBGL_VERTEX_SHADER ? "vertex" : "fragment");
int len;
GLint result;
GLuint shader;
// initialize the extensions if we haven't already
stb_glprog_init();
// allocate
shader = stbglCreateShader(type);
if (!shader) {
stb_glprog_error(error, error_buflen, "Couldn't allocate shader object in stbgl_compile_shader for ", typename, NULL);
return 0;
}
// compile
// if num_sources is negative, assume source is NULL-terminated and count the non-NULL ones
if (num_sources < 0)
for (num_sources = 0; sources[num_sources] != NULL; ++num_sources)
;
stbglShaderSource(shader, num_sources, sources, NULL);
stbglCompileShader(shader);
stbglGetShaderStatus(shader, &result);
if (result)
return shader;
// errors
stb_glprog_error(error, error_buflen, "Compile error for ", typename, " shader: ");
len = strlen(error);
if (len < error_buflen)
stbglGetShaderInfoLog(shader, error_buflen-len, NULL, error+len);
stbglDeleteShader(shader);
return 0;
}
STB_GLPROG_DECLARE GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen)
{
int len;
GLint result;
// allocate
GLuint prog = stbglCreateProgram();
if (!prog) {
stb_glprog_error(error, error_buflen, "Couldn't allocate program object in stbgl_link_program", NULL, NULL);
return 0;
}
// attach
if (vertex_shader)
stbglAttachShader(prog, vertex_shader);
if (fragment_shader)
stbglAttachShader(prog, fragment_shader);
// attribute binds
if (binds) {
int i;
// if num_binds is negative, then it is NULL terminated
if (num_binds < 0)
for (num_binds=0; binds[num_binds]; ++num_binds)
;
for (i=0; i < num_binds; ++i)
if (binds[i] && binds[i][0]) // empty binds can be NULL or ""
stbglBindAttribLocation(prog, i, binds[i]);
}
// link
stbglLinkProgram(prog);
// detach
if (vertex_shader)
stbglDetachShader(prog, vertex_shader);
if (fragment_shader)
stbglDetachShader(prog, fragment_shader);
// errors
stbglGetProgramStatus(prog, &result);
if (result)
return prog;
stb_glprog_error(error, error_buflen, "Link error: ", NULL, NULL);
len = strlen(error);
if (len < error_buflen)
stbglGetProgramInfoLog(prog, error_buflen-len, NULL, error+len);
stbglDeleteProgram(prog);
return 0;
}
STB_GLPROG_DECLARE GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen)
{
GLuint vertex, fragment, prog=0;
vertex = stbgl_compile_shader(STBGL_VERTEX_SHADER, vertex_source, -1, error, error_buflen);
if (vertex) {
fragment = stbgl_compile_shader(STBGL_FRAGMENT_SHADER, frag_source, -1, error, error_buflen);
if (fragment)
prog = stbgl_link_program(vertex, fragment, binds, -1, error, error_buflen);
if (fragment)
stbglDeleteShader(fragment);
stbglDeleteShader(vertex);
}
return prog;
}
STB_GLPROG_DECLARE void stbgl_delete_shader(GLuint shader)
{
stbglDeleteShader(shader);
}
STB_GLPROG_DECLARE void stgbl_delete_program(GLuint program)
{
stbglDeleteProgram(program);
}
GLint stbgl_find_uniform(GLuint prog, char *uniform)
{
return stbglGetUniformLocation(prog, uniform);
}
STB_GLPROG_DECLARE void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms)
{
int i;
if (num_uniforms < 0)
num_uniforms = 999999;
for (i=0; i < num_uniforms && uniforms[i]; ++i)
locations[i] = stbglGetUniformLocation(prog, uniforms[i]);
}
STB_GLPROG_DECLARE void stbglUseProgram(GLuint program)
{
stbgl_UseProgram(program);
}
#ifdef STB_GLPROG_ARB
#define STBGL_ARBIFY(name) name##ARB
#else
#define STBGL_ARBIFY(name) name
#endif
STB_GLPROG_DECLARE void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
{
STBGL_ARBIFY(glVertexAttribPointer)(index, size, type, normalized, stride, pointer);
}
STB_GLPROG_DECLARE void stbglEnableVertexAttribArray (GLuint index) { STBGL_ARBIFY(glEnableVertexAttribArray )(index); }
STB_GLPROG_DECLARE void stbglDisableVertexAttribArray(GLuint index) { STBGL_ARBIFY(glDisableVertexAttribArray)(index); }
STB_GLPROG_DECLARE void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform1fv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform2fv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform3fv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform4fv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform1iv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform2iv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform3iv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform4iv)(loc,count,v); }
STB_GLPROG_DECLARE void stbglUniform1f(GLint loc, float v0)
{ STBGL_ARBIFY(glUniform1f)(loc,v0); }
STB_GLPROG_DECLARE void stbglUniform2f(GLint loc, float v0, float v1)
{ STBGL_ARBIFY(glUniform2f)(loc,v0,v1); }
STB_GLPROG_DECLARE void stbglUniform3f(GLint loc, float v0, float v1, float v2)
{ STBGL_ARBIFY(glUniform3f)(loc,v0,v1,v2); }
STB_GLPROG_DECLARE void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3)
{ STBGL_ARBIFY(glUniform4f)(loc,v0,v1,v2,v3); }
STB_GLPROG_DECLARE void stbglUniform1i(GLint loc, GLint v0)
{ STBGL_ARBIFY(glUniform1i)(loc,v0); }
STB_GLPROG_DECLARE void stbglUniform2i(GLint loc, GLint v0, GLint v1)
{ STBGL_ARBIFY(glUniform2i)(loc,v0,v1); }
STB_GLPROG_DECLARE void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2)
{ STBGL_ARBIFY(glUniform3i)(loc,v0,v1,v2); }
STB_GLPROG_DECLARE void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3)
{ STBGL_ARBIFY(glUniform4i)(loc,v0,v1,v2,v3); }
#endif

View File

@ -0,0 +1,224 @@
/*
SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98
The WinMain function -- calls your program's main() function
*/
#include "SDL_config.h"
#ifdef __WIN32__
//#include "../../core/windows/SDL_windows.h"
/* Include this so we define UNICODE properly */
#if defined(__WIN32__)
#define WIN32_LEAN_AND_MEAN
#define STRICT
#ifndef UNICODE
#define UNICODE 1
#endif
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */
#endif
#include <windows.h>
/* Routines to convert from UTF8 to native Windows text */
#if UNICODE
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
#else
/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1))
#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1)
#endif
/* Sets an error message based on a given HRESULT */
extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
/* Sets an error message based on GetLastError(). Always return -1. */
extern int WIN_SetError(const char *prefix);
/* Wrap up the oddities of CoInitialize() into a common function. */
extern HRESULT WIN_CoInitialize(void);
extern void WIN_CoUninitialize(void);
/* Returns SDL_TRUE if we're running on Windows Vista and newer */
extern BOOL WIN_IsWindowsVistaOrGreater();
#include <stdio.h>
#include <stdlib.h>
/* Include the SDL main definition header */
#include "SDL.h"
#include "SDL_main.h"
#ifdef main
# undef main
#endif /* main */
static void
UnEscapeQuotes(char *arg)
{
char *last = NULL;
while (*arg) {
if (*arg == '"' && (last != NULL && *last == '\\')) {
char *c_curr = arg;
char *c_last = last;
while (*c_curr) {
*c_last = *c_curr;
c_last = c_curr;
c_curr++;
}
*c_last = '\0';
}
last = arg;
arg++;
}
}
/* Parse a command line buffer into arguments */
static int
ParseCommandLine(char *cmdline, char **argv)
{
char *bufp;
char *lastp = NULL;
int argc, last_argc;
argc = last_argc = 0;
for (bufp = cmdline; *bufp;) {
/* Skip leading whitespace */
while (SDL_isspace(*bufp)) {
++bufp;
}
/* Skip over argument */
if (*bufp == '"') {
++bufp;
if (*bufp) {
if (argv) {
argv[argc] = bufp;
}
++argc;
}
/* Skip over word */
lastp = bufp;
while (*bufp && (*bufp != '"' || *lastp == '\\')) {
lastp = bufp;
++bufp;
}
} else {
if (*bufp) {
if (argv) {
argv[argc] = bufp;
}
++argc;
}
/* Skip over word */
while (*bufp && !SDL_isspace(*bufp)) {
++bufp;
}
}
if (*bufp) {
if (argv) {
*bufp = '\0';
}
++bufp;
}
/* Strip out \ from \" sequences */
if (argv && last_argc != argc) {
UnEscapeQuotes(argv[last_argc]);
}
last_argc = argc;
}
if (argv) {
argv[argc] = NULL;
}
return (argc);
}
/* Show an error message */
static void
ShowError(const char *title, const char *message)
{
/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
#ifdef USE_MESSAGEBOX
MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
#else
fprintf(stderr, "%s: %s\n", title, message);
#endif
}
/* Pop up an out of memory message, returns to Windows */
static BOOL
OutOfMemory(void)
{
ShowError("Fatal Error", "Out of memory - aborting");
return FALSE;
}
#if defined(_MSC_VER)
/* The VC++ compiler needs main defined */
#define console_main main
#endif
/* This is where execution begins [console apps] */
int
console_main(int argc, char *argv[])
{
int status;
SDL_SetMainReady();
/* Run the application main() code */
status = SDL_main(argc, argv);
/* Exit cleanly, calling atexit() functions */
exit(status);
/* Hush little compiler, don't you cry... */
return 0;
}
/* This is where execution begins [windowed apps] */
int WINAPI
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
char **argv;
int argc;
char *cmdline;
/* Grab the command line */
TCHAR *text = GetCommandLine();
#if UNICODE
cmdline = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)(text), (SDL_wcslen(text)+1)*sizeof(WCHAR));
#else
cmdline = SDL_strdup(text);
#endif
if (cmdline == NULL) {
return OutOfMemory();
}
/* Parse it into argv and argc */
argc = ParseCommandLine(cmdline, NULL);
argv = SDL_stack_alloc(char *, argc + 1);
if (argv == NULL) {
return OutOfMemory();
}
ParseCommandLine(cmdline, argv);
/* Run the main program */
console_main(argc, argv);
SDL_stack_free(argv);
SDL_free(cmdline);
/* Hush little compiler, don't you cry... */
return 0;
}
#endif /* __WIN32__ */
/* vi: set ts=4 sw=4 expandtab: */

54
external/stb/stb/tests/fuzz_main.c vendored Normal file
View File

@ -0,0 +1,54 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/* fuzz target entry point, works without libFuzzer */
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
int main(int argc, char **argv)
{
FILE *f;
char *buf = NULL;
long siz_buf;
if(argc < 2)
{
fprintf(stderr, "no input file\n");
goto err;
}
f = fopen(argv[1], "rb");
if(f == NULL)
{
fprintf(stderr, "error opening input file %s\n", argv[1]);
goto err;
}
fseek(f, 0, SEEK_END);
siz_buf = ftell(f);
rewind(f);
if(siz_buf < 1) goto err;
buf = (char*)malloc((size_t)siz_buf);
if(buf == NULL)
{
fprintf(stderr, "malloc() failed\n");
goto err;
}
if(fread(buf, (size_t)siz_buf, 1, f) != 1)
{
fprintf(stderr, "fread() failed\n");
goto err;
}
(void)LLVMFuzzerTestOneInput((uint8_t*)buf, (size_t)siz_buf);
err:
free(buf);
return 0;
}

View File

@ -0,0 +1,363 @@
#define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
#define STBCC_GRID_COUNT_X_LOG2 10
#define STBCC_GRID_COUNT_Y_LOG2 10
#include "stb_connected_components.h"
#ifdef GRID_TEST
#include <windows.h>
#include <stdio.h>
#include <direct.h>
//#define STB_DEFINE
#include "stb.h"
//#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
//#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
typedef struct
{
uint16 x,y;
} point;
point leader[1024][1024];
uint32 color[1024][1024];
point find(int x, int y)
{
point p,q;
p = leader[y][x];
if (p.x == x && p.y == y)
return p;
q = find(p.x, p.y);
leader[y][x] = q;
return q;
}
void onion(int x1, int y1, int x2, int y2)
{
point p = find(x1,y1);
point q = find(x2,y2);
if (p.x == q.x && p.y == q.y)
return;
leader[p.y][p.x] = q;
}
void reference(uint8 *map, int w, int h)
{
int i,j;
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
leader[j][i].x = i;
leader[j][i].y = j;
}
}
for (j=1; j < h-1; ++j) {
for (i=1; i < w-1; ++i) {
if (map[j*w+i] == 255) {
if (map[(j+1)*w+i] == 255) onion(i,j, i,j+1);
if (map[(j)*w+i+1] == 255) onion(i,j, i+1,j);
}
}
}
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
uint32 c = 0xff000000;
if (leader[j][i].x == i && leader[j][i].y == j) {
if (map[j*w+i] == 255)
c = stb_randLCG() | 0xff404040;
}
color[j][i] = c;
}
}
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
if (leader[j][i].x != i || leader[j][i].y != j) {
point p = find(i,j);
color[j][i] = color[p.y][p.x];
}
}
}
}
void write_map(stbcc_grid *g, int w, int h, char *filename)
{
int i,j;
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i) {
unsigned int c;
c = stbcc_get_unique_id(g,i,j);
c = stb_rehash_improved(c)&0xffffff;
if (c == STBCC_NULL_UNIQUE_ID)
c = 0xff000000;
else
c = (~c)^0x555555;
if (i % 32 == 0 || j %32 == 0) {
int r = (c >> 16) & 255;
int g = (c >> 8) & 255;
int b = c & 255;
r = (r+130)/2;
g = (g+130)/2;
b = (b+130)/2;
c = 0xff000000 + (r<<16) + (g<<8) + b;
}
color[j][i] = c;
}
}
stbi_write_png(filename, w, h, 4, color, 4*w);
}
void test_connected(stbcc_grid *g)
{
int n = stbcc_query_grid_node_connection(g, 512, 90, 512, 871);
//printf("%d ", n);
}
static char *message;
LARGE_INTEGER start;
void start_timer(char *s)
{
message = s;
QueryPerformanceCounter(&start);
}
void end_timer(void)
{
LARGE_INTEGER end, freq;
double tm;
QueryPerformanceCounter(&end);
QueryPerformanceFrequency(&freq);
tm = (end.QuadPart - start.QuadPart) / (double) freq.QuadPart;
printf("%6.4lf ms: %s\n", tm * 1000, message);
}
extern void quicktest(void);
int loc[5000][2];
int main(int argc, char **argv)
{
stbcc_grid *g;
int w,h, i,j,k=0, count=0, r;
uint8 *map = stbi_load("data/map_03.png", &w, &h, 0, 1);
assert(map);
quicktest();
for (j=0; j < h; ++j)
for (i=0; i < w; ++i)
map[j*w+i] = ~map[j*w+i];
for (i=0; i < w; ++i)
for (j=0; j < h; ++j)
//map[j*w+i] = (((i+1) ^ (j+1)) >> 1) & 1 ? 255 : 0;
map[j*w+i] = stb_max(abs(i-w/2),abs(j-h/2)) & 1 ? 255 : 0;
//map[j*w+i] = (((i ^ j) >> 5) ^ (i ^ j)) & 1 ? 255 : 0;
//map[j*w+i] = stb_rand() & 1 ? 255 : 0;
#if 1
for (i=0; i < 100000; ++i)
map[(stb_rand()%h)*w + stb_rand()%w] ^= 255;
#endif
_mkdir("tests/output/stbcc");
stbi_write_png("tests/output/stbcc/reference.png", w, h, 1, map, 0);
//reference(map, w, h);
g = malloc(stbcc_grid_sizeof());
printf("Size: %d\n", stbcc_grid_sizeof());
#if 0
memset(map, 0, w*h);
stbcc_init_grid(g, map, w, h);
{
int n;
char **s = stb_stringfile("c:/x/clockwork_update.txt", &n);
write_map(g, w, h, "tests/output/stbcc/base.png");
for (i=1; i < n; i += 1) {
int x,y,t;
sscanf(s[i], "%d %d %d", &x, &y, &t);
if (i == 571678)
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_good.png", i));
stbcc_update_grid(g, x, y, t);
if (i == 571678)
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_bad.png", i));
//if (i > 571648 && i <= 571712)
//write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i));
}
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i-1));
}
return 0;
#endif
start_timer("init");
stbcc_init_grid(g, map, w, h);
end_timer();
//g = stb_file("c:/x/clockwork_path.bin", 0);
write_map(g, w, h, "tests/output/stbcc/base.png");
for (i=0; i < 5000;) {
loc[i][0] = stb_rand() % w;
loc[i][1] = stb_rand() % h;
if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
++i;
}
r = 0;
start_timer("reachable");
for (i=0; i < 2000; ++i) {
for (j=0; j < 2000; ++j) {
int x1 = loc[i][0], y1 = loc[i][1];
int x2 = loc[2000+j][0], y2 = loc[2000+j][1];
r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
}
}
end_timer();
printf("%d reachable\n", r);
printf("Cluster size: %d,%d\n", STBCC__CLUSTER_SIZE_X, STBCC__CLUSTER_SIZE_Y);
#if 1
for (j=0; j < 10; ++j) {
for (i=0; i < 5000; ++i) {
loc[i][0] = stb_rand() % w;
loc[i][1] = stb_rand() % h;
}
start_timer("updating 2500");
for (i=0; i < 2500; ++i) {
if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
stbcc_update_grid(g, loc[i][0], loc[i][1], 1);
else
stbcc_update_grid(g, loc[i][0], loc[i][1], 0);
}
end_timer();
write_map(g, w, h, stb_sprintf("tests/output/stbcc/update_random_%d.png", j*i));
}
#endif
#if 0
start_timer("removing");
count = 0;
for (i=0; i < 1800; ++i) {
int x,y,a,b;
x = stb_rand() % (w-32);
y = stb_rand() % (h-32);
if (i & 1) {
for (a=0; a < 32; ++a)
for (b=0; b < 1; ++b)
if (stbcc_query_grid_open(g, x+a, y+b)) {
stbcc_update_grid(g, x+a, y+b, 1);
++count;
}
} else {
for (a=0; a < 1; ++a)
for (b=0; b < 32; ++b)
if (stbcc_query_grid_open(g, x+a, y+b)) {
stbcc_update_grid(g, x+a, y+b, 1);
++count;
}
}
//if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i+1));
}
end_timer();
printf("Removed %d grid spaces\n", count);
write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i));
r = 0;
start_timer("reachable");
for (i=0; i < 1000; ++i) {
for (j=0; j < 1000; ++j) {
int x1 = loc[i][0], y1 = loc[i][1];
int x2 = loc[j][0], y2 = loc[j][1];
r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
}
}
end_timer();
printf("%d reachable\n", r);
start_timer("adding");
count = 0;
for (i=0; i < 1800; ++i) {
int x,y,a,b;
x = stb_rand() % (w-32);
y = stb_rand() % (h-32);
if (i & 1) {
for (a=0; a < 32; ++a)
for (b=0; b < 1; ++b)
if (!stbcc_query_grid_open(g, x+a, y+b)) {
stbcc_update_grid(g, x+a, y+b, 0);
++count;
}
} else {
for (a=0; a < 1; ++a)
for (b=0; b < 32; ++b)
if (!stbcc_query_grid_open(g, x+a, y+b)) {
stbcc_update_grid(g, x+a, y+b, 0);
++count;
}
}
//if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i+1));
}
end_timer();
write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i));
printf("Added %d grid spaces\n", count);
#endif
#if 0 // for map_02.png
start_timer("process");
for (k=0; k < 20; ++k) {
for (j=0; j < h; ++j) {
int any=0;
for (i=0; i < w; ++i) {
if (map[j*w+i] > 10 && map[j*w+i] < 250) {
//start_timer(stb_sprintf("open %d,%d", i,j));
stbcc_update_grid(g, i, j, 0);
test_connected(g);
//end_timer();
any = 1;
}
}
if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_row_%04d.png", j));
}
for (j=0; j < h; ++j) {
int any=0;
for (i=0; i < w; ++i) {
if (map[j*w+i] > 10 && map[j*w+i] < 250) {
//start_timer(stb_sprintf("close %d,%d", i,j));
stbcc_update_grid(g, i, j, 1);
test_connected(g);
//end_timer();
any = 1;
}
}
if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_row_%04d.png", j));
}
}
end_timer();
#endif
return 0;
}
#endif

95
external/stb/stb/tests/herringbone.dsp vendored Normal file
View File

@ -0,0 +1,95 @@
# Microsoft Developer Studio Project File - Name="herringbone" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=herringbone - 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 "herringbone.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 "herringbone.mak" CFG="herringbone - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "herringbone - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "herringbone - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "herringbone - 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 "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# 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 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:console /machine:I386
# ADD 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 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:console /machine:I386
!ELSEIF "$(CFG)" == "herringbone - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "herringbone___Win32_Debug"
# PROP BASE Intermediate_Dir "herringbone___Win32_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 "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
# SUBTRACT CPP /YX
# 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 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:console /debug /machine:I386 /pdbtype:sept
# ADD 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 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:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "herringbone - Win32 Release"
# Name "herringbone - Win32 Debug"
# Begin Source File
SOURCE=.\herringbone_generator.c
# End Source File
# Begin Source File
SOURCE=..\stb_herringbone_wang_tile.h
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,87 @@
#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
#include "stb_herringbone_wang_tile.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// e 12 1 1 1 1 1 1 4 4
int main(int argc, char **argv)
{
stbhw_config c = { 0 };
int w,h, num_colors,i;
unsigned char *data;
if (argc == 1) goto usage;
if (argc < 3) goto error;
switch (argv[2][0]) {
case 'c':
if (argc < 8 || argc > 10)
goto error;
num_colors = 4;
c.is_corner = 1;
break;
case 'e':
if (argc < 10 || argc > 12)
goto error;
num_colors = 6;
c.is_corner = 0;
break;
default:
goto error;
}
c.short_side_len = atoi(argv[3]);
for (i=0; i < num_colors; ++i)
c.num_color[i] = atoi(argv[4+i]);
c.num_vary_x = 1;
c.num_vary_y = 1;
if (argc > 4+i)
c.num_vary_x = atoi(argv[4+i]);
if (argc > 5+i)
c.num_vary_y = atoi(argv[5+i]);
stbhw_get_template_size(&c, &w, &h);
data = (unsigned char *) malloc(w*h*3);
if (stbhw_make_template(&c, data, w, h, w*3))
stbi_write_png(argv[1], w, h, 3, data, w*3);
else
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
return 0;
error:
fputs("Invalid command-line arguments\n\n", stderr);
usage:
fputs("Usage (see source for corner & edge type definitions):\n\n", stderr);
fputs("herringbone_generator {outfile} c {sidelen} {c0} {c1} {c2} {c3} [{vx} {vy}]\n"
" {outfile} -- filename that template will be written to as PNG\n"
" {sidelen} -- length of short side of rectangle in pixels\n"
" {c0} -- number of colors for corner type 0\n"
" {c1} -- number of colors for corner type 1\n"
" {c2} -- number of colors for corner type 2\n"
" {c3} -- number of colors for corner type 3\n"
" {vx} -- number of color-duplicating variations horizontally in template\n"
" {vy} -- number of color-duplicating variations vertically in template\n"
"\n"
, stderr);
fputs("herringbone_generator {outfile} e {sidelen} {e0} {e1} {e2} {e3} {e4} {e5} [{vx} {vy}]\n"
" {outfile} -- filename that template will be written to as PNG\n"
" {sidelen} -- length of short side of rectangle in pixels\n"
" {e0} -- number of colors for edge type 0\n"
" {e1} -- number of colors for edge type 1\n"
" {e2} -- number of colors for edge type 2\n"
" {e3} -- number of colors for edge type 3\n"
" {e4} -- number of colors for edge type 4\n"
" {e5} -- number of colors for edge type 5\n"
" {vx} -- number of color-duplicating variations horizontally in template\n"
" {vy} -- number of color-duplicating variations vertically in template\n"
, stderr);
return 1;
}

View File

@ -0,0 +1,83 @@
#include <stdio.h>
#define STB_HBWANG_MAX_X 500
#define STB_HBWANG_MAX_Y 500
#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
#include "stb_herringbone_wang_tile.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
int main(int argc, char **argv)
{
if (argc < 5) {
fprintf(stderr, "Usage: herringbone_map {inputfile} {output-width} {output-height} {outputfile}\n");
return 1;
} else {
char *filename = argv[1];
int out_w = atoi(argv[2]);
int out_h = atoi(argv[3]);
char *outfile = argv[4];
unsigned char *pixels, *out_pixels;
stbhw_tileset ts;
int w,h;
pixels = stbi_load(filename, &w, &h, 0, 3);
if (pixels == 0) {
fprintf(stderr, "Couldn't open input file '%s'\n", filename);
exit(1);
}
if (!stbhw_build_tileset_from_image(&ts, pixels, w*3, w, h)) {
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
return 1;
}
free(pixels);
#ifdef DEBUG_OUTPUT
{
int i,j,k;
// add blue borders to top-left edges of the tiles
int hstride = (ts.short_side_len*2)*3;
int vstride = (ts.short_side_len )*3;
for (i=0; i < ts.num_h_tiles; ++i) {
unsigned char *pix = ts.h_tiles[i]->pixels;
for (j=0; j < ts.short_side_len*2; ++j)
for (k=0; k < 3; ++k)
pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5;
for (j=1; j < ts.short_side_len; ++j)
for (k=0; k < 3; ++k)
pix[j*hstride+k] = (pix[j*hstride+k]*0.5+100+k*75)/1.5;
}
for (i=0; i < ts.num_v_tiles; ++i) {
unsigned char *pix = ts.v_tiles[i]->pixels;
for (j=0; j < ts.short_side_len; ++j)
for (k=0; k < 3; ++k)
pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5;
for (j=1; j < ts.short_side_len*2; ++j)
for (k=0; k < 3; ++k)
pix[j*vstride+k] = (pix[j*vstride+k]*0.5+100+k*75)/1.5;
}
}
#endif
out_pixels = malloc(out_w * out_h * 3);
if (!stbhw_generate_image(&ts, NULL, out_pixels, out_w*3, out_w, out_h)) {
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
return 1;
}
stbi_write_png(argv[4], out_w, out_h, 3, out_pixels, out_w*3);
free(out_pixels);
stbhw_free_tileset(&ts);
return 0;
}
}

View File

@ -0,0 +1,94 @@
# Microsoft Developer Studio Project File - Name="herringbone_map" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=herringbone_map - 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 "herringbone_map.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 "herringbone_map.mak" CFG="herringbone_map - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "herringbone_map - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "herringbone_map - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "herringbone_map - 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 "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# 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 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:console /machine:I386
# ADD 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 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:console /machine:I386
!ELSEIF "$(CFG)" == "herringbone_map - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "herringbone_map___Win32_Debug"
# PROP BASE Intermediate_Dir "herringbone_map___Win32_Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
# SUBTRACT CPP /YX
# 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 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:console /debug /machine:I386 /pdbtype:sept
# ADD 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 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:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "herringbone_map - Win32 Release"
# Name "herringbone_map - Win32 Debug"
# Begin Source File
SOURCE=.\herringbone_map.c
# End Source File
# Begin Source File
SOURCE=..\stb_herringbone_wang_tile.h
# End Source File
# End Target
# End Project

173
external/stb/stb/tests/image_test.c vendored Normal file
View File

@ -0,0 +1,173 @@
#define STBI_WINDOWS_UTF8
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_DEFINE
#include "stb.h"
//#define PNGSUITE_PRIMARY
#if 0
void test_ycbcr(void)
{
STBI_SIMD_ALIGN(unsigned char, y[256]);
STBI_SIMD_ALIGN(unsigned char, cb[256]);
STBI_SIMD_ALIGN(unsigned char, cr[256]);
STBI_SIMD_ALIGN(unsigned char, out1[256][4]);
STBI_SIMD_ALIGN(unsigned char, out2[256][4]);
int i,j,k;
int count = 0, bigcount=0, total=0;
for (i=0; i < 256; ++i) {
for (j=0; j < 256; ++j) {
for (k=0; k < 256; ++k) {
y [k] = k;
cb[k] = j;
cr[k] = i;
}
stbi__YCbCr_to_RGB_row(out1[0], y, cb, cr, 256, 4);
stbi__YCbCr_to_RGB_sse2(out2[0], y, cb, cr, 256, 4);
for (k=0; k < 256; ++k) {
// inaccurate proxy for values outside of RGB cube
if (out1[k][0] == 0 || out1[k][1] == 0 || out1[k][2] == 0 || out1[k][0] == 255 || out1[k][1] == 255 || out1[k][2] == 255)
continue;
++total;
if (out1[k][0] != out2[k][0] || out1[k][1] != out2[k][1] || out1[k][2] != out2[k][2]) {
int dist1 = abs(out1[k][0] - out2[k][0]);
int dist2 = abs(out1[k][1] - out2[k][1]);
int dist3 = abs(out1[k][2] - out2[k][2]);
++count;
if (out1[k][1] > out2[k][1])
++bigcount;
}
}
}
printf("So far: %d (%d big) of %d\n", count, bigcount, total);
}
printf("Final: %d (%d big) of %d\n", count, bigcount, total);
}
#endif
float hdr_data[200][200][3];
void dummy_write(void *context, void *data, int len)
{
static char dummy[1024];
if (len > 1024) len = 1024;
memcpy(dummy, data, len);
}
extern void image_write_test(void);
int main(int argc, char **argv)
{
int w,h;
//test_ycbcr();
image_write_test();
#if 0
// test hdr asserts
for (h=0; h < 100; h += 2)
for (w=0; w < 200; ++w)
hdr_data[h][w][0] = (float) rand(),
hdr_data[h][w][1] = (float) rand(),
hdr_data[h][w][2] = (float) rand();
stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
#endif
if (argc > 1) {
int i, n;
for (i=1; i < argc; ++i) {
int res;
int w2,h2,n2;
unsigned char *data;
printf("%s\n", argv[i]);
res = stbi_info(argv[i], &w2, &h2, &n2);
data = stbi_load(argv[i], &w, &h, &n, 0); if (data) free(data); else printf("Failed &n\n");
data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
data = stbi_load(argv[i], &w, &h, 0, 1); if (data) free(data); else printf("Failed 1\n");
data = stbi_load(argv[i], &w, &h, 0, 2); if (data) free(data); else printf("Failed 2\n");
data = stbi_load(argv[i], &w, &h, 0, 3); if (data) free(data); else printf("Failed 3\n");
data = stbi_load(argv[i], &w, &h, &n, 4);
assert(data);
assert(w == w2 && h == h2 && n == n2);
assert(res);
if (data) {
char fname[512];
stb_splitpath(fname, argv[i], STB_FILE);
stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
stbi_write_bmp(stb_sprintf("output/%s.bmp", fname), w, h, 4, data);
stbi_write_tga(stb_sprintf("output/%s.tga", fname), w, h, 4, data);
stbi_write_png_to_func(dummy_write,0, w, h, 4, data, w*4);
stbi_write_bmp_to_func(dummy_write,0, w, h, 4, data);
stbi_write_tga_to_func(dummy_write,0, w, h, 4, data);
free(data);
} else
printf("FAILED 4\n");
}
} else {
int i;
#ifdef PNGSUITE_PRIMARY
char **files = stb_readdir_files("pngsuite/primary");
#else
char **files = stb_readdir_files("images");
#endif
for (i=0; i < stb_arr_len(files); ++i) {
int n;
char **failed = NULL;
unsigned char *data;
printf(".");
//printf("%s\n", files[i]);
data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
data = stbi_load(files[i], &w, &h, 0, 1); if (data) free(data); else stb_arr_push(failed, "1");
data = stbi_load(files[i], &w, &h, 0, 2); if (data) free(data); else stb_arr_push(failed, "2");
data = stbi_load(files[i], &w, &h, 0, 3); if (data) free(data); else stb_arr_push(failed, "3");
data = stbi_load(files[i], &w, &h, 0, 4); if (data) ; else stb_arr_push(failed, "4");
if (data) {
char fname[512];
#ifdef PNGSUITE_PRIMARY
int w2,h2;
unsigned char *data2;
stb_splitpath(fname, files[i], STB_FILE_EXT);
data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4);
if (!data2)
printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname);
else {
if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) {
int x,y,c;
if (w == w2 && h == h2)
for (y=0; y < h; ++y)
for (x=0; x < w; ++x)
for (c=0; c < 4; ++c)
assert(data[y*w*4+x*4+c] == data2[y*w*4+x*4+c]);
printf("FAILED: %s loaded but didn't match PRIMARY_check 32-bit version\n", files[i]);
}
free(data2);
}
#else
stb_splitpath(fname, files[i], STB_FILE);
stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
#endif
free(data);
}
if (failed) {
int j;
printf("FAILED: ");
for (j=0; j < stb_arr_len(failed); ++j)
printf("%s ", failed[j]);
printf(" -- %s\n", files[i]);
}
}
printf("Tested %d files.\n", i);
}
return 0;
}

103
external/stb/stb/tests/image_test.dsp vendored Normal file
View File

@ -0,0 +1,103 @@
# Microsoft Developer Studio Project File - Name="image_test" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
CFG=image_test - 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 "image_test.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 "image_test.mak" CFG="image_test - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "image_test - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "image_test - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "image_test - 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 "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /Zi /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
# SUBTRACT CPP /YX
# 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 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:console /machine:I386
# ADD 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 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:console /debug /machine:I386
!ELSEIF "$(CFG)" == "image_test - 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\image_test"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
# SUBTRACT CPP /YX
# 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 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:console /debug /machine:I386 /pdbtype:sept
# ADD 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 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:console /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "image_test - Win32 Release"
# Name "image_test - Win32 Debug"
# Begin Source File
SOURCE=.\image_test.c
# End Source File
# Begin Source File
SOURCE=.\image_write_test.c
# End Source File
# Begin Source File
SOURCE=..\stb_image.h
# End Source File
# Begin Source File
SOURCE=..\stb_image_write.h
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,60 @@
#ifdef __clang__
#define STBIWDEF static inline
#endif
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
// using an 'F' since it has no rotational symmetries, and 6x5
// because it's a small, atypical size likely to trigger edge cases.
//
// conveniently, it's also small enough to fully fit inside a typical
// directory listing thumbnail, which simplifies checking at a glance.
static const char img6x5_template[] =
".****."
".*...."
".***.."
".*...."
".*....";
void image_write_test(void)
{
// make a RGB version of the template image
// use red on blue to detect R<->B swaps
unsigned char img6x5_rgb[6*5*3];
float img6x5_rgbf[6*5*3];
int i;
for (i = 0; i < 6*5; i++) {
int on = img6x5_template[i] == '*';
img6x5_rgb[i*3 + 0] = on ? 255 : 0;
img6x5_rgb[i*3 + 1] = 0;
img6x5_rgb[i*3 + 2] = on ? 0 : 255;
img6x5_rgbf[i*3 + 0] = on ? 1.0f : 0.0f;
img6x5_rgbf[i*3 + 1] = 0.0f;
img6x5_rgbf[i*3 + 2] = on ? 0.0f : 1.0f;
}
stbi_write_png("output/wr6x5_regular.png", 6, 5, 3, img6x5_rgb, 6*3);
stbi_write_bmp("output/wr6x5_regular.bmp", 6, 5, 3, img6x5_rgb);
stbi_write_tga("output/wr6x5_regular.tga", 6, 5, 3, img6x5_rgb);
stbi_write_jpg("output/wr6x5_regular.jpg", 6, 5, 3, img6x5_rgb, 95);
stbi_write_hdr("output/wr6x5_regular.hdr", 6, 5, 3, img6x5_rgbf);
stbi_flip_vertically_on_write(1);
stbi_write_png("output/wr6x5_flip.png", 6, 5, 3, img6x5_rgb, 6*3);
stbi_write_bmp("output/wr6x5_flip.bmp", 6, 5, 3, img6x5_rgb);
stbi_write_tga("output/wr6x5_flip.tga", 6, 5, 3, img6x5_rgb);
stbi_write_jpg("output/wr6x5_flip.jpg", 6, 5, 3, img6x5_rgb, 95);
stbi_write_hdr("output/wr6x5_flip.hdr", 6, 5, 3, img6x5_rgbf);
}
#ifdef IWT_TEST
int main(int argc, char **argv)
{
image_write_test();
return 0;
}
#endif

29
external/stb/stb/tests/ossfuzz.sh vendored Executable file
View File

@ -0,0 +1,29 @@
#!/bin/bash -eu
# This script is meant to be run by
# https://github.com/google/oss-fuzz/blob/master/projects/stb/Dockerfile
$CXX $CXXFLAGS -std=c++11 -I. -DSTBI_ONLY_PNG \
$SRC/stb/tests/stbi_read_fuzzer.c \
-o $OUT/stb_png_read_fuzzer $LIB_FUZZING_ENGINE
$CXX $CXXFLAGS -std=c++11 -I. \
$SRC/stb/tests/stbi_read_fuzzer.c \
-o $OUT/stbi_read_fuzzer $LIB_FUZZING_ENGINE
find $SRC/stb/tests/pngsuite -name "*.png" | \
xargs zip $OUT/stb_png_read_fuzzer_seed_corpus.zip
cp $SRC/stb/tests/stb_png.dict $OUT/stb_png_read_fuzzer.dict
tar xvzf $SRC/stbi/jpg.tar.gz --directory $SRC/stb/tests
tar xvzf $SRC/stbi/gif.tar.gz --directory $SRC/stb/tests
unzip $SRC/stbi/bmp.zip -d $SRC/stb/tests
unzip $SRC/stbi/tga.zip -d $SRC/stb/tests
find $SRC/stb/tests -name "*.png" -o -name "*.jpg" -o -name "*.gif" \
-o -name "*.bmp" -o -name "*.tga" -o -name "*.TGA" \
-o -name "*.ppm" -o -name "*.pgm" \
| xargs zip $OUT/stbi_read_fuzzer_seed_corpus.zip
echo "" >> $SRC/stbi/gif.dict
cat $SRC/stbi/gif.dict $SRC/stb/tests/stb_png.dict > $OUT/stbi_read_fuzzer.dict

View File

@ -0,0 +1,94 @@
# Font character oversampling for rendering from atlas textures
TL,DR: Run oversample.exe on a windows machine to see the
benefits of oversampling. It will try to use arial.ttf from the
Windows font directory unless you type the name of a .ttf file as
a command-line argument.
## Benefits of oversampling
Oversampling is a mechanism for improving subpixel rendering of characters.
Improving subpixel has a few benefits:
* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning
* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally
* Vertically-oversampled text significantly reduces aliasing when text animates vertically
* Text oversampled in both directions significantly reduces aliasing when text rotates
## What text oversampling is
A common strategy for rendering text is to cache character bitmaps
and reuse them. For hinted characters, every instance of a given
character is always identical, so this works fine. However, stb_truetype
doesn't do hinting.
For anti-aliased characters, you can actually position the characters
with subpixel precision, and get different bitmaps based on that positioning
if you re-render the vector data.
However, if you simply cache a single version of the bitmap and
draw it at different subpixel positions with a GPU, you will get
either the exact same result (if you use point-sampling on the
texture) or linear filtering. Linear filtering will cause a sub-pixel
positioned bitmap to blur further, causing a visible de-sharpening
of the character. (And, since the character wasn't hinted, it was
already blurrier than a hinted one would be, and now it gets even
more blurry.)
You can avoid this by caching multiple variants of a character which
were rendered independently from the vector data. For example, you
might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel
horizontal offset, and always require characters to fall on integer
positions vertically.
When creating a texture atlas for use on GPUs, which support bilinear
filtering, there is a better approach than caching several independent
positions, which is to allow lerping between the versions to allow
finer subpixel positioning. You can achieve these by interleaving
each of the cached bitmaps, but this turns out to be mathematically
equivalent to a simpler operation: oversampling and prefiltering the
characters.
So, setting oversampling of 2x2 in stb_truetype is equivalent to caching
each character in 4 different variations, 1 for each subpixel position
in a 2x2 set.
An advantage of this formulation is that no changes are required to
the rendering code; the exact same quad-rendering code works, it just
uses different texture coordinates. (Note this does potentially increase
texture bandwidth for text rendering since we end up minifying the texture
without using mipmapping, but you probably are not going to be fill-bound
by your text rendering.)
## What about gamma?
Gamma-correction for fonts just doesn't work. This doesn't seem to make
much sense -- it's physically correct, it simulates what we'd see if you
shrunk a font down really far, right?
But you can play with it in the oversample.exe app. If you turn it on,
white-on-black fonts become too thick (i.e. they become too bright), and
black-on-white fonts become too thin (i.e. they are insufficiently dark). There is
no way to adjust the font's inherent thickness (i.e. by switching to
bold) to fix this for both; making the font thicker will make white
text worse, and making the font thinner will make black text worse.
Obviously you could use different fonts for light and dark cases, but
this doesn't seem like a very good way for fonts to work.
Multiple people who have experimented with this independently (me,
Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all
concluded that correct gamma-correction does not produce the best
results for fonts. Font rendering just generally looks better without
gamma correction (or possibly with some arbitrary power stuck in
there, but it's not really correcting for gamma at that point). Maybe
this is in part a product of how we're used to fonts being on screens
which has changed how we expect them to look (e.g. perhaps hinting
oversharpens them and prevents the real-world thinning you'd see in
a black-on-white text).
(AGG link on text rendering, including mention of gamma:
http://www.antigrain.com/research/font_rasterization/ )
Nevertheless, even if you turn on gamma-correction, you will find that
oversampling still helps in many cases for small fonts.

332
external/stb/stb/tests/oversample/main.c vendored Normal file
View File

@ -0,0 +1,332 @@
#pragma warning(disable:4244; disable:4305; disable:4018)
#include <assert.h>
#include <ctype.h>
#define STB_WINMAIN
#include "stb_wingraph.h"
#define STB_TRUETYPE_IMPLEMENTATION
#define STB_RECT_PACK_IMPLEMENTATION
#include "stb_rect_pack.h"
#include "stb_truetype.h"
#ifndef WINGDIAPI
#define CALLBACK __stdcall
#define WINGDIAPI __declspec(dllimport)
#define APIENTRY __stdcall
#endif
#include <gl/gl.h>
#include <gl/glu.h>
#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
#define SIZE_X 1024
#define SIZE_Y 768
stbtt_packedchar chardata[6][128];
int sx=SIZE_X, sy=SIZE_Y;
#define BITMAP_W 512
#define BITMAP_H 512
unsigned char temp_bitmap[BITMAP_W][BITMAP_H];
unsigned char ttf_buffer[1 << 25];
GLuint font_tex;
float scale[2] = { 24.0f, 14.0f };
int sf[6] = { 0,1,2, 0,1,2 };
void load_fonts(void)
{
stbtt_pack_context pc;
int i;
FILE *f;
char filename[256];
char *win = getenv("windir");
if (win == NULL) win = getenv("SystemRoot");
f = fopen(stb_wingraph_commandline, "rb");
if (!f) {
if (win == NULL)
sprintf(filename, "arial.ttf", win);
else
sprintf(filename, "%s/fonts/arial.ttf", win);
f = fopen(filename, "rb");
if (!f) exit(0);
}
fread(ttf_buffer, 1, 1<<25, f);
stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);
for (i=0; i < 2; ++i) {
stbtt_PackSetOversampling(&pc, 1, 1);
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32);
stbtt_PackSetOversampling(&pc, 2, 2);
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32);
stbtt_PackSetOversampling(&pc, 3, 1);
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32);
}
stbtt_PackEnd(&pc);
glGenTextures(1, &font_tex);
glBindTexture(GL_TEXTURE_2D, font_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
int black_on_white;
void draw_init(void)
{
glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glViewport(0,0,sx,sy);
if (black_on_white)
glClearColor(255,255,255,0);
else
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,sx,sy,0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1)
{
glTexCoord2f(s0,t0); glVertex2f(x0,y0);
glTexCoord2f(s1,t0); glVertex2f(x1,y0);
glTexCoord2f(s1,t1); glVertex2f(x1,y1);
glTexCoord2f(s0,t1); glVertex2f(x0,y1);
}
int integer_align;
void print(float x, float y, int font, char *text)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, font_tex);
glBegin(GL_QUADS);
while (*text) {
stbtt_aligned_quad q;
stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align);
drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1);
}
glEnd();
}
int font=3;
int translating;
int rotating=0;
int srgb=0;
float rotate_t, translate_t;
int show_tex;
void draw_world(void)
{
int sfont = sf[font];
float x = 20;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (black_on_white)
glColor3f(0,0,0);
else
glColor3f(1,1,1);
print(80, 30, sfont, "Controls:");
print(100, 60, sfont, "S: toggle font size");
print(100, 85, sfont, "O: toggle oversampling");
print(100,110, sfont, "T: toggle translation");
print(100,135, sfont, "R: toggle rotation");
print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)");
print(100,185, sfont, "G: toggle srgb gamma-correction");
if (black_on_white)
print(100,210, sfont, "B: toggle to white-on-black");
else
print(100,210, sfont, "B: toggle to black-on-white");
print(100,235, sfont, "V: view font texture");
print(80, 300, sfont, "Current font:");
if (!show_tex) {
if (font < 3)
print(100, 350, sfont, "Font height: 24 pixels");
else
print(100, 350, sfont, "Font height: 14 pixels");
}
if (font%3==1)
print(100, 325, sfont, "2x2 oversampled text at 1:1");
else if (font%3 == 2)
print(100, 325, sfont, "3x1 oversampled text at 1:1");
else if (integer_align)
print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates");
else
print(100, 325, sfont, "1:1 text, one texel = one pixel");
if (show_tex) {
glBegin(GL_QUADS);
drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1);
glEnd();
} else {
glMatrixMode(GL_MODELVIEW);
glTranslatef(200,350,0);
if (translating)
x += fmod(translate_t*8,30);
if (rotating) {
glTranslatef(100,150,0);
glRotatef(rotate_t*2,0,0,1);
glTranslatef(-100,-150,0);
}
print(x,100, font, "This is a test");
print(x,130, font, "Now is the time for all good men to come to the aid of their country.");
print(x,160, font, "The quick brown fox jumps over the lazy dog.");
print(x,190, font, "0123456789");
}
}
void draw(void)
{
draw_init();
draw_world();
stbwingraph_SwapBuffers(NULL);
}
static int initialized=0;
static float last_dt;
int move[4];
int raw_mouse_x, raw_mouse_y;
int loopmode(float dt, int real, int in_client)
{
float actual_dt = dt;
if (!initialized) return 0;
rotate_t += dt;
translate_t += dt;
// music_sim();
if (!real)
return 0;
if (dt > 0.25) dt = 0.25;
if (dt < 0.01) dt = 0.01;
draw();
return 0;
}
int winproc(void *data, stbwingraph_event *e)
{
switch (e->type) {
case STBWGE_create:
break;
case STBWGE_char:
switch(e->key) {
case 27:
stbwingraph_ShowCursor(NULL,1);
return STBWINGRAPH_winproc_exit;
break;
case 'o': case 'O':
font = (font+1) % 3 + (font/3)*3;
break;
case 's': case 'S':
font = (font+3) % 6;
break;
case 't': case 'T':
translating = !translating;
translate_t = 0;
break;
case 'r': case 'R':
rotating = !rotating;
rotate_t = 0;
break;
case 'p': case 'P':
integer_align = !integer_align;
break;
case 'g': case 'G':
srgb = !srgb;
if (srgb)
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
else
glDisable(GL_FRAMEBUFFER_SRGB_EXT);
break;
case 'v': case 'V':
show_tex = !show_tex;
break;
case 'b': case 'B':
black_on_white = !black_on_white;
break;
}
break;
case STBWGE_mousemove:
raw_mouse_x = e->mx;
raw_mouse_y = e->my;
break;
#if 0
case STBWGE_mousewheel: do_mouse(e,0,0); break;
case STBWGE_leftdown: do_mouse(e, 1,0); break;
case STBWGE_leftup: do_mouse(e,-1,0); break;
case STBWGE_rightdown: do_mouse(e,0, 1); break;
case STBWGE_rightup: do_mouse(e,0,-1); break;
#endif
case STBWGE_keydown:
if (e->key == VK_RIGHT) move[0] = 1;
if (e->key == VK_LEFT) move[1] = 1;
if (e->key == VK_UP) move[2] = 1;
if (e->key == VK_DOWN) move[3] = 1;
break;
case STBWGE_keyup:
if (e->key == VK_RIGHT) move[0] = 0;
if (e->key == VK_LEFT) move[1] = 0;
if (e->key == VK_UP) move[2] = 0;
if (e->key == VK_DOWN) move[3] = 0;
break;
case STBWGE_size:
sx = e->width;
sy = e->height;
loopmode(0,1,0);
break;
case STBWGE_draw:
if (initialized)
loopmode(0,1,0);
break;
default:
return STBWINGRAPH_unprocessed;
}
return 0;
}
void stbwingraph_main(void)
{
stbwingraph_Priority(2);
stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0);
stbwingraph_ShowCursor(NULL, 0);
load_fonts();
initialized = 1;
stbwingraph_MainLoop(loopmode, 0.016f); // 30 fps = 0.33
}

View File

@ -0,0 +1,97 @@
# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Application" 0x0101
CFG=oversample - 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 "oversample.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 "oversample.mak" CFG="oversample - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application")
!MESSAGE "oversample - 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)" == "oversample - 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 /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /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 /nologo /subsystem:windows /machine:I386
# SUBTRACT LINK32 /map /debug
!ELSEIF "$(CFG)" == "oversample - 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 /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /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 /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "oversample - Win32 Release"
# Name "oversample - Win32 Debug"
# Begin Source File
SOURCE=.\main.c
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "oversample"=.\oversample.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

Binary file not shown.

View File

@ -0,0 +1,829 @@
// stb_wingraph.h v0.01 - public domain windows graphics programming
// wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
// doing OpenGL graphics
//
// in ONE source file, put '#define STB_DEFINE' before including this
// OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
//
// @TODO:
// 2d rendering interface (that can be done easily in software)
// STB_WINGRAPH_SOFTWARE -- 2d software rendering only
// STB_WINGRAPH_OPENGL -- OpenGL only
#ifndef INCLUDE_STB_WINGRAPH_H
#define INCLUDE_STB_WINGRAPH_H
#ifdef STB_WINMAIN
#ifndef STB_DEFINE
#define STB_DEFINE
#define STB_WINGRAPH_DISABLE_DEFINE_AT_END
#endif
#endif
#ifdef STB_DEFINE
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#endif
#ifdef __cplusplus
#define STB_EXTERN extern "C"
#else
#define STB_EXTERN
#endif
#ifdef STB_DEFINE
#ifndef _WINDOWS_
#ifdef APIENTRY
#undef APIENTRY
#endif
#ifdef WINGDIAPI
#undef WINGDIAPI
#endif
#define _WIN32_WINNT 0x0400 // WM_MOUSEWHEEL
#include <windows.h>
#endif
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <assert.h>
#endif
typedef void * stbwingraph_hwnd;
typedef void * stbwingraph_hinstance;
enum
{
STBWINGRAPH_unprocessed = -(1 << 24),
STBWINGRAPH_do_not_show,
STBWINGRAPH_winproc_exit,
STBWINGRAPH_winproc_update,
STBWINGRAPH_update_exit,
STBWINGRAPH_update_pause,
};
typedef enum
{
STBWGE__none=0,
STBWGE_create,
STBWGE_create_postshow,
STBWGE_draw,
STBWGE_destroy,
STBWGE_char,
STBWGE_keydown,
STBWGE_syskeydown,
STBWGE_keyup,
STBWGE_syskeyup,
STBWGE_deactivate,
STBWGE_activate,
STBWGE_size,
STBWGE_mousemove ,
STBWGE_leftdown , STBWGE_leftup ,
STBWGE_middledown, STBWGE_middleup,
STBWGE_rightdown , STBWGE_rightup ,
STBWGE_mousewheel,
} stbwingraph_event_type;
typedef struct
{
stbwingraph_event_type type;
// for input events (mouse, keyboard)
int mx,my; // mouse x & y
int dx,dy;
int shift, ctrl, alt;
// for keyboard events
int key;
// for STBWGE_size:
int width, height;
// for STBWGE_crate
int did_share_lists; // if true, wglShareLists succeeded
void *handle;
} stbwingraph_event;
typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
extern stbwingraph_hinstance stbwingraph_app;
extern stbwingraph_hwnd stbwingraph_primary_window;
extern int stbwingraph_request_fullscreen;
extern int stbwingraph_request_windowed;
STB_EXTERN void stbwingraph_ods(char *str, ...);
STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
char *caption, char *text, ...);
STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
unsigned int bits, int use_message_box);
STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
STB_EXTERN void stbwingraph_SwapBuffers(void *win);
STB_EXTERN void stbwingraph_Priority(int n);
STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
STB_EXTERN void stbwingraph_ShowWindow(void *window);
STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil);
STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
STB_EXTERN void stbwingraph_DestroyWindow(void *window);
STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
STB_EXTERN void stbwingraph_SetGLWindow(void *win);
typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
#ifdef STB_DEFINE
stbwingraph_hinstance stbwingraph_app;
stbwingraph_hwnd stbwingraph_primary_window;
int stbwingraph_request_fullscreen;
int stbwingraph_request_windowed;
void stbwingraph_ods(char *str, ...)
{
char buffer[1024];
va_list v;
va_start(v,str);
vsprintf(buffer, str, v);
va_end(v);
OutputDebugString(buffer);
}
int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
{
va_list v;
char buffer[1024];
va_start(v, text);
vsprintf(buffer, text, v);
va_end(v);
return MessageBox(win, buffer, caption, type);
}
void stbwingraph_Priority(int n)
{
int p;
switch (n) {
case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
case 0: p = THREAD_PRIORITY_NORMAL; break;
case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
default:
if (n < 0) p = THREAD_PRIORITY_LOWEST;
else p = THREAD_PRIORITY_HIGHEST;
}
SetThreadPriority(GetCurrentThread(), p);
}
static void stbwingraph_ResetResolution(void)
{
ChangeDisplaySettings(NULL, 0);
}
static void stbwingraph_RegisterResetResolution(void)
{
static int done=0;
if (!done) {
done = 1;
atexit(stbwingraph_ResetResolution);
}
}
int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
{
DEVMODE mode;
int res;
int i, tries=0;
for (i=0; ; ++i) {
int success = EnumDisplaySettings(NULL, i, &mode);
if (!success) break;
if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
++tries;
success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
if (success == DISP_CHANGE_SUCCESSFUL) {
stbwingraph_RegisterResetResolution();
return TRUE;
}
break;
}
}
if (!tries) {
if (use_message_box)
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
return FALSE;
}
// we tried but failed, so try explicitly doing it without specifying refresh rate
// Win95 support logic
mode.dmBitsPerPel = bits;
mode.dmPelsWidth = w;
mode.dmPelsHeight = h;
mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
switch (res) {
case DISP_CHANGE_SUCCESSFUL:
stbwingraph_RegisterResetResolution();
return TRUE;
case DISP_CHANGE_RESTART:
if (use_message_box)
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
return FALSE;
case DISP_CHANGE_FAILED:
if (use_message_box)
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
return FALSE;
case DISP_CHANGE_BADMODE:
if (use_message_box)
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
return FALSE;
default:
if (use_message_box)
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits);
return FALSE;
}
}
int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
{
HDC dc = GetDC(win);
PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
int pixel_format;
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
pfd.dwLayerMask = PFD_MAIN_PLANE;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = color_bits;
pfd.cAlphaBits = alpha_bits;
pfd.cDepthBits = depth_bits;
pfd.cStencilBits = stencil_bits;
pfd.cAccumBits = accum_bits;
pixel_format = ChoosePixelFormat(dc, &pfd);
if (!pixel_format) return FALSE;
if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
return FALSE;
SetPixelFormat(dc, pixel_format, &pfd);
return TRUE;
}
typedef struct
{
// app data
stbwingraph_window_proc func;
void *data;
// creation parameters
int color, alpha, depth, stencil, accum;
HWND share_window;
HWND window;
// internal data
HGLRC rc;
HDC dc;
int hide_mouse;
int in_client;
int active;
int did_share_lists;
int mx,my; // last mouse positions
} stbwingraph__window;
static void stbwingraph__inclient(stbwingraph__window *win, int state)
{
if (state != win->in_client) {
win->in_client = state;
if (win->hide_mouse)
ShowCursor(!state);
}
}
static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
{
e->type = type;
e->key = key;
e->shift = (GetKeyState(VK_SHIFT) < 0);
e->ctrl = (GetKeyState(VK_CONTROL) < 0);
e->alt = (GetKeyState(VK_MENU) < 0);
if (z) {
e->mx = z->mx;
e->my = z->my;
} else {
e->mx = e->my = 0;
}
e->dx = e->dy = 0;
}
static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
{
static int captured = 0;
e->type = type;
e->mx = (short) LOWORD(lparam);
e->my = (short) HIWORD(lparam);
if (!z || z->mx == -(1 << 30)) {
e->dx = e->dy = 0;
} else {
e->dx = e->mx - z->mx;
e->dy = e->my - z->my;
}
e->shift = (wparam & MK_SHIFT) != 0;
e->ctrl = (wparam & MK_CONTROL) != 0;
e->alt = (wparam & MK_ALT) != 0;
if (z) {
z->mx = e->mx;
z->my = e->my;
}
if (capture) {
if (!captured && capture == 1)
SetCapture(wnd);
captured += capture;
if (!captured && capture == -1)
ReleaseCapture();
if (captured < 0) captured = 0;
}
}
static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
{
// lparam seems bogus!
static int captured = 0;
e->type = type;
if (z) {
e->mx = z->mx;
e->my = z->my;
}
e->dx = e->dy = 0;
e->shift = (wparam & MK_SHIFT) != 0;
e->ctrl = (wparam & MK_CONTROL) != 0;
e->alt = (GetKeyState(VK_MENU) < 0);
e->key = ((int) wparam >> 16);
}
int stbwingraph_force_update;
static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
int allow_default = TRUE;
stbwingraph_event e = { STBWGE__none };
// the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
switch (msg) {
case WM_CREATE:
{
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
assert(z == NULL);
z = (stbwingraph__window *) lpcs->lpCreateParams;
SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
z->dc = GetDC(wnd);
if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
z->rc = wglCreateContext(z->dc);
if (z->rc) {
e.type = STBWGE_create;
z->did_share_lists = FALSE;
if (z->share_window) {
stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
if (wglShareLists(z->rc, y->rc))
z->did_share_lists = TRUE;
}
wglMakeCurrent(z->dc, z->rc);
return 0;
}
}
return -1;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(wnd, &ps);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
e.type = STBWGE_draw;
e.handle = wnd;
z->func(z->data, &e);
EndPaint(wnd, &ps);
return 0;
}
case WM_DESTROY:
e.type = STBWGE_destroy;
e.handle = wnd;
if (z && z->func)
z->func(z->data, &e);
wglMakeCurrent(NULL, NULL) ;
if (z) {
if (z->rc) wglDeleteContext(z->rc);
z->dc = 0;
z->rc = 0;
}
if (wnd == stbwingraph_primary_window)
PostQuitMessage (0);
return 0;
case WM_CHAR: stbwingraph__key(&e, STBWGE_char , wparam, z); break;
case WM_KEYDOWN: stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
case WM_KEYUP: stbwingraph__key(&e, STBWGE_keyup , wparam, z); break;
case WM_NCMOUSEMOVE: stbwingraph__inclient(z,0); break;
case WM_MOUSEMOVE: stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove, wparam, lparam,0,wnd, z); break;
case WM_LBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_leftdown, wparam, lparam,1,wnd, z); break;
case WM_MBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
case WM_RBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_rightdown, wparam, lparam,1,wnd, z); break;
case WM_LBUTTONUP: stbwingraph__mouse(&e, STBWGE_leftup, wparam, lparam,-1,wnd, z); break;
case WM_MBUTTONUP: stbwingraph__mouse(&e, STBWGE_middleup, wparam, lparam,-1,wnd, z); break;
case WM_RBUTTONUP: stbwingraph__mouse(&e, STBWGE_rightup, wparam, lparam,-1,wnd, z); break;
case WM_MOUSEWHEEL: stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
case WM_ACTIVATE:
allow_default = FALSE;
if (LOWORD(wparam)==WA_INACTIVE ) {
wglMakeCurrent(z->dc, NULL);
e.type = STBWGE_deactivate;
z->active = FALSE;
} else {
wglMakeCurrent(z->dc, z->rc);
e.type = STBWGE_activate;
z->active = TRUE;
}
e.handle = wnd;
z->func(z->data, &e);
return 0;
case WM_SIZE: {
RECT rect;
allow_default = FALSE;
GetClientRect(wnd, &rect);
e.type = STBWGE_size;
e.width = rect.right;
e.height = rect.bottom;
e.handle = wnd;
z->func(z->data, &e);
return 0;
}
default:
return DefWindowProc (wnd, msg, wparam, lparam);
}
if (e.type != STBWGE__none) {
int n;
e.handle = wnd;
n = z->func(z->data, &e);
if (n == STBWINGRAPH_winproc_exit) {
PostQuitMessage(0);
n = 0;
}
if (n == STBWINGRAPH_winproc_update) {
stbwingraph_force_update = TRUE;
return 1;
}
if (n != STBWINGRAPH_unprocessed)
return n;
}
return DefWindowProc (wnd, msg, wparam, lparam);
}
int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
{
WNDCLASSEX wndclass;
stbwingraph_app = hInstance;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_OWNDC;
wndclass.lpfnWndProc = (WNDPROC) stbwingraph_WinProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, iconname);
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
wndclass.lpszMenuName = "zwingraph";
wndclass.lpszClassName = "zwingraph";
wndclass.hIconSm = NULL;
if (!RegisterClassEx(&wndclass))
return FALSE;
return TRUE;
}
void stbwingraph_ShowWindow(void *window)
{
stbwingraph_event e = { STBWGE_create_postshow };
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
ShowWindow(window, SW_SHOWNORMAL);
InvalidateRect(window, NULL, TRUE);
UpdateWindow(window);
e.handle = window;
z->func(z->data, &e);
}
void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
{
HWND win;
DWORD dwstyle;
stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
if (z == NULL) return NULL;
memset(z, 0, sizeof(*z));
z->color = 24;
z->depth = 24;
z->alpha = dest_alpha;
z->stencil = stencil;
z->func = func;
z->data = data;
z->mx = -(1 << 30);
z->my = 0;
if (primary) {
if (stbwingraph_request_windowed)
fullscreen = FALSE;
else if (stbwingraph_request_fullscreen)
fullscreen = TRUE;
}
if (fullscreen) {
#ifdef STB_SIMPLE
stbwingraph_ChangeResolution(width, height, 32, 1);
#else
if (!stbwingraph_ChangeResolution(width, height, 32, 0))
return NULL;
#endif
dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
} else {
RECT rect;
dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
if (resizeable)
dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
rect.top = 0;
rect.left = 0;
rect.right = width;
rect.bottom = height;
AdjustWindowRect(&rect, dwstyle, FALSE);
width = rect.right - rect.left;
height = rect.bottom - rect.top;
}
win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
CW_USEDEFAULT,0, width, height,
NULL, NULL, stbwingraph_app, z);
if (win == NULL) return win;
if (primary) {
if (stbwingraph_primary_window)
stbwingraph_DestroyWindow(stbwingraph_primary_window);
stbwingraph_primary_window = win;
}
{
stbwingraph_event e = { STBWGE_create };
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
z->window = win;
e.did_share_lists = z->did_share_lists;
e.handle = win;
if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
stbwingraph_ShowWindow(win);
}
return win;
}
void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
{
int fullscreen = 0;
#ifndef _DEBUG
if (width == 640 && height == 480) fullscreen = 1;
if (width == 800 && height == 600) fullscreen = 1;
if (width == 1024 && height == 768) fullscreen = 1;
if (width == 1280 && height == 1024) fullscreen = 1;
if (width == 1600 && height == 1200) fullscreen = 1;
//@TODO: widescreen widths
#endif
return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
}
void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
{
if (fullscreen == -1) {
#ifdef _DEBUG
fullscreen = 0;
#else
fullscreen = 1;
#endif
}
if (fullscreen) {
if (fw) ww = fw;
if (fh) wh = fh;
}
return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
}
void stbwingraph_DestroyWindow(void *window)
{
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
DestroyWindow(window);
free(z);
if (stbwingraph_primary_window == window)
stbwingraph_primary_window = NULL;
}
void stbwingraph_ShowCursor(void *window, int visible)
{
int hide;
stbwingraph__window *win;
if (!window)
window = stbwingraph_primary_window;
win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
hide = !visible;
if (hide != win->hide_mouse) {
win->hide_mouse = hide;
if (!hide)
ShowCursor(TRUE);
else if (win->in_client)
ShowCursor(FALSE);
}
}
float stbwingraph_GetTimestep(float minimum_time)
{
float elapsedTime;
double thisTime;
static double lastTime = -1;
if (lastTime == -1)
lastTime = timeGetTime() / 1000.0 - minimum_time;
for(;;) {
thisTime = timeGetTime() / 1000.0;
elapsedTime = (float) (thisTime - lastTime);
if (elapsedTime >= minimum_time) {
lastTime = thisTime;
return elapsedTime;
}
#if 1
Sleep(2);
#endif
}
}
void stbwingraph_SetGLWindow(void *win)
{
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
if (z)
wglMakeCurrent(z->dc, z->rc);
}
void stbwingraph_MakeFonts(void *window, int font_base)
{
wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
}
// returns 1 if WM_QUIT, 0 if 'func' returned 0
int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
{
int needs_drawing = FALSE;
MSG msg;
int is_animating = TRUE;
if (mintime <= 0) mintime = 0.01f;
for(;;) {
int n;
is_animating = TRUE;
// wait for a message if: (a) we're animating and there's already a message
// or (b) we're not animating
if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
stbwingraph_force_update = FALSE;
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
return 1; // WM_QUIT
}
// only force a draw for certain messages...
// if I don't do this, we peg at 50% for some reason... must
// be a bug somewhere, because we peg at 100% when rendering...
// very weird... looks like NVIDIA is pumping some messages
// through our pipeline? well, ok, I guess if we can get
// non-user-generated messages we have to do this
if (!stbwingraph_force_update) {
switch (msg.message) {
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE:
break;
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_TIMER:
case WM_SIZE:
case WM_ACTIVATE:
needs_drawing = TRUE;
break;
}
} else
needs_drawing = TRUE;
}
// if another message, process that first
// @TODO: i don't think this is working, because I can't key ahead
// in the SVT demo app
if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
continue;
// and now call update
if (needs_drawing || is_animating) {
int real=1, in_client=1;
if (stbwingraph_primary_window) {
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
if (z && !z->active) {
real = 0;
}
if (z)
in_client = z->in_client;
}
if (stbwingraph_primary_window)
stbwingraph_SetGLWindow(stbwingraph_primary_window);
n = func(stbwingraph_GetTimestep(mintime), real, in_client);
if (n == STBWINGRAPH_update_exit)
return 0; // update_quit
is_animating = (n != STBWINGRAPH_update_pause);
needs_drawing = FALSE;
}
}
}
void stbwingraph_SwapBuffers(void *win)
{
stbwingraph__window *z;
if (win == NULL) win = stbwingraph_primary_window;
z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
if (z && z->dc)
SwapBuffers(z->dc);
}
#endif
#ifdef STB_WINMAIN
void stbwingraph_main(void);
char *stb_wingraph_commandline;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
{
char buffer[1024];
// add spaces to either side of the string
buffer[0] = ' ';
strcpy(buffer+1, lpCmdLine);
strcat(buffer, " ");
if (strstr(buffer, " -reset ")) {
ChangeDisplaySettings(NULL, 0);
exit(0);
}
if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
stbwingraph_request_windowed = TRUE;
else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
stbwingraph_request_fullscreen = TRUE;
}
stb_wingraph_commandline = lpCmdLine;
stbwingraph_DefineClass(hInstance, "appicon");
stbwingraph_main();
return 0;
}
#endif
#undef STB_EXTERN
#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
#undef STB_DEFINE
#endif
#endif // INCLUDE_STB_WINGRAPH_H

BIN
external/stb/stb/tests/pbm/basi0g16.pgm vendored Normal file

Binary file not shown.

BIN
external/stb/stb/tests/pbm/basi2c16.ppm vendored Normal file

Binary file not shown.

BIN
external/stb/stb/tests/pbm/cdfn2c08.ppm vendored Normal file

Binary file not shown.

BIN
external/stb/stb/tests/pbm/cdun2c08.ppm vendored Normal file

Binary file not shown.

BIN
external/stb/stb/tests/pbm/comment.pgm vendored Normal file

Binary file not shown.

BIN
external/stb/stb/tests/pbm/ctfn0g04.pgm vendored Normal file

Binary file not shown.

124
external/stb/stb/tests/pg_test/pg_test.c vendored Normal file
View File

@ -0,0 +1,124 @@
#define STB_DEFINE
#include "stb.h"
#define STB_PG_IMPLEMENTATION
#include "stb_pg.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
static float *hf;
static int hf_width = 10001;
static int hf_height = 10001;
static float get_height(float x, float y)
{
float h00,h01,h10,h11,h0,h1;
int ix,iy;
if (x < 0) x = 0;
if (x > hf_width-1) x = (float) hf_width-1;
if (y < 0) y = 0;
if (y > hf_height-1) y = (float) hf_height-1;
ix = (int) x; x -= ix;
iy = (int) y; y -= iy;
h00 = hf[(iy+0)*hf_height+(ix+0)];
h10 = hf[(iy+0)*hf_height+(ix+1)];
h01 = hf[(iy+1)*hf_height+(ix+0)];
h11 = hf[(iy+1)*hf_height+(ix+1)];
h0 = stb_lerp(y, h00, h01);
h1 = stb_lerp(y, h10, h11);
return stb_lerp(x, h0, h1);
}
void stbpg_tick(float dt)
{
int i=0,j=0;
int step = 1;
glUseProgram(0);
glClearColor(0.6f,0.7f,1.0f,1.0f);
glClearDepth(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
#if 1
glEnable(GL_CULL_FACE);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, 1920/1080.0f, 0.02f, 8000.0f);
//glOrtho(-8,8,-6,6, -100, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(-90, 1,0,0); // z-up
{
float x,y;
stbpg_get_mouselook(&x,&y);
glRotatef(-y, 1,0,0);
glRotatef(-x, 0,0,1);
}
{
static float cam_x = 1000;
static float cam_y = 1000;
static float cam_z = 700;
float x=0,y=0;
stbpg_get_keymove(&x,&y);
cam_x += x*dt*5.0f;
cam_y += y*dt*5.0f;
glTranslatef(-cam_x, -cam_y, -cam_z);
if (cam_x >= 0 && cam_x < hf_width && cam_y >= 0 && cam_y < hf_height)
cam_z = get_height(cam_x, cam_y) + 1.65f; // average eye height in meters
}
for (j=501; j+1 < 1500+0*hf_height; j += step) {
glBegin(GL_QUAD_STRIP);
for (i=501; i < 1500+0*hf_width; i += step) {
static int flip=0;
if (flip)
glColor3f(0.5,0.5,0.5);
else
glColor3f(0.4f,0.4f,0.4f);
flip = !flip;
glVertex3f((float) i, (float) j+step,hf[(j+step)*hf_width+i]);
glVertex3f((float) i, (float) j ,hf[ j *hf_width+i]);
}
glEnd();
}
glBegin(GL_LINES);
glColor3f(1,0,0); glVertex3f(10,0,0); glVertex3f(0,0,0);
glColor3f(0,1,0); glVertex3f(0,10,0); glVertex3f(0,0,0);
glColor3f(0,0,1); glVertex3f(0,0,10); glVertex3f(0,0,0);
glEnd();
#endif
}
void stbpg_main(int argc, char **argv)
{
int i,j;
#if 0
int w,h,c;
unsigned short *data = stbi_load_16("c:/x/ned_1m/test2.png", &w, &h, &c, 1);
stb_filewrite("c:/x/ned_1m/x73_y428_10012_10012.bin", data, w*h*2);
#else
unsigned short *data = stb_file("c:/x/ned_1m/x73_y428_10012_10012.bin", NULL);
int w=10012, h = 10012;
#endif
hf = malloc(hf_width * hf_height * 4);
for (j=0; j < hf_height; ++j)
for (i=0; i < hf_width; ++i)
hf[j*hf_width+i] = data[j*w+i] / 32.0f;
stbpg_gl_compat_version(1,1);
stbpg_windowed("terrain_edit", 1920, 1080);
stbpg_run();
return;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,9 @@
PngSuite
--------
Permission to use, copy, modify and distribute these images for any
purpose and without fee is hereby granted.
(c) Willem van Schaik, 1996, 2011

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Some files were not shown because too many files have changed in this diff Show More