Merge remote-tracking branch 'nothings/master'

Conflicts:
	stb_truetype.h
pull/72/head
ocornut 2015-05-28 08:55:13 +01:00
commit 1b157be513
43 changed files with 21701 additions and 307 deletions

View File

@ -1,25 +1,34 @@
<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->
stb
===
single-file public domain libraries for C/C++
library | lastest version | category | description
--------------------- | ---- | -------- | --------------------------------
**stb_vorbis.c** | 1.04 | audio | decode ogg vorbis files from file/memory to float/16-bit signed output
**stb_image.h** | 2.01 | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
**stb_truetype.h** | 1.02 | graphics | parse, decode, and rasterize characters from truetype fonts
**stb_image_write.h** | 0.96 | graphics | image writing to disk: PNG, TGA, BMP
**stb_image_resize.h** | 0.90 | graphics | resize images larger/smaller with good quality
**stb_rect_pack.h** | 0.05 | graphics | simple 2D rectangle packer with decent quality
**stretchy_buffer.h** | 1.01 | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
**stb_textedit.h** | 1.5 | UI | guts of a text editor for games etc implementing them from scratch
**stb_dxt.h** | 1.04 | 3D&nbsp;graphics | Fabian "ryg" Giesen's real-time DXT compressor
**stb_perlin.h** | 0.2 | 3D&nbsp;graphics | revised Perlin noise (3D input, 1D output)
**stb_tilemap_editor.h** | 0.30 | games | embeddable tilemap editor
**stb_herringbone_wang_tile.h** | 0.6 | games | herringbone Wang tile map generator
**stb_c_lexer.h** | 0.06 | parsing | simplify writing parsers for C-like languages
**stb_divide.h** | 0.91 | math | more useful 32-bit modulus e.g. "euclidean divide"
**stb.h** | 2.24 | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff
library | lastest version | category | LoC | description
--------------------- | ---- | -------- | --- | --------------------------------
**stb_vorbis.c** | 1.05 | audio | 5445 | decode ogg vorbis files from file/memory to float/16-bit signed output
**stb_image.h** | 2.05 | graphics | 6433 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
**stb_truetype.h** | 1.05 | graphics | 2632 | parse, decode, and rasterize characters from truetype fonts
**stb_image_write.h** | 0.98 | graphics | 730 | image writing to disk: PNG, TGA, BMP
**stb_image_resize.h** | 0.90 | graphics | 2585 | resize images larger/smaller with good quality
**stb_rect_pack.h** | 0.06 | graphics | 560 | simple 2D rectangle packer with decent quality
**stretchy_buffer.h** | 1.02 | utility | 210 | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
**stb_textedit.h** | 1.6 | UI | 1290 | guts of a text editor for games etc implementing them from scratch
**stb_voxel_render.h** | 0.80 | 3D&nbsp;graphics | 3644 | Minecraft-esque voxel rendering "engine" with many more features
**stb_dxt.h** | 1.04 | 3D&nbsp;graphics | 624 | Fabian "ryg" Giesen's real-time DXT compressor
**stb_perlin.h** | 0.2 | 3D&nbsp;graphics | 175 | revised Perlin noise (3D input, 1D output)
**stb_easy_font.h** | 0.5 | 3D&nbsp;graphics | 220 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
**stb_tilemap_editor.h** | 0.35 | game&nbsp;dev | 4120 | embeddable tilemap editor
**stb_herringbone_wa...** | 0.6 | game&nbsp;dev | 1217 | herringbone Wang tile map generator
**stb_c_lexer.h** | 0.06 | parsing | 809 | simplify writing parsers for C-like languages
**stb_divide.h** | 0.91 | math | 373 | more useful 32-bit modulus e.g. "euclidean divide"
**stb.h** | 2.24 | misc | 14086 | helper functions for C, mostly redundant in C++; basically author's personal stuff
**stb_leakcheck.h** | 0.2 | misc | 117 | quick-and-dirty malloc/free leak-checking
Total libraries: 18
Total lines of C code: 45270
FAQ
---
@ -42,6 +51,40 @@ attribution requirement). They may be less featureful, slower,
and/or use more memory. If you're already using an equivalent
library, there's probably no good reason to switch.
#### Why do you list "lines of code"? It's a terrible metric.
Just to give you some idea of the internal complexity of the library,
to help you manage your expectations, or to let you know what you're
getting into. While not all the libraries are written in the same
style, they're certainly similar styles, and so comparisons between
the libraries are probably still meaningful.
Note though that the lines do include both the implementation, the
part that corresponds to a header file, and the documentation.
#### Why single-file headers?
Windows doesn't have standard directories where libraries
live. That makes deploying libraries in Windows a lot more
painful than open source developers on Unix-derivates generally
realize. (It also makes library dependencies a lot worse in Windows.)
There's also a common problem in Windows where a library was built
against a different version of the runtime library, which causes
link conflicts and confusion. Shipping the libs as headers means
you normally just compile them straight into your project without
making libraries, thus sidestepping that problem.
Making them a single file makes it very easy to just
drop them into a project that needs them. (Of course you can
still put them in a proper shared library tree if you want.)
Why not two files, one a header and one an implementation?
The difference between 10 files and 9 files is not a big deal,
but the difference between 2 files and 1 file is a big deal.
You don't need to zip or tar the files up, you don't have to
remember to attach *two* files, etc.
#### Why "stb"? Is this something to do with Set-Top Boxes?
No, they are just the initials for my name, Sean T. Barrett.
@ -66,12 +109,9 @@ Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
#### Why public domain?
Because more people will use it. Because it's not viral, people
are not obligated to give back, so you could argue that it hurts
the *development* of it, and then because it doesn't develop as
well it's not as good, and then because it's not as good, in the
long run maybe fewer people will use it. I have total respect for
that opinion, but I just don't believe it myself for most software.
I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons.
Some of them are listed here:
https://github.com/nothings/stb/blob/master/docs/why_public_domain.md
#### Why C?

BIN
data/easy_font_raw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

View File

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

116
docs/why_public_domain.md Normal file
View File

@ -0,0 +1,116 @@
My collected rationales for placing these libraries
in the public domain:
1. Public domain vs. viral licenses
Why is this library public domain?
Because more people will use it. Because it's not viral, people are
not obligated to give back, so you could argue that it hurts the
development of it, and then because it doesn't develop as well it's
not as good, and then because it's not as good, in the long run
maybe fewer people will use it. I have total respect for that
opinion, but I just don't believe it myself for most software.
2. Public domain vs. attribution-required licenses
The primary difference between public domain and, say, a Creative Commons
commercial / non-share-alike / attribution license is solely the
requirement for attribution. (Similarly the BSD license and such.)
While I would *appreciate* acknowledgement and attribution, I believe
that it is foolish to place a legal encumberment (i.e. a license) on
the software *solely* to get attribution.
In other words, I'm arguing that PD is superior to the BSD license and
the Creative Commons 'Attribution' license. If the license offers
anything besides attribution -- as does, e.g., CC NonCommercial-ShareAlike,
or the GPL -- that's a separate discussion.
3. Other aspects of BSD-style licenses besides attribution
Permissive licenses like zlib and BSD license are perfectly reasonable
in their requirements, but they are very wordy and
have only two benefits over public domain: legally-mandated
attribution and liability-control. I do not believe these
are worth the excessive verbosity and user-unfriendliness
these licenses induce, especially in the single-file
case where those licenses tend to be at the top of
the file, the first thing you see.
To the specific points, I have had no trouble receiving
attribution for my libraries; liability in the face of
no explicit disclaimer of liability is an open question,
but one I have a lot of difficulty imagining there being
any actual doubt about in court. Sometimes I explicitly
note in my libraries that I make no guarantees about them
being fit for purpose, but it's pretty absurd to do this;
as a whole, it comes across as "here is a library to decode
vorbis audio files, but it may not actually work and if
you have problems it's not my fault, but also please
report bugs so I can fix them"--so dumb!
4. full discussion from stb_howto.txt on what YOU should do for YOUR libs
```
EASY-TO-COMPLY LICENSE
I make my libraries public domain. You don't have to.
But my goal in releasing stb-style libraries is to
reduce friction for potential users as much as
possible. That means:
a. easy to build (what this file is mostly about)
b. easy to invoke (which requires good API design)
c. easy to deploy (which is about licensing)
I choose to place all my libraries in the public
domain, abjuring copyright, rather than license
the libraries. This has some benefits and some
drawbacks.
Any license which is "viral" to modifications
causes worries for lawyers, even if their programmers
aren't modifying it.
Any license which requires crediting in documentation
adds friction which can add up. Valve used to have
a page with a list of all of these on their web site,
and it was insane, and obviously nobody ever looked
at it so why would you care whether your credit appeared
there?
Permissive licenses like zlib and BSD license are
perfectly reasonable, but they are very wordy and
have only two benefits over public domain: legally-mandated
attribution and liability-control. I do not believe these
are worth the excessive verbosity and user-unfriendliness
these licenses induce, especially in the single-file
case where those licenses tend to be at the top of
the file, the first thing you see. (To the specific
points, I have had no trouble receiving attribution
for my libraries; liability in the face of no explicit
disclaimer of liability is an open question.)
However, public domain has frictions of its own, because
public domain declarations aren't necessary recognized
in the USA and some other locations. For that reason,
I recommend a declaration along these lines:
// This software is in the public domain. Where that dedication is not
// recognized, you are granted a perpetual, irrevocable license to copy
// and modify this file as you see fit.
I typically place this declaration at the end of the initial
comment block of the file and just say 'public domain'
at the top.
I have had people say they couldn't use one of my
libraries because it was only "public domain" and didn't
have the additional fallback clause, who asked if
I could dual-license it under a traditional license.
My answer: they can create a derivative work by
modifying one character, and then license that however
they like. (Indeed, *adding* the zlib or BSD license
would be such a modification!) Unfortunately, their
lawyers reportedly didn't like that answer. :(
```

6
stb.h
View File

@ -1400,12 +1400,18 @@ int stb_is_pow2(unsigned int n)
#pragma warning(disable: 4035) // disable warning about no return value
int stb_log2_floor(unsigned int n)
{
#if _MSC_VER > 1700
DWORD i;
_BitScanReverse(&i, n);
return i != 0 ? i : -1;
#else
__asm {
bsr eax,n
jnz done
mov eax,-1
}
done:;
#endif
}
#pragma warning(pop)
#else

220
stb_easy_font.h Normal file
View File

@ -0,0 +1,220 @@
// stb_easy_font.h - v0.5 - bitmap font for 3D rendering - public domain
// Sean Barrett, Feb 2015
//
// Easy-to-deploy,
// reasonably compact,
// extremely inefficient performance-wise,
// crappy-looking,
// ASCII-only,
// bitmap font for use in 3D APIs.
//
// Intended for when you just want to get some text displaying
// in a 3D app as quickly as possible.
//
// Doesn't use any textures, instead builds characters out of quads.
//
// DOCUMENTATION:
//
// int stb_easy_font_width(char *text)
//
// Takes a string without newlines and returns the horizontal size.
//
// int stb_easy_font_print(float x, float y,
// char *text, unsigned char color[4],
// void *vertex_buffer, int vbuf_size)
//
// Takes a string (which can contain '\n') and fills out a
// vertex buffer with renderable data to draw the string.
// Output data assumes increasing x is rightwards, increasing y
// is downwards.
//
// The vertex data is divided into quads, i.e. there are four
// vertices in the vertex buffer for each quad.
//
// The vertices are stored in an interleaved format:
//
// x:float
// y:float
// z:float
// color:uint8[4]
//
// You can ignore z and color if you get them from elsewhere
// This format was chosen in the hopes it would make it
// easier for you to reuse existing buffer-drawing code.
//
// If you pass in NULL for color, it becomes 255,255,255,255.
//
// Returns the number of quads.
//
// If the buffer isn't large enough, it will truncate.
// Expect it to use an average of ~270 bytes per character.
//
// If your API doesn't draw quads, build a reusable index
// list that allows you to render quads as indexed triangles.
//
// void stb_easy_font_spacing(float spacing)
//
// Use positive values to expand the space between characters,
// and small negative values (no smaller than -1.5) to contract
// the space between characters.
//
// E.g. spacing = 1 adds one "pixel" of spacing between the
// characters. spacing = -1 is reasonable but feels a bit too
// compact to me; -0.5 is a reasonable compromise as long as
// you're scaling the font up.
//
// SAMPLE CODE:
//
// Here's sample code for old OpenGL; it's a lot more complicated
// to make work on modern APIs, and that's your problem.
//
#if 0
void print_string(float x, float y, char *text, float r, float g, float b)
{
static char buffer[99999]; // ~500 chars
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);
}
#endif
#ifndef INCLUDE_STB_EASY_FONT_H
#define INCLUDE_STB_EASY_FONT_H
#include <stdlib.h>
struct {
unsigned char advance;
unsigned char h_seg;
unsigned char v_seg;
} stb_easy_font_charinfo[96] = {
{ 5, 0, 0 }, { 3, 0, 0 }, { 5, 1, 1 }, { 7, 1, 4 },
{ 7, 3, 7 }, { 7, 6, 12 }, { 7, 8, 19 }, { 4, 16, 21 },
{ 4, 17, 22 }, { 4, 19, 23 }, { 23, 21, 24 }, { 23, 22, 31 },
{ 20, 23, 34 }, { 22, 23, 36 }, { 19, 24, 36 }, { 21, 25, 36 },
{ 6, 25, 39 }, { 6, 27, 43 }, { 6, 28, 45 }, { 6, 30, 49 },
{ 6, 33, 53 }, { 6, 34, 57 }, { 6, 40, 58 }, { 6, 46, 59 },
{ 6, 47, 62 }, { 6, 55, 64 }, { 19, 57, 68 }, { 20, 59, 68 },
{ 21, 61, 69 }, { 22, 66, 69 }, { 21, 68, 69 }, { 7, 73, 69 },
{ 9, 75, 74 }, { 6, 78, 81 }, { 6, 80, 85 }, { 6, 83, 90 },
{ 6, 85, 91 }, { 6, 87, 95 }, { 6, 90, 96 }, { 7, 92, 97 },
{ 6, 96,102 }, { 5, 97,106 }, { 6, 99,107 }, { 6,100,110 },
{ 6,100,115 }, { 7,101,116 }, { 6,101,121 }, { 6,101,125 },
{ 6,102,129 }, { 7,103,133 }, { 6,104,140 }, { 6,105,145 },
{ 7,107,149 }, { 6,108,151 }, { 7,109,155 }, { 7,109,160 },
{ 7,109,165 }, { 7,118,167 }, { 6,118,172 }, { 4,120,176 },
{ 6,122,177 }, { 4,122,181 }, { 23,124,182 }, { 22,129,182 },
{ 4,130,182 }, { 22,131,183 }, { 6,133,187 }, { 22,135,191 },
{ 6,137,192 }, { 22,139,196 }, { 5,144,197 }, { 22,147,198 },
{ 6,150,202 }, { 19,151,206 }, { 21,152,207 }, { 6,155,209 },
{ 3,160,210 }, { 23,160,211 }, { 22,164,216 }, { 22,165,220 },
{ 22,167,224 }, { 22,169,228 }, { 21,171,232 }, { 21,173,233 },
{ 5,178,233 }, { 22,179,234 }, { 23,180,238 }, { 23,180,243 },
{ 23,180,248 }, { 22,189,248 }, { 22,191,252 }, { 5,196,252 },
{ 3,203,252 }, { 5,203,253 }, { 22,210,253 }, { 0,214,253 },
};
unsigned char stb_easy_font_hseg[214] = {
97,37,69,84,28,51,2,18,10,49,98,41,65,25,81,105,33,9,97,1,97,37,37,36,
81,10,98,107,3,100,3,99,58,51,4,99,58,8,73,81,10,50,98,8,73,81,4,10,50,
98,8,25,33,65,81,10,50,17,65,97,25,33,25,49,9,65,20,68,1,65,25,49,41,
11,105,13,101,76,10,50,10,50,98,11,99,10,98,11,50,99,11,50,11,99,8,57,
58,3,99,99,107,10,10,11,10,99,11,5,100,41,65,57,41,65,9,17,81,97,3,107,
9,97,1,97,33,25,9,25,41,100,41,26,82,42,98,27,83,42,98,26,51,82,8,41,
35,8,10,26,82,114,42,1,114,8,9,73,57,81,41,97,18,8,8,25,26,26,82,26,82,
26,82,41,25,33,82,26,49,73,35,90,17,81,41,65,57,41,65,25,81,90,114,20,
84,73,57,41,49,25,33,65,81,9,97,1,97,25,33,65,81,57,33,25,41,25,
};
unsigned char stb_easy_font_vseg[253] = {
4,2,8,10,15,8,15,33,8,15,8,73,82,73,57,41,82,10,82,18,66,10,21,29,1,65,
27,8,27,9,65,8,10,50,97,74,66,42,10,21,57,41,29,25,14,81,73,57,26,8,8,
26,66,3,8,8,15,19,21,90,58,26,18,66,18,105,89,28,74,17,8,73,57,26,21,
8,42,41,42,8,28,22,8,8,30,7,8,8,26,66,21,7,8,8,29,7,7,21,8,8,8,59,7,8,
8,15,29,8,8,14,7,57,43,10,82,7,7,25,42,25,15,7,25,41,15,21,105,105,29,
7,57,57,26,21,105,73,97,89,28,97,7,57,58,26,82,18,57,57,74,8,30,6,8,8,
14,3,58,90,58,11,7,74,43,74,15,2,82,2,42,75,42,10,67,57,41,10,7,2,42,
74,106,15,2,35,8,8,29,7,8,8,59,35,51,8,8,15,35,30,35,8,8,30,7,8,8,60,
36,8,45,7,7,36,8,43,8,44,21,8,8,44,35,8,8,43,23,8,8,43,35,8,8,31,21,15,
20,8,8,28,18,58,89,58,26,21,89,73,89,29,20,8,8,30,7,
};
typedef struct
{
unsigned char c[4];
} stb_easy_font_color;
static int stb_easy_font_draw_segs(float x, float y, unsigned char *segs, int num_segs, int vertical, stb_easy_font_color c, char *vbuf, int vbuf_size, int offset)
{
int i,j;
for (i=0; i < num_segs; ++i) {
int len = segs[i] & 7;
x += (float) ((segs[i] >> 3) & 1);
if (len && offset+64 <= vbuf_size) {
float y0 = y + (float) (segs[i]>>4);
for (j=0; j < 4; ++j) {
* (float *) (vbuf+offset+0) = x + (j==1 || j==2 ? (vertical ? 1 : len) : 0);
* (float *) (vbuf+offset+4) = y0 + ( j >= 2 ? (vertical ? len : 1) : 0);
* (float *) (vbuf+offset+8) = 0.f;
* (stb_easy_font_color *) (vbuf+offset+12) = c;
offset += 16;
}
}
}
return offset;
}
float stb_easy_font_spacing_val = 0;
static void stb_easy_font_spacing(float spacing)
{
stb_easy_font_spacing_val = spacing;
}
static int stb_easy_font_print(float x, float y, char *text, unsigned char color[4], void *vertex_buffer, int vbuf_size)
{
char *vbuf = (char *) vertex_buffer;
float start_x = x;
int offset = 0;
stb_easy_font_color c = { 255,255,255,255 }; // use structure copying to avoid needing depending on memcpy()
if (color) { c.c[0] = color[0]; c.c[1] = color[1]; c.c[2] = color[2]; c.c[3] = color[3]; }
while (*text && offset < vbuf_size) {
if (*text == '\n') {
y += 12;
x = start_x;
} else {
unsigned char advance = stb_easy_font_charinfo[*text-32].advance;
float y_ch = advance & 16 ? y+1 : y;
int h_seg, v_seg, num_h, num_v;
h_seg = stb_easy_font_charinfo[*text-32 ].h_seg;
v_seg = stb_easy_font_charinfo[*text-32 ].v_seg;
num_h = stb_easy_font_charinfo[*text-32+1].h_seg - h_seg;
num_v = stb_easy_font_charinfo[*text-32+1].v_seg - v_seg;
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_hseg[h_seg], num_h, 0, c, vbuf, vbuf_size, offset);
offset = stb_easy_font_draw_segs(x, y_ch, &stb_easy_font_vseg[v_seg], num_v, 1, c, vbuf, vbuf_size, offset);
x += advance & 15;
x += stb_easy_font_spacing_val;
}
++text;
}
return (unsigned) offset/64;
}
static int stb_easy_font_width(char *text)
{
float len = 0;
while (*text) {
len += stb_easy_font_charinfo[*text-32].advance & 15;
len += stb_easy_font_spacing_val;
++text;
}
return (int) ceil(len);
}
#endif

View File

@ -1,4 +1,4 @@
/* stb_image - v2.01 - public domain image loader - http://nothings.org/stb_image.h
/* stb_image - v2.05 - public domain image loader - http://nothings.org/stb_image.h
no warranty implied; use at your own risk
Do this:
@ -143,6 +143,12 @@
Latest revision history:
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
2.03 (2015-04-12) additional corruption checking
stbi_set_flip_vertically_on_load
fix NEON support; fix mingw support
2.02 (2015-01-19) fix incorrect assert, fix warning
2.01 (2015-01-17) fix various warnings
2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD
@ -155,8 +161,6 @@
1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)
optimize PNG
fix bug in interlaced PNG with user-specified channel count
1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG
1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc
See end of file for full revision history.
@ -179,7 +183,7 @@
James "moose2000" Brown (iPhone PNG) Roy Eltham
Ben "Disch" Wenger (io callbacks) Luke Graham
Omar Cornut (1/2/4-bit PNG) Thomas Ruf
John Bartholomew
Nicolas Guillemot (vertical flip) John Bartholomew
Ken Hamada
Optimizations & bugfixes Cort Stratton
Fabian "ryg" Giesen Blazej Dariusz Roszkowski
@ -195,6 +199,9 @@
Sergio Gonzalez
Cass Everitt
Engin Manap
Martins Mozeiko
Joseph Thomson
Phil Jordan
License:
This software is in the public domain. Where that dedication is not
@ -486,6 +493,8 @@ STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultipl
// or just pass them through "as-is"
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
// flip the image vertically, so the first pixel in the output array is the bottom left
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
@ -623,7 +632,15 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#define STBI_FREE(p) free(p)
#endif
#if defined(__GNUC__) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
// x86/x64 detection
#if defined(__x86_64__) || defined(_M_X64)
#define STBI__X64_TARGET
#elif defined(__i386) || defined(_M_IX86)
#define STBI__X86_TARGET
#endif
#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
// NOTE: not clear do we actually need this for the 64-bit path?
// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
// (but compiling with -msse2 allows the compiler to use SSE2 everywhere;
// this is just broken and gcc are jerks for not fixing it properly
@ -631,7 +648,22 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#define STBI_NO_SIMD
#endif
#if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
//
// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
// simultaneously enabling "-mstackrealign".
//
// See https://github.com/nothings/stb/issues/81 for more information.
//
// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
#define STBI_NO_SIMD
#endif
#if !defined(STBI_NO_SIMD) && defined(STBI__X86_TARGET)
#define STBI_SSE2
#include <emmintrin.h>
@ -884,7 +916,14 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
#endif
static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
static int stbi__vertically_flip_on_load = 0;
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
{
stbi__vertically_flip_on_load = flag_true_if_should_flip;
}
static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
#ifndef STBI_NO_JPEG
if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp);
@ -924,6 +963,53 @@ static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp
return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
}
static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
unsigned char *result = stbi__load_main(s, x, y, comp, req_comp);
if (stbi__vertically_flip_on_load && result != NULL) {
int w = *x, h = *y;
int depth = req_comp ? req_comp : *comp;
int row,col,z;
stbi_uc temp;
// @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once
for (row = 0; row < (h>>1); row++) {
for (col = 0; col < w; col++) {
for (z = 0; z < depth; z++) {
temp = result[(row * w + col) * depth + z];
result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];
result[((h - row - 1) * w + col) * depth + z] = temp;
}
}
}
}
return result;
}
static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
{
if (stbi__vertically_flip_on_load && result != NULL) {
int w = *x, h = *y;
int depth = req_comp ? req_comp : *comp;
int row,col,z;
float temp;
// @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once
for (row = 0; row < (h>>1); row++) {
for (col = 0; col < w; col++) {
for (z = 0; z < depth; z++) {
temp = result[(row * w + col) * depth + z];
result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];
result[((h - row - 1) * w + col) * depth + z] = temp;
}
}
}
}
}
#ifndef STBI_NO_STDIO
static FILE *stbi__fopen(char const *filename, char const *mode)
@ -954,7 +1040,7 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req
unsigned char *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi_load_main(&s,x,y,comp,req_comp);
result = stbi__load_flip(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
@ -967,25 +1053,29 @@ STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, i
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi_load_main(&s,x,y,comp,req_comp);
return stbi__load_flip(&s,x,y,comp,req_comp);
}
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi_load_main(&s,x,y,comp,req_comp);
return stbi__load_flip(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_LINEAR
static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
unsigned char *data;
#ifndef STBI_NO_HDR
if (stbi__hdr_test(s))
return stbi__hdr_load(s,x,y,comp,req_comp);
if (stbi__hdr_test(s)) {
float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp);
if (hdr_data)
stbi__float_postprocess(hdr_data,x,y,comp,req_comp);
return hdr_data;
}
#endif
data = stbi_load_main(s, x, y, comp, req_comp);
data = stbi__load_flip(s, x, y, comp, req_comp);
if (data)
return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
@ -995,14 +1085,14 @@ STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, in
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi_loadf_main(&s,x,y,comp,req_comp);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi_loadf_main(&s,x,y,comp,req_comp);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_STDIO
@ -1020,7 +1110,7 @@ STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_
{
stbi__context s;
stbi__start_file(&s,f);
return stbi_loadf_main(&s,x,y,comp,req_comp);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#endif // !STBI_NO_STDIO
@ -1143,6 +1233,10 @@ stbi_inline static int stbi__at_eof(stbi__context *s)
static void stbi__skip(stbi__context *s, int n)
{
if (n < 0) {
s->img_buffer = s->img_buffer_end;
return;
}
if (s->io.read) {
int blen = (int) (s->img_buffer_end - s->img_buffer);
if (blen < n) {
@ -1551,6 +1645,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
k = stbi_lrot(j->code_buffer, n);
STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
j->code_bits -= n;
@ -1756,8 +1851,11 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
if (r)
j->eob_run += stbi__jpeg_get_bits(j, r);
r = 64; // force end of block
} else
r = 16; // r=15 is the code for 16 0s
} else {
// r=15 s=0 should write 16 0s, so we just do
// a run of 15 0s and then write s (which is 0),
// so we don't have to do anything special here
}
} else {
if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
// sign bit
@ -1769,7 +1867,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
// advance by r
while (k <= j->spec_end) {
short *p = &data[stbi__jpeg_dezigzag[k]];
short *p = &data[stbi__jpeg_dezigzag[k++]];
if (*p != 0) {
if (stbi__jpeg_get_bit(j))
if ((*p & bit)==0) {
@ -1778,15 +1876,12 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
else
*p -= bit;
}
++k;
} else {
if (r == 0) {
if (s)
data[stbi__jpeg_dezigzag[k++]] = (short) s;
*p = (short) s;
break;
}
--r;
++k;
}
}
} while (k <= j->spec_end);
@ -2699,6 +2794,10 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
int m;
for (m = 0; m < 4; m++) {
j->img_comp[m].raw_data = NULL;
j->img_comp[m].raw_coeff = NULL;
}
j->restart_interval = 0;
if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
m = stbi__get_marker(j);
@ -3011,7 +3110,7 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons
__m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
__m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
__m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
__m128i y_bias = _mm_set1_epi8((char) 128);
__m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
__m128i xw = _mm_set1_epi16(255); // alpha channel
for (; i+7 < count; i += 8) {
@ -3378,7 +3477,8 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
++sizes[sizelist[i]];
sizes[0] = 0;
for (i=1; i < 16; ++i)
STBI_ASSERT(sizes[i] <= (1 << i));
if (sizes[i] > (1 << i))
return stbi__err("bad sizes", "Corrupt PNG");
code = 0;
for (i=1; i < 16; ++i) {
next_code[i] = code;
@ -3386,7 +3486,7 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num)
z->firstsymbol[i] = (stbi__uint16) k;
code = (code + sizes[i]);
if (sizes[i])
if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG");
if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
z->maxcode[i] = code << (16-i); // preshift for inner loop
code <<= 1;
k += sizes[i];
@ -3555,9 +3655,9 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
p = (stbi_uc *) (zout - dist);
if (dist == 1) { // run of one byte; common in images.
stbi_uc v = *p;
do *zout++ = v; while (--len);
if (len) { do *zout++ = v; while (--len); }
} else {
do *zout++ = *p++; while (--len);
if (len) { do *zout++ = *p++; while (--len); }
}
}
}
@ -3585,7 +3685,7 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a)
n = 0;
while (n < hlit + hdist) {
int c = stbi__zhuffman_decode(a, &z_codelength);
STBI_ASSERT(c >= 0 && c < 19);
if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
if (c < 16)
lencodes[n++] = (stbi_uc) c;
else if (c == 16) {
@ -4017,7 +4117,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
cur[i*2+0] = cur[i];
}
} else {
assert(img_n == 3);
STBI_ASSERT(img_n == 3);
for (i=x-1; i >= 0; --i) {
cur[i*4+3] = 255;
cur[i*4+2] = cur[i*3+2];
@ -4282,6 +4382,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi_uc *p;
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
@ -4641,7 +4742,7 @@ static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int
}
} else {
for (i=0; i < (int) s->img_x; ++i) {
stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s));
stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
int a;
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
@ -4798,7 +4899,7 @@ static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int
*y = tga_height;
if (comp) *comp = tga_comp;
tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp );
tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp );
if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
// skip to the data's starting position (offset usually = 0)
@ -5459,6 +5560,7 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
stbi__gif_lzw *p;
lzw_cs = stbi__get8(s);
if (lzw_cs > 12) return NULL;
clear = 1 << lzw_cs;
first = 1;
codesize = lzw_cs + 1;
@ -6190,6 +6292,12 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
/*
revision history:
2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
2.03 (2015-04-12) extra corruption checking (mmozeiko)
stbi_set_flip_vertically_on_load (nguillemot)
fix NEON support; fix mingw support
2.02 (2015-01-19) fix incorrect assert, fix warning
2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
@ -6275,7 +6383,7 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
1.21 fix use of 'stbi_uc' in header (reported by jon blow)
1.20 added support for Softimage PIC, by Tom Seddon
1.19 bug in interlaced PNG corruption check (found by ryg)
1.18 2008-08-02
1.18 (2008-08-02)
fix a threading bug (local mutable static)
1.17 support interlaced PNG
1.16 major bugfix - stbi__convert_format converted one too many pixels
@ -6320,5 +6428,6 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int
0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
0.51 obey req_comp requests, 1-component jpegs return as 1-component,
on 'test' only check type, not whether we support this variant
0.50 first released version
0.50 (2006-11-19)
first released version
*/

View File

@ -1,4 +1,4 @@
/* stb_image_write - v0.96 - public domain - http://nothings.org/stb/stb_image_write.h
/* stb_image_write - v0.98 - public domain - http://nothings.org/stb/stb_image_write.h
writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010
no warranty implied; use at your own risk
@ -9,7 +9,6 @@
in the file that you want to have the implementation.
Will probably not work correctly with strict-aliasing optimizations.
ABOUT:
@ -22,6 +21,13 @@ ABOUT:
for source code compactness and simplicitly, not optimal image file size
or run-time performance.
BUILDING:
You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.
You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace
malloc,realloc,free.
You can define STBIW_MEMMOVE() to replace memmove()
USAGE:
There are four functions, one for each image file format:
@ -32,7 +38,7 @@ USAGE:
int stbi_write_hdr(char const *filename, int w, int h, int comp, const void *data);
Each function returns 0 on failure and non-0 on success.
The functions create an image file defined by the parameters. The image
is a rectangle of pixels stored from left-to-right, top-to-bottom.
Each pixel contains 'comp' channels of data stored interleaved with 8-bits
@ -45,7 +51,7 @@ USAGE:
PNG creates output files with the same number of components as the input.
The BMP format expands Y to RGB in the file format and does not
output alpha.
PNG supports writing rectangles of data even when the bytes storing rows of
data are not consecutive in memory (e.g. sub-rectangles of a larger image),
by supplying the stride between the beginning of adjacent rows. The other
@ -65,8 +71,10 @@ CREDITS:
Baldur Karlsson
TGA monochrome:
Jean-Sebastien Guay
Bugfixes:
Chribba@github
misc enhancements:
Tim Kelsey
bugfixes:
github:Chribba
*/
#ifndef INCLUDE_STB_IMAGE_WRITE_H
@ -93,9 +101,31 @@ extern int stbi_write_hdr(char const *filename, int w, int h, int comp, const fl
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && defined(STBIW_REALLOC)
// ok
#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC)
// ok
#else
#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC."
#endif
#ifndef STBIW_MALLOC
#define STBIW_MALLOC(sz) malloc(sz)
#define STBIW_REALLOC(p,sz) realloc(p,sz)
#define STBIW_FREE(p) free(p)
#endif
#ifndef STBIW_MEMMOVE
#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)
#endif
#ifndef STBIW_ASSERT
#include <assert.h>
#define STBIW_ASSERT(x) assert(x)
#endif
typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];
@ -113,7 +143,7 @@ static void writefv(FILE *f, const char *fmt, va_list v)
b[2]=(unsigned char)(x>>16); b[3]=(unsigned char)(x>>24);
fwrite(b,4,1,f); break; }
default:
assert(0);
STBIW_ASSERT(0);
return;
}
}
@ -135,7 +165,7 @@ static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp,
if (y <= 0)
return;
if (vdir < 0)
if (vdir < 0)
j_end = -1, j = y-1;
else
j_end = y, j = 0;
@ -229,18 +259,18 @@ void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)
}
}
void stbiw__write_rle_data(FILE *f, int length, unsigned char databyte)
void stbiw__write_run_data(FILE *f, int length, unsigned char databyte)
{
unsigned char lengthbyte = 0x80 | (unsigned char)(length & 0x7f);
assert(length <= 0x7f);
unsigned char lengthbyte = (unsigned char) (length+128);
STBIW_ASSERT(length+128 <= 255);
fwrite(&lengthbyte, 1, 1, f);
fwrite(&databyte, 1, 1, f);
}
void stbiw__write_nonrle_data(FILE *f, int length, unsigned char *data)
void stbiw__write_dump_data(FILE *f, int length, unsigned char *data)
{
unsigned char lengthbyte = (unsigned char )(length & 0xff);
assert(length <= 0x7f);
STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code
fwrite(&lengthbyte, 1, 1, f);
fwrite(data, length, 1, f);
}
@ -272,6 +302,7 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
fwrite(rgbe, 4, 1, f);
}
} else {
int c,r;
/* encode into scratch buffer */
for (x=0; x < width; x++) {
switch(comp) {
@ -294,50 +325,41 @@ void stbiw__write_hdr_scanline(FILE *f, int width, int comp, unsigned char *scra
fwrite(scanlineheader, 4, 1, f);
/* RLE each component separately */
for (x=0; x < 4; x++) {
unsigned char *comp = &scratch[width*x];
int runstart = 0, head = 0, rlerun = 0;
for (c=0; c < 4; c++) {
unsigned char *comp = &scratch[width*c];
while (head < width) {
head++;
if (head - runstart == 127 && rlerun == 1) {
// max length RLE run
stbiw__write_rle_data(f, head - runstart, comp[runstart]);
rlerun = 0;
runstart = head;
} else if (head - runstart == 128 && rlerun == 0) {
// max length non-RLE run
stbiw__write_nonrle_data(f, head - runstart, comp+runstart);
rlerun = 0;
runstart = head;
} else if (comp[head] != comp[head-1] && rlerun == 1) {
// end of RLE run
stbiw__write_rle_data(f, head - runstart, comp[runstart]);
rlerun = 0;
runstart = head;
} else {
// continue accumulating RLE run
if (rlerun == 1) continue;
// see if we can start an RLE run, at least 3 bytes same
if (rlerun == 0 && head - runstart >= 2
&& comp[head] == comp[head-1]
&& comp[head] == comp[head-2]) {
// found a run. Flush non-run (if there is anything) and then start an RLE run
if (head - runstart > 2) {
stbiw__write_nonrle_data(f, head-2 - runstart, comp+runstart);
}
rlerun = 1;
runstart = head-2;
x = 0;
while (x < width) {
// find first run
r = x;
while (r+2 < width) {
if (comp[r] == comp[r+1] && comp[r] == comp[r+2])
break;
++r;
}
if (r+2 >= width)
r = width;
// dump up to first run
while (x < r) {
int len = r-x;
if (len > 128) len = 128;
stbiw__write_dump_data(f, len, &comp[x]);
x += len;
}
// if there's a run, output it
if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd
// find next byte after run
while (r < width && comp[r] == comp[x])
++r;
// output run up to r
while (x < r) {
int len = r-x;
if (len > 127) len = 127;
stbiw__write_run_data(f, len, comp[x]);
x += len;
}
}
}
// flush remaining sequence (if any)
if (rlerun == 1) stbiw__write_rle_data(f, head - runstart, comp[runstart]);
else if (head - runstart > 0) stbiw__write_nonrle_data(f, head - runstart, comp+runstart);
}
}
}
@ -350,12 +372,12 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da
f = fopen(filename, "wb");
if (f) {
/* Each component is stored separately. Allocate scratch space for full output scanline. */
unsigned char *scratch = (unsigned char *) malloc(x*4);
unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);
fprintf(f, "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n" );
fprintf(f, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n" , y, x);
for(i=0; i < y; i++)
stbiw__write_hdr_scanline(f, x, comp, scratch, data + comp*i*x);
free(scratch);
STBIW_FREE(scratch);
fclose(f);
}
return f != NULL;
@ -375,13 +397,13 @@ int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *da
#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))
#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0)
#define stbiw__sbfree(a) ((a) ? free(stbiw__sbraw(a)),0 : 0)
#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)
static void *stbiw__sbgrowf(void **arr, int increment, int itemsize)
{
int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;
void *p = realloc(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
assert(p);
void *p = STBIW_REALLOC(*arr ? stbiw__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
STBIW_ASSERT(p);
if (p) {
if (!*arr) ((int *) p)[1] = 0;
*arr = (void *) ((int *) p + 2);
@ -466,7 +488,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
i=0;
while (i < data_len-3) {
// hash next 3 bytes of data to be compressed
// hash next 3 bytes of data to be compressed
int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;
unsigned char *bestloc = 0;
unsigned char **hlist = hash_table[h];
@ -479,7 +501,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
}
// when hash table entry is too long, delete half the entries
if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {
memcpy(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);
stbiw__sbn(hash_table[h]) = quality;
}
stbiw__sbpush(hash_table[h],data+i);
@ -502,7 +524,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
if (bestloc) {
int d = (int) (data+i - bestloc); // distance back
assert(d <= 32767 && best <= 258);
STBIW_ASSERT(d <= 32767 && best <= 258);
for (j=0; best > lengthc[j+1]-1; ++j);
stbiw__zlib_huff(j+257);
if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);
@ -543,7 +565,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l
}
*out_len = stbiw__sbn(out);
// make returned pointer freeable
memmove(stbiw__sbraw(out), out, *out_len);
STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);
return (unsigned char *) stbiw__sbraw(out);
}
@ -590,8 +612,8 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
if (stride_bytes == 0)
stride_bytes = x * n;
filt = (unsigned char *) malloc((x*n+1) * y); if (!filt) return 0;
line_buffer = (signed char *) malloc(x * n); if (!line_buffer) { free(filt); return 0; }
filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;
line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }
for (j=0; j < y; ++j) {
static int mapping[] = { 0,1,2,3,4 };
static int firstmap[] = { 0,1,0,5,6 };
@ -630,20 +652,20 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
}
// when we get here, best contains the filter type, and line_buffer contains the data
filt[j*(x*n+1)] = (unsigned char) best;
memcpy(filt+j*(x*n+1)+1, line_buffer, x*n);
STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);
}
free(line_buffer);
STBIW_FREE(line_buffer);
zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory
free(filt);
STBIW_FREE(filt);
if (!zlib) return 0;
// each tag requires 12 bytes of overhead
out = (unsigned char *) malloc(8 + 12+13 + 12+zlen + 12);
out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
if (!out) return 0;
*out_len = 8 + 12+13 + 12+zlen + 12;
o=out;
memcpy(o,sig,8); o+= 8;
STBIW_MEMMOVE(o,sig,8); o+= 8;
stbiw__wp32(o, 13); // header length
stbiw__wptag(o, "IHDR");
stbiw__wp32(o, x);
@ -657,14 +679,16 @@ unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, in
stbiw__wp32(o, zlen);
stbiw__wptag(o, "IDAT");
memcpy(o, zlib, zlen); o += zlen; free(zlib);
STBIW_MEMMOVE(o, zlib, zlen);
o += zlen;
STBIW_FREE(zlib);
stbiw__wpcrc(&o, zlen);
stbiw__wp32(o,0);
stbiw__wptag(o, "IEND");
stbiw__wpcrc(&o,0);
assert(o == out + *out_len);
STBIW_ASSERT(o == out + *out_len);
return out;
}
@ -676,16 +700,19 @@ int stbi_write_png(char const *filename, int x, int y, int comp, const void *dat
unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);
if (!png) return 0;
f = fopen(filename, "wb");
if (!f) { free(png); return 0; }
if (!f) { STBIW_FREE(png); return 0; }
fwrite(png, 1, len, f);
fclose(f);
free(png);
STBIW_FREE(png);
return 1;
}
#endif // STB_IMAGE_WRITE_IMPLEMENTATION
/* Revision history
0.98 (2015-04-08)
added STBIW_MALLOC, STBIW_ASSERT etc
0.97 (2015-01-18)
fixed HDR asserts, rewrote HDR rle logic
0.96 (2015-01-17)
add HDR output
fix monochrome BMP

117
stb_leakcheck.h Normal file
View File

@ -0,0 +1,117 @@
// stb_leakcheck.h - v0.2 - quick & dirty malloc leak-checking - public domain
#ifdef STB_LEAKCHECK_IMPLEMENTATION
#undef STB_LEAKCHECK_IMPLEMENTATION // don't implenment more than once
// if we've already included leakcheck before, undefine the macros
#ifdef malloc
#undef malloc
#undef free
#undef realloc
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
typedef struct malloc_info stb_leakcheck_malloc_info;
struct malloc_info
{
char *file;
int line;
size_t size;
stb_leakcheck_malloc_info *next,*prev;
};
static stb_leakcheck_malloc_info *mi_head;
void *stb_leakcheck_malloc(size_t sz, char *file, int line)
{
stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi));
if (mi == NULL) return mi;
mi->file = file;
mi->line = line;
mi->next = mi_head;
if (mi_head)
mi->next->prev = mi;
mi->prev = NULL;
mi->size = (int) sz;
mi_head = mi;
return mi+1;
}
void stb_leakcheck_free(void *ptr)
{
if (ptr != NULL) {
stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1;
mi->size = ~mi->size;
#ifndef STB_LEAKCHECK_SHOWALL
if (mi->prev == NULL) {
assert(mi_head == mi);
mi_head = mi->next;
} else
mi->prev->next = mi->next;
if (mi->next)
mi->next->prev = mi->prev;
#endif
}
}
void *stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line)
{
if (ptr == NULL) {
return stb_leakcheck_malloc(sz, file, line);
} else if (sz == 0) {
stb_leakcheck_free(ptr);
return NULL;
} else {
stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1;
if (sz <= mi->size)
return ptr;
else {
#ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE
void *q = stb_leakcheck_malloc(sz, mi->file, mi->line);
#else
void *q = stb_leakcheck_malloc(sz, file, line);
#endif
if (q) {
memcpy(q, ptr, mi->size);
stb_leakcheck_free(ptr);
}
return q;
}
}
}
void stb_leakcheck_dumpmem(void)
{
stb_leakcheck_malloc_info *mi = mi_head;
while (mi) {
if ((ptrdiff_t) mi->size >= 0)
printf("LEAKED: %s (%4d): %8z bytes at %p\n", mi->file, mi->line, mi->size, mi+1);
mi = mi->next;
}
#ifdef STB_LEAKCHECK_SHOWALL
mi = mi_head;
while (mi) {
if ((ptrdiff_t) mi->size < 0)
printf("FREED : %s (%4d): %8z bytes at %p\n", mi->file, mi->line, ~mi->size, mi+1);
mi = mi->next;
}
#endif
}
#endif // STB_LEAKCHECK_IMPLEMENTATION
#ifndef INCLUDE_STB_LEAKCHECK_H
#define INCLUDE_STB_LEAKCHECK_H
#define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__)
#define free(p) stb_leakcheck_free(p)
#define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__)
extern void * stb_leakcheck_malloc(size_t sz, char *file, int line);
extern void * stb_leakcheck_realloc(void *ptr, size_t sz, char *file, int line);
extern void stb_leakcheck_free(void *ptr);
extern void stb_leakcheck_dumpmem(void);
#endif // INCLUDE_STB_LEAKCHECK_H

View File

@ -1,4 +1,4 @@
// stb_rect_pack.h - v0.05 - public domain - rectangle packing
// stb_rect_pack.h - v0.06 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
@ -13,6 +13,7 @@
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
@ -20,8 +21,18 @@
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// Bugfixes / warning fixes
// [your name could be here]
//
// Version history:
//
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
@ -169,7 +180,10 @@ struct stbrp_context
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
@ -524,7 +538,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
}
// sort according to heuristic
qsort(rects, num_rects, sizeof(rects[0]), rect_height_compare);
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
@ -537,7 +551,7 @@ STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int n
}
// unsort
qsort(rects, num_rects, sizeof(rects[0]), rect_original_order);
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags
for (i=0; i < num_rects; ++i)

View File

@ -1,4 +1,4 @@
// stb_textedit.h - v1.5 - public domain - Sean Barrett
// stb_textedit.h - v1.6 - public domain - Sean Barrett
// Development of this library was sponsored by RAD Game Tools
//
// This C header file implements the guts of a multi-line text-editing
@ -24,12 +24,14 @@
//
// DEPENDENCIES
//
// Uses the C runtime function 'memmove'. Uses no other functions.
// Performs no runtime allocations.
// Uses the C runtime function 'memmove', which you can override
// by defining STB_TEXTEDIT_memmove before the implementation.
// Uses no other functions. Performs no runtime allocations.
//
//
// VERSION HISTORY
//
// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove
// 1.5 (2014-09-10) add support for secondary keys for OS X
// 1.4 (2014-08-17) fix signed/unsigned warnings
// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary
@ -45,6 +47,7 @@
// Ulf Winklemann: move-by-word in 1.1
// Scott Graham: mouse selection bugfix in 1.3
// Fabian Giesen: secondary key inputs in 1.5
// Martins Mozeiko: STB_TEXTEDIT_memmove
//
// USAGE
//
@ -358,7 +361,10 @@ typedef struct
// included just the "header" portion
#ifdef STB_TEXTEDIT_IMPLEMENTATION
#include <string.h> // memmove
#ifndef STB_TEXTEDIT_memmove
#include <string.h>
#define STB_TEXTEDIT_memmove memmove
#endif
/////////////////////////////////////////////////////////////////////////////
@ -1038,13 +1044,13 @@ static void stb_textedit_discard_undo(StbUndoState *state)
int n = state->undo_rec[0].insert_length, i;
// delete n characters from all other records
state->undo_char_point = state->undo_char_point - (short) n; // vsnet05
memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
for (i=0; i < state->undo_point; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it
}
--state->undo_point;
memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
}
}
@ -1062,13 +1068,13 @@ static void stb_textedit_discard_redo(StbUndoState *state)
int n = state->undo_rec[k].insert_length, i;
// delete n characters from all other records
state->redo_char_point = state->redo_char_point + (short) n; // vsnet05
memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));
for (i=state->redo_point; i < k; ++i)
if (state->undo_rec[i].char_storage >= 0)
state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05
}
++state->redo_point;
memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0])));
STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0])));
}
}

View File

@ -1,4 +1,4 @@
// stb_tilemap_editor.h - v0.30 - Sean Barrett - http://nothings.org/stb
// stb_tilemap_editor.h - v0.35 - Sean Barrett - http://nothings.org/stb
// placed in the public domain - not copyrighted - first released 2014-09
//
// Embeddable tilemap editor for C/C++
@ -275,6 +275,10 @@
// either approach allows cut&pasting between levels.)
//
// REVISION HISTORY
// 0.35 layername button changes
// - layername buttons grow with the layer panel
// - fix stbte_create_map being declared as stbte_create
// - fix declaration of stbte_create_map
// 0.30 properties release
// - properties panel for editing user-defined "object" properties
// - can link each tile to one other tile
@ -296,11 +300,16 @@
// Support STBTE_HITTEST_TILE above
// ?Cancel drags by clicking other button? - may be fixed
// Finish support for toolbar at side
// Layer name buttons grow to fill box
//
// CREDITS
//
// Written by Sean Barrett, September & October 2014.
//
// Main editor & features
// Sean Barrett
// Additional features:
// Josh Huelsman
// Bugfixes:
// [this could be you!]
//
// LICENSE
//
@ -339,7 +348,7 @@ enum
// creation
//
extern stbte_tilemap *stbte_create(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
// create an editable tilemap
// map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X
// map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y
@ -352,7 +361,7 @@ extern stbte_tilemap *stbte_create(int map_x, int map_y, int map_layers, int spa
extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
// call this repeatedly for each tile to install the tile definitions into the editable tilemap
// tm : tilemap created by stbte_create
// tm : tilemap created by stbte_create_map
// id : unique identifier for each tile, 0 <= id < 32768
// layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7
// (note that onscreen, the editor numbers the layers from 1 not 0)
@ -938,6 +947,7 @@ struct stbte_tilemap
int tileinfo_dirty;
stbte__layer layerinfo[STBTE_MAX_LAYERS];
int has_layer_names;
int layername_width;
int layer_scroll;
int propmode;
int solo_layer;
@ -1016,6 +1026,7 @@ stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacin
tm->layer_scroll = 0;
tm->propmode = 0;
tm->has_layer_names = 0;
tm->layername_width = 0;
tm->undo_available_valid = 0;
for (i=0; i < tm->num_layers; ++i) {
@ -1088,12 +1099,17 @@ void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layerm
tm->tileinfo_dirty = 1;
}
static int stbte__text_width(const char *str);
void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
{
STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
if (layer >= 0 && layer < tm->num_layers) {
int width;
tm->layerinfo[layer].name = layername;
tm->has_layer_names = 1;
width = stbte__text_width(layername);
tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
}
}
@ -3382,14 +3398,21 @@ static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
{
int i, y, n;
int x1 = x0+w;
int y1 = y0+h;
int xoff = tm->has_layer_names ? 50 : 20;
static char *propmodes[3] = {
"default", "always", "never"
};
int num_rows;
int i, y, n;
int x1 = x0+w;
int y1 = y0+h;
int xoff = 20;
if (tm->has_layer_names) {
int side = stbte__ui.panel[STBTE__panel_layers].side;
xoff = stbte__region[side].width - 42;
xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10);
}
x0 += 2;
y0 += 5;
if (!tm->has_layer_names) {
@ -3427,7 +3450,7 @@ static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
n = stbte__text_width("prop:")+2;
stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel));
i = w - n - 4;
if (i > 45) i = 45;
if (i > 50) i = 50;
if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0))
tm->propmode = (tm->propmode+1)%3;
#endif

View File

@ -1,4 +1,4 @@
// stb_truetype.h - v1.02 - public domain
// stb_truetype.h - v1.05 - public domain
// authored from 2009-2014 by Sean Barrett / RAD Game Tools
//
// This library processes TrueType files:
@ -34,12 +34,20 @@
// Johan Duparc
// Hou Qiming
// Fabian "ryg" Giesen
// Martins Mozeiko
// Cap Petschulat
// Omar Cornut
// github:aloucks
// Peter LaValle
//
// Misc other:
// Ryan Gordon
//
// VERSION HISTORY
//
// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
// 1.04 (2015-04-15) typo in example
// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
// non-oversampled; STBTT_POINT_SIZE for packed case only
@ -83,6 +91,9 @@
// before the #include of this file. This expands out the actual
// implementation into that C/C++ file.
//
// To make the implementation private to the file that generates the implementation,
// #define STBTT_STATIC
//
// Simple 3D API (don't ship this, but it's fine for tools and quick start)
// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
// stbtt_GetBakedQuad() -- compute quad to draw for a given char
@ -222,16 +233,16 @@
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h"
char ttf_buffer[1<<20];
unsigned char ttf_buffer[1<<20];
unsigned char temp_bitmap[512*512];
stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
GLstbtt_uint ftex;
GLuint ftex;
void my_stbtt_initfont(void)
{
fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
// can free ttf_buffer at this point
glGenTextures(1, &ftex);
glBindTexture(GL_TEXTURE_2D, ftex);
@ -243,6 +254,7 @@ void my_stbtt_initfont(void)
void my_stbtt_print(float x, float y, char *text)
{
// assume orthographic projection with units = screen pixels, origin at top left
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS);
while (*text) {
@ -428,6 +440,12 @@ int main(int arg, char **argv)
#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
#define __STB_INCLUDE_STB_TRUETYPE_H__
#ifdef STBTT_STATIC
#define STBTT_DEF static
#else
#define STBTT_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -445,7 +463,7 @@ typedef struct
float xoff,yoff,xadvance;
} stbtt_bakedchar;
extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
float pixel_height, // height of font in pixels
unsigned char *pixels, int pw, int ph, // bitmap to be filled in
int first_char, int num_chars, // characters to bake
@ -461,7 +479,7 @@ typedef struct
float x1,y1,s1,t1; // bottom-right
} stbtt_aligned_quad;
extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
int char_index, // character to display
float *xpos, float *ypos, // pointers to current position in screen pixel space
stbtt_aligned_quad *q, // output: quad to draw
@ -498,7 +516,7 @@ typedef struct stbtt_fontinfo stbtt_fontinfo;
typedef struct stbrp_rect stbrp_rect;
#endif
extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
// Initializes a packing context stored in the passed-in stbtt_pack_context.
// Future calls using this context will pack characters into the bitmap passed
// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
@ -509,12 +527,12 @@ extern int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int
//
// Returns 0 on failure, 1 on success.
extern void stbtt_PackEnd (stbtt_pack_context *spc);
STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
// Cleans up the packing context and frees all memory.
#define STBTT_POINT_SIZE(x) (-(x))
extern int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
// Creates character bitmaps from the font_index'th font found in fontdata (use
// font_index=0 if you don't know what that is). It creates num_chars_in_range
@ -537,19 +555,19 @@ typedef struct
stbtt_packedchar *chardata_for_range; // output
} stbtt_pack_range;
extern int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
// Creates character bitmaps from multiple ranges of characters stored in
// ranges. This will usually create a better-packed bitmap than multiple
// calls to stbtt_PackFontRange.
extern int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
extern int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
// Those functions are called by stbtt_PackFontRanges(). If you want to
// pack multiple fonts or custom data into a same texture, you may copy
// the contents of stbtt_PackFontRanges() and create a custom version
// using those functions.
extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
// Oversampling a font increases the quality by allowing higher-quality subpixel
// positioning, and is especially valuable at smaller text sizes.
//
@ -561,7 +579,7 @@ extern void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_ov
// oversampled textures with bilinear filtering. Look at the readme in
// stb/tests/oversample for information about oversampled fonts
extern void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above
STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above
int char_index, // character to display
float *xpos, float *ypos, // pointers to current position in screen pixel space
stbtt_aligned_quad *q, // output: quad to draw
@ -587,7 +605,7 @@ struct stbtt_pack_context {
//
//
extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
// Each .ttf/.ttc file may have more than one font. Each font has a sequential
// index number starting from 0. Call this function to get the font offset for
// a given index; it returns -1 if the index is out of range. A regular .ttf
@ -611,7 +629,7 @@ typedef struct stbtt_fontinfo
int indexToLocFormat; // format needed to map from glyph index to glyph
} stbtt_fontinfo;
extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
// Given an offset into the file that defines a font, this function builds
// the necessary cached info for the rest of the system. You must allocate
// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
@ -623,7 +641,7 @@ extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int o
//
// CHARACTER TO GLYPH-INDEX CONVERSIOn
int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
// If you're going to perform multiple operations on the same character
// and you want a speed-up, call this function with the character you're
// going to process, then use glyph-based functions instead of the
@ -635,7 +653,7 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
// CHARACTER PROPERTIES
//
extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
// computes a scale factor to produce a font whose "height" is 'pixels' tall.
// Height is measured as the distance from the highest ascender to the lowest
// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
@ -643,12 +661,12 @@ extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels)
// scale = pixels / (ascent - descent)
// so if you prefer to measure height by the ascent only, use a similar calculation.
extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
// computes a scale factor to produce a font whose EM size is mapped to
// 'pixels' tall. This is probably what traditional APIs compute, but
// I'm not positive.
extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
// ascent is the coordinate above the baseline the font extends; descent
// is the coordinate below the baseline the font extends (i.e. it is typically negative)
// lineGap is the spacing between one row's descent and the next row's ascent...
@ -656,23 +674,23 @@ extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *
// these are expressed in unscaled coordinates, so you must multiply by
// the scale factor for a given size
extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
// the bounding box around all possible characters
extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
// leftSideBearing is the offset from the current horizontal position to the left edge of the character
// advanceWidth is the offset from the current horizontal position to the next horizontal position
// these are expressed in unscaled coordinates
extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
// an additional amount to add to the 'advance' value between ch1 and ch2
extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
// as above, but takes one or more glyph indices for greater efficiency
@ -700,11 +718,11 @@ extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *
} stbtt_vertex;
#endif
extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
// returns non-zero if nothing is drawn for this glyph
extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
// returns # of vertices and fills *vertices with the pointer to them
// these are expressed in "unscaled" coordinates
//
@ -715,7 +733,7 @@ extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbt
// draws a quadratic bezier from previous endpoint to
// its x,y, using cx,cy as the bezier control point.
extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// frees the data allocated above
//////////////////////////////////////////////////////////////////////////////
@ -723,10 +741,10 @@ extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// BITMAP RENDERING
//
extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
// frees the bitmap allocated below
extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
// allocates a large-enough single-channel 8bpp bitmap and renders the
// specified character/glyph at the specified scale into it, with
// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
@ -735,39 +753,39 @@ extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float
//
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
// shift for the character
extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
// width and height and positioning info for it first.
extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
// shift for the character
extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
// get the bbox of the bitmap centered around the glyph origin; so the
// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
// the bitmap top left is (leftSideBearing*scale,iy0).
// (Note that the bitmap uses y-increases-down, but the shape uses
// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
// shift for the character
// the following functions are equivalent to the above functions, but operate
// on glyph indices instead of Unicode codepoints (for efficiency)
extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
// @TODO: don't expose this structure
@ -777,7 +795,7 @@ typedef struct
unsigned char *pixels;
} stbtt__bitmap;
extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata);
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata);
//////////////////////////////////////////////////////////////////////////////
//
@ -801,7 +819,7 @@ extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stb
// You have to have called stbtt_InitFont() first.
extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
// returns the offset (not index) of the font that matches, or -1 if none
// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
// if you use any other flag, use a font name like "Arial"; this checks
@ -812,11 +830,11 @@ extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *nam
#define STBTT_MACSTYLE_UNDERSCORE 4
#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
// returns 1/0 whether the first string interpreted as utf8 is identical to
// the second string interpreted as big-endian utf16... useful for strings from next func
extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
// returns the string (which may be big-endian double byte, e.g. for unicode)
// and puts the length in bytes in *length.
//
@ -915,10 +933,10 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS
#else
stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
#endif
@ -949,7 +967,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart,
return 0;
}
int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
{
// if it's just a font, there's only one valid index
if (stbtt__isfont(font_collection))
@ -968,7 +986,7 @@ int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
return -1;
}
int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
{
stbtt_uint8 *data = (stbtt_uint8 *) data2;
stbtt_uint32 cmap, t;
@ -1025,7 +1043,7 @@ int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontsta
return 1;
}
int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
{
stbtt_uint8 *data = info->data;
stbtt_uint32 index_map = info->index_map;
@ -1050,7 +1068,6 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
stbtt_uint16 item, offset, start, end;
// do a binary search of the segments
stbtt_uint32 endCount = index_map + 14;
@ -1067,8 +1084,8 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
// now decrement to bias correctly to find smallest
search -= 2;
while (entrySelector) {
stbtt_uint16 end;
searchRange >>= 1;
start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2);
end = ttUSHORT(data + search + searchRange*2);
if (unicode_codepoint > end)
search += searchRange*2;
@ -1076,19 +1093,21 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
}
search += 2;
item = (stbtt_uint16) ((search - endCount) >> 1);
{
stbtt_uint16 offset, start;
stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
end = ttUSHORT(data + index_map + 14 + 2 + 2*item);
if (unicode_codepoint < start)
return 0;
STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
if (unicode_codepoint < start)
return 0;
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
if (offset == 0)
return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
if (offset == 0)
return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
}
} else if (format == 12 || format == 13) {
stbtt_uint32 ngroups = ttULONG(data+index_map+12);
stbtt_int32 low,high;
@ -1117,7 +1136,7 @@ int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
return 0;
}
int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
{
return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
}
@ -1149,7 +1168,7 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
return g1==g2 ? -1 : g1; // if length is 0, return -1
}
int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
{
int g = stbtt__GetGlyfOffset(info, glyph_index);
if (g < 0) return 0;
@ -1161,12 +1180,12 @@ int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int
return 1;
}
int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
{
return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
}
int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
{
stbtt_int16 numberOfContours;
int g = stbtt__GetGlyfOffset(info, glyph_index);
@ -1191,7 +1210,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_
return num_vertices;
}
int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
{
stbtt_int16 numberOfContours;
stbtt_uint8 *endPtsOfContours;
@ -1417,7 +1436,7 @@ int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_verte
return num_vertices;
}
void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
{
stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
if (glyph_index < numOfLongHorMetrics) {
@ -1429,7 +1448,7 @@ void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *ad
}
}
int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@ -1459,26 +1478,26 @@ int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph
return 0;
}
int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
{
if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
return 0;
return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
}
void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
{
stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
}
void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
{
if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
}
void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
{
*x0 = ttSHORT(info->data + info->head + 36);
*y0 = ttSHORT(info->data + info->head + 38);
@ -1486,19 +1505,19 @@ void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int
*y1 = ttSHORT(info->data + info->head + 42);
}
float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
{
int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
return (float) height / fheight;
}
float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
{
int unitsPerEm = ttUSHORT(info->data + info->head + 18);
return pixels / unitsPerEm;
}
void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
{
STBTT_free(v, info->userdata);
}
@ -1508,7 +1527,7 @@ void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
// antialiasing software rasterizer
//
void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
{
int x0,y0,x1,y1;
if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
@ -1526,17 +1545,17 @@ void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, floa
}
}
void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
{
stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
}
void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
{
stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
}
void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
{
stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
}
@ -1817,7 +1836,7 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x
}
// returns number of contours
stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
{
stbtt__point *points=0;
int num_points=0;
@ -1886,7 +1905,7 @@ error:
return NULL;
}
void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
{
float scale = scale_x > scale_y ? scale_y : scale_x;
int winding_count, *winding_lengths;
@ -1898,12 +1917,12 @@ void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vert
}
}
void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
{
STBTT_free(bitmap, userdata);
}
unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
{
int ix0,iy0,ix1,iy1;
stbtt__bitmap gbm;
@ -1940,12 +1959,12 @@ unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float sc
return gbm.pixels;
}
unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
}
void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
{
int ix0,iy0;
stbtt_vertex *vertices;
@ -1964,27 +1983,27 @@ void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *ou
STBTT_free(vertices, info->userdata);
}
void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
{
stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
}
unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
}
void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
{
stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
}
unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
{
return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
}
void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
{
stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
}
@ -1995,7 +2014,7 @@ void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output
//
// This is SUPER-CRAPPY packing to keep source code small
extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
float pixel_height, // height of font in pixels
unsigned char *pixels, int pw, int ph, // bitmap to be filled in
int first_char, int num_chars, // characters to bake
@ -2040,13 +2059,13 @@ extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font
return bottom_y;
}
void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
{
float d3d_bias = opengl_fillrule ? 0 : -0.5f;
float ipw = 1.0f / pw, iph = 1.0f / ph;
stbtt_bakedchar *b = chardata + char_index;
int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5);
int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5);
int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
q->x0 = round_x + d3d_bias;
q->y0 = round_y + d3d_bias;
@ -2143,7 +2162,7 @@ static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rect
// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
{
stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
int num_nodes = pw - padding;
@ -2174,13 +2193,13 @@ int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int
return 1;
}
void stbtt_PackEnd (stbtt_pack_context *spc)
STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
{
STBTT_free(spc->nodes , spc->user_allocator_context);
STBTT_free(spc->pack_info, spc->user_allocator_context);
}
void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
{
STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
@ -2200,7 +2219,7 @@ static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_i
for (j=0; j < h; ++j) {
int i;
unsigned int total;
memset(buffer, 0, kernel_width);
STBTT_memset(buffer, 0, kernel_width);
total = 0;
@ -2254,7 +2273,7 @@ static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_i
for (j=0; j < w; ++j) {
int i;
unsigned int total;
memset(buffer, 0, kernel_width);
STBTT_memset(buffer, 0, kernel_width);
total = 0;
@ -2313,7 +2332,7 @@ static float stbtt__oversample_shift(int oversample)
}
// rects array must be big enough to accommodate all characters in the given ranges
int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
int i,j,k;
@ -2331,7 +2350,7 @@ int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *inf
&x0,&y0,&x1,&y1);
rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
++k;
++k;
}
}
@ -2339,7 +2358,7 @@ int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *inf
}
// rects array must be big enough to accommodate all characters in the given ranges
int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
{
float recip_h = 1.0f / spc->h_oversample;
float recip_v = 1.0f / spc->v_oversample;
@ -2409,7 +2428,7 @@ int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo
return return_value;
}
int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
{
stbtt_fontinfo info;
int i,j,n, return_value = 1;
@ -2440,10 +2459,11 @@ int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int f
return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
STBTT_free(rects, spc->user_allocator_context);
return return_value;
}
int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
{
stbtt_pack_range range;
@ -2454,14 +2474,14 @@ int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int fo
return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
}
void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
{
float ipw = 1.0f / pw, iph = 1.0f / ph;
stbtt_packedchar *b = chardata + char_index;
if (align_to_integer) {
float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5);
float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5);
float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
q->x0 = x;
q->y0 = y;
q->x1 = x + b->xoff2 - b->xoff;
@ -2527,14 +2547,14 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8
return i;
}
int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
{
return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2);
}
// returns results in whatever encoding you request... but note that 2-byte encodings
// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
{
stbtt_int32 i,count,stringOffset;
stbtt_uint8 *fc = font->data;
@ -2631,7 +2651,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam
return 0;
}
int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
{
stbtt_int32 i;
for (i=0;;++i) {

View File

@ -1,4 +1,4 @@
// Ogg Vorbis audio decoder - v1.04 - public domain
// Ogg Vorbis audio decoder - v1.05 - public domain
// http://nothings.org/stb_vorbis/
//
// Written by Sean Barrett in 2007, last updated in 2014
@ -24,12 +24,13 @@
// Casey Muratori John Bolton Gargaj
// Laurent Gomila Marc LeBlanc Ronny Chevalier
// Bernhard Wodo Evan Balster "alxprd"@github
// Tom Beaumont Ingo Leitgeb
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
// (If you reported a bug but do not appear in this list, it is because
// someone else reported the bug before you. There were too many of you to
// list them all because I was lax about updating for a long time, sorry.)
//
// Partial history:
// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant
// 1.04 - 2014/08/27 - fix missing const-correct case in API
// 1.03 - 2014/08/07 - warning fixes
// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows
@ -553,7 +554,7 @@ enum STBVorbisError
#define NULL 0
#endif
#ifndef _MSC_VER
#if !defined(_MSC_VER) && !(defined(__MINGW32__) && defined(__forceinline))
#if __GNUC__
#define __forceinline inline
#else
@ -5397,6 +5398,7 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in
#endif // STB_VORBIS_NO_PULLDATA_API
/* Version history
1.05 - 2015/04/19 - don't define __forceinline if it's redundant
1.04 - 2014/08/27 - fix missing const-correct case in API
1.03 - 2014/08/07 - Warning fixes
1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows

3644
stb_voxel_render.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
// stretchy_buffer.h - v1.01 - public domain - nothings.org/stb
// stretchy_buffer.h - v1.02 - public domain - nothings.org/stb
// a vector<>-like dynamic array for C
//
// version history:
// 1.02 - compiles as C++, but untested
// 1.01 - added a "common uses" documentation section
// 1.0 - fixed bug in the version I posted prematurely
// 0.9 - rewrite to try to avoid strict-aliasing optimization
@ -193,7 +194,7 @@ static void * stb__sbgrowf(void *arr, int increment, int itemsize)
int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
int min_needed = stb_sb_count(arr) + increment;
int m = dbl_cur > min_needed ? dbl_cur : min_needed;
int *p = realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
if (p) {
if (!arr)
p[1] = 0;

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

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

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

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

View File

@ -0,0 +1,927 @@
// This file takes minecraft chunks (decoded by cave_parse) and
// uses stb_voxel_render to turn them into vertex buffers.
#define STB_GLEXT_DECLARE "glext_list.h"
#include "stb_gl.h"
#include "stb_image.h"
#include "stb_glprog.h"
#include "caveview.h"
#include "cave_parse.h"
#include "stb.h"
#include "sdl.h"
#include "sdl_thread.h"
#include <math.h>
//#define VHEIGHT_TEST
//#define STBVOX_OPTIMIZED_VHEIGHT
#define STBVOX_CONFIG_MODE 1
#define STBVOX_CONFIG_OPENGL_MODELVIEW
#define STBVOX_CONFIG_PREFER_TEXBUFFER
//#define STBVOX_CONFIG_LIGHTING_SIMPLE
#define STBVOX_CONFIG_FOG_SMOOTHSTEP
//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // this doesn't work properly alpha test without next #define
//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better
//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
#define STBVOX_CONFIG_DISABLE_TEX2
#define STBVOX_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 48 // use to avoid building underground stuff
// allows you to see what perf would be like if underground was efficiently culled,
// or if you were making a game without underground
enum
{
C_empty,
C_solid,
C_trans,
C_cross,
C_water,
C_slab,
C_stair,
C_force,
};
unsigned char geom_map[] =
{
STBVOX_GEOM_empty,
STBVOX_GEOM_solid,
STBVOX_GEOM_transp,
STBVOX_GEOM_crossed_pair,
STBVOX_GEOM_solid,
STBVOX_GEOM_slab_lower,
STBVOX_GEOM_floor_slope_north_is_top,
STBVOX_GEOM_force,
};
unsigned char minecraft_info[256][7] =
{
{ C_empty, 0,0,0,0,0,0 },
{ C_solid, 1,1,1,1,1,1 },
{ C_solid, 3,3,3,3,40,2 },
{ C_solid, 2,2,2,2,2,2 },
{ C_solid, 16,16,16,16,16,16 },
{ C_solid, 4,4,4,4,4,4 },
{ C_cross, 15,15,15,15 },
{ C_solid, 17,17,17,17,17,17 },
// 8
{ C_water, 223,223,223,223,223,223 },
{ C_water, 223,223,223,223,223,223 },
{ C_solid, 255,255,255,255,255,255 },
{ C_solid, 255,255,255,255,255,255 },
{ C_solid, 18,18,18,18,18,18 },
{ C_solid, 19,19,19,19,19,19 },
{ C_solid, 32,32,32,32,32,32 },
{ C_solid, 33,33,33,33,33,33 },
// 16
{ C_solid, 34,34,34,34,34,34 },
{ C_solid, 20,20,20,20,21,21 },
#ifdef FANCY_LEAVES
{ C_force, 52,52,52,52,52,52 }, // leaves
#else
{ C_solid, 53,53,53,53,53,53 }, // leaves
#endif
{ C_solid, 24,24,24,24,24,24 },
{ C_trans, 49,49,49,49,49,49 }, // glass
{ C_solid, 160,160,160,160,160,160 },
{ C_solid, 144,144,144,144,144,144 },
{ C_solid, 46,45,45,45,62,62 },
// 24
{ C_solid, 192,192,192,192, 176,176 },
{ C_solid, 74,74,74,74,74,74 },
{ C_empty }, // bed
{ C_empty }, // powered rail
{ C_empty }, // detector rail
{ C_solid, 106,108,109,108,108,108 },
{ C_empty }, // cobweb=11
{ C_cross, 39,39,39,39 },
// 32
{ C_cross, 55,55,55,55,0,0 },
{ C_solid, 107,108,109,108,108,108 },
{ C_empty }, // piston head
{ C_solid, 64,64,64,64,64,64 }, // various colors
{ C_empty }, // unused
{ C_cross, 13,13,13,13,0,0 },
{ C_cross, 12,12,12,12,0,0 },
{ C_cross, 29,29,29,29,0,0 },
// 40
{ C_cross, 28,28,28,28,0,0 },
{ C_solid, 23,23,23,23,23,23 },
{ C_solid, 22,22,22,22,22,22 },
{ C_solid, 5,5,5,5,6,6, },
{ C_slab , 5,5,5,5,6,6, },
{ C_solid, 7,7,7,7,7,7, },
{ C_solid, 8,8,8,8,9,10 },
{ C_solid, 35,35,35,35,4,4, },
// 48
{ C_solid, 36,36,36,36,36,36 },
{ C_solid, 37,37,37,37,37,37 },
{ C_cross, 80,80,80,80,80,80 }, // torch
{ C_empty }, // fire
{ C_trans, 65,65,65,65,65,65 },
{ C_stair, 4,4,4,4,4,4 },
{ C_solid, 26,26,26,27,25,25 },
{ C_empty }, // redstone
// 56
{ C_solid, 50,50,50,50,50,50 },
{ C_solid, 26,26,26,26,26,26 },
{ C_solid, 60,59,59,59,43,43 },
{ C_cross, 95,95,95,95 },
{ C_solid, 2,2,2,2,86,2 },
{ C_solid, 44,45,45,45,62,62 },
{ C_solid, 61,45,45,45,62,62 },
{ C_empty }, // sign
// 64
{ C_empty }, // door
{ C_empty }, // ladder
{ C_empty }, // rail
{ C_stair, 16,16,16,16,16,16 }, // cobblestone stairs
{ C_empty }, // sign
{ C_empty }, // lever
{ C_empty }, // stone pressure plate
{ C_empty }, // iron door
// 72
{ C_empty }, // wooden pressure
{ C_solid, 51,51,51,51,51,51 },
{ C_solid, 51,51,51,51,51,51 },
{ C_empty },
{ C_empty },
{ C_empty },
{ C_empty }, // snow on block below, do as half slab?
{ C_solid, 67,67,67,67,67,67 },
// 80
{ C_solid, 66,66,66,66,66,66 },
{ C_solid, 70,70,70,70,69,71 },
{ C_solid, 72,72,72,72,72,72 },
{ C_cross, 73,73,73,73,73,73 },
{ C_solid, 74,74,74,74,75,74 },
{ C_empty }, // fence
{ C_solid,119,118,118,118,102,102 },
{ C_solid,103,103,103,103,103,103 },
// 88
{ C_solid, 104,104,104,104,104,104 },
{ C_solid, 105,105,105,105,105,105 },
{ C_solid, 167,167,167,167,167,167 },
{ C_solid, 120,118,118,118,102,102 },
{ C_empty }, // cake
{ C_empty }, // repeater
{ C_empty }, // repeater
{ C_solid, 49,49,49,49,49,49 }, // colored glass
// 96
{ C_empty },
{ C_empty },
{ C_solid, 54,54,54,54,54,54 },
{ C_solid, 125,125,125,125,125,125 },
{ C_solid, 126,126,126,126,126,126 },
{ C_empty }, // bars
{ C_trans, 49,49,49,49,49,49 }, // glass pane
{ C_solid, 136,136,136,136,137,137 }, // melon
// 104
{ C_empty }, // pumpkin stem
{ C_empty }, // melon stem
{ C_empty }, // vines
{ C_empty }, // gate
{ C_stair, 7,7,7,7,7,7, }, // brick stairs
{ C_stair, 54,54,54,54,54,54 }, // stone brick stairs
{ C_empty }, // mycelium
{ C_empty }, // lily pad
// 112
{ C_solid, 224,224,224,224,224,224 },
{ C_empty }, // nether brick fence
{ C_stair, 224,224,224,224,224,224 }, // nether brick stairs
{ C_empty }, // nether wart
{ C_solid, 182,182,182,182,166,183 },
{ C_empty }, // brewing stand
{ C_empty }, // cauldron
{ C_empty }, // end portal
// 120
{ C_solid, 159,159,159,159,158,158 },
{ C_solid, 175,175,175,175,175,175 },
{ C_empty }, // dragon egg
{ C_solid, 211,211,211,211,211,211 },
{ C_solid, 212,212,212,212,212,212 },
{ C_solid, 4,4,4,4,4,4, }, // wood double-slab
{ C_slab , 4,4,4,4,4,4, }, // wood slab
{ C_empty }, // cocoa
// 128
{ C_solid, 192,192,192,192,176,176 }, // sandstone stairs
{ C_solid, 32,32,32,32,32,32 }, // emerald ore
{ C_solid, 26,26,26,27,25,25 }, // ender chest
{ C_empty },
{ C_empty },
{ C_solid, 23,23,23,23,23,23 }, // emerald block
{ C_solid, 198,198,198,198,198,198 }, // spruce stairs
{ C_solid, 214,214,214,214,214,214 }, // birch stairs
// 136
{ C_stair, 199,199,199,199,199,199 }, // jungle stairs
{ C_empty }, // command block
{ C_empty }, // beacon
{ C_slab, 16,16,16,16,16,16 }, // cobblestone wall
{ C_empty }, // flower pot
{ C_empty }, // carrot
{ C_empty }, // potatoes
{ C_empty }, // wooden button
// 144
{ C_empty }, // mob head
{ C_empty }, // anvil
{ C_solid, 26,26,26,27,25,25 }, // trapped chest
{ C_empty }, // weighted pressure plate light
{ C_empty }, // weighted pressure plat eheavy
{ C_empty }, // comparator inactive
{ C_empty }, // comparator active
{ C_empty }, // daylight sensor
// 152
{ C_solid, 135,135,135,135,135,135 }, // redstone block
{ C_solid, 0,0,0,0,0,0, }, // nether quartz ore
{ C_empty }, // hopper
{ C_solid, 22,22,22,22,22,22 }, // quartz block
{ C_stair, 22,22,22,22,22,22 }, // quartz stairs
{ C_empty }, // activator rail
{ C_solid, 46,45,45,45,62,62 }, // dropper
{ C_solid, 72,72,72,72,72,72 }, // stained clay
// 160
{ C_trans, 49,49,49,49,49,49 }, // stained glass pane
#ifdef FANCY_LEAVES
{ C_force, 52,52,52,52,52,52 }, // leaves
#else
{ C_solid, 53,53,53,53,53,53 }, // acacia leaves
#endif
{ C_solid, 20,20,20,20,21,21 }, // acacia tree
{ C_solid, 199,199,199,199,199,199 }, // acacia wood stairs
{ C_solid, 198,198,198,198,198,198 }, // dark oak stairs
{ C_solid, 146,146,146,146,146,146 }, // slime block
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
// 168
{ C_empty },
{ C_empty },
{ C_empty },
{ C_empty },
{ C_solid, 72,72,72,72,72,72 }, // hardened clay
{ C_empty },
{ C_empty },
{ C_empty },
// 176
{ C_empty },
{ C_empty },
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
};
unsigned char minecraft_tex1_for_blocktype[256][6];
unsigned char effective_blocktype[256];
unsigned char minecraft_color_for_blocktype[256][6];
unsigned char minecraft_geom_for_blocktype[256];
uint8 build_buffer[BUILD_BUFFER_SIZE];
uint8 face_buffer[FACE_BUFFER_SIZE];
//GLuint vbuf, fbuf, fbuf_tex;
//unsigned char tex1_for_blocktype[256][6];
//unsigned char blocktype[34][34][257];
//unsigned char lighting[34][34][257];
// a superchunk is 64x64x256, with the border blocks computed as well,
// which means we need 4x4 chunks plus 16 border chunks plus 4 corner chunks
#define SUPERCHUNK_X 4
#define SUPERCHUNK_Y 4
unsigned char remap_data[16][16];
unsigned char remap[256];
unsigned char rotate_data[4] = { 1,3,2,0 };
void convert_fastchunk_inplace(fast_chunk *fc)
{
int i;
int num_blocks=0, step=0;
unsigned char rot[4096];
#ifndef IN_PLACE
unsigned char *storage;
#endif
memset(rot, 0, 4096);
for (i=0; i < 16; ++i)
num_blocks += fc->blockdata[i] != NULL;
#ifndef IN_PLACE
storage = malloc(16*16*16*2 * num_blocks);
#endif
for (i=0; i < 16; ++i) {
if (fc->blockdata[i]) {
int o=0;
unsigned char *bd,*dd,*lt,*sky;
unsigned char *out, *outb;
// this ordering allows us to determine which data we can safely overwrite for in-place processing
bd = fc->blockdata[i];
dd = fc->data[i];
lt = fc->light[i];
sky = fc->skylight[i];
#ifdef IN_PLACE
out = bd;
#else
out = storage + 16*16*16*2*step;
#endif
// bd is written in place, but also reads from dd
for (o=0; o < 16*16*16/2; o += 1) {
unsigned char v1,v2;
unsigned char d = dd[o];
v1 = bd[o*2+0];
v2 = bd[o*2+1];
if (remap[v1])
{
//unsigned char d = bd[o] & 15;
v1 = remap_data[remap[v1]][d&15];
rot[o*2+0] = rotate_data[d&3];
} else
v1 = effective_blocktype[v1];
if (remap[v2])
{
//unsigned char d = bd[o] >> 4;
v2 = remap_data[remap[v2]][d>>4];
rot[o*2+1] = rotate_data[(d>>4)&3];
} else
v2 = effective_blocktype[v2];
out[o*2+0] = v1;
out[o*2+1] = v2;
}
// this reads from lt & sky
#ifndef IN_PLACE
outb = out + 16*16*16;
++step;
#endif
// MC used to write in this order and it makes it possible to compute in-place
if (dd < sky && sky < lt) {
// @TODO go this path always if !IN_PLACE
#ifdef IN_PLACE
outb = dd;
#endif
for (o=0; o < 16*16*16/2; ++o) {
int bright;
bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16;
if (bright > 255) bright = 255;
if (bright < 32) bright = 32;
outb[o*2+0] = STBVOX_MAKE_LIGHTING_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
}
#ifdef VHEIGHT_TEST
effective_blocktype[168] = 168;
minecraft_tex1_for_blocktype[168][0] = 1;
minecraft_tex1_for_blocktype[168][1] = 1;
minecraft_tex1_for_blocktype[168][2] = 1;
minecraft_tex1_for_blocktype[168][3] = 1;
minecraft_tex1_for_blocktype[168][4] = 1;
minecraft_tex1_for_blocktype[168][5] = 1;
minecraft_geom_for_blocktype[168] = STBVOX_GEOM_floor_vheight_12;
effective_blocktype[169] = 169;
minecraft_tex1_for_blocktype[169][0] = 1;
minecraft_tex1_for_blocktype[169][1] = 1;
minecraft_tex1_for_blocktype[169][2] = 1;
minecraft_tex1_for_blocktype[169][3] = 1;
minecraft_tex1_for_blocktype[169][4] = 1;
minecraft_tex1_for_blocktype[169][5] = 1;
minecraft_geom_for_blocktype[169] = STBVOX_GEOM_ceil_vheight_03;
#endif
remap[53] = 1;
remap[67] = 2;
remap[108] = 3;
remap[109] = 4;
remap[114] = 5;
remap[136] = 6;
remap[156] = 7;
for (i=0; i < 256; ++i)
if (remap[i])
build_stair_rotations(i, remap_data[remap[i]]);
remap[35] = 8;
build_wool_variations(35, remap_data[remap[35]]);
remap[5] = 11;
build_wood_variations(5, remap_data[remap[5]]);
// set the remap flags for these so they write the rotation values
remap_in_place(54, 9);
remap_in_place(146, 10);
}
// Timing stats while optimizing the single-threaded builder
// 32..-32, 32..-32, SKIP_TERRAIN=0, !FANCY_LEAVES on 'mcrealm' data set
// 6.27s - reblocked to do 16 z at a time instead of 256 (still using 66x66x258), 4 meshes in parallel
// 5.96s - reblocked to use FAST_CHUNK (no intermediate data structure)
// 5.45s - unknown change, or previous measurement was wrong
// 6.12s - use preconverted data, not in-place
// 5.91s - use preconverted, in-place
// 5.34s - preconvert, in-place, avoid dependency chain (suggested by ryg)
// 5.34s - preconvert, in-place, avoid dependency chain, use bit-table instead of byte-table
// 5.50s - preconvert, in-place, branchless
// 6.42s - non-preconvert, avoid dependency chain (not an error)
// 5.40s - non-preconvert, w/dependency chain (same as earlier)
// 5.50s - non-FAST_CHUNK, reblocked outer loop for better cache reuse
// 4.73s - FAST_CHUNK non-preconvert, reblocked outer loop
// 4.25s - preconvert, in-place, reblocked outer loop
// 4.18s - preconvert, in-place, unrolled again
// 4.10s - 34x34 1 mesh instead of 66x66 and 4 meshes (will make it easier to do multiple threads)
// 4.83s - building bitmasks but not using them (2 bits per block, one if empty, one if solid)
// 5.16s - using empty bitmasks to early out
// 5.01s - using solid & empty bitmasks to early out - "foo"
// 4.64s - empty bitmask only, test 8 at a time, then test geom
// 4.72s - empty bitmask only, 8 at a time, then test bits
// 4.46s - split bitmask building into three loops (each byte is separate)
// 4.42s - further optimize computing bitmask
// 4.58s - using solid & empty bitmasks to early out, same as "foo" but faster bitmask building
// 4.12s - using solid & empty bitmasks to efficiently test neighbors
// 4.04s - using 16-bit fetches (not endian-independent)
// - note this is first place that beats previous best '4.10s - 34x34 1 mesh'
// 4.30s - current time with bitmasks disabled again (note was 4.10s earlier)
// 3.95s - bitmasks enabled again, no other changes
// 4.00s - current time with bitmasks disabled again, no other changes -- wide variation that is time dependent?
// (note that most of the numbers listed here are median of 3 values already)
// 3.98s - bitmasks enabled
// Bitmasks removed from the code as not worth the complexity increase
// Raw data for Q&A:
//
// 26% parsing & loading minecraft files (4/5ths of which is zlib decode)
// 39% building mesh from stb input format
// 18% converting from minecraft blocks to stb blocks
// 9% reordering from minecraft axis order to stb axis order
// 7% uploading vertex buffer to OpenGL

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

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

View File

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

View File

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

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

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

View File

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

50
tests/caveview/caveview.h Normal file
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
tests/caveview/glext.h Normal file

File diff suppressed because it is too large Load Diff

View File

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

0
tests/caveview/main.c Normal file
View File

1103
tests/caveview/stb_gl.h Normal file

File diff suppressed because it is too large Load Diff

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

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

View File

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

View File

@ -51,10 +51,24 @@ void test_ycbcr(void)
}
#endif
float hdr_data[200][200][3];
int main(int argc, char **argv)
{
int w,h;
//test_ycbcr();
#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;

View File

@ -122,6 +122,10 @@ SOURCE=..\stb_image_write.h
# End Source File
# Begin Source File
SOURCE=..\stb_leakcheck.h
# End Source File
# Begin Source File
SOURCE=..\stb_perlin.h
# End Source File
# Begin Source File
@ -146,6 +150,10 @@ SOURCE=..\stb_vorbis.c
# End Source File
# Begin Source File
SOURCE=..\stb_voxel_render.h
# End Source File
# Begin Source File
SOURCE=..\stretchy_buffer.h
# End Source File
# Begin Source File
@ -162,6 +170,10 @@ SOURCE=.\test_truetype.c
# End Source File
# Begin Source File
SOURCE=.\test_vorbis.c
# End Source File
# Begin Source File
SOURCE=.\textedit_sample.c
# End Source File
# End Target

View File

@ -80,4 +80,3 @@ int main(int argc, char **argv)
}
return 0;
}

View File

@ -7,6 +7,7 @@
#define STB_HERRINGBONE_WANG_TILE_IMEPLEMENTATIOn
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_RECT_PACK_IMPLEMENTATION
#define STB_VOXEL_RENDER_IMPLEMENTATION
#include "stb_herringbone_wang_tile.h"
#include "stb_image.h"
@ -18,6 +19,9 @@
#include "stb_image_resize.h"
#include "stb_rect_pack.h"
#define STBVOX_CONFIG_MODE 1
#include "stb_voxel_render.h"
#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) 0
#define STBTE_DRAW_TILE(x,y,id,highlight,data) 0
#define STB_TILEMAP_EDITOR_IMPLEMENTATION

View File

@ -7,6 +7,7 @@
#define STB_IMAGE_IMPLEMENTATION
#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
#define STB_RECT_PACK_IMPLEMENTATION
#define STB_VOXEL_RENDER_IMPLEMENTATION
#define STBI_MALLOC my_malloc
#define STBI_FREE my_free
@ -26,7 +27,111 @@ void my_free(void *) { }
#include "stb_divide.h"
#include "stb_herringbone_wang_tile.h"
#define STBVOX_CONFIG_MODE 1
#include "stb_voxel_render.h"
#define STBTE_DRAW_RECT(x0,y0,x1,y1,color) do ; while(0)
#define STBTE_DRAW_TILE(x,y,id,highlight,data) do ; while(0)
#define STB_TILEMAP_EDITOR_IMPLEMENTATION
#include "stb_tilemap_editor.h"
#include "stb_easy_font.h"
#define STB_LEAKCHECK_IMPLEMENTATION
#include "stb_leakcheck.h"
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"
#include "stretchy_buffer.h"
////////////////////////////////////////////////////////////
//
// text edit
#include <stdlib.h>
#include <string.h> // memmove
#include <ctype.h> // isspace
#define STB_TEXTEDIT_CHARTYPE char
#define STB_TEXTEDIT_STRING text_control
// get the base type
#include "stb_textedit.h"
// define our editor structure
typedef struct
{
char *string;
int stringlen;
STB_TexteditState state;
} text_control;
// define the functions we need
void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i)
{
int remaining_chars = str->stringlen - start_i;
row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here
row->x0 = 0;
row->x1 = 20; // need to account for actual size of characters
row->baseline_y_delta = 1.25;
row->ymin = -1;
row->ymax = 0;
}
int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num)
{
memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos+num));
str->stringlen -= num;
return 1; // always succeeds
}
int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num)
{
str->string = (char *) realloc(str->string, str->stringlen + num);
memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos);
memcpy(&str->string[pos], newtext, num);
str->stringlen += num;
return 1; // always succeeds
}
// define all the #defines needed
#define KEYDOWN_BIT 0x80000000
#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen)
#define STB_TEXTEDIT_LAYOUTROW layout_func
#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced
#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key))
#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i])
#define STB_TEXTEDIT_NEWLINE '\n'
#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch)
#define STB_TEXTEDIT_DELETECHARS delete_chars
#define STB_TEXTEDIT_INSERTCHARS insert_chars
#define STB_TEXTEDIT_K_SHIFT 0x40000000
#define STB_TEXTEDIT_K_CONTROL 0x20000000
#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc
#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT
#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP
#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN
#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME
#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END
#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL)
#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL)
#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE
#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE
#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z')
#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y')
#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT
#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL)
#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL)
#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented
#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented
#define STB_TEXTEDIT_IMPLEMENTATION
#include "stb_textedit.h"

18
tests/test_vorbis.c Normal file
View File

@ -0,0 +1,18 @@
#define STB_VORBIS_HEADER_ONLY
#include "stb_vorbis.c"
#include "stb.h"
extern void stb_vorbis_dumpmem(void);
#ifdef VORBIS_TEST
int main(int argc, char **argv)
{
size_t memlen;
unsigned char *mem = stb_fileu("c:/x/01.ogg", &memlen);
int chan, samplerate;
short *output;
int samples = stb_vorbis_decode_memory(mem, memlen, &chan, &samplerate, &output);
stb_vorbis_dumpmem();
return 0;
}
#endif

View File

@ -20,6 +20,40 @@ attribution requirement). They may be less featureful, slower,
and/or use more memory. If you're already using an equivalent
library, there's probably no good reason to switch.
#### Why do you list "lines of code"? It's a terrible metric.
Just to give you some idea of the internal complexity of the library,
to help you manage your expectations, or to let you know what you're
getting into. While not all the libraries are written in the same
style, they're certainly similar styles, and so comparisons between
the libraries are probably still meaningful.
Note though that the lines do include both the implementation, the
part that corresponds to a header file, and the documentation.
#### Why single-file headers?
Windows doesn't have standard directories where libraries
live. That makes deploying libraries in Windows a lot more
painful than open source developers on Unix-derivates generally
realize. (It also makes library dependencies a lot worse in Windows.)
There's also a common problem in Windows where a library was built
against a different version of the runtime library, which causes
link conflicts and confusion. Shipping the libs as headers means
you normally just compile them straight into your project without
making libraries, thus sidestepping that problem.
Making them a single file makes it very easy to just
drop them into a project that needs them. (Of course you can
still put them in a proper shared library tree if you want.)
Why not two files, one a header and one an implementation?
The difference between 10 files and 9 files is not a big deal,
but the difference between 2 files and 1 file is a big deal.
You don't need to zip or tar the files up, you don't have to
remember to attach *two* files, etc.
#### Why "stb"? Is this something to do with Set-Top Boxes?
No, they are just the initials for my name, Sean T. Barrett.
@ -44,12 +78,9 @@ Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
#### Why public domain?
Because more people will use it. Because it's not viral, people
are not obligated to give back, so you could argue that it hurts
the *development* of it, and then because it doesn't develop as
well it's not as good, and then because it's not as good, in the
long run maybe fewer people will use it. I have total respect for
that opinion, but I just don't believe it myself for most software.
I prefer it over GPL, LGPL, BSD, zlib, etc. for many reasons.
Some of them are listed here:
https://github.com/nothings/stb/blob/master/docs/why_public_domain.md
#### Why C?

View File

@ -3,5 +3,5 @@ stb
single-file public domain libraries for C/C++
library | lastest version | category | description
--------------------- | ---- | -------- | --------------------------------
library | lastest version | category | LoC | description
--------------------- | ---- | -------- | --- | --------------------------------

View File

@ -1,15 +1,18 @@
stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output
stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts
stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP
stb_image_resize.h | graphics | resize images larger/smaller with good quality
stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality
stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
stb_textedit.h | UI | guts of a text editor for games etc implementing them from scratch
stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output)
stb_tilemap_editor.h | games | embeddable tilemap editor
stb_herringbone_wang_tile.h | games | herringbone Wang tile map generator
stb_c_lexer.h | parsing | simplify writing parsers for C-like languages
stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide"
stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff
stb_vorbis.c | audio | decode ogg vorbis files from file/memory to float/16-bit signed output
stb_image.h | graphics | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
stb_truetype.h | graphics | parse, decode, and rasterize characters from truetype fonts
stb_image_write.h | graphics | image writing to disk: PNG, TGA, BMP
stb_image_resize.h | graphics | resize images larger/smaller with good quality
stb_rect_pack.h | graphics | simple 2D rectangle packer with decent quality
stretchy_buffer.h | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++
stb_textedit.h | UI | guts of a text editor for games etc implementing them from scratch
stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features
stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output)
stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
stb_tilemap_editor.h | game dev | embeddable tilemap editor
stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator
stb_c_lexer.h | parsing | simplify writing parsers for C-like languages
stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide"
stb.h | misc | helper functions for C, mostly redundant in C++; basically author's personal stuff
stb_leakcheck.h | misc | quick-and-dirty malloc/free leak-checking

211
tools/easy_font_maker.c Normal file
View File

@ -0,0 +1,211 @@
// This program was used to encode the data for stb_simple_font.h
#define STB_DEFINE
#include "stb.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int w,h;
uint8 *data;
int last_x[2], last_y[2];
int num_seg[2], non_empty;
#if 0
typedef struct
{
unsigned short first_segment;
unsigned char advance;
} chardata;
typedef struct
{
unsigned char x:4;
unsigned char y:4;
unsigned char len:3;
unsigned char dir:1;
} segment;
segment *segments;
void add_seg(int x, int y, int len, int horizontal)
{
segment s;
s.x = x;
s.y = y;
s.len = len;
s.dir = horizontal;
assert(s.x == x);
assert(s.y == y);
assert(s.len == len);
stb_arr_push(segments, s);
}
#else
typedef struct
{
unsigned char first_segment:8;
unsigned char first_v_segment:8;
unsigned char advance:5;
unsigned char voff:1;
} chardata;
#define X_LIMIT 1
#define LEN_LIMIT 7
typedef struct
{
unsigned char dx:1;
unsigned char y:4;
unsigned char len:3;
} segment;
segment *segments;
segment *vsegments;
void add_seg(int x, int y, int len, int horizontal)
{
segment s;
while (x - last_x[horizontal] > X_LIMIT) {
add_seg(last_x[horizontal] + X_LIMIT, 0, 0, horizontal);
}
while (len > LEN_LIMIT) {
add_seg(x, y, LEN_LIMIT, horizontal);
len -= LEN_LIMIT;
x += LEN_LIMIT*horizontal;
y += LEN_LIMIT*!horizontal;
}
s.dx = x - last_x[horizontal];
s.y = y;
s.len = len;
non_empty += len != 0;
//assert(s.x == x);
assert(s.y == y);
assert(s.len == len);
++num_seg[horizontal];
if (horizontal)
stb_arr_push(segments, s);
else
stb_arr_push(vsegments, s);
last_x[horizontal] = x;
}
void print_segments(segment *s)
{
int i, hpos;
printf(" ");
hpos = 4;
for (i=0; i < stb_arr_len(s); ++i) {
// repack for portability
unsigned char seg = s[i].len + s[i].dx*8 + s[i].y*16;
hpos += printf("%d,", seg);
if (hpos > 72 && i+1 < stb_arr_len(s)) {
hpos = 4;
printf("\n ");
}
}
printf("\n");
}
#endif
chardata charinfo[128];
int parse_char(int x, chardata *c, int offset)
{
int start_x = x, end_x, top_y = 0, y;
c->first_segment = stb_arr_len(segments);
c->first_v_segment = stb_arr_len(vsegments) - offset;
assert(c->first_segment == stb_arr_len(segments));
assert(c->first_v_segment + offset == stb_arr_len(vsegments));
// find advance distance
end_x = x+1;
while (data[end_x*3] == 255)
++end_x;
c->advance = end_x - start_x + 1;
last_x[0] = last_x[1] = 0;
last_y[0] = last_y[1] = 0;
for (y=2; y < h; ++y) {
for (x=start_x; x < end_x; ++x) {
if (data[y*3*w+x*3+1] < 255) {
top_y = y;
break;
}
}
if (top_y)
break;
}
c->voff = top_y > 2;
if (top_y > 2)
top_y = 3;
for (x=start_x; x < end_x; ++x) {
int y;
for (y=2; y < h; ++y) {
if (data[y*3*w+x*3+1] < 255) {
if (data[y*3*w+x*3+0] == 255) { // red
int len=0;
while (y+len < h && data[(y+len)*3*w+x*3+0] == 255 && data[(y+len)*3*w+x*3+1] == 0) {
data[(y+len)*3*w+x*3+0] = 0;
++len;
}
add_seg(x-start_x,y-top_y,len,0);
}
if (data[y*3*w+x*3+2] == 255) { // blue
int len=0;
while (x+len < end_x && data[y*3*w+(x+len)*3+2] == 255 && data[y*3*w+(x+len)*3+1] == 0) {
data[y*3*w+(x+len)*3+2] = 0;
++len;
}
add_seg(x-start_x,y-top_y,len,1);
}
}
}
}
return end_x;
}
int main(int argc, char **argv)
{
int c, x=0;
data = stbi_load("easy_font_raw.png", &w, &h, 0, 3);
for (c=32; c < 127; ++c) {
x = parse_char(x, &charinfo[c], 0);
printf("%3d -- %3d %3d\n", c, charinfo[c].first_segment, charinfo[c].first_v_segment);
}
printf("===\n");
printf("%d %d %d\n", num_seg[0], num_seg[1], non_empty);
printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments));
printf("%d\n", sizeof(segments[0]) * stb_arr_len(segments) + sizeof(segments[0]) * stb_arr_len(vsegments) + sizeof(charinfo[32])*95);
printf("struct {\n"
" unsigned char advance;\n"
" unsigned char h_seg;\n"
" unsigned char v_seg;\n"
"} stb_easy_font_charinfo[96] = {\n");
charinfo[c].first_segment = stb_arr_len(segments);
charinfo[c].first_v_segment = stb_arr_len(vsegments);
for (c=32; c < 128; ++c) {
if ((c & 3) == 0) printf(" ");
printf("{ %2d,%3d,%3d },",
charinfo[c].advance + 16*charinfo[c].voff,
charinfo[c].first_segment,
charinfo[c].first_v_segment);
if ((c & 3) == 3) printf("\n"); else printf(" ");
}
printf("};\n\n");
printf("unsigned char stb_easy_font_hseg[%d] = {\n", stb_arr_len(segments));
print_segments(segments);
printf("};\n\n");
printf("unsigned char stb_easy_font_vseg[%d] = {\n", stb_arr_len(vsegments));
print_segments(vsegments);
printf("};\n");
return 0;
}

View File

@ -4,23 +4,23 @@
int main(int argc, char **argv)
{
int i;
int hlen, flen, listlen;
int hlen, flen, listlen, total_lines = 0;
char *header = stb_file("README.header.md", &hlen); // stb_file - read file into malloc()ed buffer
char *footer = stb_file("README.footer.md", &flen); // stb_file - read file into malloc()ed buffer
char **list = stb_stringfile("README.list", &listlen); // stb_stringfile - read file lines into malloced array of strings
FILE *f = fopen("../README.md", "wb");
fprintf(f, "<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->\n\n");
fwrite(header, 1, hlen, f);
for (i=0; i < listlen; ++i) {
int num,j;
char **tokens = stb_tokens_stripwhite(list[i], "|", &num); // stb_tokens -- tokenize string into malloced array of strings
FILE *g = fopen(stb_sprintf("../%s", tokens[0]), "rb"); // stb_sprintf -- sprintf to a static temp buffer (not threadsafe or secure)
char buffer[256], *s1, *s2;
fread(buffer, 1, 256, g);
fclose(g);
buffer[255] = 0;
s1 = strchr(buffer, '-');
int num_lines;
char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines);
char *s1, *s2,*s3;
s1 = strchr(lines[0], '-');
if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit
s2 = strchr(s1+2, '-');
if (!s2) stb_fatal("Couldn't find '-' after version number in %s", tokens[0]); // stb_fatal -- print error message & exit
@ -28,16 +28,32 @@ int main(int argc, char **argv)
s1 += 1;
s1 = stb_trimwhite(s1); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace
if (*s1 == 'v') ++s1;
fprintf(f, "**%s** | %s", tokens[0], s1);
s3 = tokens[0];
stb_trimwhite(s3);
if (strlen(s3) < 21) {
fprintf(f, "**%s** | %s", tokens[0], s1);
} else {
char buffer[256];
strncpy(buffer, s3, 18);
buffer[18] = 0;
strcat(buffer, "...");
fprintf(f, "**%s** | %s", buffer, s1);
}
s1 = stb_trimwhite(tokens[1]); // stb_trimwhite -- advance pointer to after whitespace & delete trailing whitespace
s2 = stb_dupreplace(s1, " ", "&nbsp;"); // stb_dupreplace -- search & replace string and malloc result
fprintf(f, " | %s", s2);
free(s2);
fprintf(f, " | %d", num_lines);
total_lines += num_lines;
for (j=2; j < num; ++j)
fprintf(f, " | %s", tokens[j]);
fprintf(f, "\n");
}
fprintf(f, "\n");
fprintf(f, "Total libraries: %d \n", listlen);
fprintf(f, "Total lines of C code: %d\n\n", total_lines);
fwrite(footer, 1, flen, f);
fclose(f);

View File

@ -87,6 +87,10 @@ SOURCE=.\make_readme.c
# End Source File
# Begin Source File
SOURCE=.\README.header.md
# End Source File
# Begin Source File
SOURCE=.\README.list
# End Source File
# End Target