Merge commit '3cd551098b8dc5a2b55d45a2a63bb9a219d31f2e' as 'external/stb/stb'
32
external/stb/stb/.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Pull Requests and Issues are both welcome.
|
||||||
|
|
||||||
|
# Responsiveness
|
||||||
|
|
||||||
|
General priority order is:
|
||||||
|
|
||||||
|
* Crashes
|
||||||
|
* Security issues in stb_image
|
||||||
|
* Bugs
|
||||||
|
* Security concerns in other libs
|
||||||
|
* Warnings
|
||||||
|
* Enhancements (new features, performance improvement, etc)
|
||||||
|
|
||||||
|
Pull requests get priority over Issues. Some pull requests I take
|
||||||
|
as written; some I modify myself; some I will request changes before
|
||||||
|
accepting them. Because I've ended up supporting a lot of libraries
|
||||||
|
(20 as I write this, with more on the way), I am somewhat slow to
|
||||||
|
address things. Many issues have been around for a long time.
|
||||||
|
|
||||||
|
# Pull requests
|
||||||
|
|
||||||
|
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
|
||||||
|
* Do NOT update the version number in the file. (This just causes conflicts.)
|
||||||
|
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
|
||||||
|
* Your change needs to compile as both C and C++. Pre-C99 compilers should be supported (e.g. declare at start of block)
|
||||||
|
|
||||||
|
# Specific libraries
|
||||||
|
|
||||||
|
I generally do not want new file formats for stb_image because
|
||||||
|
we are trying to improve its security, so increasing its attack
|
||||||
|
surface is counter-productive.
|
||||||
|
|
24
external/stb/stb/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: if you're having trouble using a library, try the support forum instead
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
4
external/stb/stb/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: support forum
|
||||||
|
url: https://github.com/nothings/stb/discussions/categories/q-a
|
||||||
|
about: having trouble using an stb library? don't create an issue, post in the forum
|
20
external/stb/stb/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: 4 enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
6
external/stb/stb/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
* Delete this list before clicking CREATE PULL REQUEST
|
||||||
|
* Make sure you're using a special branch just for this pull request. (Sometimes people unknowingly use a default branch, then later update that branch, which updates the pull request with the other changes if it hasn't been merged yet.)
|
||||||
|
* Do NOT update the version number in the file. (This just causes conflicts.)
|
||||||
|
* Do add your name to the list of contributors. (Don't worry about the formatting.) I'll try to remember to add it if you don't, but I sometimes forget as it's an extra step.
|
||||||
|
|
||||||
|
If you get something above wrong, don't fret it, it's not the end of the world.
|
23
external/stb/stb/.github/workflows/ci-fuzz.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'stb'
|
||||||
|
dry-run: false
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'stb'
|
||||||
|
fuzz-seconds: 900
|
||||||
|
dry-run: false
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
3
external/stb/stb/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.exe
|
8
external/stb/stb/.travis.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
language: C
|
||||||
|
arch:
|
||||||
|
- AMD64
|
||||||
|
- ppc64le
|
||||||
|
install: true
|
||||||
|
script:
|
||||||
|
- cd tests
|
||||||
|
- make all
|
37
external/stb/stb/LICENSE
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
182
external/stb/stb/README.md
vendored
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
<!--- THIS FILE IS AUTOMATICALLY GENERATED, DO NOT CHANGE IT BY HAND --->
|
||||||
|
|
||||||
|
stb
|
||||||
|
===
|
||||||
|
|
||||||
|
single-file public domain (or MIT licensed) libraries for C/C++
|
||||||
|
|
||||||
|
Noteworthy:
|
||||||
|
|
||||||
|
* image loader: [stb_image.h](stb_image.h)
|
||||||
|
* image writer: [stb_image_write.h](stb_image_write.h)
|
||||||
|
* image resizer: [stb_image_resize.h](stb_image_resize.h)
|
||||||
|
* font text rasterizer: [stb_truetype.h](stb_truetype.h)
|
||||||
|
* typesafe containers: [stb_ds.h](stb_ds.h)
|
||||||
|
|
||||||
|
Most libraries by stb, except: stb_dxt by Fabian "ryg" Giesen, stb_image_resize
|
||||||
|
by Jorge L. "VinoBS" Rodriguez, and stb_sprintf by Jeff Roberts.
|
||||||
|
|
||||||
|
<a name="stb_libs"></a>
|
||||||
|
|
||||||
|
library | lastest version | category | LoC | description
|
||||||
|
--------------------- | ---- | -------- | --- | --------------------------------
|
||||||
|
**[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
|
||||||
|
**[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
|
||||||
|
**[stb_image.h](stb_image.h)** | 2.28 | graphics | 7987 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
|
||||||
|
**[stb_truetype.h](stb_truetype.h)** | 1.26 | graphics | 5077 | parse, decode, and rasterize characters from truetype fonts
|
||||||
|
**[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
|
||||||
|
**[stb_image_resize.h](stb_image_resize.h)** | 0.97 | graphics | 2634 | resize images larger/smaller with good quality
|
||||||
|
**[stb_rect_pack.h](stb_rect_pack.h)** | 1.01 | graphics | 623 | simple 2D rectangle packer with decent quality
|
||||||
|
**[stb_perlin.h](stb_perlin.h)** | 0.5 | graphics | 428 | perlin's revised simplex noise w/ different seeds
|
||||||
|
**[stb_ds.h](stb_ds.h)** | 0.67 | utility | 1895 | typesafe dynamic array and hash tables for C, will compile in C++
|
||||||
|
**[stb_sprintf.h](stb_sprintf.h)** | 1.10 | utility | 1906 | fast sprintf, snprintf for C/C++
|
||||||
|
**[stb_textedit.h](stb_textedit.h)** | 1.14 | user interface | 1429 | guts of a text editor for games etc implementing them from scratch
|
||||||
|
**[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
|
||||||
|
**[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
|
||||||
|
**[stb_easy_font.h](stb_easy_font.h)** | 1.1 | 3D graphics | 305 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
|
||||||
|
**[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game dev | 4187 | embeddable tilemap editor
|
||||||
|
**[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator
|
||||||
|
**[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 940 | simplify writing parsers for C-like languages
|
||||||
|
**[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
|
||||||
|
**[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
|
||||||
|
**[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
|
||||||
|
**[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
|
||||||
|
|
||||||
|
Total libraries: 21
|
||||||
|
Total lines of C code: 43117
|
||||||
|
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
#### What's the license?
|
||||||
|
|
||||||
|
These libraries are in the public domain. You can do anything you
|
||||||
|
want with them. You have no legal obligation
|
||||||
|
to do anything else, although I appreciate attribution.
|
||||||
|
|
||||||
|
They are also licensed under the MIT open source license, if you have lawyers
|
||||||
|
who are unhappy with public domain. Every source file includes an explicit
|
||||||
|
dual-license for you to choose from.
|
||||||
|
|
||||||
|
#### How do I use these libraries?
|
||||||
|
|
||||||
|
The idea behind single-header file libraries is that they're easy to distribute and deploy
|
||||||
|
because all the code is contained in a single file. By default, the .h files in here act as
|
||||||
|
their own header files, i.e. they declare the functions contained in the file but don't
|
||||||
|
actually result in any code getting compiled.
|
||||||
|
|
||||||
|
So in addition, you should select _exactly one_ C/C++ source file that actually instantiates
|
||||||
|
the code, preferably a file you're not editing frequently. This file should define a
|
||||||
|
specific macro (this is documented per-library) to actually enable the function definitions.
|
||||||
|
For example, to use stb_image, you should have exactly one C/C++ file that doesn't
|
||||||
|
include stb_image.h regularly, but instead does
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
The right macro to define is pointed out right at the top of each of these libraries.
|
||||||
|
|
||||||
|
#### <a name="other_libs"></a> Are there other single-file public-domain/open source libraries with minimal dependencies out there?
|
||||||
|
|
||||||
|
[Yes.](https://github.com/nothings/single_file_libs)
|
||||||
|
|
||||||
|
#### If I wrap an stb library in a new library, does the new library have to be public domain/MIT?
|
||||||
|
|
||||||
|
No, because it's public domain you can freely relicense it to whatever license your new
|
||||||
|
library wants to be.
|
||||||
|
|
||||||
|
#### What's the deal with SSE support in GCC-based compilers?
|
||||||
|
|
||||||
|
stb_image will either use SSE2 (if you compile with -msse2) or
|
||||||
|
will not use any SIMD at all, rather than trying to detect the
|
||||||
|
processor at runtime and handle it correctly. As I understand it,
|
||||||
|
the approved path in GCC for runtime-detection require
|
||||||
|
you to use multiple source files, one for each CPU configuration.
|
||||||
|
Because stb_image is a header-file library that compiles in only
|
||||||
|
one source file, there's no approved way to build both an
|
||||||
|
SSE-enabled and a non-SSE-enabled variation.
|
||||||
|
|
||||||
|
While we've tried to work around it, we've had multiple issues over
|
||||||
|
the years due to specific versions of gcc breaking what we're doing,
|
||||||
|
so we've given up on it. See https://github.com/nothings/stb/issues/280
|
||||||
|
and https://github.com/nothings/stb/issues/410 for examples.
|
||||||
|
|
||||||
|
#### Some of these libraries seem redundant to existing open source libraries. Are they better somehow?
|
||||||
|
|
||||||
|
Generally they're only better in that they're easier to integrate,
|
||||||
|
easier to use, and easier to release (single file; good API; no
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Can I link directly to the table of stb libraries?
|
||||||
|
|
||||||
|
You can use [this URL](https://github.com/nothings/stb#stb_libs) to link directly to that list.
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
This was not chosen out of egomania, but as a moderately sane
|
||||||
|
way of namespacing the filenames and source function names.
|
||||||
|
|
||||||
|
#### Will you add more image types to stb_image.h?
|
||||||
|
|
||||||
|
No. As stb_image use has grown, it has become more important
|
||||||
|
for us to focus on security of the codebase. Adding new image
|
||||||
|
formats increases the amount of code we need to secure, so it
|
||||||
|
is no longer worth adding new formats.
|
||||||
|
|
||||||
|
#### Do you have any advice on how to create my own single-file library?
|
||||||
|
|
||||||
|
Yes. https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
|
||||||
|
|
||||||
|
#### Why public domain?
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
Primarily, because I use C, not C++. But it does also make it easier
|
||||||
|
for other people to use them from other languages.
|
||||||
|
|
||||||
|
#### Why not C99? stdint.h, declare-anywhere, etc.
|
||||||
|
|
||||||
|
I still use MSVC 6 (1998) as my IDE because it has better human factors
|
||||||
|
for me than later versions of MSVC.
|
BIN
external/stb/stb/data/atari_8bit_font_revised.png
vendored
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
external/stb/stb/data/easy_font_raw.png
vendored
Normal file
After Width: | Height: | Size: 645 B |
4
external/stb/stb/data/herringbone/license.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
All files in this directory are in the public domain. Where
|
||||||
|
a public domain declaration is not recognized, you are granted
|
||||||
|
a license to freely use, modify, and redistribute them in
|
||||||
|
any way you choose.
|
BIN
external/stb/stb/data/herringbone/template_caves_limit_connectivity.png
vendored
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
external/stb/stb/data/herringbone/template_caves_tiny_corridors.png
vendored
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
external/stb/stb/data/herringbone/template_corner_caves.png
vendored
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
external/stb/stb/data/herringbone/template_horizontal_corridors_v1.png
vendored
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
external/stb/stb/data/herringbone/template_horizontal_corridors_v2.png
vendored
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
external/stb/stb/data/herringbone/template_horizontal_corridors_v3.png
vendored
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
external/stb/stb/data/herringbone/template_limit_connectivity_fat.png
vendored
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
external/stb/stb/data/herringbone/template_limited_connectivity.png
vendored
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
external/stb/stb/data/herringbone/template_maze_2_wide.png
vendored
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
external/stb/stb/data/herringbone/template_maze_plus_2_wide.png
vendored
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
external/stb/stb/data/herringbone/template_open_areas.png
vendored
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
external/stb/stb/data/herringbone/template_ref2_corner_caves.png
vendored
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
external/stb/stb/data/herringbone/template_rooms_and_corridors.png
vendored
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
external/stb/stb/data/herringbone/template_rooms_and_corridors_2_wide_diagonal_bias.png
vendored
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
external/stb/stb/data/herringbone/template_rooms_limit_connectivity.png
vendored
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
external/stb/stb/data/herringbone/template_round_rooms_diagonal_corridors.png
vendored
Normal file
After Width: | Height: | Size: 8.0 KiB |
BIN
external/stb/stb/data/herringbone/template_sean_dungeon.png
vendored
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
external/stb/stb/data/herringbone/template_simple_caves_2_wide.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
external/stb/stb/data/herringbone/template_square_rooms_with_random_rects.png
vendored
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
external/stb/stb/data/map_01.png
vendored
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
external/stb/stb/data/map_02.png
vendored
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
external/stb/stb/data/map_03.png
vendored
Normal file
After Width: | Height: | Size: 9.4 KiB |
1055
external/stb/stb/deprecated/rrsprintf.h
vendored
Normal file
13111
external/stb/stb/deprecated/stb.h
vendored
Normal file
4678
external/stb/stb/deprecated/stb_image.c
vendored
Normal file
29
external/stb/stb/deprecated/stretch_test.c
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// check that stb_truetype compiles with no stb_rect_pack.h
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include "stb_truetype.h"
|
||||||
|
|
||||||
|
#define STB_DS_IMPLEMENTATION
|
||||||
|
#include "stb_ds.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main(int arg, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int *arr = NULL;
|
||||||
|
|
||||||
|
for (i=0; i < 1000000; ++i)
|
||||||
|
arrput(arr, i);
|
||||||
|
|
||||||
|
assert(arrlen(arr) == 1000000);
|
||||||
|
for (i=0; i < 1000000; ++i)
|
||||||
|
assert(arr[i] == i);
|
||||||
|
|
||||||
|
arrfree(arr);
|
||||||
|
arr = NULL;
|
||||||
|
|
||||||
|
for (i=0; i < 1000; ++i)
|
||||||
|
arrput(arr, 1000);
|
||||||
|
assert(arrlen(arr) == 1000000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
263
external/stb/stb/deprecated/stretchy_buffer.h
vendored
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
// stretchy_buffer.h - v1.04 - public domain - nothings.org/stb
|
||||||
|
// a vector<>-like dynamic array for C
|
||||||
|
//
|
||||||
|
// version history:
|
||||||
|
// 1.04 - fix warning
|
||||||
|
// 1.03 - compile as C++ maybe
|
||||||
|
// 1.02 - tweaks to syntax for no good reason
|
||||||
|
// 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
|
||||||
|
// issues, but won't compile as C++
|
||||||
|
//
|
||||||
|
// Will probably not work correctly with strict-aliasing optimizations.
|
||||||
|
//
|
||||||
|
// The idea:
|
||||||
|
//
|
||||||
|
// This implements an approximation to C++ vector<> for C, in that it
|
||||||
|
// provides a generic definition for dynamic arrays which you can
|
||||||
|
// still access in a typesafe way using arr[i] or *(arr+i). However,
|
||||||
|
// it is simply a convenience wrapper around the common idiom of
|
||||||
|
// of keeping a set of variables (in a struct or globals) which store
|
||||||
|
// - pointer to array
|
||||||
|
// - the length of the "in-use" part of the array
|
||||||
|
// - the current size of the allocated array
|
||||||
|
//
|
||||||
|
// I find it to be the single most useful non-built-in-structure when
|
||||||
|
// programming in C (hash tables a close second), but to be clear
|
||||||
|
// it lacks many of the capabilities of C++ vector<>: there is no
|
||||||
|
// range checking, the object address isn't stable (see next section
|
||||||
|
// for details), the set of methods available is small (although
|
||||||
|
// the file stb.h has another implementation of stretchy buffers
|
||||||
|
// called 'stb_arr' which provides more methods, e.g. for insertion
|
||||||
|
// and deletion).
|
||||||
|
//
|
||||||
|
// How to use:
|
||||||
|
//
|
||||||
|
// Unlike other stb header file libraries, there is no need to
|
||||||
|
// define an _IMPLEMENTATION symbol. Every #include creates as
|
||||||
|
// much implementation is needed.
|
||||||
|
//
|
||||||
|
// stretchy_buffer.h does not define any types, so you do not
|
||||||
|
// need to #include it to before defining data types that are
|
||||||
|
// stretchy buffers, only in files that *manipulate* stretchy
|
||||||
|
// buffers.
|
||||||
|
//
|
||||||
|
// If you want a stretchy buffer aka dynamic array containing
|
||||||
|
// objects of TYPE, declare such an array as:
|
||||||
|
//
|
||||||
|
// TYPE *myarray = NULL;
|
||||||
|
//
|
||||||
|
// (There is no typesafe way to distinguish between stretchy
|
||||||
|
// buffers and regular arrays/pointers; this is necessary to
|
||||||
|
// make ordinary array indexing work on these objects.)
|
||||||
|
//
|
||||||
|
// Unlike C++ vector<>, the stretchy_buffer has the same
|
||||||
|
// semantics as an object that you manually malloc and realloc.
|
||||||
|
// The pointer may relocate every time you add a new object
|
||||||
|
// to it, so you:
|
||||||
|
//
|
||||||
|
// 1. can't take long-term pointers to elements of the array
|
||||||
|
// 2. have to return the pointer from functions which might expand it
|
||||||
|
// (either as a return value or by storing it to a ptr-to-ptr)
|
||||||
|
//
|
||||||
|
// Now you can do the following things with this array:
|
||||||
|
//
|
||||||
|
// sb_free(TYPE *a) free the array
|
||||||
|
// sb_count(TYPE *a) the number of elements in the array
|
||||||
|
// sb_push(TYPE *a, TYPE v) adds v on the end of the array, a la push_back
|
||||||
|
// sb_add(TYPE *a, int n) adds n uninitialized elements at end of array & returns pointer to first added
|
||||||
|
// sb_last(TYPE *a) returns an lvalue of the last item in the array
|
||||||
|
// a[n] access the nth (counting from 0) element of the array
|
||||||
|
//
|
||||||
|
// #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
|
||||||
|
// names of the form 'stb_sb_' if you have a name that would
|
||||||
|
// otherwise collide.
|
||||||
|
//
|
||||||
|
// Note that these are all macros and many of them evaluate
|
||||||
|
// their arguments more than once, so the arguments should
|
||||||
|
// be side-effect-free.
|
||||||
|
//
|
||||||
|
// Note that 'TYPE *a' in sb_push and sb_add must be lvalues
|
||||||
|
// so that the library can overwrite the existing pointer if
|
||||||
|
// the object has to be reallocated.
|
||||||
|
//
|
||||||
|
// In an out-of-memory condition, the code will try to
|
||||||
|
// set up a null-pointer or otherwise-invalid-pointer
|
||||||
|
// exception to happen later. It's possible optimizing
|
||||||
|
// compilers could detect this write-to-null statically
|
||||||
|
// and optimize away some of the code, but it should only
|
||||||
|
// be along the failure path. Nevertheless, for more security
|
||||||
|
// in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||||
|
// to a statement such as assert(0) or exit(1) or something
|
||||||
|
// to force a failure when out-of-memory occurs.
|
||||||
|
//
|
||||||
|
// Common use:
|
||||||
|
//
|
||||||
|
// The main application for this is when building a list of
|
||||||
|
// things with an unknown quantity, either due to loading from
|
||||||
|
// a file or through a process which produces an unpredictable
|
||||||
|
// number.
|
||||||
|
//
|
||||||
|
// My most common idiom is something like:
|
||||||
|
//
|
||||||
|
// SomeStruct *arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// SomeStruct new_one;
|
||||||
|
// new_one.whatever = whatever;
|
||||||
|
// new_one.whatup = whatup;
|
||||||
|
// new_one.foobar = barfoo;
|
||||||
|
// sb_push(arr, new_one);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// and various closely-related factorings of that. For example,
|
||||||
|
// you might have several functions to create/init new SomeStructs,
|
||||||
|
// and if you use the above idiom, you might prefer to make them
|
||||||
|
// return structs rather than take non-const-pointers-to-structs,
|
||||||
|
// so you can do things like:
|
||||||
|
//
|
||||||
|
// SomeStruct *arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// if (case_A) {
|
||||||
|
// sb_push(arr, some_func1());
|
||||||
|
// } else if (case_B) {
|
||||||
|
// sb_push(arr, some_func2());
|
||||||
|
// } else {
|
||||||
|
// sb_push(arr, some_func3());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Note that the above relies on the fact that sb_push doesn't
|
||||||
|
// evaluate its second argument more than once. The macros do
|
||||||
|
// evaluate the *array* argument multiple times, and numeric
|
||||||
|
// arguments may be evaluated multiple times, but you can rely
|
||||||
|
// on the second argument of sb_push being evaluated only once.
|
||||||
|
//
|
||||||
|
// Of course, you don't have to store bare objects in the array;
|
||||||
|
// if you need the objects to have stable pointers, store an array
|
||||||
|
// of pointers instead:
|
||||||
|
//
|
||||||
|
// SomeStruct **arr = NULL;
|
||||||
|
// while (something)
|
||||||
|
// {
|
||||||
|
// SomeStruct *new_one = malloc(sizeof(*new_one));
|
||||||
|
// new_one->whatever = whatever;
|
||||||
|
// new_one->whatup = whatup;
|
||||||
|
// new_one->foobar = barfoo;
|
||||||
|
// sb_push(arr, new_one);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// How it works:
|
||||||
|
//
|
||||||
|
// A long-standing tradition in things like malloc implementations
|
||||||
|
// is to store extra data before the beginning of the block returned
|
||||||
|
// to the user. The stretchy buffer implementation here uses the
|
||||||
|
// same trick; the current-count and current-allocation-size are
|
||||||
|
// stored before the beginning of the array returned to the user.
|
||||||
|
// (This means you can't directly free() the pointer, because the
|
||||||
|
// allocated pointer is different from the type-safe pointer provided
|
||||||
|
// to the user.)
|
||||||
|
//
|
||||||
|
// The details are trivial and implementation is straightforward;
|
||||||
|
// the main trick is in realizing in the first place that it's
|
||||||
|
// possible to do this in a generic, type-safe way in C.
|
||||||
|
//
|
||||||
|
// Contributors:
|
||||||
|
//
|
||||||
|
// Timothy Wright (github:ZenToad)
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
#define STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
|
||||||
|
#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
|
||||||
|
#define sb_free stb_sb_free
|
||||||
|
#define sb_push stb_sb_push
|
||||||
|
#define sb_count stb_sb_count
|
||||||
|
#define sb_add stb_sb_add
|
||||||
|
#define sb_last stb_sb_last
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define stb_sb_free(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
||||||
|
#define stb_sb_push(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||||
|
#define stb_sb_count(a) ((a) ? stb__sbn(a) : 0)
|
||||||
|
#define stb_sb_add(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||||
|
#define stb_sb_last(a) ((a)[stb__sbn(a)-1])
|
||||||
|
|
||||||
|
#define stb__sbraw(a) ((int *) (void *) (a) - 2)
|
||||||
|
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||||
|
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
|
||||||
|
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||||
|
#define stb__sbgrow(a,n) (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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 = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
|
||||||
|
if (p) {
|
||||||
|
if (!arr)
|
||||||
|
p[1] = 0;
|
||||||
|
p[0] = m;
|
||||||
|
return p+2;
|
||||||
|
} else {
|
||||||
|
#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
|
||||||
|
STRETCHY_BUFFER_OUT_OF_MEMORY ;
|
||||||
|
#endif
|
||||||
|
return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB_STRETCHY_BUFFER_H_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
28
external/stb/stb/deprecated/stretchy_buffer.txt
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// stretchy buffer // init: NULL // free: sbfree() // push_back: sbpush() // size: sbcount() //
|
||||||
|
#define sbfree(a) ((a) ? free(stb__sbraw(a)),0 : 0)
|
||||||
|
#define sbpush(a,v) (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
|
||||||
|
#define sbcount(a) ((a) ? stb__sbn(a) : 0)
|
||||||
|
#define sbadd(a,n) (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
|
||||||
|
#define sblast(a) ((a)[stb__sbn(a)-1])
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define stb__sbraw(a) ((int *) (a) - 2)
|
||||||
|
#define stb__sbm(a) stb__sbraw(a)[0]
|
||||||
|
#define stb__sbn(a) stb__sbraw(a)[1]
|
||||||
|
|
||||||
|
#define stb__sbneedgrow(a,n) ((a)==0 || stb__sbn(a)+n >= stb__sbm(a))
|
||||||
|
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
|
||||||
|
#define stb__sbgrow(a,n) stb__sbgrowf((void **) &(a), (n), sizeof(*(a)))
|
||||||
|
|
||||||
|
static void stb__sbgrowf(void **arr, int increment, int itemsize)
|
||||||
|
{
|
||||||
|
int m = *arr ? 2*stb__sbm(*arr)+increment : increment+1;
|
||||||
|
void *p = realloc(*arr ? stb__sbraw(*arr) : 0, itemsize * m + sizeof(int)*2);
|
||||||
|
assert(p);
|
||||||
|
if (p) {
|
||||||
|
if (!*arr) ((int *) p)[1] = 0;
|
||||||
|
*arr = (void *) ((int *) p + 2);
|
||||||
|
stb__sbm(*arr) = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
external/stb/stb/docs/other_libs.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Moved to https://github.com/nothings/single_file_libs
|
185
external/stb/stb/docs/stb_howto.txt
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
Lessons learned about how to make a header-file library
|
||||||
|
V1.0
|
||||||
|
September 2013 Sean Barrett
|
||||||
|
|
||||||
|
Things to do in an stb-style header-file library,
|
||||||
|
and rationales:
|
||||||
|
|
||||||
|
|
||||||
|
1. #define LIBRARYNAME_IMPLEMENTATION
|
||||||
|
|
||||||
|
Use a symbol like the above to control creating
|
||||||
|
the implementation. (I used a far-less-clear name
|
||||||
|
in my first header-file library; it became
|
||||||
|
clear that was a mistake once I had multiple
|
||||||
|
libraries.)
|
||||||
|
|
||||||
|
Include a "header-file" section with header-file
|
||||||
|
guards and declarations for all the functions,
|
||||||
|
but only guard the implementation with LIBRARYNAME_IMPLEMENTATION,
|
||||||
|
not the header-file guard. That way, if client's
|
||||||
|
header file X includes your header file for
|
||||||
|
declarations, they can still include header file X
|
||||||
|
in the source file that creates the implementation;
|
||||||
|
if you guard the implementation too, then the first
|
||||||
|
include (before the #define) creates the declarations,
|
||||||
|
and the second one (after the #define) does nothing.
|
||||||
|
|
||||||
|
|
||||||
|
2. AVOID DEPENDENCIES
|
||||||
|
|
||||||
|
Don't rely on anything other than the C standard libraries.
|
||||||
|
|
||||||
|
(If you're creating a library specifically to leverage/wrap
|
||||||
|
some other library, then obviously you can rely on that
|
||||||
|
library. But if that library is public domain, you might
|
||||||
|
be better off directly embedding the source, to reduce
|
||||||
|
dependencies for your clients. But of course now you have
|
||||||
|
to update whenever that library updates.)
|
||||||
|
|
||||||
|
If you use stdlib, consider wrapping all stdlib calls in
|
||||||
|
macros, and then conditionally define those macros to the
|
||||||
|
stdlib function, allowing the user to replace them.
|
||||||
|
|
||||||
|
For functions with side effects, like memory allocations,
|
||||||
|
consider letting the user pass in a context and pass
|
||||||
|
that in to the macros. (The stdlib versions will ignore
|
||||||
|
the parameter.) Otherwise, users may have to use global
|
||||||
|
or thread-local variables to achieve the same effect.
|
||||||
|
|
||||||
|
|
||||||
|
3. AVOID MALLOC
|
||||||
|
|
||||||
|
You can't always do this, but when you can, embedded developers
|
||||||
|
will appreciate it. I almost never bother avoiding, as it's
|
||||||
|
too much work (and in some cases is pretty infeasible;
|
||||||
|
see http://nothings.org/gamedev/font_rendering_malloc.txt ).
|
||||||
|
But it's definitely something one of the things I've gotten
|
||||||
|
the most pushback on from potential users.
|
||||||
|
|
||||||
|
|
||||||
|
4. ALLOW STATIC IMPLEMENTATION
|
||||||
|
|
||||||
|
Have a #define which makes function declarations and
|
||||||
|
function definitions static. This makes the implementation
|
||||||
|
private to the source file that creates it. This allows
|
||||||
|
people to use your library multiple times in their project
|
||||||
|
without collision. (This is only necessary if your library
|
||||||
|
has configuration macros or global state, or if your
|
||||||
|
library has multiple versions that are not backwards
|
||||||
|
compatible. I've run into both of those cases.)
|
||||||
|
|
||||||
|
|
||||||
|
5. MAKE ACCESSIBLE FROM C
|
||||||
|
|
||||||
|
Making your code accessible from C instead of C++ (i.e.
|
||||||
|
either coding in C, or using extern "C") makes it more
|
||||||
|
straightforward to be used in C and in other languages,
|
||||||
|
which often only have support for C bindings, not C++.
|
||||||
|
(One of the earliest results I found in googling for
|
||||||
|
stb_image was a Haskell wrapper.) Otherwise, people
|
||||||
|
have to wrap it in another set of function calls, and
|
||||||
|
the whole point here is to make it convenient for people
|
||||||
|
to use, isn't it? (See below.)
|
||||||
|
|
||||||
|
I prefer to code entirely in C, so the source file that
|
||||||
|
instantiates the implementation can be C itself, for
|
||||||
|
those crazy people out there who are programming in C.
|
||||||
|
But it's probably not a big hardship for a C programmer
|
||||||
|
to create a single C++ source file to instantiate your
|
||||||
|
library.
|
||||||
|
|
||||||
|
|
||||||
|
6. NAMESPACE PRIVATE FUNCTIONS
|
||||||
|
|
||||||
|
Try to avoid having names in your source code that
|
||||||
|
will cause conflicts with identical names in client
|
||||||
|
code. You can do this either by namespacing in C++,
|
||||||
|
or prefixing with your library name in C.
|
||||||
|
|
||||||
|
In C, generally, I use the same prefix for API
|
||||||
|
functions and private symbols, such as "stbtt_"
|
||||||
|
for stb_truetype; but private functions (and
|
||||||
|
static globals) use a second underscore as
|
||||||
|
in "stbtt__" to further minimize the chance of
|
||||||
|
additional collisions in the unlikely but not
|
||||||
|
impossible event that users write wrapper
|
||||||
|
functions that have names of the form "stbtt_".
|
||||||
|
(Consider the user that has used "stbtt_foo"
|
||||||
|
*successfully*, and then upgrades to a new
|
||||||
|
version of your library which has a new private
|
||||||
|
function named either "stbtt_foo" or "stbtt__foo".)
|
||||||
|
|
||||||
|
Note that the double-underscore is reserved for
|
||||||
|
use by the compiler, but (1) there is nothing
|
||||||
|
reserved for "middleware", i.e. libraries
|
||||||
|
desiring to avoid conflicts with user symbols
|
||||||
|
have no other good options, and (2) in practice
|
||||||
|
no compilers use double-underscore in the middle
|
||||||
|
rather than the beginning/end. (Unfortunately,
|
||||||
|
there is at least one videogame-console compiler that
|
||||||
|
will warn about double-underscores by default.)
|
||||||
|
|
||||||
|
|
||||||
|
7. 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 dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute 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. :(
|
173
external/stb/stb/docs/stb_voxel_render_interview.md
vendored
Normal 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!*
|
117
external/stb/stb/docs/why_public_domain.md
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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 has a huge list
|
||||||
|
(http://nothings.org/remote/ThirdPartyLegalNotices_steam_2019.html)
|
||||||
|
of all of these included in each game they ship,
|
||||||
|
and it's insane, and obviously nobody ever looks
|
||||||
|
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 dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute 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. :(
|
||||||
|
```
|
940
external/stb/stb/stb_c_lexer.h
vendored
Normal file
@ -0,0 +1,940 @@
|
|||||||
|
// stb_c_lexer.h - v0.12 - public domain Sean Barrett 2013
|
||||||
|
// lexer for making little C-like languages with recursive-descent parsers
|
||||||
|
//
|
||||||
|
// This file provides both the interface and the implementation.
|
||||||
|
// To instantiate the implementation,
|
||||||
|
// #define STB_C_LEXER_IMPLEMENTATION
|
||||||
|
// in *ONE* source file, before #including this file.
|
||||||
|
//
|
||||||
|
// The default configuration is fairly close to a C lexer, although
|
||||||
|
// suffixes on integer constants are not handled (you can override this).
|
||||||
|
//
|
||||||
|
// History:
|
||||||
|
// 0.12 fix compilation bug for NUL support; better support separate inclusion
|
||||||
|
// 0.11 fix clang static analysis warning
|
||||||
|
// 0.10 fix warnings
|
||||||
|
// 0.09 hex floats, no-stdlib fixes
|
||||||
|
// 0.08 fix bad pointer comparison
|
||||||
|
// 0.07 fix mishandling of hexadecimal constants parsed by strtol
|
||||||
|
// 0.06 fix missing next character after ending quote mark (Andreas Fredriksson)
|
||||||
|
// 0.05 refixed get_location because github version had lost the fix
|
||||||
|
// 0.04 fix octal parsing bug
|
||||||
|
// 0.03 added STB_C_LEX_DISCARD_PREPROCESSOR option
|
||||||
|
// refactor API to simplify (only one struct instead of two)
|
||||||
|
// change literal enum names to have 'lit' at the end
|
||||||
|
// 0.02 first public release
|
||||||
|
//
|
||||||
|
// Status:
|
||||||
|
// - haven't tested compiling as C++
|
||||||
|
// - haven't tested the float parsing path
|
||||||
|
// - haven't tested the non-default-config paths (e.g. non-stdlib)
|
||||||
|
// - only tested default-config paths by eyeballing output of self-parse
|
||||||
|
//
|
||||||
|
// - haven't implemented multiline strings
|
||||||
|
// - haven't implemented octal/hex character constants
|
||||||
|
// - haven't implemented support for unicode CLEX_char
|
||||||
|
// - need to expand error reporting so you don't just get "CLEX_parse_error"
|
||||||
|
//
|
||||||
|
// Contributors:
|
||||||
|
// Arpad Goretity (bugfix)
|
||||||
|
// Alan Hickman (hex floats)
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
#ifdef STB_C_LEXER_IMPLEMENTATION
|
||||||
|
#ifndef STB_C_LEXER_DEFINITIONS
|
||||||
|
// to change the default parsing rules, copy the following lines
|
||||||
|
// into your C/C++ file *before* including this, and then replace
|
||||||
|
// the Y's with N's for the ones you don't want. This needs to be
|
||||||
|
// set to the same values for every place in your program where
|
||||||
|
// stb_c_lexer.h is included.
|
||||||
|
// --BEGIN--
|
||||||
|
|
||||||
|
#if defined(Y) || defined(N)
|
||||||
|
#error "Can only use stb_c_lexer in contexts where the preprocessor symbols 'Y' and 'N' are not defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit
|
||||||
|
#define STB_C_LEX_C99_HEX_FLOATS N // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit
|
||||||
|
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
|
||||||
|
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
|
||||||
|
#define STB_C_LEX_C_SQ_STRINGS N // single-quote-delimited strings with escapes CLEX_ssstring
|
||||||
|
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
|
||||||
|
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
|
||||||
|
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
|
||||||
|
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
|
||||||
|
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
|
||||||
|
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
|
||||||
|
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
|
||||||
|
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
|
||||||
|
#define STB_C_LEX_EQUAL_ARROW N // "=>" CLEX_eqarrow
|
||||||
|
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
|
||||||
|
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
|
||||||
|
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
|
||||||
|
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
|
||||||
|
// "<<=" CLEX_shleq ">>=" CLEX_shreq
|
||||||
|
|
||||||
|
#define STB_C_LEX_PARSE_SUFFIXES N // letters after numbers are parsed as part of those numbers, and must be in suffix list below
|
||||||
|
#define STB_C_LEX_DECIMAL_SUFFIXES "" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
|
||||||
|
#define STB_C_LEX_HEX_SUFFIXES "" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_OCTAL_SUFFIXES "" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_FLOAT_SUFFIXES "" //
|
||||||
|
|
||||||
|
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
|
||||||
|
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
|
||||||
|
#define STB_C_LEX_MULTILINE_DSTRINGS N // allow newlines in double-quoted strings
|
||||||
|
#define STB_C_LEX_MULTILINE_SSTRINGS N // allow newlines in single-quoted strings
|
||||||
|
#define STB_C_LEX_USE_STDLIB Y // use strtod,strtol for parsing #s; otherwise inaccurate hack
|
||||||
|
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
|
||||||
|
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
|
||||||
|
|
||||||
|
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES N // if Y, all CLEX_ token names are defined, even if never returned
|
||||||
|
// leaving it as N should help you catch config bugs
|
||||||
|
|
||||||
|
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
|
||||||
|
// still have #line, #pragma, etc)
|
||||||
|
|
||||||
|
//#define STB_C_LEX_ISWHITE(str) ... // return length in bytes of whitespace characters if first char is whitespace
|
||||||
|
|
||||||
|
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
|
||||||
|
// --END--
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_C_LEXER_H
|
||||||
|
#define INCLUDE_STB_C_LEXER_H
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// lexer variables
|
||||||
|
char *input_stream;
|
||||||
|
char *eof;
|
||||||
|
char *parse_point;
|
||||||
|
char *string_storage;
|
||||||
|
int string_storage_len;
|
||||||
|
|
||||||
|
// lexer parse location for error messages
|
||||||
|
char *where_firstchar;
|
||||||
|
char *where_lastchar;
|
||||||
|
|
||||||
|
// lexer token variables
|
||||||
|
long token;
|
||||||
|
double real_number;
|
||||||
|
long int_number;
|
||||||
|
char *string;
|
||||||
|
int string_len;
|
||||||
|
} stb_lexer;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int line_number;
|
||||||
|
int line_offset;
|
||||||
|
} stb_lex_location;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length);
|
||||||
|
// this function initialize the 'lexer' structure
|
||||||
|
// Input:
|
||||||
|
// - input_stream points to the file to parse, loaded into memory
|
||||||
|
// - input_stream_end points to the end of the file, or NULL if you use 0-for-EOF
|
||||||
|
// - string_store is storage the lexer can use for storing parsed strings and identifiers
|
||||||
|
// - store_length is the length of that storage
|
||||||
|
|
||||||
|
extern int stb_c_lexer_get_token(stb_lexer *lexer);
|
||||||
|
// this function returns non-zero if a token is parsed, or 0 if at EOF
|
||||||
|
// Output:
|
||||||
|
// - lexer->token is the token ID, which is unicode code point for a single-char token, < 0 for a multichar or eof or error
|
||||||
|
// - lexer->real_number is a double constant value for CLEX_floatlit, or CLEX_intlit if STB_C_LEX_INTEGERS_AS_DOUBLES
|
||||||
|
// - lexer->int_number is an integer constant for CLEX_intlit if !STB_C_LEX_INTEGERS_AS_DOUBLES, or character for CLEX_charlit
|
||||||
|
// - lexer->string is a 0-terminated string for CLEX_dqstring or CLEX_sqstring or CLEX_identifier
|
||||||
|
// - lexer->string_len is the byte length of lexer->string
|
||||||
|
|
||||||
|
extern void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc);
|
||||||
|
// this inefficient function returns the line number and character offset of a
|
||||||
|
// given location in the file as returned by stb_lex_token. Because it's inefficient,
|
||||||
|
// you should only call it for errors, not for every token.
|
||||||
|
// For error messages of invalid tokens, you typically want the location of the start
|
||||||
|
// of the token (which caused the token to be invalid). For bugs involving legit
|
||||||
|
// tokens, you can report the first or the range.
|
||||||
|
// Output:
|
||||||
|
// - loc->line_number is the line number in the file, counting from 1, of the location
|
||||||
|
// - loc->line_offset is the char-offset in the line, counting from 0, of the location
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CLEX_eof = 256,
|
||||||
|
CLEX_parse_error,
|
||||||
|
CLEX_intlit ,
|
||||||
|
CLEX_floatlit ,
|
||||||
|
CLEX_id ,
|
||||||
|
CLEX_dqstring ,
|
||||||
|
CLEX_sqstring ,
|
||||||
|
CLEX_charlit ,
|
||||||
|
CLEX_eq ,
|
||||||
|
CLEX_noteq ,
|
||||||
|
CLEX_lesseq ,
|
||||||
|
CLEX_greatereq ,
|
||||||
|
CLEX_andand ,
|
||||||
|
CLEX_oror ,
|
||||||
|
CLEX_shl ,
|
||||||
|
CLEX_shr ,
|
||||||
|
CLEX_plusplus ,
|
||||||
|
CLEX_minusminus ,
|
||||||
|
CLEX_pluseq ,
|
||||||
|
CLEX_minuseq ,
|
||||||
|
CLEX_muleq ,
|
||||||
|
CLEX_diveq ,
|
||||||
|
CLEX_modeq ,
|
||||||
|
CLEX_andeq ,
|
||||||
|
CLEX_oreq ,
|
||||||
|
CLEX_xoreq ,
|
||||||
|
CLEX_arrow ,
|
||||||
|
CLEX_eqarrow ,
|
||||||
|
CLEX_shleq, CLEX_shreq,
|
||||||
|
|
||||||
|
CLEX_first_unused_token
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif // INCLUDE_STB_C_LEXER_H
|
||||||
|
|
||||||
|
#ifdef STB_C_LEXER_IMPLEMENTATION
|
||||||
|
|
||||||
|
// Hacky definitions so we can easily #if on them
|
||||||
|
#define Y(x) 1
|
||||||
|
#define N(x) 0
|
||||||
|
|
||||||
|
#if STB_C_LEX_INTEGERS_AS_DOUBLES(x)
|
||||||
|
typedef double stb__clex_int;
|
||||||
|
#define intfield real_number
|
||||||
|
#define STB__clex_int_as_double
|
||||||
|
#else
|
||||||
|
typedef long stb__clex_int;
|
||||||
|
#define intfield int_number
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Convert these config options to simple conditional #defines so we can more
|
||||||
|
// easily test them once we've change the meaning of Y/N
|
||||||
|
|
||||||
|
#if STB_C_LEX_PARSE_SUFFIXES(x)
|
||||||
|
#define STB__clex_parse_suffixes
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C99_HEX_FLOATS(x)
|
||||||
|
#define STB__clex_hex_floats
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_HEX_INTS(x)
|
||||||
|
#define STB__clex_hex_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_DECIMAL_INTS(x)
|
||||||
|
#define STB__clex_decimal_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_OCTAL_INTS(x)
|
||||||
|
#define STB__clex_octal_ints
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_C_DECIMAL_FLOATS(x)
|
||||||
|
#define STB__clex_decimal_floats
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_DISCARD_PREPROCESSOR(x)
|
||||||
|
#define STB__clex_discard_preprocessor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STB_C_LEX_USE_STDLIB(x) && (!defined(STB__clex_hex_floats) || __STDC_VERSION__ >= 199901L)
|
||||||
|
#define STB__CLEX_use_stdlib
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Now for the rest of the file we'll use the basic definition where
|
||||||
|
// where Y expands to its contents and N expands to nothing
|
||||||
|
#undef Y
|
||||||
|
#define Y(a) a
|
||||||
|
#undef N
|
||||||
|
#define N(a)
|
||||||
|
|
||||||
|
// API function
|
||||||
|
void stb_c_lexer_init(stb_lexer *lexer, const char *input_stream, const char *input_stream_end, char *string_store, int store_length)
|
||||||
|
{
|
||||||
|
lexer->input_stream = (char *) input_stream;
|
||||||
|
lexer->eof = (char *) input_stream_end;
|
||||||
|
lexer->parse_point = (char *) input_stream;
|
||||||
|
lexer->string_storage = string_store;
|
||||||
|
lexer->string_storage_len = store_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API function
|
||||||
|
void stb_c_lexer_get_location(const stb_lexer *lexer, const char *where, stb_lex_location *loc)
|
||||||
|
{
|
||||||
|
char *p = lexer->input_stream;
|
||||||
|
int line_number = 1;
|
||||||
|
int char_offset = 0;
|
||||||
|
while (*p && p < where) {
|
||||||
|
if (*p == '\n' || *p == '\r') {
|
||||||
|
p += (p[0]+p[1] == '\r'+'\n' ? 2 : 1); // skip newline
|
||||||
|
line_number += 1;
|
||||||
|
char_offset = 0;
|
||||||
|
} else {
|
||||||
|
++p;
|
||||||
|
++char_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loc->line_number = line_number;
|
||||||
|
loc->line_offset = char_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// main helper function for returning a parsed token
|
||||||
|
static int stb__clex_token(stb_lexer *lexer, int token, char *start, char *end)
|
||||||
|
{
|
||||||
|
lexer->token = token;
|
||||||
|
lexer->where_firstchar = start;
|
||||||
|
lexer->where_lastchar = end;
|
||||||
|
lexer->parse_point = end+1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function for returning eof
|
||||||
|
static int stb__clex_eof(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
lexer->token = CLEX_eof;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__clex_iswhite(int x)
|
||||||
|
{
|
||||||
|
return x == ' ' || x == '\t' || x == '\r' || x == '\n' || x == '\f';
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *stb__strchr(const char *str, int ch)
|
||||||
|
{
|
||||||
|
for (; *str; ++str)
|
||||||
|
if (*str == ch)
|
||||||
|
return str;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse suffixes at the end of a number
|
||||||
|
static int stb__clex_parse_suffixes(stb_lexer *lexer, long tokenid, char *start, char *cur, const char *suffixes)
|
||||||
|
{
|
||||||
|
#ifdef STB__clex_parse_suffixes
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = 0;
|
||||||
|
|
||||||
|
while ((*cur >= 'a' && *cur <= 'z') || (*cur >= 'A' && *cur <= 'Z')) {
|
||||||
|
if (stb__strchr(suffixes, *cur) == 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||||
|
if (lexer->string_len+1 >= lexer->string_storage_len)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, cur);
|
||||||
|
lexer->string[lexer->string_len++] = *cur++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
suffixes = suffixes; // attempt to suppress warnings
|
||||||
|
#endif
|
||||||
|
return stb__clex_token(lexer, tokenid, start, cur-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STB__CLEX_use_stdlib
|
||||||
|
static double stb__clex_pow(double base, unsigned int exponent)
|
||||||
|
{
|
||||||
|
double value=1;
|
||||||
|
for ( ; exponent; exponent >>= 1) {
|
||||||
|
if (exponent & 1)
|
||||||
|
value *= base;
|
||||||
|
base *= base;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double stb__clex_parse_float(char *p, char **q)
|
||||||
|
{
|
||||||
|
char *s = p;
|
||||||
|
double value=0;
|
||||||
|
int base=10;
|
||||||
|
int exponent=0;
|
||||||
|
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
if (*p == '0') {
|
||||||
|
if (p[1] == 'x' || p[1] == 'X') {
|
||||||
|
base=16;
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (*p >= '0' && *p <= '9')
|
||||||
|
value = value*base + (*p++ - '0');
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
else if (base == 16 && *p >= 'a' && *p <= 'f')
|
||||||
|
value = value*base + 10 + (*p++ - 'a');
|
||||||
|
else if (base == 16 && *p >= 'A' && *p <= 'F')
|
||||||
|
value = value*base + 10 + (*p++ - 'A');
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p == '.') {
|
||||||
|
double pow, addend = 0;
|
||||||
|
++p;
|
||||||
|
for (pow=1; ; pow*=base) {
|
||||||
|
if (*p >= '0' && *p <= '9')
|
||||||
|
addend = addend*base + (*p++ - '0');
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
else if (base == 16 && *p >= 'a' && *p <= 'f')
|
||||||
|
addend = addend*base + 10 + (*p++ - 'a');
|
||||||
|
else if (base == 16 && *p >= 'A' && *p <= 'F')
|
||||||
|
addend = addend*base + 10 + (*p++ - 'A');
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value += addend / pow;
|
||||||
|
}
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
if (base == 16) {
|
||||||
|
// exponent required for hex float literal
|
||||||
|
if (*p != 'p' && *p != 'P') {
|
||||||
|
*q = s;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
exponent = 1;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
exponent = (*p == 'e' || *p == 'E');
|
||||||
|
|
||||||
|
if (exponent) {
|
||||||
|
int sign = p[1] == '-';
|
||||||
|
unsigned int exponent=0;
|
||||||
|
double power=1;
|
||||||
|
++p;
|
||||||
|
if (*p == '-' || *p == '+')
|
||||||
|
++p;
|
||||||
|
while (*p >= '0' && *p <= '9')
|
||||||
|
exponent = exponent*10 + (*p++ - '0');
|
||||||
|
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
if (base == 16)
|
||||||
|
power = stb__clex_pow(2, exponent);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
power = stb__clex_pow(10, exponent);
|
||||||
|
if (sign)
|
||||||
|
value /= power;
|
||||||
|
else
|
||||||
|
value *= power;
|
||||||
|
}
|
||||||
|
*q = p;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int stb__clex_parse_char(char *p, char **q)
|
||||||
|
{
|
||||||
|
if (*p == '\\') {
|
||||||
|
*q = p+2; // tentatively guess we'll parse two characters
|
||||||
|
switch(p[1]) {
|
||||||
|
case '\\': return '\\';
|
||||||
|
case '\'': return '\'';
|
||||||
|
case '"': return '"';
|
||||||
|
case 't': return '\t';
|
||||||
|
case 'f': return '\f';
|
||||||
|
case 'n': return '\n';
|
||||||
|
case 'r': return '\r';
|
||||||
|
case '0': return '\0'; // @TODO ocatal constants
|
||||||
|
case 'x': case 'X': return -1; // @TODO hex constants
|
||||||
|
case 'u': return -1; // @TODO unicode constants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*q = p+1;
|
||||||
|
return (unsigned char) *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__clex_parse_string(stb_lexer *lexer, char *p, int type)
|
||||||
|
{
|
||||||
|
char *start = p;
|
||||||
|
char delim = *p++; // grab the " or ' for later matching
|
||||||
|
char *out = lexer->string_storage;
|
||||||
|
char *outend = lexer->string_storage + lexer->string_storage_len;
|
||||||
|
while (*p != delim) {
|
||||||
|
int n;
|
||||||
|
if (*p == '\\') {
|
||||||
|
char *q;
|
||||||
|
n = stb__clex_parse_char(p, &q);
|
||||||
|
if (n < 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, q);
|
||||||
|
p = q;
|
||||||
|
} else {
|
||||||
|
// @OPTIMIZE: could speed this up by looping-while-not-backslash
|
||||||
|
n = (unsigned char) *p++;
|
||||||
|
}
|
||||||
|
if (out+1 > outend)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, p);
|
||||||
|
// @TODO expand unicode escapes to UTF8
|
||||||
|
*out++ = (char) n;
|
||||||
|
}
|
||||||
|
*out = 0;
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = (int) (out - lexer->string_storage);
|
||||||
|
return stb__clex_token(lexer, type, start, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_c_lexer_get_token(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
char *p = lexer->parse_point;
|
||||||
|
|
||||||
|
// skip whitespace and comments
|
||||||
|
for (;;) {
|
||||||
|
#ifdef STB_C_LEX_ISWHITE
|
||||||
|
while (p != lexer->stream_end) {
|
||||||
|
int n;
|
||||||
|
n = STB_C_LEX_ISWHITE(p);
|
||||||
|
if (n == 0) break;
|
||||||
|
if (lexer->eof && lexer->eof - lexer->parse_point < n)
|
||||||
|
return stb__clex_token(tok, CLEX_parse_error, p,lexer->eof-1);
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (p != lexer->eof && stb__clex_iswhite(*p))
|
||||||
|
++p;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STB_C_LEX_CPP_COMMENTS(
|
||||||
|
if (p != lexer->eof && p[0] == '/' && p[1] == '/') {
|
||||||
|
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
STB_C_LEX_C_COMMENTS(
|
||||||
|
if (p != lexer->eof && p[0] == '/' && p[1] == '*') {
|
||||||
|
char *start = p;
|
||||||
|
p += 2;
|
||||||
|
while (p != lexer->eof && (p[0] != '*' || p[1] != '/'))
|
||||||
|
++p;
|
||||||
|
if (p == lexer->eof)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start, p-1);
|
||||||
|
p += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
#ifdef STB__clex_discard_preprocessor
|
||||||
|
// @TODO this discards everything after a '#', regardless
|
||||||
|
// of where in the line the # is, rather than requiring it
|
||||||
|
// be at the start. (because this parser doesn't otherwise
|
||||||
|
// check for line breaks!)
|
||||||
|
if (p != lexer->eof && p[0] == '#') {
|
||||||
|
while (p != lexer->eof && *p != '\r' && *p != '\n')
|
||||||
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == lexer->eof)
|
||||||
|
return stb__clex_eof(lexer);
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
default:
|
||||||
|
if ( (*p >= 'a' && *p <= 'z')
|
||||||
|
|| (*p >= 'A' && *p <= 'Z')
|
||||||
|
|| *p == '_' || (unsigned char) *p >= 128 // >= 128 is UTF8 char
|
||||||
|
STB_C_LEX_DOLLAR_IDENTIFIER( || *p == '$' ) )
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
lexer->string = lexer->string_storage;
|
||||||
|
lexer->string_len = n;
|
||||||
|
do {
|
||||||
|
if (n+1 >= lexer->string_storage_len)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p, p+n);
|
||||||
|
lexer->string[n] = p[n];
|
||||||
|
++n;
|
||||||
|
} while (
|
||||||
|
(p[n] >= 'a' && p[n] <= 'z')
|
||||||
|
|| (p[n] >= 'A' && p[n] <= 'Z')
|
||||||
|
|| (p[n] >= '0' && p[n] <= '9') // allow digits in middle of identifier
|
||||||
|
|| p[n] == '_' || (unsigned char) p[n] >= 128
|
||||||
|
STB_C_LEX_DOLLAR_IDENTIFIER( || p[n] == '$' )
|
||||||
|
);
|
||||||
|
lexer->string[n] = 0;
|
||||||
|
return stb__clex_token(lexer, CLEX_id, p, p+n-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for EOF
|
||||||
|
STB_C_LEX_0_IS_EOF(
|
||||||
|
if (*p == 0)
|
||||||
|
return stb__clex_eof(lexer);
|
||||||
|
)
|
||||||
|
|
||||||
|
single_char:
|
||||||
|
// not an identifier, return the character as itself
|
||||||
|
return stb__clex_token(lexer, *p, p, p);
|
||||||
|
|
||||||
|
case '+':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_INCREMENTS(if (p[1] == '+') return stb__clex_token(lexer, CLEX_plusplus, p,p+1);)
|
||||||
|
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_pluseq , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '-':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_INCREMENTS(if (p[1] == '-') return stb__clex_token(lexer, CLEX_minusminus, p,p+1);)
|
||||||
|
STB_C_LEX_C_ARITHEQ( if (p[1] == '=') return stb__clex_token(lexer, CLEX_minuseq , p,p+1);)
|
||||||
|
STB_C_LEX_C_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_arrow , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '&':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_LOGICAL( if (p[1] == '&') return stb__clex_token(lexer, CLEX_andand, p,p+1);)
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_andeq , p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '|':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_LOGICAL( if (p[1] == '|') return stb__clex_token(lexer, CLEX_oror, p,p+1);)
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p[1] == '=') return stb__clex_token(lexer, CLEX_oreq, p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '=':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_eq, p,p+1);)
|
||||||
|
STB_C_LEX_EQUAL_ARROW( if (p[1] == '>') return stb__clex_token(lexer, CLEX_eqarrow, p,p+1);)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '!':
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_noteq, p,p+1);)
|
||||||
|
goto single_char;
|
||||||
|
case '^':
|
||||||
|
STB_C_LEX_C_BITWISEEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_xoreq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '%':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_modeq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '*':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_muleq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '/':
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+1 != lexer->eof && p[1] == '=') return stb__clex_token(lexer, CLEX_diveq, p,p+1));
|
||||||
|
goto single_char;
|
||||||
|
case '<':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_lesseq, p,p+1);)
|
||||||
|
STB_C_LEX_C_SHIFTS( if (p[1] == '<') {
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||||
|
return stb__clex_token(lexer, CLEX_shleq, p,p+2);)
|
||||||
|
return stb__clex_token(lexer, CLEX_shl, p,p+1);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
case '>':
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
STB_C_LEX_C_COMPARISONS(if (p[1] == '=') return stb__clex_token(lexer, CLEX_greatereq, p,p+1);)
|
||||||
|
STB_C_LEX_C_SHIFTS( if (p[1] == '>') {
|
||||||
|
STB_C_LEX_C_ARITHEQ(if (p+2 != lexer->eof && p[2] == '=')
|
||||||
|
return stb__clex_token(lexer, CLEX_shreq, p,p+2);)
|
||||||
|
return stb__clex_token(lexer, CLEX_shr, p,p+1);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
goto single_char;
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
STB_C_LEX_C_DQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_dqstring);)
|
||||||
|
goto single_char;
|
||||||
|
case '\'':
|
||||||
|
STB_C_LEX_C_SQ_STRINGS(return stb__clex_parse_string(lexer, p, CLEX_sqstring);)
|
||||||
|
STB_C_LEX_C_CHARS(
|
||||||
|
{
|
||||||
|
char *start = p;
|
||||||
|
lexer->int_number = stb__clex_parse_char(p+1, &p);
|
||||||
|
if (lexer->int_number < 0)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start,start);
|
||||||
|
if (p == lexer->eof || *p != '\'')
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, start,p);
|
||||||
|
return stb__clex_token(lexer, CLEX_charlit, start, p+1);
|
||||||
|
})
|
||||||
|
goto single_char;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
#if defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
|
||||||
|
if (p+1 != lexer->eof) {
|
||||||
|
if (p[1] == 'x' || p[1] == 'X') {
|
||||||
|
char *q;
|
||||||
|
|
||||||
|
#ifdef STB__clex_hex_floats
|
||||||
|
for (q=p+2;
|
||||||
|
q != lexer->eof && ((*q >= '0' && *q <= '9') || (*q >= 'a' && *q <= 'f') || (*q >= 'A' && *q <= 'F'));
|
||||||
|
++q);
|
||||||
|
if (q != lexer->eof) {
|
||||||
|
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'p' || *q == 'P')) {
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->real_number = strtod((char *) p, (char**) &q);
|
||||||
|
#else
|
||||||
|
lexer->real_number = stb__clex_parse_float(p, &q);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (p == q)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p,q);
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB__CLEX_hex_floats
|
||||||
|
|
||||||
|
#ifdef STB__clex_hex_ints
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) &q, 16);
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
stb__clex_int n=0;
|
||||||
|
for (q=p+2; q != lexer->eof; ++q) {
|
||||||
|
if (*q >= '0' && *q <= '9')
|
||||||
|
n = n*16 + (*q - '0');
|
||||||
|
else if (*q >= 'a' && *q <= 'f')
|
||||||
|
n = n*16 + (*q - 'a') + 10;
|
||||||
|
else if (*q >= 'A' && *q <= 'F')
|
||||||
|
n = n*16 + (*q - 'A') + 10;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lexer->int_number = n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (q == p+2)
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p-2,p-1);
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_HEX_SUFFIXES);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // defined(STB__clex_hex_ints) || defined(STB__clex_hex_floats)
|
||||||
|
// can't test for octal because we might parse '0.0' as float or as '0' '.' '0',
|
||||||
|
// so have to do float first
|
||||||
|
|
||||||
|
/* FALL THROUGH */
|
||||||
|
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
|
||||||
|
|
||||||
|
#ifdef STB__clex_decimal_floats
|
||||||
|
{
|
||||||
|
char *q = p;
|
||||||
|
while (q != lexer->eof && (*q >= '0' && *q <= '9'))
|
||||||
|
++q;
|
||||||
|
if (q != lexer->eof) {
|
||||||
|
if (*q == '.' STB_C_LEX_FLOAT_NO_DECIMAL(|| *q == 'e' || *q == 'E')) {
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->real_number = strtod((char *) p, (char**) &q);
|
||||||
|
#else
|
||||||
|
lexer->real_number = stb__clex_parse_float(p, &q);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_floatlit, p,q, STB_C_LEX_FLOAT_SUFFIXES);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB__clex_decimal_floats
|
||||||
|
|
||||||
|
#ifdef STB__clex_octal_ints
|
||||||
|
if (p[0] == '0') {
|
||||||
|
char *q = p;
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) &q, 8);
|
||||||
|
#else
|
||||||
|
stb__clex_int n=0;
|
||||||
|
while (q != lexer->eof) {
|
||||||
|
if (*q >= '0' && *q <= '7')
|
||||||
|
n = n*8 + (*q - '0');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
if (q != lexer->eof && (*q == '8' || *q=='9'))
|
||||||
|
return stb__clex_token(lexer, CLEX_parse_error, p, q);
|
||||||
|
lexer->int_number = n;
|
||||||
|
#endif
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||||
|
}
|
||||||
|
#endif // STB__clex_octal_ints
|
||||||
|
|
||||||
|
#ifdef STB__clex_decimal_ints
|
||||||
|
{
|
||||||
|
char *q = p;
|
||||||
|
#ifdef STB__CLEX_use_stdlib
|
||||||
|
lexer->int_number = strtol((char *) p, (char **) &q, 10);
|
||||||
|
#else
|
||||||
|
stb__clex_int n=0;
|
||||||
|
while (q != lexer->eof) {
|
||||||
|
if (*q >= '0' && *q <= '9')
|
||||||
|
n = n*10 + (*q - '0');
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++q;
|
||||||
|
}
|
||||||
|
lexer->int_number = n;
|
||||||
|
#endif
|
||||||
|
return stb__clex_parse_suffixes(lexer, CLEX_intlit, p,q, STB_C_LEX_OCTAL_SUFFIXES);
|
||||||
|
}
|
||||||
|
#endif // STB__clex_decimal_ints
|
||||||
|
goto single_char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // STB_C_LEXER_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifdef STB_C_LEXER_SELF_TEST
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static void print_token(stb_lexer *lexer)
|
||||||
|
{
|
||||||
|
switch (lexer->token) {
|
||||||
|
case CLEX_id : printf("_%s", lexer->string); break;
|
||||||
|
case CLEX_eq : printf("=="); break;
|
||||||
|
case CLEX_noteq : printf("!="); break;
|
||||||
|
case CLEX_lesseq : printf("<="); break;
|
||||||
|
case CLEX_greatereq : printf(">="); break;
|
||||||
|
case CLEX_andand : printf("&&"); break;
|
||||||
|
case CLEX_oror : printf("||"); break;
|
||||||
|
case CLEX_shl : printf("<<"); break;
|
||||||
|
case CLEX_shr : printf(">>"); break;
|
||||||
|
case CLEX_plusplus : printf("++"); break;
|
||||||
|
case CLEX_minusminus: printf("--"); break;
|
||||||
|
case CLEX_arrow : printf("->"); break;
|
||||||
|
case CLEX_andeq : printf("&="); break;
|
||||||
|
case CLEX_oreq : printf("|="); break;
|
||||||
|
case CLEX_xoreq : printf("^="); break;
|
||||||
|
case CLEX_pluseq : printf("+="); break;
|
||||||
|
case CLEX_minuseq : printf("-="); break;
|
||||||
|
case CLEX_muleq : printf("*="); break;
|
||||||
|
case CLEX_diveq : printf("/="); break;
|
||||||
|
case CLEX_modeq : printf("%%="); break;
|
||||||
|
case CLEX_shleq : printf("<<="); break;
|
||||||
|
case CLEX_shreq : printf(">>="); break;
|
||||||
|
case CLEX_eqarrow : printf("=>"); break;
|
||||||
|
case CLEX_dqstring : printf("\"%s\"", lexer->string); break;
|
||||||
|
case CLEX_sqstring : printf("'\"%s\"'", lexer->string); break;
|
||||||
|
case CLEX_charlit : printf("'%s'", lexer->string); break;
|
||||||
|
#if defined(STB__clex_int_as_double) && !defined(STB__CLEX_use_stdlib)
|
||||||
|
case CLEX_intlit : printf("#%g", lexer->real_number); break;
|
||||||
|
#else
|
||||||
|
case CLEX_intlit : printf("#%ld", lexer->int_number); break;
|
||||||
|
#endif
|
||||||
|
case CLEX_floatlit : printf("%g", lexer->real_number); break;
|
||||||
|
default:
|
||||||
|
if (lexer->token >= 0 && lexer->token < 256)
|
||||||
|
printf("%c", (int) lexer->token);
|
||||||
|
else {
|
||||||
|
printf("<<<UNKNOWN TOKEN %ld >>>\n", lexer->token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force a test
|
||||||
|
of parsing
|
||||||
|
multiline comments */
|
||||||
|
|
||||||
|
/*/ comment /*/
|
||||||
|
/**/ extern /**/
|
||||||
|
|
||||||
|
void dummy(void)
|
||||||
|
{
|
||||||
|
double some_floats[] = {
|
||||||
|
1.0501, -10.4e12, 5E+10,
|
||||||
|
#if 0 // not supported in C++ or C-pre-99, so don't try to compile it, but let our parser test it
|
||||||
|
0x1.0p+24, 0xff.FP-8, 0x1p-23,
|
||||||
|
#endif
|
||||||
|
4.
|
||||||
|
};
|
||||||
|
(void) sizeof(some_floats);
|
||||||
|
(void) some_floats[1];
|
||||||
|
|
||||||
|
printf("test %d",1); // https://github.com/nothings/stb/issues/13
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
FILE *f = fopen("stb_c_lexer.h","rb");
|
||||||
|
char *text = (char *) malloc(1 << 20);
|
||||||
|
int len = f ? (int) fread(text, 1, 1<<20, f) : -1;
|
||||||
|
stb_lexer lex;
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "Error opening file\n");
|
||||||
|
free(text);
|
||||||
|
fclose(f);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
stb_c_lexer_init(&lex, text, text+len, (char *) malloc(0x10000), 0x10000);
|
||||||
|
while (stb_c_lexer_get_token(&lex)) {
|
||||||
|
if (lex.token == CLEX_parse_error) {
|
||||||
|
printf("\n<<<PARSE ERROR>>>\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_token(&lex);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1049
external/stb/stb/stb_connected_components.h
vendored
Normal file
433
external/stb/stb/stb_divide.h
vendored
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
// stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010
|
||||||
|
// Three kinds of divide/modulus of signed integers.
|
||||||
|
//
|
||||||
|
// HISTORY
|
||||||
|
//
|
||||||
|
// v0.94 Fix integer overflow issues
|
||||||
|
// v0.93 2020-02-02 Write useful exit() value from main()
|
||||||
|
// v0.92 2019-02-25 Fix warning
|
||||||
|
// v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C
|
||||||
|
// Check result with 64-bit math to catch such cases
|
||||||
|
// v0.90 2010-02-24 First public release
|
||||||
|
//
|
||||||
|
// USAGE
|
||||||
|
//
|
||||||
|
// In *ONE* source file, put:
|
||||||
|
//
|
||||||
|
// #define STB_DIVIDE_IMPLEMENTATION
|
||||||
|
// // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1
|
||||||
|
// // #define C_INTEGER_DIVISION_FLOORS // see Note 2
|
||||||
|
// #include "stb_divide.h"
|
||||||
|
//
|
||||||
|
// Other source files should just include stb_divide.h
|
||||||
|
//
|
||||||
|
// Note 1: On platforms/compilers that you know signed C division
|
||||||
|
// truncates, you can #define C_INTEGER_DIVISION_TRUNCATES.
|
||||||
|
//
|
||||||
|
// Note 2: On platforms/compilers that you know signed C division
|
||||||
|
// floors (rounds to negative infinity), you can #define
|
||||||
|
// C_INTEGER_DIVISION_FLOORS.
|
||||||
|
//
|
||||||
|
// You can #define STB_DIVIDE_TEST in which case the implementation
|
||||||
|
// will generate a main() and compiling the result will create a
|
||||||
|
// program that tests the implementation. Run it with no arguments
|
||||||
|
// and any output indicates an error; run it with any argument and
|
||||||
|
// it will also print the test results. Define STB_DIVIDE_TEST_64
|
||||||
|
// to a 64-bit integer type to avoid overflows in the result-checking
|
||||||
|
// which give false negatives.
|
||||||
|
//
|
||||||
|
// ABOUT
|
||||||
|
//
|
||||||
|
// This file provides three different consistent divide/mod pairs
|
||||||
|
// implemented on top of arbitrary C/C++ division, including correct
|
||||||
|
// handling of overflow of intermediate calculations:
|
||||||
|
//
|
||||||
|
// trunc: a/b truncates to 0, a%b has same sign as a
|
||||||
|
// floor: a/b truncates to -inf, a%b has same sign as b
|
||||||
|
// eucl: a/b truncates to sign(b)*inf, a%b is non-negative
|
||||||
|
//
|
||||||
|
// Not necessarily optimal; I tried to keep it generally efficient,
|
||||||
|
// but there may be better ways.
|
||||||
|
//
|
||||||
|
// Briefly, for those who are not familiar with the problem, we note
|
||||||
|
// the reason these divides exist and are interesting:
|
||||||
|
//
|
||||||
|
// 'trunc' is easy to implement in hardware (strip the signs,
|
||||||
|
// compute, reapply the signs), thus is commonly defined
|
||||||
|
// by many languages (including C99)
|
||||||
|
//
|
||||||
|
// 'floor' is simple to define and better behaved than trunc;
|
||||||
|
// for example it divides integers into fixed-size buckets
|
||||||
|
// without an extra-wide bucket at 0, and for a fixed
|
||||||
|
// divisor N there are only |N| possible moduli.
|
||||||
|
//
|
||||||
|
// 'eucl' guarantees fixed-sized buckets *and* a non-negative
|
||||||
|
// modulus and defines division to be whatever is needed
|
||||||
|
// to achieve that result.
|
||||||
|
//
|
||||||
|
// See "The Euclidean definition of the functions div and mod"
|
||||||
|
// by Raymond Boute (1992), or "Division and Modulus for Computer
|
||||||
|
// Scientists" by Daan Leijen (2001)
|
||||||
|
//
|
||||||
|
// We assume of the built-in C division:
|
||||||
|
// (a) modulus is the remainder for the corresponding division
|
||||||
|
// (b) a/b truncates if a and b are the same sign
|
||||||
|
//
|
||||||
|
// Property (a) requires (a/b)*b + (a%b)==a, and is required by C.
|
||||||
|
// Property (b) seems to be true of all hardware but is *not* satisfied
|
||||||
|
// by the euclidean division operator we define, so it's possibly not
|
||||||
|
// always true. If any such platform turns up, we can add more cases.
|
||||||
|
// (Possibly only stb_div_trunc currently relies on property (b).)
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_DIVIDE_H
|
||||||
|
#define INCLUDE_STB_DIVIDE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by);
|
||||||
|
extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by);
|
||||||
|
extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by);
|
||||||
|
extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by);
|
||||||
|
extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by);
|
||||||
|
extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_DIVIDE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901
|
||||||
|
#ifndef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
#define C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INT_MIN
|
||||||
|
#include <limits.h> // if you have no limits.h, #define INT_MIN yourself
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the following macros are designed to allow testing
|
||||||
|
// other platforms by simulating them
|
||||||
|
#ifndef STB_DIVIDE_TEST_FLOOR
|
||||||
|
#define stb__div(a,b) ((a)/(b))
|
||||||
|
#define stb__mod(a,b) ((a)%(b))
|
||||||
|
#else
|
||||||
|
// implement floor-style divide on trunc platform
|
||||||
|
#ifndef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
#error "floor test requires truncating division"
|
||||||
|
#endif
|
||||||
|
#undef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
int stb__div(int v1, int v2)
|
||||||
|
{
|
||||||
|
int q = v1/v2, r = v1%v2;
|
||||||
|
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
|
||||||
|
return q-1;
|
||||||
|
else
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb__mod(int v1, int v2)
|
||||||
|
{
|
||||||
|
int r = v1%v2;
|
||||||
|
if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0))
|
||||||
|
return r+v2;
|
||||||
|
else
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int stb_div_trunc(int v1, int v2)
|
||||||
|
{
|
||||||
|
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
return v1/v2;
|
||||||
|
#else
|
||||||
|
if (v1 >= 0 && v2 <= 0)
|
||||||
|
return -stb__div(-v1,v2); // both negative to avoid overflow
|
||||||
|
if (v1 <= 0 && v2 >= 0)
|
||||||
|
if (v1 != INT_MIN)
|
||||||
|
return -stb__div(v1,-v2); // both negative to avoid overflow
|
||||||
|
else
|
||||||
|
return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point
|
||||||
|
else
|
||||||
|
return v1/v2; // same sign, so expect truncation
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_div_floor(int v1, int v2)
|
||||||
|
{
|
||||||
|
#ifdef C_INTEGER_DIVISION_FLOORS
|
||||||
|
return v1/v2;
|
||||||
|
#else
|
||||||
|
if (v1 >= 0 && v2 < 0) {
|
||||||
|
if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows
|
||||||
|
return -stb__div((v2+1)-v1,v2); // nope, so just compute it
|
||||||
|
else
|
||||||
|
return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0);
|
||||||
|
}
|
||||||
|
if (v1 < 0 && v2 >= 0) {
|
||||||
|
if (v1 != INT_MIN) {
|
||||||
|
if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows
|
||||||
|
return -stb__div((v1+1)-v2,-v2); // nope, so just compute it
|
||||||
|
else
|
||||||
|
return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0);
|
||||||
|
} else // it must be possible to compute -(v1+v2) without overflowing
|
||||||
|
return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1);
|
||||||
|
} else
|
||||||
|
return v1/v2; // same sign, so expect truncation
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_div_eucl(int v1, int v2)
|
||||||
|
{
|
||||||
|
int q,r;
|
||||||
|
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
q = v1/v2;
|
||||||
|
r = v1%v2;
|
||||||
|
#else
|
||||||
|
// handle every quadrant separately, since we can't rely on q and r flor
|
||||||
|
if (v1 >= 0)
|
||||||
|
if (v2 >= 0)
|
||||||
|
return stb__div(v1,v2);
|
||||||
|
else if (v2 != INT_MIN)
|
||||||
|
q = -stb__div(v1,-v2), r = stb__mod(v1,-v2);
|
||||||
|
else
|
||||||
|
q = 0, r = v1;
|
||||||
|
else if (v1 != INT_MIN)
|
||||||
|
if (v2 >= 0)
|
||||||
|
q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2);
|
||||||
|
else if (v2 != INT_MIN)
|
||||||
|
q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2);
|
||||||
|
else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2
|
||||||
|
q = 1, r = v1-q*v2;
|
||||||
|
else // if v1 is INT_MIN, we have to move away from overflow place
|
||||||
|
if (v2 >= 0)
|
||||||
|
q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2);
|
||||||
|
else if (v2 != INT_MIN)
|
||||||
|
q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2);
|
||||||
|
else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow
|
||||||
|
q = 1, r = 0;
|
||||||
|
#endif
|
||||||
|
if (r >= 0)
|
||||||
|
return q;
|
||||||
|
else
|
||||||
|
return q + (v2 > 0 ? -1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_mod_trunc(int v1, int v2)
|
||||||
|
{
|
||||||
|
#ifdef C_INTEGER_DIVISION_TRUNCATES
|
||||||
|
return v1%v2;
|
||||||
|
#else
|
||||||
|
if (v1 >= 0) { // modulus result should always be positive
|
||||||
|
int r = stb__mod(v1,v2);
|
||||||
|
if (r >= 0)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return r - (v2 < 0 ? v2 : -v2);
|
||||||
|
} else { // modulus result should always be negative
|
||||||
|
int r = stb__mod(v1,v2);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return r + (v2 < 0 ? v2 : -v2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_mod_floor(int v1, int v2)
|
||||||
|
{
|
||||||
|
#ifdef C_INTEGER_DIVISION_FLOORS
|
||||||
|
return v1%v2;
|
||||||
|
#else
|
||||||
|
if (v2 >= 0) { // result should always be positive
|
||||||
|
int r = stb__mod(v1,v2);
|
||||||
|
if (r >= 0)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return r + v2;
|
||||||
|
} else { // result should always be negative
|
||||||
|
int r = stb__mod(v1,v2);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return r + v2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int stb_mod_eucl(int v1, int v2)
|
||||||
|
{
|
||||||
|
int r = stb__mod(v1,v2);
|
||||||
|
|
||||||
|
if (r >= 0)
|
||||||
|
return r;
|
||||||
|
else
|
||||||
|
return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow]
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STB_DIVIDE_TEST
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
int show=0;
|
||||||
|
int err=0;
|
||||||
|
|
||||||
|
void stbdiv_check(int q, int r, int a, int b, char *type, int dir)
|
||||||
|
{
|
||||||
|
if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) {
|
||||||
|
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r);
|
||||||
|
err++;
|
||||||
|
} else
|
||||||
|
if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid
|
||||||
|
if (r <= -abs(b) || r >= abs(b)) {
|
||||||
|
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
#ifdef STB_DIVIDE_TEST_64
|
||||||
|
{
|
||||||
|
STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b;
|
||||||
|
if (q64*b64+r64 != a64) {
|
||||||
|
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (q*b+r != a) {
|
||||||
|
fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q);
|
||||||
|
err++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void test(int a, int b)
|
||||||
|
{
|
||||||
|
int q,r;
|
||||||
|
if (show) printf("(%+11d,%+d) | ", a,b);
|
||||||
|
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b);
|
||||||
|
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a);
|
||||||
|
q = stb_div_floor(a,b), r = stb_mod_floor(a,b);
|
||||||
|
if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b);
|
||||||
|
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b);
|
||||||
|
if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testh(int a, int b)
|
||||||
|
{
|
||||||
|
int q,r;
|
||||||
|
if (show) printf("(%08x,%08x) |\n", a,b);
|
||||||
|
q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a);
|
||||||
|
if (show) printf(" (%08x,%08x)", q,r);
|
||||||
|
q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b);
|
||||||
|
if (show) printf(" (%08x,%08x)", q,r);
|
||||||
|
q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1);
|
||||||
|
if (show) printf(" (%08x,%08x)\n ", q,r);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc > 1) show=1;
|
||||||
|
|
||||||
|
test(8,3);
|
||||||
|
test(8,-3);
|
||||||
|
test(-8,3);
|
||||||
|
test(-8,-3);
|
||||||
|
test(1,2);
|
||||||
|
test(1,-2);
|
||||||
|
test(-1,2);
|
||||||
|
test(-1,-2);
|
||||||
|
test(8,4);
|
||||||
|
test(8,-4);
|
||||||
|
test(-8,4);
|
||||||
|
test(-8,-4);
|
||||||
|
|
||||||
|
test(INT_MAX,1);
|
||||||
|
test(INT_MIN,1);
|
||||||
|
test(INT_MIN+1,1);
|
||||||
|
test(INT_MAX,-1);
|
||||||
|
//test(INT_MIN,-1); // this traps in MSVC, so we leave it untested
|
||||||
|
test(INT_MIN+1,-1);
|
||||||
|
test(INT_MIN,-2);
|
||||||
|
test(INT_MIN+1,2);
|
||||||
|
test(INT_MIN+1,-2);
|
||||||
|
test(INT_MAX,2);
|
||||||
|
test(INT_MAX,-2);
|
||||||
|
test(INT_MIN+1,2);
|
||||||
|
test(INT_MIN+1,-2);
|
||||||
|
test(INT_MIN,2);
|
||||||
|
test(INT_MIN,-2);
|
||||||
|
test(INT_MIN,7);
|
||||||
|
test(INT_MIN,-7);
|
||||||
|
test(INT_MIN+1,4);
|
||||||
|
test(INT_MIN+1,-4);
|
||||||
|
|
||||||
|
testh(-7, INT_MIN);
|
||||||
|
testh(-1, INT_MIN);
|
||||||
|
testh(1, INT_MIN);
|
||||||
|
testh(7, INT_MIN);
|
||||||
|
|
||||||
|
testh(INT_MAX-1, INT_MIN);
|
||||||
|
testh(INT_MAX, INT_MIN);
|
||||||
|
testh(INT_MIN, INT_MIN);
|
||||||
|
testh(INT_MIN+1, INT_MIN);
|
||||||
|
|
||||||
|
testh(INT_MAX-1, INT_MAX);
|
||||||
|
testh(INT_MAX , INT_MAX);
|
||||||
|
testh(INT_MIN , INT_MAX);
|
||||||
|
testh(INT_MIN+1, INT_MAX);
|
||||||
|
|
||||||
|
return err > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
#endif // STB_DIVIDE_TEST
|
||||||
|
#endif // STB_DIVIDE_IMPLEMENTATION
|
||||||
|
#endif // INCLUDE_STB_DIVIDE_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1895
external/stb/stb/stb_ds.h
vendored
Normal file
719
external/stb/stb/stb_dxt.h
vendored
Normal file
@ -0,0 +1,719 @@
|
|||||||
|
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor - public domain
|
||||||
|
// original by fabian "ryg" giesen - ported to C by stb
|
||||||
|
// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// call stb_compress_dxt_block() for every block (you must pad)
|
||||||
|
// source should be a 4x4 block of RGBA data in row-major order;
|
||||||
|
// Alpha channel is not stored if you specify alpha=0 (but you
|
||||||
|
// must supply some constant alpha in the alpha channel).
|
||||||
|
// You can turn on dithering and "high quality" using mode.
|
||||||
|
//
|
||||||
|
// version history:
|
||||||
|
// v1.12 - (ryg) fix bug in single-color table generator
|
||||||
|
// v1.11 - (ryg) avoid racy global init, better single-color tables, remove dither
|
||||||
|
// v1.10 - (i.c) various small quality improvements
|
||||||
|
// v1.09 - (stb) update documentation re: surprising alpha channel requirement
|
||||||
|
// v1.08 - (stb) fix bug in dxt-with-alpha block
|
||||||
|
// v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC
|
||||||
|
// v1.06 - (stb) fix to known-broken 1.05
|
||||||
|
// v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski)
|
||||||
|
// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec);
|
||||||
|
// single color match fix (allow for inexact color interpolation);
|
||||||
|
// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps.
|
||||||
|
// v1.03 - (stb) endianness support
|
||||||
|
// v1.02 - (stb) fix alpha encoding bug
|
||||||
|
// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom
|
||||||
|
// v1.00 - (stb) first release
|
||||||
|
//
|
||||||
|
// contributors:
|
||||||
|
// Rich Geldreich (more accurate index selection)
|
||||||
|
// Kevin Schmidt (#defines for "freestanding" compilation)
|
||||||
|
// github:ppiastucki (BC4 support)
|
||||||
|
// Ignacio Castano - improve DXT endpoint quantization
|
||||||
|
// Alan Hickman - static table initialization
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_DXT_H
|
||||||
|
#define STB_INCLUDE_STB_DXT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_DXT_STATIC
|
||||||
|
#define STBDDEF static
|
||||||
|
#else
|
||||||
|
#define STBDDEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compression mode (bitflags)
|
||||||
|
#define STB_DXT_NORMAL 0
|
||||||
|
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
|
||||||
|
#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
|
||||||
|
|
||||||
|
STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode);
|
||||||
|
STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel);
|
||||||
|
STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel);
|
||||||
|
|
||||||
|
#define STB_COMPRESS_DXT_BLOCK
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // STB_INCLUDE_STB_DXT_H
|
||||||
|
|
||||||
|
#ifdef STB_DXT_IMPLEMENTATION
|
||||||
|
|
||||||
|
// configuration options for DXT encoder. set them in the project/makefile or just define
|
||||||
|
// them at the top.
|
||||||
|
|
||||||
|
// STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
// use a rounding bias during color interpolation. this is closer to what "ideal"
|
||||||
|
// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03)
|
||||||
|
// implicitly had this turned on.
|
||||||
|
//
|
||||||
|
// in case you're targeting a specific type of hardware (e.g. console programmers):
|
||||||
|
// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer
|
||||||
|
// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias.
|
||||||
|
// you also see "(a*5 + b*3) / 8" on some old GPU designs.
|
||||||
|
// #define STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if !defined(STBD_FABS)
|
||||||
|
#include <math.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBD_FABS
|
||||||
|
#define STBD_FABS(x) fabs(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const unsigned char stb__OMatch5[256][2] = {
|
||||||
|
{ 0, 0 }, { 0, 0 }, { 0, 1 }, { 0, 1 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 1 },
|
||||||
|
{ 1, 1 }, { 1, 1 }, { 1, 2 }, { 0, 4 }, { 2, 1 }, { 2, 1 }, { 2, 1 }, { 2, 2 },
|
||||||
|
{ 2, 2 }, { 2, 2 }, { 2, 3 }, { 1, 5 }, { 3, 2 }, { 3, 2 }, { 4, 0 }, { 3, 3 },
|
||||||
|
{ 3, 3 }, { 3, 3 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 5 }, { 4, 3 }, { 4, 3 },
|
||||||
|
{ 5, 2 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 4, 5 }, { 5, 4 }, { 5, 4 }, { 5, 4 },
|
||||||
|
{ 6, 3 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 4, 8 }, { 6, 5 }, { 6, 5 }, { 6, 5 },
|
||||||
|
{ 6, 6 }, { 6, 6 }, { 6, 6 }, { 6, 7 }, { 5, 9 }, { 7, 6 }, { 7, 6 }, { 8, 4 },
|
||||||
|
{ 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 7, 8 }, { 7, 8 }, { 7, 9 }, { 8, 7 },
|
||||||
|
{ 8, 7 }, { 9, 6 }, { 8, 8 }, { 8, 8 }, { 8, 9 }, { 8, 9 }, { 9, 8 }, { 9, 8 },
|
||||||
|
{ 9, 8 }, { 10, 7 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 8, 12 }, { 10, 9 }, { 10, 9 },
|
||||||
|
{ 10, 9 }, { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 11 }, { 9, 13 }, { 11, 10 }, { 11, 10 },
|
||||||
|
{ 12, 8 }, { 11, 11 }, { 11, 11 }, { 11, 11 }, { 11, 12 }, { 11, 12 }, { 11, 12 }, { 11, 13 },
|
||||||
|
{ 12, 11 }, { 12, 11 }, { 13, 10 }, { 12, 12 }, { 12, 12 }, { 12, 13 }, { 12, 13 }, { 13, 12 },
|
||||||
|
{ 13, 12 }, { 13, 12 }, { 14, 11 }, { 13, 13 }, { 13, 13 }, { 13, 14 }, { 12, 16 }, { 14, 13 },
|
||||||
|
{ 14, 13 }, { 14, 13 }, { 14, 14 }, { 14, 14 }, { 14, 14 }, { 14, 15 }, { 13, 17 }, { 15, 14 },
|
||||||
|
{ 15, 14 }, { 16, 12 }, { 15, 15 }, { 15, 15 }, { 15, 15 }, { 15, 16 }, { 15, 16 }, { 15, 16 },
|
||||||
|
{ 15, 17 }, { 16, 15 }, { 16, 15 }, { 17, 14 }, { 16, 16 }, { 16, 16 }, { 16, 17 }, { 16, 17 },
|
||||||
|
{ 17, 16 }, { 17, 16 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 17 }, { 17, 18 }, { 16, 20 },
|
||||||
|
{ 18, 17 }, { 18, 17 }, { 18, 17 }, { 18, 18 }, { 18, 18 }, { 18, 18 }, { 18, 19 }, { 17, 21 },
|
||||||
|
{ 19, 18 }, { 19, 18 }, { 20, 16 }, { 19, 19 }, { 19, 19 }, { 19, 19 }, { 19, 20 }, { 19, 20 },
|
||||||
|
{ 19, 20 }, { 19, 21 }, { 20, 19 }, { 20, 19 }, { 21, 18 }, { 20, 20 }, { 20, 20 }, { 20, 21 },
|
||||||
|
{ 20, 21 }, { 21, 20 }, { 21, 20 }, { 21, 20 }, { 22, 19 }, { 21, 21 }, { 21, 21 }, { 21, 22 },
|
||||||
|
{ 20, 24 }, { 22, 21 }, { 22, 21 }, { 22, 21 }, { 22, 22 }, { 22, 22 }, { 22, 22 }, { 22, 23 },
|
||||||
|
{ 21, 25 }, { 23, 22 }, { 23, 22 }, { 24, 20 }, { 23, 23 }, { 23, 23 }, { 23, 23 }, { 23, 24 },
|
||||||
|
{ 23, 24 }, { 23, 24 }, { 23, 25 }, { 24, 23 }, { 24, 23 }, { 25, 22 }, { 24, 24 }, { 24, 24 },
|
||||||
|
{ 24, 25 }, { 24, 25 }, { 25, 24 }, { 25, 24 }, { 25, 24 }, { 26, 23 }, { 25, 25 }, { 25, 25 },
|
||||||
|
{ 25, 26 }, { 24, 28 }, { 26, 25 }, { 26, 25 }, { 26, 25 }, { 26, 26 }, { 26, 26 }, { 26, 26 },
|
||||||
|
{ 26, 27 }, { 25, 29 }, { 27, 26 }, { 27, 26 }, { 28, 24 }, { 27, 27 }, { 27, 27 }, { 27, 27 },
|
||||||
|
{ 27, 28 }, { 27, 28 }, { 27, 28 }, { 27, 29 }, { 28, 27 }, { 28, 27 }, { 29, 26 }, { 28, 28 },
|
||||||
|
{ 28, 28 }, { 28, 29 }, { 28, 29 }, { 29, 28 }, { 29, 28 }, { 29, 28 }, { 30, 27 }, { 29, 29 },
|
||||||
|
{ 29, 29 }, { 29, 30 }, { 29, 30 }, { 30, 29 }, { 30, 29 }, { 30, 29 }, { 30, 30 }, { 30, 30 },
|
||||||
|
{ 30, 30 }, { 30, 31 }, { 30, 31 }, { 31, 30 }, { 31, 30 }, { 31, 30 }, { 31, 31 }, { 31, 31 },
|
||||||
|
};
|
||||||
|
static const unsigned char stb__OMatch6[256][2] = {
|
||||||
|
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
|
||||||
|
{ 2, 2 }, { 2, 3 }, { 3, 2 }, { 3, 3 }, { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 },
|
||||||
|
{ 4, 4 }, { 4, 5 }, { 5, 4 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 6, 5 }, { 6, 6 },
|
||||||
|
{ 6, 6 }, { 6, 7 }, { 7, 6 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 8, 7 }, { 8, 8 },
|
||||||
|
{ 8, 8 }, { 8, 9 }, { 9, 8 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 10, 9 }, { 10, 10 },
|
||||||
|
{ 10, 10 }, { 10, 11 }, { 11, 10 }, { 8, 16 }, { 11, 11 }, { 11, 12 }, { 12, 11 }, { 9, 17 },
|
||||||
|
{ 12, 12 }, { 12, 13 }, { 13, 12 }, { 11, 16 }, { 13, 13 }, { 13, 14 }, { 14, 13 }, { 12, 17 },
|
||||||
|
{ 14, 14 }, { 14, 15 }, { 15, 14 }, { 14, 16 }, { 15, 15 }, { 15, 16 }, { 16, 14 }, { 16, 15 },
|
||||||
|
{ 17, 14 }, { 16, 16 }, { 16, 17 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 18 }, { 18, 17 },
|
||||||
|
{ 20, 14 }, { 18, 18 }, { 18, 19 }, { 19, 18 }, { 21, 15 }, { 19, 19 }, { 19, 20 }, { 20, 19 },
|
||||||
|
{ 20, 20 }, { 20, 20 }, { 20, 21 }, { 21, 20 }, { 21, 21 }, { 21, 21 }, { 21, 22 }, { 22, 21 },
|
||||||
|
{ 22, 22 }, { 22, 22 }, { 22, 23 }, { 23, 22 }, { 23, 23 }, { 23, 23 }, { 23, 24 }, { 24, 23 },
|
||||||
|
{ 24, 24 }, { 24, 24 }, { 24, 25 }, { 25, 24 }, { 25, 25 }, { 25, 25 }, { 25, 26 }, { 26, 25 },
|
||||||
|
{ 26, 26 }, { 26, 26 }, { 26, 27 }, { 27, 26 }, { 24, 32 }, { 27, 27 }, { 27, 28 }, { 28, 27 },
|
||||||
|
{ 25, 33 }, { 28, 28 }, { 28, 29 }, { 29, 28 }, { 27, 32 }, { 29, 29 }, { 29, 30 }, { 30, 29 },
|
||||||
|
{ 28, 33 }, { 30, 30 }, { 30, 31 }, { 31, 30 }, { 30, 32 }, { 31, 31 }, { 31, 32 }, { 32, 30 },
|
||||||
|
{ 32, 31 }, { 33, 30 }, { 32, 32 }, { 32, 33 }, { 33, 32 }, { 34, 31 }, { 33, 33 }, { 33, 34 },
|
||||||
|
{ 34, 33 }, { 36, 30 }, { 34, 34 }, { 34, 35 }, { 35, 34 }, { 37, 31 }, { 35, 35 }, { 35, 36 },
|
||||||
|
{ 36, 35 }, { 36, 36 }, { 36, 36 }, { 36, 37 }, { 37, 36 }, { 37, 37 }, { 37, 37 }, { 37, 38 },
|
||||||
|
{ 38, 37 }, { 38, 38 }, { 38, 38 }, { 38, 39 }, { 39, 38 }, { 39, 39 }, { 39, 39 }, { 39, 40 },
|
||||||
|
{ 40, 39 }, { 40, 40 }, { 40, 40 }, { 40, 41 }, { 41, 40 }, { 41, 41 }, { 41, 41 }, { 41, 42 },
|
||||||
|
{ 42, 41 }, { 42, 42 }, { 42, 42 }, { 42, 43 }, { 43, 42 }, { 40, 48 }, { 43, 43 }, { 43, 44 },
|
||||||
|
{ 44, 43 }, { 41, 49 }, { 44, 44 }, { 44, 45 }, { 45, 44 }, { 43, 48 }, { 45, 45 }, { 45, 46 },
|
||||||
|
{ 46, 45 }, { 44, 49 }, { 46, 46 }, { 46, 47 }, { 47, 46 }, { 46, 48 }, { 47, 47 }, { 47, 48 },
|
||||||
|
{ 48, 46 }, { 48, 47 }, { 49, 46 }, { 48, 48 }, { 48, 49 }, { 49, 48 }, { 50, 47 }, { 49, 49 },
|
||||||
|
{ 49, 50 }, { 50, 49 }, { 52, 46 }, { 50, 50 }, { 50, 51 }, { 51, 50 }, { 53, 47 }, { 51, 51 },
|
||||||
|
{ 51, 52 }, { 52, 51 }, { 52, 52 }, { 52, 52 }, { 52, 53 }, { 53, 52 }, { 53, 53 }, { 53, 53 },
|
||||||
|
{ 53, 54 }, { 54, 53 }, { 54, 54 }, { 54, 54 }, { 54, 55 }, { 55, 54 }, { 55, 55 }, { 55, 55 },
|
||||||
|
{ 55, 56 }, { 56, 55 }, { 56, 56 }, { 56, 56 }, { 56, 57 }, { 57, 56 }, { 57, 57 }, { 57, 57 },
|
||||||
|
{ 57, 58 }, { 58, 57 }, { 58, 58 }, { 58, 58 }, { 58, 59 }, { 59, 58 }, { 59, 59 }, { 59, 59 },
|
||||||
|
{ 59, 60 }, { 60, 59 }, { 60, 60 }, { 60, 60 }, { 60, 61 }, { 61, 60 }, { 61, 61 }, { 61, 61 },
|
||||||
|
{ 61, 62 }, { 62, 61 }, { 62, 62 }, { 62, 62 }, { 62, 63 }, { 63, 62 }, { 63, 63 }, { 63, 63 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stb__Mul8Bit(int a, int b)
|
||||||
|
{
|
||||||
|
int t = a*b + 128;
|
||||||
|
return (t + (t >> 8)) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stb__From16Bit(unsigned char *out, unsigned short v)
|
||||||
|
{
|
||||||
|
int rv = (v & 0xf800) >> 11;
|
||||||
|
int gv = (v & 0x07e0) >> 5;
|
||||||
|
int bv = (v & 0x001f) >> 0;
|
||||||
|
|
||||||
|
// expand to 8 bits via bit replication
|
||||||
|
out[0] = (rv * 33) >> 2;
|
||||||
|
out[1] = (gv * 65) >> 4;
|
||||||
|
out[2] = (bv * 33) >> 2;
|
||||||
|
out[3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short stb__As16Bit(int r, int g, int b)
|
||||||
|
{
|
||||||
|
return (unsigned short)((stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31));
|
||||||
|
}
|
||||||
|
|
||||||
|
// linear interpolation at 1/3 point between a and b, using desired rounding type
|
||||||
|
static int stb__Lerp13(int a, int b)
|
||||||
|
{
|
||||||
|
#ifdef STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
// with rounding bias
|
||||||
|
return a + stb__Mul8Bit(b-a, 0x55);
|
||||||
|
#else
|
||||||
|
// without rounding bias
|
||||||
|
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed.
|
||||||
|
return (2*a + b) / 3;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// lerp RGB color
|
||||||
|
static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2)
|
||||||
|
{
|
||||||
|
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
|
||||||
|
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
|
||||||
|
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1)
|
||||||
|
{
|
||||||
|
stb__From16Bit(color+ 0, c0);
|
||||||
|
stb__From16Bit(color+ 4, c1);
|
||||||
|
stb__Lerp13RGB(color+ 8, color+0, color+4);
|
||||||
|
stb__Lerp13RGB(color+12, color+4, color+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The color matching function
|
||||||
|
static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
int dirr = color[0*4+0] - color[1*4+0];
|
||||||
|
int dirg = color[0*4+1] - color[1*4+1];
|
||||||
|
int dirb = color[0*4+2] - color[1*4+2];
|
||||||
|
int dots[16];
|
||||||
|
int stops[4];
|
||||||
|
int i;
|
||||||
|
int c0Point, halfPoint, c3Point;
|
||||||
|
|
||||||
|
for(i=0;i<16;i++)
|
||||||
|
dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb;
|
||||||
|
|
||||||
|
for(i=0;i<4;i++)
|
||||||
|
stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb;
|
||||||
|
|
||||||
|
// think of the colors as arranged on a line; project point onto that line, then choose
|
||||||
|
// next color out of available ones. we compute the crossover points for "best color in top
|
||||||
|
// half"/"best in bottom half" and then the same inside that subinterval.
|
||||||
|
//
|
||||||
|
// relying on this 1d approximation isn't always optimal in terms of euclidean distance,
|
||||||
|
// but it's very close and a lot faster.
|
||||||
|
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
|
||||||
|
|
||||||
|
c0Point = (stops[1] + stops[3]);
|
||||||
|
halfPoint = (stops[3] + stops[2]);
|
||||||
|
c3Point = (stops[2] + stops[0]);
|
||||||
|
|
||||||
|
for (i=15;i>=0;i--) {
|
||||||
|
int dot = dots[i]*2;
|
||||||
|
mask <<= 2;
|
||||||
|
|
||||||
|
if(dot < halfPoint)
|
||||||
|
mask |= (dot < c0Point) ? 1 : 3;
|
||||||
|
else
|
||||||
|
mask |= (dot < c3Point) ? 2 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The color optimization function. (Clever code, part 1)
|
||||||
|
static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16)
|
||||||
|
{
|
||||||
|
int mind,maxd;
|
||||||
|
unsigned char *minp, *maxp;
|
||||||
|
double magn;
|
||||||
|
int v_r,v_g,v_b;
|
||||||
|
static const int nIterPower = 4;
|
||||||
|
float covf[6],vfr,vfg,vfb;
|
||||||
|
|
||||||
|
// determine color distribution
|
||||||
|
int cov[6];
|
||||||
|
int mu[3],min[3],max[3];
|
||||||
|
int ch,i,iter;
|
||||||
|
|
||||||
|
for(ch=0;ch<3;ch++)
|
||||||
|
{
|
||||||
|
const unsigned char *bp = ((const unsigned char *) block) + ch;
|
||||||
|
int muv,minv,maxv;
|
||||||
|
|
||||||
|
muv = minv = maxv = bp[0];
|
||||||
|
for(i=4;i<64;i+=4)
|
||||||
|
{
|
||||||
|
muv += bp[i];
|
||||||
|
if (bp[i] < minv) minv = bp[i];
|
||||||
|
else if (bp[i] > maxv) maxv = bp[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
mu[ch] = (muv + 8) >> 4;
|
||||||
|
min[ch] = minv;
|
||||||
|
max[ch] = maxv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine covariance matrix
|
||||||
|
for (i=0;i<6;i++)
|
||||||
|
cov[i] = 0;
|
||||||
|
|
||||||
|
for (i=0;i<16;i++)
|
||||||
|
{
|
||||||
|
int r = block[i*4+0] - mu[0];
|
||||||
|
int g = block[i*4+1] - mu[1];
|
||||||
|
int b = block[i*4+2] - mu[2];
|
||||||
|
|
||||||
|
cov[0] += r*r;
|
||||||
|
cov[1] += r*g;
|
||||||
|
cov[2] += r*b;
|
||||||
|
cov[3] += g*g;
|
||||||
|
cov[4] += g*b;
|
||||||
|
cov[5] += b*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert covariance matrix to float, find principal axis via power iter
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
covf[i] = cov[i] / 255.0f;
|
||||||
|
|
||||||
|
vfr = (float) (max[0] - min[0]);
|
||||||
|
vfg = (float) (max[1] - min[1]);
|
||||||
|
vfb = (float) (max[2] - min[2]);
|
||||||
|
|
||||||
|
for(iter=0;iter<nIterPower;iter++)
|
||||||
|
{
|
||||||
|
float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
|
||||||
|
float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
|
||||||
|
float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
|
||||||
|
|
||||||
|
vfr = r;
|
||||||
|
vfg = g;
|
||||||
|
vfb = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
magn = STBD_FABS(vfr);
|
||||||
|
if (STBD_FABS(vfg) > magn) magn = STBD_FABS(vfg);
|
||||||
|
if (STBD_FABS(vfb) > magn) magn = STBD_FABS(vfb);
|
||||||
|
|
||||||
|
if(magn < 4.0f) { // too small, default to luminance
|
||||||
|
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
|
||||||
|
v_g = 587;
|
||||||
|
v_b = 114;
|
||||||
|
} else {
|
||||||
|
magn = 512.0 / magn;
|
||||||
|
v_r = (int) (vfr * magn);
|
||||||
|
v_g = (int) (vfg * magn);
|
||||||
|
v_b = (int) (vfb * magn);
|
||||||
|
}
|
||||||
|
|
||||||
|
minp = maxp = block;
|
||||||
|
mind = maxd = block[0]*v_r + block[1]*v_g + block[2]*v_b;
|
||||||
|
// Pick colors at extreme points
|
||||||
|
for(i=1;i<16;i++)
|
||||||
|
{
|
||||||
|
int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b;
|
||||||
|
|
||||||
|
if (dot < mind) {
|
||||||
|
mind = dot;
|
||||||
|
minp = block+i*4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dot > maxd) {
|
||||||
|
maxd = dot;
|
||||||
|
maxp = block+i*4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]);
|
||||||
|
*pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float stb__midpoints5[32] = {
|
||||||
|
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f, 0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
|
||||||
|
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f, 0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
static const float stb__midpoints6[64] = {
|
||||||
|
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f, 0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
|
||||||
|
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f, 0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
|
||||||
|
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f, 0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
|
||||||
|
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f, 0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned short stb__Quantize5(float x)
|
||||||
|
{
|
||||||
|
unsigned short q;
|
||||||
|
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||||
|
q = (unsigned short)(x * 31);
|
||||||
|
q += (x > stb__midpoints5[q]);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short stb__Quantize6(float x)
|
||||||
|
{
|
||||||
|
unsigned short q;
|
||||||
|
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||||
|
q = (unsigned short)(x * 63);
|
||||||
|
q += (x > stb__midpoints6[q]);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The refinement function. (Clever code, part 2)
|
||||||
|
// Tries to optimize colors to suit block contents better.
|
||||||
|
// (By solving a least squares system via normal equations+Cramer's rule)
|
||||||
|
static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask)
|
||||||
|
{
|
||||||
|
static const int w1Tab[4] = { 3,0,2,1 };
|
||||||
|
static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
|
||||||
|
// ^some magic to save a lot of multiplies in the accumulating loop...
|
||||||
|
// (precomputed products of weights for least squares system, accumulated inside one 32-bit register)
|
||||||
|
|
||||||
|
float f;
|
||||||
|
unsigned short oldMin, oldMax, min16, max16;
|
||||||
|
int i, akku = 0, xx,xy,yy;
|
||||||
|
int At1_r,At1_g,At1_b;
|
||||||
|
int At2_r,At2_g,At2_b;
|
||||||
|
unsigned int cm = mask;
|
||||||
|
|
||||||
|
oldMin = *pmin16;
|
||||||
|
oldMax = *pmax16;
|
||||||
|
|
||||||
|
if((mask ^ (mask<<2)) < 4) // all pixels have the same index?
|
||||||
|
{
|
||||||
|
// yes, linear system would be singular; solve using optimal
|
||||||
|
// single-color match on average color
|
||||||
|
int r = 8, g = 8, b = 8;
|
||||||
|
for (i=0;i<16;++i) {
|
||||||
|
r += block[i*4+0];
|
||||||
|
g += block[i*4+1];
|
||||||
|
b += block[i*4+2];
|
||||||
|
}
|
||||||
|
|
||||||
|
r >>= 4; g >>= 4; b >>= 4;
|
||||||
|
|
||||||
|
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||||
|
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||||
|
} else {
|
||||||
|
At1_r = At1_g = At1_b = 0;
|
||||||
|
At2_r = At2_g = At2_b = 0;
|
||||||
|
for (i=0;i<16;++i,cm>>=2) {
|
||||||
|
int step = cm&3;
|
||||||
|
int w1 = w1Tab[step];
|
||||||
|
int r = block[i*4+0];
|
||||||
|
int g = block[i*4+1];
|
||||||
|
int b = block[i*4+2];
|
||||||
|
|
||||||
|
akku += prods[step];
|
||||||
|
At1_r += w1*r;
|
||||||
|
At1_g += w1*g;
|
||||||
|
At1_b += w1*b;
|
||||||
|
At2_r += r;
|
||||||
|
At2_g += g;
|
||||||
|
At2_b += b;
|
||||||
|
}
|
||||||
|
|
||||||
|
At2_r = 3*At2_r - At1_r;
|
||||||
|
At2_g = 3*At2_g - At1_g;
|
||||||
|
At2_b = 3*At2_b - At1_b;
|
||||||
|
|
||||||
|
// extract solutions and decide solvability
|
||||||
|
xx = akku >> 16;
|
||||||
|
yy = (akku >> 8) & 0xff;
|
||||||
|
xy = (akku >> 0) & 0xff;
|
||||||
|
|
||||||
|
f = 3.0f / 255.0f / (xx*yy - xy*xy);
|
||||||
|
|
||||||
|
max16 = stb__Quantize5((At1_r*yy - At2_r * xy) * f) << 11;
|
||||||
|
max16 |= stb__Quantize6((At1_g*yy - At2_g * xy) * f) << 5;
|
||||||
|
max16 |= stb__Quantize5((At1_b*yy - At2_b * xy) * f) << 0;
|
||||||
|
|
||||||
|
min16 = stb__Quantize5((At2_r*xx - At1_r * xy) * f) << 11;
|
||||||
|
min16 |= stb__Quantize6((At2_g*xx - At1_g * xy) * f) << 5;
|
||||||
|
min16 |= stb__Quantize5((At2_b*xx - At1_b * xy) * f) << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pmin16 = min16;
|
||||||
|
*pmax16 = max16;
|
||||||
|
return oldMin != min16 || oldMax != max16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color block compression
|
||||||
|
static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode)
|
||||||
|
{
|
||||||
|
unsigned int mask;
|
||||||
|
int i;
|
||||||
|
int refinecount;
|
||||||
|
unsigned short max16, min16;
|
||||||
|
unsigned char color[4*4];
|
||||||
|
|
||||||
|
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
|
||||||
|
|
||||||
|
// check if block is constant
|
||||||
|
for (i=1;i<16;i++)
|
||||||
|
if (((unsigned int *) block)[i] != ((unsigned int *) block)[0])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(i == 16) { // constant color
|
||||||
|
int r = block[0], g = block[1], b = block[2];
|
||||||
|
mask = 0xaaaaaaaa;
|
||||||
|
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||||
|
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||||
|
} else {
|
||||||
|
// first step: PCA+map along principal axis
|
||||||
|
stb__OptimizeColorsBlock(block,&max16,&min16);
|
||||||
|
if (max16 != min16) {
|
||||||
|
stb__EvalColors(color,max16,min16);
|
||||||
|
mask = stb__MatchColorsBlock(block,color);
|
||||||
|
} else
|
||||||
|
mask = 0;
|
||||||
|
|
||||||
|
// third step: refine (multiple times if requested)
|
||||||
|
for (i=0;i<refinecount;i++) {
|
||||||
|
unsigned int lastmask = mask;
|
||||||
|
|
||||||
|
if (stb__RefineBlock(block,&max16,&min16,mask)) {
|
||||||
|
if (max16 != min16) {
|
||||||
|
stb__EvalColors(color,max16,min16);
|
||||||
|
mask = stb__MatchColorsBlock(block,color);
|
||||||
|
} else {
|
||||||
|
mask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mask == lastmask)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the color block
|
||||||
|
if(max16 < min16)
|
||||||
|
{
|
||||||
|
unsigned short t = min16;
|
||||||
|
min16 = max16;
|
||||||
|
max16 = t;
|
||||||
|
mask ^= 0x55555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0] = (unsigned char) (max16);
|
||||||
|
dest[1] = (unsigned char) (max16 >> 8);
|
||||||
|
dest[2] = (unsigned char) (min16);
|
||||||
|
dest[3] = (unsigned char) (min16 >> 8);
|
||||||
|
dest[4] = (unsigned char) (mask);
|
||||||
|
dest[5] = (unsigned char) (mask >> 8);
|
||||||
|
dest[6] = (unsigned char) (mask >> 16);
|
||||||
|
dest[7] = (unsigned char) (mask >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpha block compression (this is easy for a change)
|
||||||
|
static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride)
|
||||||
|
{
|
||||||
|
int i,dist,bias,dist4,dist2,bits,mask;
|
||||||
|
|
||||||
|
// find min/max color
|
||||||
|
int mn,mx;
|
||||||
|
mn = mx = src[0];
|
||||||
|
|
||||||
|
for (i=1;i<16;i++)
|
||||||
|
{
|
||||||
|
if (src[i*stride] < mn) mn = src[i*stride];
|
||||||
|
else if (src[i*stride] > mx) mx = src[i*stride];
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode them
|
||||||
|
dest[0] = (unsigned char)mx;
|
||||||
|
dest[1] = (unsigned char)mn;
|
||||||
|
dest += 2;
|
||||||
|
|
||||||
|
// determine bias and emit color indices
|
||||||
|
// given the choice of mx/mn, these indices are optimal:
|
||||||
|
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
|
||||||
|
dist = mx-mn;
|
||||||
|
dist4 = dist*4;
|
||||||
|
dist2 = dist*2;
|
||||||
|
bias = (dist < 8) ? (dist - 1) : (dist/2 + 2);
|
||||||
|
bias -= mn * 7;
|
||||||
|
bits = 0,mask=0;
|
||||||
|
|
||||||
|
for (i=0;i<16;i++) {
|
||||||
|
int a = src[i*stride]*7 + bias;
|
||||||
|
int ind,t;
|
||||||
|
|
||||||
|
// select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max).
|
||||||
|
t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t;
|
||||||
|
t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t;
|
||||||
|
ind += (a >= dist);
|
||||||
|
|
||||||
|
// turn linear scale into DXT index (0/1 are extremal pts)
|
||||||
|
ind = -ind & 7;
|
||||||
|
ind ^= (2 > ind);
|
||||||
|
|
||||||
|
// write index
|
||||||
|
mask |= ind << bits;
|
||||||
|
if((bits += 3) >= 8) {
|
||||||
|
*dest++ = (unsigned char)mask;
|
||||||
|
mask >>= 8;
|
||||||
|
bits -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode)
|
||||||
|
{
|
||||||
|
unsigned char data[16][4];
|
||||||
|
if (alpha) {
|
||||||
|
int i;
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4);
|
||||||
|
dest += 8;
|
||||||
|
// make a new copy of the data in which alpha is opaque,
|
||||||
|
// because code uses a fast test for color constancy
|
||||||
|
memcpy(data, src, 4*16);
|
||||||
|
for (i=0; i < 16; ++i)
|
||||||
|
data[i][3] = 255;
|
||||||
|
src = &data[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
stb__CompressColorBlock(dest,(unsigned char*) src,mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src)
|
||||||
|
{
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src)
|
||||||
|
{
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src,2);
|
||||||
|
stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2);
|
||||||
|
}
|
||||||
|
#endif // STB_DXT_IMPLEMENTATION
|
||||||
|
|
||||||
|
// Compile with STB_DXT_IMPLEMENTATION and STB_DXT_GENERATE_TABLES
|
||||||
|
// defined to generate the tables above.
|
||||||
|
#ifdef STB_DXT_GENERATE_TABLES
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
const char *omatch_names[] = { "stb__OMatch5", "stb__OMatch6" };
|
||||||
|
int dequant_mults[2] = { 33*4, 65 }; // .4 fixed-point dequant multipliers
|
||||||
|
|
||||||
|
// optimal endpoint tables
|
||||||
|
for (i = 0; i < 2; ++i) {
|
||||||
|
int dequant = dequant_mults[i];
|
||||||
|
int size = i ? 64 : 32;
|
||||||
|
printf("static const unsigned char %s[256][2] = {\n", omatch_names[i]);
|
||||||
|
for (int j = 0; j < 256; ++j) {
|
||||||
|
int mn, mx;
|
||||||
|
int best_mn = 0, best_mx = 0;
|
||||||
|
int best_err = 256 * 100;
|
||||||
|
for (mn=0;mn<size;mn++) {
|
||||||
|
for (mx=0;mx<size;mx++) {
|
||||||
|
int mine = (mn * dequant) >> 4;
|
||||||
|
int maxe = (mx * dequant) >> 4;
|
||||||
|
int err = abs(stb__Lerp13(maxe, mine) - j) * 100;
|
||||||
|
|
||||||
|
// DX10 spec says that interpolation must be within 3% of "correct" result,
|
||||||
|
// add this as error term. Normally we'd expect a random distribution of
|
||||||
|
// +-1.5% error, but nowhere in the spec does it say that the error has to be
|
||||||
|
// unbiased - better safe than sorry.
|
||||||
|
err += abs(maxe - mine) * 3;
|
||||||
|
|
||||||
|
if(err < best_err) {
|
||||||
|
best_mn = mn;
|
||||||
|
best_mx = mx;
|
||||||
|
best_err = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((j % 8) == 0) printf(" "); // 2 spaces, third is done below
|
||||||
|
printf(" { %2d, %2d },", best_mx, best_mn);
|
||||||
|
if ((j % 8) == 7) printf("\n");
|
||||||
|
}
|
||||||
|
printf("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
305
external/stb/stb/stb_easy_font.h
vendored
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
// stb_easy_font.h - v1.1 - 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)
|
||||||
|
// int stb_easy_font_height(char *text)
|
||||||
|
//
|
||||||
|
// Takes a string and returns the horizontal size and the
|
||||||
|
// vertical size (which can vary if 'text' has newlines).
|
||||||
|
//
|
||||||
|
// 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 vertex-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.
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
//
|
||||||
|
// VERSION HISTORY
|
||||||
|
//
|
||||||
|
// (2020-02-02) 1.1 make everything static so can compile it in more than one src file
|
||||||
|
// (2017-01-15) 1.0 space character takes same space as numbers; fix bad spacing of 'f'
|
||||||
|
// (2016-01-22) 0.7 width() supports multiline text; add height()
|
||||||
|
// (2015-09-13) 0.6 #include <math.h>; updated license
|
||||||
|
// (2015-02-01) 0.5 First release
|
||||||
|
//
|
||||||
|
// CONTRIBUTORS
|
||||||
|
//
|
||||||
|
// github:vassvik -- bug report
|
||||||
|
// github:podsvirov -- fix multiple definition errors
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
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>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
static struct stb_easy_font_info_struct {
|
||||||
|
unsigned char advance;
|
||||||
|
unsigned char h_seg;
|
||||||
|
unsigned char v_seg;
|
||||||
|
} stb_easy_font_charinfo[96] = {
|
||||||
|
{ 6, 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 }, { 6,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 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
float max_len = 0;
|
||||||
|
while (*text) {
|
||||||
|
if (*text == '\n') {
|
||||||
|
if (len > max_len) max_len = len;
|
||||||
|
len = 0;
|
||||||
|
} else {
|
||||||
|
len += stb_easy_font_charinfo[*text-32].advance & 15;
|
||||||
|
len += stb_easy_font_spacing_val;
|
||||||
|
}
|
||||||
|
++text;
|
||||||
|
}
|
||||||
|
if (len > max_len) max_len = len;
|
||||||
|
return (int) ceil(max_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb_easy_font_height(char *text)
|
||||||
|
{
|
||||||
|
float y = 0;
|
||||||
|
int nonempty_line=0;
|
||||||
|
while (*text) {
|
||||||
|
if (*text == '\n') {
|
||||||
|
y += 12;
|
||||||
|
nonempty_line = 0;
|
||||||
|
} else {
|
||||||
|
nonempty_line = 1;
|
||||||
|
}
|
||||||
|
++text;
|
||||||
|
}
|
||||||
|
return (int) ceil(y + (nonempty_line ? 12 : 0));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1221
external/stb/stb/stb_herringbone_wang_tile.h
vendored
Normal file
680
external/stb/stb/stb_hexwave.h
vendored
Normal file
@ -0,0 +1,680 @@
|
|||||||
|
// stb_hexwave - v0.5 - public domain, initial release 2021-04-01
|
||||||
|
//
|
||||||
|
// A flexible anti-aliased (bandlimited) digital audio oscillator.
|
||||||
|
//
|
||||||
|
// This library generates waveforms of a variety of shapes made of
|
||||||
|
// line segments. It does not do envelopes, LFO effects, etc.; it
|
||||||
|
// merely tries to solve the problem of generating an artifact-free
|
||||||
|
// morphable digital waveform with a variety of spectra, and leaves
|
||||||
|
// it to the user to rescale the waveform and mix multiple voices, etc.
|
||||||
|
//
|
||||||
|
// Compiling:
|
||||||
|
//
|
||||||
|
// In one C/C++ file that #includes this file, do
|
||||||
|
//
|
||||||
|
// #define STB_HEXWAVE_IMPLEMENTATION
|
||||||
|
// #include "stb_hexwave.h"
|
||||||
|
//
|
||||||
|
// Optionally, #define STB_HEXWAVE_STATIC before including
|
||||||
|
// the header to cause the definitions to be private to the
|
||||||
|
// implementation file (i.e. to be "static" instead of "extern").
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
//
|
||||||
|
// Optionally performs memory allocation during initialization,
|
||||||
|
// never allocates otherwise.
|
||||||
|
//
|
||||||
|
// License:
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// Initialization:
|
||||||
|
//
|
||||||
|
// hexwave_init(32,16,NULL); // read "header section" for alternatives
|
||||||
|
//
|
||||||
|
// Create oscillator:
|
||||||
|
//
|
||||||
|
// HexWave *osc = malloc(sizeof(*osc)); // or "new HexWave", or declare globally or on stack
|
||||||
|
// hexwave_create(osc, reflect_flag, peak_time, half_height, zero_wait);
|
||||||
|
// see "Waveform shapes" below for the meaning of these parameters
|
||||||
|
//
|
||||||
|
// Generate audio:
|
||||||
|
//
|
||||||
|
// hexwave_generate_samples(output, number_of_samples, osc, oscillator_freq)
|
||||||
|
// where:
|
||||||
|
// output is a buffer where the library will store floating point audio samples
|
||||||
|
// number_of_samples is the number of audio samples to generate
|
||||||
|
// osc is a pointer to a Hexwave
|
||||||
|
// oscillator_freq is the frequency of the oscillator divided by the sample rate
|
||||||
|
//
|
||||||
|
// The output samples will continue from where the samples generated by the
|
||||||
|
// previous hexwave_generate_samples() on this oscillator ended.
|
||||||
|
//
|
||||||
|
// Change oscillator waveform:
|
||||||
|
//
|
||||||
|
// hexwave_change(osc, reflect_flag, peak_time, half_height, zero_wait);
|
||||||
|
// can call in between calls to hexwave_generate_samples
|
||||||
|
//
|
||||||
|
// Waveform shapes:
|
||||||
|
//
|
||||||
|
// All waveforms generated by hexwave are constructed from six line segments
|
||||||
|
// characterized by 3 parameters.
|
||||||
|
//
|
||||||
|
// See demonstration: https://www.youtube.com/watch?v=hsUCrAsDN-M
|
||||||
|
//
|
||||||
|
// reflect=0 reflect=1
|
||||||
|
//
|
||||||
|
// 0-----P---1 0-----P---1 peak_time = P
|
||||||
|
// . 1 . 1
|
||||||
|
// /\_ : /\_ :
|
||||||
|
// / \_ : / \_ :
|
||||||
|
// / \.H / \.H half_height = H
|
||||||
|
// / | : / | :
|
||||||
|
// _____/ |_:___ _____/ | : _____
|
||||||
|
// . : \ | . | : /
|
||||||
|
// . : \ | . | : /
|
||||||
|
// . : \ _/ . \_: /
|
||||||
|
// . : \ _/ . :_ /
|
||||||
|
// . -1 \/ . -1 \/
|
||||||
|
// 0 - Z - - - - 1 0 - Z - - - - 1 zero_wait = Z
|
||||||
|
//
|
||||||
|
// Classic waveforms:
|
||||||
|
// peak half zero
|
||||||
|
// reflect time height wait
|
||||||
|
// Sawtooth 1 0 0 0
|
||||||
|
// Square 1 0 1 0
|
||||||
|
// Triangle 1 0.5 0 0
|
||||||
|
//
|
||||||
|
// Some waveforms can be produced in multiple ways, which is useful when morphing
|
||||||
|
// into other waveforms, and there are a few more notable shapes:
|
||||||
|
//
|
||||||
|
// peak half zero
|
||||||
|
// reflect time height wait
|
||||||
|
// Sawtooth 1 1 any 0
|
||||||
|
// Sawtooth (8va) 1 0 -1 0
|
||||||
|
// Triangle 1 0.5 0 0
|
||||||
|
// Square 1 0 1 0
|
||||||
|
// Square 0 0 1 0
|
||||||
|
// Triangle 0 0.5 0 0
|
||||||
|
// Triangle 0 0 -1 0
|
||||||
|
// AlternatingSaw 0 0 0 0
|
||||||
|
// AlternatingSaw 0 1 any 0
|
||||||
|
// Stairs 0 0 1 0.5
|
||||||
|
//
|
||||||
|
// The "Sawtooth (8va)" waveform is identical to a sawtooth wave with 2x the
|
||||||
|
// frequency, but when morphed with other values, it becomes an overtone of
|
||||||
|
// the base frequency.
|
||||||
|
//
|
||||||
|
// Morphing waveforms:
|
||||||
|
//
|
||||||
|
// Sweeping peak_time morphs the waveform while producing various spectra.
|
||||||
|
// Sweeping half_height effectively crossfades between two waveforms; useful, but less exciting.
|
||||||
|
// Sweeping zero_wait produces a similar effect no matter the reset of the waveform,
|
||||||
|
// a sort of high-pass/PWM effect where the wave becomes silent at zero_wait=1.
|
||||||
|
//
|
||||||
|
// You can trivially morph between any two waveforms from the above table
|
||||||
|
// which only differ in one column.
|
||||||
|
//
|
||||||
|
// Crossfade between classic waveforms:
|
||||||
|
// peak half zero
|
||||||
|
// Start End reflect time height wait
|
||||||
|
// ----- --- ------- ---- ------ ----
|
||||||
|
// Triangle Square 0 0 -1..1 0
|
||||||
|
// Saw Square 1 0 0..1 0
|
||||||
|
// Triangle Saw 1 0.5 0..2 0
|
||||||
|
//
|
||||||
|
// The last morph uses uses half-height values larger than 1, which means it will
|
||||||
|
// be louder and the output should be scaled down by half to compensate, or better
|
||||||
|
// by dynamically tracking the morph: volume_scale = 1 - half_height/4
|
||||||
|
//
|
||||||
|
// Non-crossfade morph between classic waveforms, most require changing
|
||||||
|
// two parameters at the same time:
|
||||||
|
// peak half zero
|
||||||
|
// Start End reflect time height wait
|
||||||
|
// ----- --- ------- ---- ------ ----
|
||||||
|
// Square Triangle any 0..0.5 1..0 0
|
||||||
|
// Square Saw 1 0..1 1..any 0
|
||||||
|
// Triangle Saw 1 0.5..1 0..-1 0
|
||||||
|
//
|
||||||
|
// Other noteworthy morphs between simple shapes:
|
||||||
|
// peak half zero
|
||||||
|
// Start Halfway End reflect time height wait
|
||||||
|
// ----- --------- --- ------- ---- ------ ----
|
||||||
|
// Saw (8va,neg) Saw (pos) 1 0..1 -1 0
|
||||||
|
// Saw (neg) Saw (pos) 1 0..1 0 0
|
||||||
|
// Triangle AlternatingSaw 0 0..1 -1 0
|
||||||
|
// AlternatingSaw Triangle AlternatingSaw 0 0..1 0 0
|
||||||
|
// Square AlternatingSaw 0 0..1 1 0
|
||||||
|
// Triangle Triangle AlternatingSaw 0 0..1 -1..1 0
|
||||||
|
// Square AlternatingSaw 0 0..1 1..0 0
|
||||||
|
// Saw (8va) Triangle Saw 1 0..1 -1..1 0
|
||||||
|
// Saw (neg) Saw (pos) 1 0..1 0..1 0
|
||||||
|
// AlternatingSaw AlternatingSaw 0 0..1 0..any 0
|
||||||
|
//
|
||||||
|
// The last entry is noteworthy because the morph from the halfway point to either
|
||||||
|
// endpoint sounds very different. For example, an LFO sweeping back and forth over
|
||||||
|
// the whole range will morph between the middle timbre and the AlternatingSaw
|
||||||
|
// timbre in two different ways, alternating.
|
||||||
|
//
|
||||||
|
// Entries with "any" for half_height are whole families of morphs, as you can pick
|
||||||
|
// any value you want as the endpoint for half_height.
|
||||||
|
//
|
||||||
|
// You can always morph between any two waveforms with the same value of 'reflect'
|
||||||
|
// by just sweeping the parameters simultaneously. There will never be artifacts
|
||||||
|
// and the result will always be useful, if not necessarily what you want.
|
||||||
|
//
|
||||||
|
// You can vary the sound of two-parameter morphs by ramping them differently,
|
||||||
|
// e.g. if the morph goes from t=0..1, then square-to-triangle looks like:
|
||||||
|
// peak_time = lerp(t, 0, 0.5)
|
||||||
|
// half_height = lerp(t, 1, 0 )
|
||||||
|
// but you can also do things like:
|
||||||
|
// peak_time = lerp(smoothstep(t), 0, 0.5)
|
||||||
|
// half_height = cos(PI/2 * t)
|
||||||
|
//
|
||||||
|
// How it works:
|
||||||
|
//
|
||||||
|
// hexwave use BLEP to bandlimit discontinuities and BLAMP
|
||||||
|
// to bandlimit C1 discontinuities. This is not polyBLEP
|
||||||
|
// (polynomial BLEP), it is table-driven BLEP. It is
|
||||||
|
// also not minBLEP (minimum-phase BLEP), as that complicates
|
||||||
|
// things for little benefit once BLAMP is involved.
|
||||||
|
//
|
||||||
|
// The previous oscillator frequency is remembered, and when
|
||||||
|
// the frequency changes, a BLAMP is generated to remove the
|
||||||
|
// C1 discontinuity, which reduces artifacts for sweeps/LFO.
|
||||||
|
//
|
||||||
|
// Changes to an oscillator timbre using hexwave_change() actually
|
||||||
|
// wait until the oscillator finishes its current cycle. All
|
||||||
|
// waveforms with non-zero "zero_wait" settings pass through 0
|
||||||
|
// and have 0-slope at the start of a cycle, which means changing
|
||||||
|
// the settings is artifact free at that time. (If zero_wait is 0,
|
||||||
|
// the code still treats it as passing through 0 with 0-slope; it'll
|
||||||
|
// apply the necessary fixups to make it artifact free as if it does
|
||||||
|
// transition to 0 with 0-slope vs. the waveform at the end of
|
||||||
|
// the cycle, then adds the fixups for a non-0 and non-0 slope
|
||||||
|
// at the start of the cycle, which cancels out if zero_wait is 0,
|
||||||
|
// and still does the right thing if zero_wait is 0 when the
|
||||||
|
// settings are updated.)
|
||||||
|
//
|
||||||
|
// BLEP/BLAMP normally requires overlapping buffers, but this
|
||||||
|
// is hidden from the user by generating the waveform to a
|
||||||
|
// temporary buffer and saving the overlap regions internally
|
||||||
|
// between calls. (It is slightly more complicated; see code.)
|
||||||
|
//
|
||||||
|
// By design all shapes have 0 DC offset; this is one reason
|
||||||
|
// hexwave uses zero_wait instead of standard PWM.
|
||||||
|
//
|
||||||
|
// The internals of hexwave could support any arbitrary shape
|
||||||
|
// made of line segments, but I chose not to expose this
|
||||||
|
// generality in favor of a simple, easy-to-use API.
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_HEXWAVE_H
|
||||||
|
#define STB_INCLUDE_STB_HEXWAVE_H
|
||||||
|
|
||||||
|
#ifndef STB_HEXWAVE_MAX_BLEP_LENGTH
|
||||||
|
#define STB_HEXWAVE_MAX_BLEP_LENGTH 64 // good enough for anybody
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_HEXWAVE_STATIC
|
||||||
|
#define STB_HEXWAVE_DEF static
|
||||||
|
#else
|
||||||
|
#define STB_HEXWAVE_DEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct HexWave HexWave;
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer);
|
||||||
|
// width: size of BLEP, from 4..64, larger is slower & more memory but less aliasing
|
||||||
|
// oversample: 2+, number of subsample positions, larger uses more memory but less noise
|
||||||
|
// user_buffer: optional, if provided the library will perform no allocations.
|
||||||
|
// 16*width*(oversample+1) bytes, must stay allocated as long as library is used
|
||||||
|
// technically it only needs: 8*( width * (oversample + 1))
|
||||||
|
// + 8*((width * oversample) + 1) bytes
|
||||||
|
//
|
||||||
|
// width can be larger than 64 if you define STB_HEXWAVE_MAX_BLEP_LENGTH to a larger value
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer);
|
||||||
|
// user_buffer: pass in same parameter as passed to hexwave_init
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
|
||||||
|
// see docs above for description
|
||||||
|
//
|
||||||
|
// reflect is tested as 0 or non-zero
|
||||||
|
// peak_time is clamped to 0..1
|
||||||
|
// half_height is not clamped
|
||||||
|
// zero_wait is clamped to 0..1
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait);
|
||||||
|
// see docs
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq);
|
||||||
|
// output: buffer where the library will store generated floating point audio samples
|
||||||
|
// number_of_samples: the number of audio samples to generate
|
||||||
|
// osc: pointer to a Hexwave initialized with 'hexwave_create'
|
||||||
|
// oscillator_freq: frequency of the oscillator divided by the sample rate
|
||||||
|
|
||||||
|
// private:
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int reflect;
|
||||||
|
float peak_time;
|
||||||
|
float zero_wait;
|
||||||
|
float half_height;
|
||||||
|
} HexWaveParameters;
|
||||||
|
|
||||||
|
struct HexWave
|
||||||
|
{
|
||||||
|
float t, prev_dt;
|
||||||
|
HexWaveParameters current, pending;
|
||||||
|
int have_pending;
|
||||||
|
float buffer[STB_HEXWAVE_MAX_BLEP_LENGTH];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_HEXWAVE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||||
|
#include <stdlib.h> // malloc,free
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h> // memset,memcpy,memmove
|
||||||
|
#include <math.h> // sin,cos,fabs
|
||||||
|
|
||||||
|
#define hexwave_clamp(v,a,b) ((v) < (a) ? (a) : (v) > (b) ? (b) : (v))
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_change(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
|
||||||
|
{
|
||||||
|
hex->pending.reflect = reflect;
|
||||||
|
hex->pending.peak_time = hexwave_clamp(peak_time,0,1);
|
||||||
|
hex->pending.half_height = half_height;
|
||||||
|
hex->pending.zero_wait = hexwave_clamp(zero_wait,0,1);
|
||||||
|
// put a barrier here to allow changing from a different thread than the generator
|
||||||
|
hex->have_pending = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_create(HexWave *hex, int reflect, float peak_time, float half_height, float zero_wait)
|
||||||
|
{
|
||||||
|
memset(hex, 0, sizeof(*hex));
|
||||||
|
hexwave_change(hex, reflect, peak_time, half_height, zero_wait);
|
||||||
|
hex->current = hex->pending;
|
||||||
|
hex->have_pending = 0;
|
||||||
|
hex->t = 0;
|
||||||
|
hex->prev_dt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
int width; // width of fixup in samples
|
||||||
|
int oversample; // number of oversampled versions (there's actually one more to allow lerpign)
|
||||||
|
float *blep;
|
||||||
|
float *blamp;
|
||||||
|
} hexblep;
|
||||||
|
|
||||||
|
static void hex_add_oversampled_bleplike(float *output, float time_since_transition, float scale, float *data)
|
||||||
|
{
|
||||||
|
float *d1,*d2;
|
||||||
|
float lerpweight;
|
||||||
|
int i, bw = hexblep.width;
|
||||||
|
|
||||||
|
int slot = (int) (time_since_transition * hexblep.oversample);
|
||||||
|
if (slot >= hexblep.oversample)
|
||||||
|
slot = hexblep.oversample-1; // clamp in case the floats overshoot
|
||||||
|
|
||||||
|
d1 = &data[ slot *bw];
|
||||||
|
d2 = &data[(slot+1)*bw];
|
||||||
|
|
||||||
|
lerpweight = time_since_transition * hexblep.oversample - slot;
|
||||||
|
for (i=0; i < bw; ++i)
|
||||||
|
output[i] += scale * (d1[i] + (d2[i]-d1[i])*lerpweight);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hex_blep (float *output, float time_since_transition, float scale)
|
||||||
|
{
|
||||||
|
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hex_blamp(float *output, float time_since_transition, float scale)
|
||||||
|
{
|
||||||
|
hex_add_oversampled_bleplike(output, time_since_transition, scale, hexblep.blamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float t,v,s; // time, value, slope
|
||||||
|
} hexvert;
|
||||||
|
|
||||||
|
// each half of the waveform needs 4 vertices to represent 3 line
|
||||||
|
// segments, plus 1 more for wraparound
|
||||||
|
static void hexwave_generate_linesegs(hexvert vert[9], HexWave *hex, float dt)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
float min_len = dt / 256.0f;
|
||||||
|
|
||||||
|
vert[0].t = 0;
|
||||||
|
vert[0].v = 0;
|
||||||
|
vert[1].t = hex->current.zero_wait*0.5f;
|
||||||
|
vert[1].v = 0;
|
||||||
|
vert[2].t = 0.5f*hex->current.peak_time + vert[1].t*(1-hex->current.peak_time);
|
||||||
|
vert[2].v = 1;
|
||||||
|
vert[3].t = 0.5f;
|
||||||
|
vert[3].v = hex->current.half_height;
|
||||||
|
|
||||||
|
if (hex->current.reflect) {
|
||||||
|
for (j=4; j <= 7; ++j) {
|
||||||
|
vert[j].t = 1 - vert[7-j].t;
|
||||||
|
vert[j].v = - vert[7-j].v;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (j=4; j <= 7; ++j) {
|
||||||
|
vert[j].t = 0.5f + vert[j-4].t;
|
||||||
|
vert[j].v = - vert[j-4].v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vert[8].t = 1;
|
||||||
|
vert[8].v = 0;
|
||||||
|
|
||||||
|
for (j=0; j < 8; ++j) {
|
||||||
|
if (vert[j+1].t <= vert[j].t + min_len) {
|
||||||
|
// if change takes place over less than a fraction of a sample treat as discontinuity
|
||||||
|
//
|
||||||
|
// otherwise the slope computation can blow up to arbitrarily large and we
|
||||||
|
// try to generate a huge BLAMP and the result is wrong.
|
||||||
|
//
|
||||||
|
// why does this happen if the math is right? i believe if done perfectly,
|
||||||
|
// the two BLAMPs on either side of the slope would cancel out, but our
|
||||||
|
// BLAMPs have only limited sub-sample precision and limited integration
|
||||||
|
// accuracy. or maybe it's just the math blowing up w/ floating point precision
|
||||||
|
// limits as we try to make x * (1/x) cancel out
|
||||||
|
//
|
||||||
|
// min_len verified artifact-free even near nyquist with only oversample=4
|
||||||
|
vert[j+1].t = vert[j].t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vert[8].t != 1.0f) {
|
||||||
|
// if the above fixup moved the endpoint away from 1.0, move it back,
|
||||||
|
// along with any other vertices that got moved to the same time
|
||||||
|
float t = vert[8].t;
|
||||||
|
for (j=5; j <= 8; ++j)
|
||||||
|
if (vert[j].t == t)
|
||||||
|
vert[j].t = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute the exact slopes from the final fixed-up positions
|
||||||
|
for (j=0; j < 8; ++j)
|
||||||
|
if (vert[j+1].t == vert[j].t)
|
||||||
|
vert[j].s = 0;
|
||||||
|
else
|
||||||
|
vert[j].s = (vert[j+1].v - vert[j].v) / (vert[j+1].t - vert[j].t);
|
||||||
|
|
||||||
|
// wraparound at end
|
||||||
|
vert[8].t = 1;
|
||||||
|
vert[8].v = vert[0].v;
|
||||||
|
vert[8].s = vert[0].s;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_generate_samples(float *output, int num_samples, HexWave *hex, float freq)
|
||||||
|
{
|
||||||
|
hexvert vert[9];
|
||||||
|
int pass,i,j;
|
||||||
|
float t = hex->t;
|
||||||
|
float temp_output[2*STB_HEXWAVE_MAX_BLEP_LENGTH];
|
||||||
|
int buffered_length = sizeof(float)*hexblep.width;
|
||||||
|
float dt = (float) fabs(freq);
|
||||||
|
float recip_dt = (dt == 0.0f) ? 0.0f : 1.0f / dt;
|
||||||
|
|
||||||
|
int halfw = hexblep.width/2;
|
||||||
|
// all sample times are biased by halfw to leave room for BLEP/BLAMP to go back in time
|
||||||
|
|
||||||
|
if (num_samples <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// convert parameters to times and slopes
|
||||||
|
hexwave_generate_linesegs(vert, hex, dt);
|
||||||
|
|
||||||
|
if (hex->prev_dt != dt) {
|
||||||
|
// if frequency changes, add a fixup at the derivative discontinuity starting at now
|
||||||
|
float slope;
|
||||||
|
for (j=1; j < 6; ++j)
|
||||||
|
if (t < vert[j].t)
|
||||||
|
break;
|
||||||
|
slope = vert[j].s;
|
||||||
|
if (slope != 0)
|
||||||
|
hex_blamp(output, 0, (dt - hex->prev_dt)*slope);
|
||||||
|
hex->prev_dt = dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the buffered data from last call and clear the rest of the output array
|
||||||
|
memset(output, 0, sizeof(float)*num_samples);
|
||||||
|
memset(temp_output, 0, 2*hexblep.width*sizeof(float));
|
||||||
|
|
||||||
|
if (num_samples >= hexblep.width) {
|
||||||
|
memcpy(output, hex->buffer, buffered_length);
|
||||||
|
} else {
|
||||||
|
// if the output is shorter than hexblep.width, we do all synthesis to temp_output
|
||||||
|
memcpy(temp_output, hex->buffer, buffered_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pass=0; pass < 2; ++pass) {
|
||||||
|
int i0,i1;
|
||||||
|
float *out;
|
||||||
|
|
||||||
|
// we want to simulate having one buffer that is num_output + hexblep.width
|
||||||
|
// samples long, without putting that requirement on the user, and without
|
||||||
|
// allocating a temp buffer that's as long as the whole thing. so we use two
|
||||||
|
// overlapping buffers, one the user's buffer and one a fixed-length temp
|
||||||
|
// buffer.
|
||||||
|
|
||||||
|
if (pass == 0) {
|
||||||
|
if (num_samples < hexblep.width)
|
||||||
|
continue;
|
||||||
|
// run as far as we can without overwriting the end of the user's buffer
|
||||||
|
out = output;
|
||||||
|
i0 = 0;
|
||||||
|
i1 = num_samples - hexblep.width;
|
||||||
|
} else {
|
||||||
|
// generate the rest into a temp buffer
|
||||||
|
out = temp_output;
|
||||||
|
i0 = 0;
|
||||||
|
if (num_samples >= hexblep.width)
|
||||||
|
i1 = hexblep.width;
|
||||||
|
else
|
||||||
|
i1 = num_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine current segment
|
||||||
|
for (j=0; j < 8; ++j)
|
||||||
|
if (t < vert[j+1].t)
|
||||||
|
break;
|
||||||
|
|
||||||
|
i = i0;
|
||||||
|
for(;;) {
|
||||||
|
while (t < vert[j+1].t) {
|
||||||
|
if (i == i1)
|
||||||
|
goto done;
|
||||||
|
out[i+halfw] += vert[j].v + vert[j].s*(t - vert[j].t);
|
||||||
|
t += dt;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
// transition from lineseg starting at j to lineseg starting at j+1
|
||||||
|
|
||||||
|
if (vert[j].t == vert[j+1].t)
|
||||||
|
hex_blep(out+i, recip_dt*(t-vert[j+1].t), (vert[j+1].v - vert[j].v));
|
||||||
|
hex_blamp(out+i, recip_dt*(t-vert[j+1].t), dt*(vert[j+1].s - vert[j].s));
|
||||||
|
++j;
|
||||||
|
|
||||||
|
if (j == 8) {
|
||||||
|
// change to different waveform if there's a change pending
|
||||||
|
j = 0;
|
||||||
|
t -= 1.0; // t was >= 1.f if j==8
|
||||||
|
if (hex->have_pending) {
|
||||||
|
float prev_s0 = vert[j].s;
|
||||||
|
float prev_v0 = vert[j].v;
|
||||||
|
hex->current = hex->pending;
|
||||||
|
hex->have_pending = 0;
|
||||||
|
hexwave_generate_linesegs(vert, hex, dt);
|
||||||
|
// the following never occurs with this oscillator, but it makes
|
||||||
|
// the code work in more general cases
|
||||||
|
if (vert[j].v != prev_v0)
|
||||||
|
hex_blep (out+i, recip_dt*t, (vert[j].v - prev_v0));
|
||||||
|
if (vert[j].s != prev_s0)
|
||||||
|
hex_blamp(out+i, recip_dt*t, dt*(vert[j].s - prev_s0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point, we've written output[] and temp_output[]
|
||||||
|
if (num_samples >= hexblep.width) {
|
||||||
|
// the first half of temp[] overlaps the end of output, the second half will be the new start overlap
|
||||||
|
for (i=0; i < hexblep.width; ++i)
|
||||||
|
output[num_samples-hexblep.width + i] += temp_output[i];
|
||||||
|
memcpy(hex->buffer, temp_output+hexblep.width, buffered_length);
|
||||||
|
} else {
|
||||||
|
for (i=0; i < num_samples; ++i)
|
||||||
|
output[i] = temp_output[i];
|
||||||
|
memcpy(hex->buffer, temp_output+num_samples, buffered_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
hex->t = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_HEXWAVE_DEF void hexwave_shutdown(float *user_buffer)
|
||||||
|
{
|
||||||
|
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||||
|
if (user_buffer != 0) {
|
||||||
|
free(hexblep.blep);
|
||||||
|
free(hexblep.blamp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer should be NULL or must be 4*(width*(oversample+1)*2 +
|
||||||
|
STB_HEXWAVE_DEF void hexwave_init(int width, int oversample, float *user_buffer)
|
||||||
|
{
|
||||||
|
int halfwidth = width/2;
|
||||||
|
int half = halfwidth*oversample;
|
||||||
|
int blep_buffer_count = width*(oversample+1);
|
||||||
|
int n = 2*half+1;
|
||||||
|
#ifdef STB_HEXWAVE_NO_ALLOCATION
|
||||||
|
float *buffers = user_buffer;
|
||||||
|
#else
|
||||||
|
float *buffers = user_buffer ? user_buffer : (float *) malloc(sizeof(float) * n * 2);
|
||||||
|
#endif
|
||||||
|
float *step = buffers+0*n;
|
||||||
|
float *ramp = buffers+1*n;
|
||||||
|
float *blep_buffer, *blamp_buffer;
|
||||||
|
double integrate_impulse=0, integrate_step=0;
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
if (width > STB_HEXWAVE_MAX_BLEP_LENGTH)
|
||||||
|
width = STB_HEXWAVE_MAX_BLEP_LENGTH;
|
||||||
|
|
||||||
|
if (user_buffer == 0) {
|
||||||
|
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||||
|
blep_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
|
||||||
|
blamp_buffer = (float *) malloc(sizeof(float)*blep_buffer_count);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
blep_buffer = ramp+n;
|
||||||
|
blamp_buffer = blep_buffer + blep_buffer_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute BLEP and BLAMP by integerating windowed sinc
|
||||||
|
for (i=0; i < n; ++i) {
|
||||||
|
for (j=0; j < 16; ++j) {
|
||||||
|
float sinc_t = 3.141592f* (i-half) / oversample;
|
||||||
|
float sinc = (i==half) ? 1.0f : (float) sin(sinc_t) / (sinc_t);
|
||||||
|
float wt = 2.0f*3.1415926f * i / (n-1);
|
||||||
|
float window = (float) (0.355768 - 0.487396*cos(wt) + 0.144232*cos(2*wt) - 0.012604*cos(3*wt)); // Nuttall
|
||||||
|
double value = window * sinc;
|
||||||
|
integrate_impulse += value/16;
|
||||||
|
integrate_step += integrate_impulse/16;
|
||||||
|
}
|
||||||
|
step[i] = (float) integrate_impulse;
|
||||||
|
ramp[i] = (float) integrate_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
// renormalize
|
||||||
|
for (i=0; i < n; ++i) {
|
||||||
|
step[i] = step[i] * (float) (1.0 / step[n-1]); // step needs to reach to 1.0
|
||||||
|
ramp[i] = ramp[i] * (float) (halfwidth / ramp[n-1]); // ramp needs to become a slope of 1.0 after oversampling
|
||||||
|
}
|
||||||
|
|
||||||
|
// deinterleave to allow efficient interpolation e.g. w/SIMD
|
||||||
|
for (j=0; j <= oversample; ++j) {
|
||||||
|
for (i=0; i < width; ++i) {
|
||||||
|
blep_buffer [j*width+i] = step[j+i*oversample];
|
||||||
|
blamp_buffer[j*width+i] = ramp[j+i*oversample];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract out the naive waveform; note we can't do this to the raw data
|
||||||
|
// above, because we want the discontinuity to be in a different locations
|
||||||
|
// for j=0 and j=oversample (which exists to provide something to interpolate against)
|
||||||
|
for (j=0; j <= oversample; ++j) {
|
||||||
|
// subtract step
|
||||||
|
for (i=halfwidth; i < width; ++i)
|
||||||
|
blep_buffer [j*width+i] -= 1.0f;
|
||||||
|
// subtract ramp
|
||||||
|
for (i=halfwidth; i < width; ++i)
|
||||||
|
blamp_buffer[j*width+i] -= (j+i*oversample-half)*(1.0f/oversample);
|
||||||
|
}
|
||||||
|
|
||||||
|
hexblep.blep = blep_buffer;
|
||||||
|
hexblep.blamp = blamp_buffer;
|
||||||
|
hexblep.width = width;
|
||||||
|
hexblep.oversample = oversample;
|
||||||
|
|
||||||
|
#ifndef STB_HEXWAVE_NO_ALLOCATION
|
||||||
|
if (user_buffer == 0)
|
||||||
|
free(buffers);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif // STB_HEXWAVE_IMPLEMENTATION
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
7984
external/stb/stb/stb_image.h
vendored
Normal file
2634
external/stb/stb/stb_image_resize.h
vendored
Normal file
1724
external/stb/stb/stb_image_write.h
vendored
Normal file
295
external/stb/stb/stb_include.h
vendored
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
// stb_include.h - v0.02 - parse and process #include directives - public domain
|
||||||
|
//
|
||||||
|
// To build this, in one source file that includes this file do
|
||||||
|
// #define STB_INCLUDE_IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// This program parses a string and replaces lines of the form
|
||||||
|
// #include "foo"
|
||||||
|
// with the contents of a file named "foo". It also embeds the
|
||||||
|
// appropriate #line directives. Note that all include files must
|
||||||
|
// reside in the location specified in the path passed to the API;
|
||||||
|
// it does not check multiple directories.
|
||||||
|
//
|
||||||
|
// If the string contains a line of the form
|
||||||
|
// #inject
|
||||||
|
// then it will be replaced with the contents of the string 'inject' passed to the API.
|
||||||
|
//
|
||||||
|
// Options:
|
||||||
|
//
|
||||||
|
// Define STB_INCLUDE_LINE_GLSL to get GLSL-style #line directives
|
||||||
|
// which use numbers instead of filenames.
|
||||||
|
//
|
||||||
|
// Define STB_INCLUDE_LINE_NONE to disable output of #line directives.
|
||||||
|
//
|
||||||
|
// Standard libraries:
|
||||||
|
//
|
||||||
|
// stdio.h FILE, fopen, fclose, fseek, ftell
|
||||||
|
// stdlib.h malloc, realloc, free
|
||||||
|
// string.h strcpy, strncmp, memcpy
|
||||||
|
//
|
||||||
|
// Credits:
|
||||||
|
//
|
||||||
|
// Written by Sean Barrett.
|
||||||
|
//
|
||||||
|
// Fixes:
|
||||||
|
// Michal Klos
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_INCLUDE_H
|
||||||
|
#define STB_INCLUDE_STB_INCLUDE_H
|
||||||
|
|
||||||
|
// Do include-processing on the string 'str'. To free the return value, pass it to free()
|
||||||
|
char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
|
||||||
|
|
||||||
|
// Concatenate the strings 'strs' and do include-processing on the result. To free the return value, pass it to free()
|
||||||
|
char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename_for_line_directive, char error[256]);
|
||||||
|
|
||||||
|
// Load the file 'filename' and do include-processing on the string therein. note that
|
||||||
|
// 'filename' is opened directly; 'path_to_includes' is not used. To free the return value, pass it to free()
|
||||||
|
char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256]);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef STB_INCLUDE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static char *stb_include_load_file(char *filename, size_t *plen)
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
size_t len;
|
||||||
|
FILE *f = fopen(filename, "rb");
|
||||||
|
if (f == 0) return 0;
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
len = (size_t) ftell(f);
|
||||||
|
if (plen) *plen = len;
|
||||||
|
text = (char *) malloc(len+1);
|
||||||
|
if (text == 0) return 0;
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
fread(text, 1, len, f);
|
||||||
|
fclose(f);
|
||||||
|
text[len] = 0;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int offset;
|
||||||
|
int end;
|
||||||
|
char *filename;
|
||||||
|
int next_line_after;
|
||||||
|
} include_info;
|
||||||
|
|
||||||
|
static include_info *stb_include_append_include(include_info *array, int len, int offset, int end, char *filename, int next_line)
|
||||||
|
{
|
||||||
|
include_info *z = (include_info *) realloc(array, sizeof(*z) * (len+1));
|
||||||
|
z[len].offset = offset;
|
||||||
|
z[len].end = end;
|
||||||
|
z[len].filename = filename;
|
||||||
|
z[len].next_line_after = next_line;
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stb_include_free_includes(include_info *array, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < len; ++i)
|
||||||
|
free(array[i].filename);
|
||||||
|
free(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb_include_isspace(int ch)
|
||||||
|
{
|
||||||
|
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// find location of all #include and #inject
|
||||||
|
static int stb_include_find_includes(char *text, include_info **plist)
|
||||||
|
{
|
||||||
|
int line_count = 1;
|
||||||
|
int inc_count = 0;
|
||||||
|
char *s = text, *start;
|
||||||
|
include_info *list = NULL;
|
||||||
|
while (*s) {
|
||||||
|
// parse is always at start of line when we reach here
|
||||||
|
start = s;
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
++s;
|
||||||
|
if (*s == '#') {
|
||||||
|
++s;
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
++s;
|
||||||
|
if (0==strncmp(s, "include", 7) && stb_include_isspace(s[7])) {
|
||||||
|
s += 7;
|
||||||
|
while (*s == ' ' || *s == '\t')
|
||||||
|
++s;
|
||||||
|
if (*s == '"') {
|
||||||
|
char *t = ++s;
|
||||||
|
while (*t != '"' && *t != '\n' && *t != '\r' && *t != 0)
|
||||||
|
++t;
|
||||||
|
if (*t == '"') {
|
||||||
|
char *filename = (char *) malloc(t-s+1);
|
||||||
|
memcpy(filename, s, t-s);
|
||||||
|
filename[t-s] = 0;
|
||||||
|
s=t;
|
||||||
|
while (*s != '\r' && *s != '\n' && *s != 0)
|
||||||
|
++s;
|
||||||
|
// s points to the newline, so s-start is everything except the newline
|
||||||
|
list = stb_include_append_include(list, inc_count++, start-text, s-text, filename, line_count+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (0==strncmp(s, "inject", 6) && (stb_include_isspace(s[6]) || s[6]==0)) {
|
||||||
|
while (*s != '\r' && *s != '\n' && *s != 0)
|
||||||
|
++s;
|
||||||
|
list = stb_include_append_include(list, inc_count++, start-text, s-text, NULL, line_count+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (*s != '\r' && *s != '\n' && *s != 0)
|
||||||
|
++s;
|
||||||
|
if (*s == '\r' || *s == '\n') {
|
||||||
|
s = s + (s[0] + s[1] == '\r' + '\n' ? 2 : 1);
|
||||||
|
}
|
||||||
|
++line_count;
|
||||||
|
}
|
||||||
|
*plist = list;
|
||||||
|
return inc_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid dependency on sprintf()
|
||||||
|
static void stb_include_itoa(char str[9], int n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < 8; ++i)
|
||||||
|
str[i] = ' ';
|
||||||
|
str[i] = 0;
|
||||||
|
|
||||||
|
for (i=1; i < 8; ++i) {
|
||||||
|
str[7-i] = '0' + (n % 10);
|
||||||
|
n /= 10;
|
||||||
|
if (n == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *stb_include_append(char *str, size_t *curlen, char *addstr, size_t addlen)
|
||||||
|
{
|
||||||
|
str = (char *) realloc(str, *curlen + addlen);
|
||||||
|
memcpy(str + *curlen, addstr, addlen);
|
||||||
|
*curlen += addlen;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *stb_include_string(char *str, char *inject, char *path_to_includes, char *filename, char error[256])
|
||||||
|
{
|
||||||
|
char temp[4096];
|
||||||
|
include_info *inc_list;
|
||||||
|
int i, num = stb_include_find_includes(str, &inc_list);
|
||||||
|
size_t source_len = strlen(str);
|
||||||
|
char *text=0;
|
||||||
|
size_t textlen=0, last=0;
|
||||||
|
for (i=0; i < num; ++i) {
|
||||||
|
text = stb_include_append(text, &textlen, str+last, inc_list[i].offset - last);
|
||||||
|
// write out line directive for the include
|
||||||
|
#ifndef STB_INCLUDE_LINE_NONE
|
||||||
|
#ifdef STB_INCLUDE_LINE_GLSL
|
||||||
|
if (textlen != 0) // GLSL #version must appear first, so don't put a #line at the top
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
strcpy(temp, "#line ");
|
||||||
|
stb_include_itoa(temp+6, 1);
|
||||||
|
strcat(temp, " ");
|
||||||
|
#ifdef STB_INCLUDE_LINE_GLSL
|
||||||
|
stb_include_itoa(temp+15, i+1);
|
||||||
|
#else
|
||||||
|
strcat(temp, "\"");
|
||||||
|
if (inc_list[i].filename == 0)
|
||||||
|
strcmp(temp, "INJECT");
|
||||||
|
else
|
||||||
|
strcat(temp, inc_list[i].filename);
|
||||||
|
strcat(temp, "\"");
|
||||||
|
#endif
|
||||||
|
strcat(temp, "\n");
|
||||||
|
text = stb_include_append(text, &textlen, temp, strlen(temp));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (inc_list[i].filename == 0) {
|
||||||
|
if (inject != 0)
|
||||||
|
text = stb_include_append(text, &textlen, inject, strlen(inject));
|
||||||
|
} else {
|
||||||
|
char *inc;
|
||||||
|
strcpy(temp, path_to_includes);
|
||||||
|
strcat(temp, "/");
|
||||||
|
strcat(temp, inc_list[i].filename);
|
||||||
|
inc = stb_include_file(temp, inject, path_to_includes, error);
|
||||||
|
if (inc == NULL) {
|
||||||
|
stb_include_free_includes(inc_list, num);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
text = stb_include_append(text, &textlen, inc, strlen(inc));
|
||||||
|
free(inc);
|
||||||
|
}
|
||||||
|
// write out line directive
|
||||||
|
#ifndef STB_INCLUDE_LINE_NONE
|
||||||
|
strcpy(temp, "\n#line ");
|
||||||
|
stb_include_itoa(temp+6, inc_list[i].next_line_after);
|
||||||
|
strcat(temp, " ");
|
||||||
|
#ifdef STB_INCLUDE_LINE_GLSL
|
||||||
|
stb_include_itoa(temp+15, 0);
|
||||||
|
#else
|
||||||
|
strcat(temp, filename != 0 ? filename : "source-file");
|
||||||
|
#endif
|
||||||
|
text = stb_include_append(text, &textlen, temp, strlen(temp));
|
||||||
|
// no newlines, because we kept the #include newlines, which will get appended next
|
||||||
|
#endif
|
||||||
|
last = inc_list[i].end;
|
||||||
|
}
|
||||||
|
text = stb_include_append(text, &textlen, str+last, source_len - last + 1); // append '\0'
|
||||||
|
stb_include_free_includes(inc_list, num);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *stb_include_strings(char **strs, int count, char *inject, char *path_to_includes, char *filename, char error[256])
|
||||||
|
{
|
||||||
|
char *text;
|
||||||
|
char *result;
|
||||||
|
int i;
|
||||||
|
size_t length=0;
|
||||||
|
for (i=0; i < count; ++i)
|
||||||
|
length += strlen(strs[i]);
|
||||||
|
text = (char *) malloc(length+1);
|
||||||
|
length = 0;
|
||||||
|
for (i=0; i < count; ++i) {
|
||||||
|
strcpy(text + length, strs[i]);
|
||||||
|
length += strlen(strs[i]);
|
||||||
|
}
|
||||||
|
result = stb_include_string(text, inject, path_to_includes, filename, error);
|
||||||
|
free(text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *stb_include_file(char *filename, char *inject, char *path_to_includes, char error[256])
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *result;
|
||||||
|
char *text = stb_include_load_file(filename, &len);
|
||||||
|
if (text == NULL) {
|
||||||
|
strcpy(error, "Error: couldn't load '");
|
||||||
|
strcat(error, filename);
|
||||||
|
strcat(error, "'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
result = stb_include_string(text, inject, path_to_includes, filename, error);
|
||||||
|
free(text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 // @TODO, GL_ARB_shader_language_include-style system that doesn't touch filesystem
|
||||||
|
char *stb_include_preloaded(char *str, char *inject, char *includes[][2], char error[256])
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // STB_INCLUDE_IMPLEMENTATION
|
194
external/stb/stb/stb_leakcheck.h
vendored
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
// stb_leakcheck.h - v0.6 - quick & dirty malloc leak-checking - public domain
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file.
|
||||||
|
|
||||||
|
#ifdef STB_LEAKCHECK_IMPLEMENTATION
|
||||||
|
#undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once
|
||||||
|
|
||||||
|
// if we've already included leakcheck before, undefine the macros
|
||||||
|
#ifdef malloc
|
||||||
|
#undef malloc
|
||||||
|
#undef free
|
||||||
|
#undef realloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STB_LEAKCHECK_OUTPUT_PIPE
|
||||||
|
#define STB_LEAKCHECK_OUTPUT_PIPE stdout
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
typedef struct malloc_info stb_leakcheck_malloc_info;
|
||||||
|
|
||||||
|
struct malloc_info
|
||||||
|
{
|
||||||
|
const 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, const 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;
|
||||||
|
free(mi);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *stb_leakcheck_realloc(void *ptr, size_t sz, const 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stblkck_internal_print(const char *reason, stb_leakcheck_malloc_info *mi)
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1900 // 1900=VS 2015
|
||||||
|
// Compilers that use the old MS C runtime library don't have %zd
|
||||||
|
// and the older ones don't even have %lld either... however, the old compilers
|
||||||
|
// without "long long" don't support 64-bit targets either, so here's the
|
||||||
|
// compromise:
|
||||||
|
#if _MSC_VER < 1400 // before VS 2005
|
||||||
|
fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %8d bytes at %p\n", reason, mi->file, mi->line, (int)mi->size, (void*)(mi+1));
|
||||||
|
#else
|
||||||
|
fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %16lld bytes at %p\n", reason, mi->file, mi->line, (long long)mi->size, (void*)(mi+1));
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// Assume we have %zd on other targets.
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
__mingw_fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1));
|
||||||
|
#else
|
||||||
|
fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_leakcheck_dumpmem(void)
|
||||||
|
{
|
||||||
|
stb_leakcheck_malloc_info *mi = mi_head;
|
||||||
|
while (mi) {
|
||||||
|
if ((ptrdiff_t) mi->size >= 0)
|
||||||
|
stblkck_internal_print("LEAKED", mi);
|
||||||
|
mi = mi->next;
|
||||||
|
}
|
||||||
|
#ifdef STB_LEAKCHECK_SHOWALL
|
||||||
|
mi = mi_head;
|
||||||
|
while (mi) {
|
||||||
|
if ((ptrdiff_t) mi->size < 0)
|
||||||
|
stblkck_internal_print("FREED ", mi);
|
||||||
|
mi = mi->next;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif // STB_LEAKCHECK_IMPLEMENTATION
|
||||||
|
|
||||||
|
#if !defined(INCLUDE_STB_LEAKCHECK_H) || !defined(malloc)
|
||||||
|
#define INCLUDE_STB_LEAKCHECK_H
|
||||||
|
|
||||||
|
#include <stdlib.h> // we want to define the macros *after* stdlib to avoid a slew of errors
|
||||||
|
|
||||||
|
#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, const char *file, int line);
|
||||||
|
extern void * stb_leakcheck_realloc(void *ptr, size_t sz, const char *file, int line);
|
||||||
|
extern void stb_leakcheck_free(void *ptr);
|
||||||
|
extern void stb_leakcheck_dumpmem(void);
|
||||||
|
|
||||||
|
#endif // INCLUDE_STB_LEAKCHECK_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
428
external/stb/stb/stb_perlin.h
vendored
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
// stb_perlin.h - v0.5 - perlin noise
|
||||||
|
// public domain single-file C implementation by Sean Barrett
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// to create the implementation,
|
||||||
|
// #define STB_PERLIN_IMPLEMENTATION
|
||||||
|
// in *one* C/CPP file that includes this file.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Documentation:
|
||||||
|
//
|
||||||
|
// float stb_perlin_noise3( float x,
|
||||||
|
// float y,
|
||||||
|
// float z,
|
||||||
|
// int x_wrap=0,
|
||||||
|
// int y_wrap=0,
|
||||||
|
// int z_wrap=0)
|
||||||
|
//
|
||||||
|
// This function computes a random value at the coordinate (x,y,z).
|
||||||
|
// Adjacent random values are continuous but the noise fluctuates
|
||||||
|
// its randomness with period 1, i.e. takes on wholly unrelated values
|
||||||
|
// at integer points. Specifically, this implements Ken Perlin's
|
||||||
|
// revised noise function from 2002.
|
||||||
|
//
|
||||||
|
// The "wrap" parameters can be used to create wraparound noise that
|
||||||
|
// wraps at powers of two. The numbers MUST be powers of two. Specify
|
||||||
|
// 0 to mean "don't care". (The noise always wraps every 256 due
|
||||||
|
// details of the implementation, even if you ask for larger or no
|
||||||
|
// wrapping.)
|
||||||
|
//
|
||||||
|
// float stb_perlin_noise3_seed( float x,
|
||||||
|
// float y,
|
||||||
|
// float z,
|
||||||
|
// int x_wrap=0,
|
||||||
|
// int y_wrap=0,
|
||||||
|
// int z_wrap=0,
|
||||||
|
// int seed)
|
||||||
|
//
|
||||||
|
// As above, but 'seed' selects from multiple different variations of the
|
||||||
|
// noise function. The current implementation only uses the bottom 8 bits
|
||||||
|
// of 'seed', but possibly in the future more bits will be used.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Fractal Noise:
|
||||||
|
//
|
||||||
|
// Three common fractal noise functions are included, which produce
|
||||||
|
// a wide variety of nice effects depending on the parameters
|
||||||
|
// provided. Note that each function will call stb_perlin_noise3
|
||||||
|
// 'octaves' times, so this parameter will affect runtime.
|
||||||
|
//
|
||||||
|
// float stb_perlin_ridge_noise3(float x, float y, float z,
|
||||||
|
// float lacunarity, float gain, float offset, int octaves)
|
||||||
|
//
|
||||||
|
// float stb_perlin_fbm_noise3(float x, float y, float z,
|
||||||
|
// float lacunarity, float gain, int octaves)
|
||||||
|
//
|
||||||
|
// float stb_perlin_turbulence_noise3(float x, float y, float z,
|
||||||
|
// float lacunarity, float gain, int octaves)
|
||||||
|
//
|
||||||
|
// Typical values to start playing with:
|
||||||
|
// octaves = 6 -- number of "octaves" of noise3() to sum
|
||||||
|
// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
|
||||||
|
// gain = 0.5 -- relative weighting applied to each successive octave
|
||||||
|
// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Contributors:
|
||||||
|
// Jack Mott - additional noise functions
|
||||||
|
// Jordan Peck - seeded noise
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
|
||||||
|
extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed);
|
||||||
|
extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves);
|
||||||
|
extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
|
||||||
|
extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
|
||||||
|
extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_PERLIN_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <math.h> // fabs()
|
||||||
|
|
||||||
|
// not same permutation table as Perlin's reference to avoid copyright issues;
|
||||||
|
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
|
||||||
|
static unsigned char stb__perlin_randtab[512] =
|
||||||
|
{
|
||||||
|
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||||
|
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||||
|
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||||
|
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||||
|
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||||
|
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||||
|
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||||
|
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||||
|
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||||
|
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||||
|
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||||
|
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||||
|
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||||
|
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||||
|
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||||
|
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||||
|
|
||||||
|
// and a second copy so we don't need an extra mask or static initializer
|
||||||
|
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
|
||||||
|
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
|
||||||
|
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
|
||||||
|
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
|
||||||
|
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
|
||||||
|
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
|
||||||
|
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
|
||||||
|
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
|
||||||
|
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
|
||||||
|
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
|
||||||
|
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
|
||||||
|
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
|
||||||
|
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
|
||||||
|
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
|
||||||
|
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
|
||||||
|
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// perlin's gradient has 12 cases so some get used 1/16th of the time
|
||||||
|
// and some 2/16ths. We reduce bias by changing those fractions
|
||||||
|
// to 5/64ths and 6/64ths
|
||||||
|
|
||||||
|
// this array is designed to match the previous implementation
|
||||||
|
// of gradient hash: indices[stb__perlin_randtab[i]&63]
|
||||||
|
static unsigned char stb__perlin_randtab_grad_idx[512] =
|
||||||
|
{
|
||||||
|
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
|
||||||
|
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
|
||||||
|
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
|
||||||
|
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
|
||||||
|
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
|
||||||
|
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
|
||||||
|
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
|
||||||
|
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
|
||||||
|
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
|
||||||
|
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
|
||||||
|
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
|
||||||
|
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
|
||||||
|
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
|
||||||
|
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
|
||||||
|
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
|
||||||
|
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
|
||||||
|
|
||||||
|
// and a second copy so we don't need an extra mask or static initializer
|
||||||
|
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
|
||||||
|
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
|
||||||
|
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
|
||||||
|
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
|
||||||
|
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
|
||||||
|
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
|
||||||
|
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
|
||||||
|
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
|
||||||
|
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
|
||||||
|
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
|
||||||
|
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
|
||||||
|
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
|
||||||
|
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
|
||||||
|
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
|
||||||
|
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
|
||||||
|
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
static float stb__perlin_lerp(float a, float b, float t)
|
||||||
|
{
|
||||||
|
return a + (b-a) * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int stb__perlin_fastfloor(float a)
|
||||||
|
{
|
||||||
|
int ai = (int) a;
|
||||||
|
return (a < ai) ? ai-1 : ai;
|
||||||
|
}
|
||||||
|
|
||||||
|
// different grad function from Perlin's, but easy to modify to match reference
|
||||||
|
static float stb__perlin_grad(int grad_idx, float x, float y, float z)
|
||||||
|
{
|
||||||
|
static float basis[12][4] =
|
||||||
|
{
|
||||||
|
{ 1, 1, 0 },
|
||||||
|
{ -1, 1, 0 },
|
||||||
|
{ 1,-1, 0 },
|
||||||
|
{ -1,-1, 0 },
|
||||||
|
{ 1, 0, 1 },
|
||||||
|
{ -1, 0, 1 },
|
||||||
|
{ 1, 0,-1 },
|
||||||
|
{ -1, 0,-1 },
|
||||||
|
{ 0, 1, 1 },
|
||||||
|
{ 0,-1, 1 },
|
||||||
|
{ 0, 1,-1 },
|
||||||
|
{ 0,-1,-1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
float *grad = basis[grad_idx];
|
||||||
|
return grad[0]*x + grad[1]*y + grad[2]*z;
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
|
||||||
|
{
|
||||||
|
float u,v,w;
|
||||||
|
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||||
|
float n00,n01,n10,n11;
|
||||||
|
float n0,n1;
|
||||||
|
|
||||||
|
unsigned int x_mask = (x_wrap-1) & 255;
|
||||||
|
unsigned int y_mask = (y_wrap-1) & 255;
|
||||||
|
unsigned int z_mask = (z_wrap-1) & 255;
|
||||||
|
int px = stb__perlin_fastfloor(x);
|
||||||
|
int py = stb__perlin_fastfloor(y);
|
||||||
|
int pz = stb__perlin_fastfloor(z);
|
||||||
|
int x0 = px & x_mask, x1 = (px+1) & x_mask;
|
||||||
|
int y0 = py & y_mask, y1 = (py+1) & y_mask;
|
||||||
|
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
|
||||||
|
int r0,r1, r00,r01,r10,r11;
|
||||||
|
|
||||||
|
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||||
|
|
||||||
|
x -= px; u = stb__perlin_ease(x);
|
||||||
|
y -= py; v = stb__perlin_ease(y);
|
||||||
|
z -= pz; w = stb__perlin_ease(z);
|
||||||
|
|
||||||
|
r0 = stb__perlin_randtab[x0+seed];
|
||||||
|
r1 = stb__perlin_randtab[x1+seed];
|
||||||
|
|
||||||
|
r00 = stb__perlin_randtab[r0+y0];
|
||||||
|
r01 = stb__perlin_randtab[r0+y1];
|
||||||
|
r10 = stb__perlin_randtab[r1+y0];
|
||||||
|
r11 = stb__perlin_randtab[r1+y1];
|
||||||
|
|
||||||
|
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
|
||||||
|
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
|
||||||
|
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
|
||||||
|
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
|
||||||
|
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
|
||||||
|
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
|
||||||
|
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
|
||||||
|
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
|
||||||
|
|
||||||
|
n00 = stb__perlin_lerp(n000,n001,w);
|
||||||
|
n01 = stb__perlin_lerp(n010,n011,w);
|
||||||
|
n10 = stb__perlin_lerp(n100,n101,w);
|
||||||
|
n11 = stb__perlin_lerp(n110,n111,w);
|
||||||
|
|
||||||
|
n0 = stb__perlin_lerp(n00,n01,v);
|
||||||
|
n1 = stb__perlin_lerp(n10,n11,v);
|
||||||
|
|
||||||
|
return stb__perlin_lerp(n0,n1,u);
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
|
||||||
|
{
|
||||||
|
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed)
|
||||||
|
{
|
||||||
|
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float frequency = 1.0f;
|
||||||
|
float prev = 1.0f;
|
||||||
|
float amplitude = 0.5f;
|
||||||
|
float sum = 0.0f;
|
||||||
|
|
||||||
|
for (i = 0; i < octaves; i++) {
|
||||||
|
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i);
|
||||||
|
r = offset - (float) fabs(r);
|
||||||
|
r = r*r;
|
||||||
|
sum += r*amplitude*prev;
|
||||||
|
prev = r;
|
||||||
|
frequency *= lacunarity;
|
||||||
|
amplitude *= gain;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float frequency = 1.0f;
|
||||||
|
float amplitude = 1.0f;
|
||||||
|
float sum = 0.0f;
|
||||||
|
|
||||||
|
for (i = 0; i < octaves; i++) {
|
||||||
|
sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
|
||||||
|
frequency *= lacunarity;
|
||||||
|
amplitude *= gain;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float frequency = 1.0f;
|
||||||
|
float amplitude = 1.0f;
|
||||||
|
float sum = 0.0f;
|
||||||
|
|
||||||
|
for (i = 0; i < octaves; i++) {
|
||||||
|
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
|
||||||
|
sum += (float) fabs(r);
|
||||||
|
frequency *= lacunarity;
|
||||||
|
amplitude *= gain;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
|
||||||
|
{
|
||||||
|
float u,v,w;
|
||||||
|
float n000,n001,n010,n011,n100,n101,n110,n111;
|
||||||
|
float n00,n01,n10,n11;
|
||||||
|
float n0,n1;
|
||||||
|
|
||||||
|
int px = stb__perlin_fastfloor(x);
|
||||||
|
int py = stb__perlin_fastfloor(y);
|
||||||
|
int pz = stb__perlin_fastfloor(z);
|
||||||
|
int x_wrap2 = (x_wrap ? x_wrap : 256);
|
||||||
|
int y_wrap2 = (y_wrap ? y_wrap : 256);
|
||||||
|
int z_wrap2 = (z_wrap ? z_wrap : 256);
|
||||||
|
int x0 = px % x_wrap2, x1;
|
||||||
|
int y0 = py % y_wrap2, y1;
|
||||||
|
int z0 = pz % z_wrap2, z1;
|
||||||
|
int r0,r1, r00,r01,r10,r11;
|
||||||
|
|
||||||
|
if (x0 < 0) x0 += x_wrap2;
|
||||||
|
if (y0 < 0) y0 += y_wrap2;
|
||||||
|
if (z0 < 0) z0 += z_wrap2;
|
||||||
|
x1 = (x0+1) % x_wrap2;
|
||||||
|
y1 = (y0+1) % y_wrap2;
|
||||||
|
z1 = (z0+1) % z_wrap2;
|
||||||
|
|
||||||
|
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
|
||||||
|
|
||||||
|
x -= px; u = stb__perlin_ease(x);
|
||||||
|
y -= py; v = stb__perlin_ease(y);
|
||||||
|
z -= pz; w = stb__perlin_ease(z);
|
||||||
|
|
||||||
|
r0 = stb__perlin_randtab[x0];
|
||||||
|
r0 = stb__perlin_randtab[r0+seed];
|
||||||
|
r1 = stb__perlin_randtab[x1];
|
||||||
|
r1 = stb__perlin_randtab[r1+seed];
|
||||||
|
|
||||||
|
r00 = stb__perlin_randtab[r0+y0];
|
||||||
|
r01 = stb__perlin_randtab[r0+y1];
|
||||||
|
r10 = stb__perlin_randtab[r1+y0];
|
||||||
|
r11 = stb__perlin_randtab[r1+y1];
|
||||||
|
|
||||||
|
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
|
||||||
|
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
|
||||||
|
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
|
||||||
|
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
|
||||||
|
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
|
||||||
|
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
|
||||||
|
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
|
||||||
|
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
|
||||||
|
|
||||||
|
n00 = stb__perlin_lerp(n000,n001,w);
|
||||||
|
n01 = stb__perlin_lerp(n010,n011,w);
|
||||||
|
n10 = stb__perlin_lerp(n100,n101,w);
|
||||||
|
n11 = stb__perlin_lerp(n110,n111,w);
|
||||||
|
|
||||||
|
n0 = stb__perlin_lerp(n00,n01,v);
|
||||||
|
n1 = stb__perlin_lerp(n10,n11,v);
|
||||||
|
|
||||||
|
return stb__perlin_lerp(n0,n1,u);
|
||||||
|
}
|
||||||
|
#endif // STB_PERLIN_IMPLEMENTATION
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
623
external/stb/stb/stb_rect_pack.h
vendored
Normal file
@ -0,0 +1,623 @@
|
|||||||
|
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||||
|
// Sean Barrett 2014
|
||||||
|
//
|
||||||
|
// Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
// Does not do rotation.
|
||||||
|
//
|
||||||
|
// Before #including,
|
||||||
|
//
|
||||||
|
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// in the file that you want to have the implementation.
|
||||||
|
//
|
||||||
|
// Not necessarily the awesomest packing method, but better than
|
||||||
|
// the totally naive one in stb_truetype (which is primarily what
|
||||||
|
// this is meant to replace).
|
||||||
|
//
|
||||||
|
// Has only had a few tests run, may have issues.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Please note: better rectangle packers are welcome! Please
|
||||||
|
// implement them to the same API, but with a different init
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// Credits
|
||||||
|
//
|
||||||
|
// Library
|
||||||
|
// Sean Barrett
|
||||||
|
// Minor features
|
||||||
|
// Martins Mozeiko
|
||||||
|
// github:IntellectualKitty
|
||||||
|
//
|
||||||
|
// Bugfixes / warning fixes
|
||||||
|
// Jeremy Jaussaud
|
||||||
|
// Fabian Giesen
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
//
|
||||||
|
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||||
|
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||||
|
// 0.99 (2019-02-07) warning fixes
|
||||||
|
// 0.11 (2017-03-03) return packing success/fail result
|
||||||
|
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||||
|
// 0.09 (2016-08-27) fix compiler warnings
|
||||||
|
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// INCLUDE SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||||
|
|
||||||
|
#define STB_RECT_PACK_VERSION 1
|
||||||
|
|
||||||
|
#ifdef STBRP_STATIC
|
||||||
|
#define STBRP_DEF static
|
||||||
|
#else
|
||||||
|
#define STBRP_DEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct stbrp_context stbrp_context;
|
||||||
|
typedef struct stbrp_node stbrp_node;
|
||||||
|
typedef struct stbrp_rect stbrp_rect;
|
||||||
|
|
||||||
|
typedef int stbrp_coord;
|
||||||
|
|
||||||
|
#define STBRP__MAXVAL 0x7fffffff
|
||||||
|
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||||
|
// Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||||
|
// are 'num_rects' many of them.
|
||||||
|
//
|
||||||
|
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||||
|
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||||
|
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||||
|
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||||
|
// have the 'was_packed' flag set to 0.
|
||||||
|
//
|
||||||
|
// You should not try to access the 'rects' array from another thread
|
||||||
|
// while this function is running, as the function temporarily reorders
|
||||||
|
// the array while it executes.
|
||||||
|
//
|
||||||
|
// To pack into another rectangle, you need to call stbrp_init_target
|
||||||
|
// again. To continue packing into the same rectangle, you can call
|
||||||
|
// this function again. Calling this multiple times with multiple rect
|
||||||
|
// arrays will probably produce worse packing results than calling it
|
||||||
|
// a single time with the full rectangle array, but the option is
|
||||||
|
// available.
|
||||||
|
//
|
||||||
|
// The function returns 1 if all of the rectangles were successfully
|
||||||
|
// packed and 0 otherwise.
|
||||||
|
|
||||||
|
struct stbrp_rect
|
||||||
|
{
|
||||||
|
// reserved for your use:
|
||||||
|
int id;
|
||||||
|
|
||||||
|
// input:
|
||||||
|
stbrp_coord w, h;
|
||||||
|
|
||||||
|
// output:
|
||||||
|
stbrp_coord x, y;
|
||||||
|
int was_packed; // non-zero if valid packing
|
||||||
|
|
||||||
|
}; // 16 bytes, nominally
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||||
|
// Initialize a rectangle packer to:
|
||||||
|
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||||
|
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||||
|
//
|
||||||
|
// You must call this function every time you start packing into a new target.
|
||||||
|
//
|
||||||
|
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||||
|
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||||
|
// the call (or calls) finish.
|
||||||
|
//
|
||||||
|
// Note: to guarantee best results, either:
|
||||||
|
// 1. make sure 'num_nodes' >= 'width'
|
||||||
|
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||||
|
//
|
||||||
|
// If you don't do either of the above things, widths will be quantized to multiples
|
||||||
|
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||||
|
//
|
||||||
|
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||||
|
// may run out of temporary storage and be unable to pack some rectangles.
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||||
|
// Optionally call this function after init but before doing any packing to
|
||||||
|
// change the handling of the out-of-temp-memory scenario, described above.
|
||||||
|
// If you call init again, this will be reset to the default (false).
|
||||||
|
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||||
|
// Optionally select which packing heuristic the library should use. Different
|
||||||
|
// heuristics will produce better/worse results for different data sets.
|
||||||
|
// If you call init again, this will be reset to the default.
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP_HEURISTIC_Skyline_default=0,
|
||||||
|
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||||
|
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// the details of the following structures don't matter to you, but they must
|
||||||
|
// be visible so you can handle the memory allocations for them
|
||||||
|
|
||||||
|
struct stbrp_node
|
||||||
|
{
|
||||||
|
stbrp_coord x,y;
|
||||||
|
stbrp_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stbrp_context
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int align;
|
||||||
|
int init_mode;
|
||||||
|
int heuristic;
|
||||||
|
int num_nodes;
|
||||||
|
stbrp_node *active_head;
|
||||||
|
stbrp_node *free_head;
|
||||||
|
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPLEMENTATION SECTION
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#ifndef STBRP_SORT
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define STBRP_SORT qsort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBRP_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#define STBRP_ASSERT assert
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define STBRP__NOTUSED(v) (void)(v)
|
||||||
|
#define STBRP__CDECL __cdecl
|
||||||
|
#else
|
||||||
|
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||||
|
#define STBRP__CDECL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBRP__INIT_skyline = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||||
|
{
|
||||||
|
switch (context->init_mode) {
|
||||||
|
case STBRP__INIT_skyline:
|
||||||
|
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||||
|
context->heuristic = heuristic;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
STBRP_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||||
|
{
|
||||||
|
if (allow_out_of_mem)
|
||||||
|
// if it's ok to run out of memory, then don't bother aligning them;
|
||||||
|
// this gives better packing, but may fail due to OOM (even though
|
||||||
|
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||||
|
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||||
|
context->align = 1;
|
||||||
|
else {
|
||||||
|
// if it's not ok to run out of memory, then quantize the widths
|
||||||
|
// so that num_nodes is always enough nodes.
|
||||||
|
//
|
||||||
|
// I.e. num_nodes * align >= width
|
||||||
|
// align >= width / num_nodes
|
||||||
|
// align = ceil(width/num_nodes)
|
||||||
|
|
||||||
|
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i < num_nodes-1; ++i)
|
||||||
|
nodes[i].next = &nodes[i+1];
|
||||||
|
nodes[i].next = NULL;
|
||||||
|
context->init_mode = STBRP__INIT_skyline;
|
||||||
|
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||||
|
context->free_head = &nodes[0];
|
||||||
|
context->active_head = &context->extra[0];
|
||||||
|
context->width = width;
|
||||||
|
context->height = height;
|
||||||
|
context->num_nodes = num_nodes;
|
||||||
|
stbrp_setup_allow_out_of_mem(context, 0);
|
||||||
|
|
||||||
|
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||||
|
context->extra[0].x = 0;
|
||||||
|
context->extra[0].y = 0;
|
||||||
|
context->extra[0].next = &context->extra[1];
|
||||||
|
context->extra[1].x = (stbrp_coord) width;
|
||||||
|
context->extra[1].y = (1<<30);
|
||||||
|
context->extra[1].next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find minimum y position if it starts at x1
|
||||||
|
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||||
|
{
|
||||||
|
stbrp_node *node = first;
|
||||||
|
int x1 = x0 + width;
|
||||||
|
int min_y, visited_width, waste_area;
|
||||||
|
|
||||||
|
STBRP__NOTUSED(c);
|
||||||
|
|
||||||
|
STBRP_ASSERT(first->x <= x0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// skip in case we're past the node
|
||||||
|
while (node->next->x <= x0)
|
||||||
|
++node;
|
||||||
|
#else
|
||||||
|
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STBRP_ASSERT(node->x <= x0);
|
||||||
|
|
||||||
|
min_y = 0;
|
||||||
|
waste_area = 0;
|
||||||
|
visited_width = 0;
|
||||||
|
while (node->x < x1) {
|
||||||
|
if (node->y > min_y) {
|
||||||
|
// raise min_y higher.
|
||||||
|
// we've accounted for all waste up to min_y,
|
||||||
|
// but we'll now add more waste for everything we've visted
|
||||||
|
waste_area += visited_width * (node->y - min_y);
|
||||||
|
min_y = node->y;
|
||||||
|
// the first time through, visited_width might be reduced
|
||||||
|
if (node->x < x0)
|
||||||
|
visited_width += node->next->x - x0;
|
||||||
|
else
|
||||||
|
visited_width += node->next->x - node->x;
|
||||||
|
} else {
|
||||||
|
// add waste area
|
||||||
|
int under_width = node->next->x - node->x;
|
||||||
|
if (under_width + visited_width > width)
|
||||||
|
under_width = width - visited_width;
|
||||||
|
waste_area += under_width * (min_y - node->y);
|
||||||
|
visited_width += under_width;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pwaste = waste_area;
|
||||||
|
return min_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
stbrp_node **prev_link;
|
||||||
|
} stbrp__findresult;
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||||
|
{
|
||||||
|
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||||
|
stbrp__findresult fr;
|
||||||
|
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||||
|
|
||||||
|
// align to multiple of c->align
|
||||||
|
width = (width + c->align - 1);
|
||||||
|
width -= width % c->align;
|
||||||
|
STBRP_ASSERT(width % c->align == 0);
|
||||||
|
|
||||||
|
// if it can't possibly fit, bail immediately
|
||||||
|
if (width > c->width || height > c->height) {
|
||||||
|
fr.prev_link = NULL;
|
||||||
|
fr.x = fr.y = 0;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
while (node->x + width <= c->width) {
|
||||||
|
int y,waste;
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||||
|
// bottom left
|
||||||
|
if (y < best_y) {
|
||||||
|
best_y = y;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// best-fit
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
// can only use it if it first vertically
|
||||||
|
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||||
|
|
||||||
|
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||||
|
//
|
||||||
|
// e.g, if fitting
|
||||||
|
//
|
||||||
|
// ____________________
|
||||||
|
// |____________________|
|
||||||
|
//
|
||||||
|
// into
|
||||||
|
//
|
||||||
|
// | |
|
||||||
|
// | ____________|
|
||||||
|
// |____________|
|
||||||
|
//
|
||||||
|
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||||
|
//
|
||||||
|
// This makes BF take about 2x the time
|
||||||
|
|
||||||
|
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||||
|
tail = c->active_head;
|
||||||
|
node = c->active_head;
|
||||||
|
prev = &c->active_head;
|
||||||
|
// find first node that's admissible
|
||||||
|
while (tail->x < width)
|
||||||
|
tail = tail->next;
|
||||||
|
while (tail) {
|
||||||
|
int xpos = tail->x - width;
|
||||||
|
int y,waste;
|
||||||
|
STBRP_ASSERT(xpos >= 0);
|
||||||
|
// find the left position that matches this
|
||||||
|
while (node->next->x <= xpos) {
|
||||||
|
prev = &node->next;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||||
|
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||||
|
if (y + height <= c->height) {
|
||||||
|
if (y <= best_y) {
|
||||||
|
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||||
|
best_x = xpos;
|
||||||
|
STBRP_ASSERT(y <= best_y);
|
||||||
|
best_y = y;
|
||||||
|
best_waste = waste;
|
||||||
|
best = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.prev_link = best;
|
||||||
|
fr.x = best_x;
|
||||||
|
fr.y = best_y;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||||
|
{
|
||||||
|
// find best position according to heuristic
|
||||||
|
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||||
|
stbrp_node *node, *cur;
|
||||||
|
|
||||||
|
// bail if:
|
||||||
|
// 1. it failed
|
||||||
|
// 2. the best node doesn't fit (we don't always check this)
|
||||||
|
// 3. we're out of memory
|
||||||
|
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||||
|
res.prev_link = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on success, create new node
|
||||||
|
node = context->free_head;
|
||||||
|
node->x = (stbrp_coord) res.x;
|
||||||
|
node->y = (stbrp_coord) (res.y + height);
|
||||||
|
|
||||||
|
context->free_head = node->next;
|
||||||
|
|
||||||
|
// insert the new node into the right starting point, and
|
||||||
|
// let 'cur' point to the remaining nodes needing to be
|
||||||
|
// stiched back in
|
||||||
|
|
||||||
|
cur = *res.prev_link;
|
||||||
|
if (cur->x < res.x) {
|
||||||
|
// preserve the existing one, so start testing with the next one
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
cur->next = node;
|
||||||
|
cur = next;
|
||||||
|
} else {
|
||||||
|
*res.prev_link = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from here, traverse cur and free the nodes, until we get to one
|
||||||
|
// that shouldn't be freed
|
||||||
|
while (cur->next && cur->next->x <= res.x + width) {
|
||||||
|
stbrp_node *next = cur->next;
|
||||||
|
// move the current node to the free list
|
||||||
|
cur->next = context->free_head;
|
||||||
|
context->free_head = cur;
|
||||||
|
cur = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stitch the list back in
|
||||||
|
node->next = cur;
|
||||||
|
|
||||||
|
if (cur->x < res.x + width)
|
||||||
|
cur->x = (stbrp_coord) (res.x + width);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur->x < context->width) {
|
||||||
|
STBRP_ASSERT(cur->x < cur->next->x);
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(cur->next == NULL);
|
||||||
|
|
||||||
|
{
|
||||||
|
int count=0;
|
||||||
|
cur = context->active_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
cur = context->free_head;
|
||||||
|
while (cur) {
|
||||||
|
cur = cur->next;
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
STBRP_ASSERT(count == context->num_nodes+2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
if (p->h > q->h)
|
||||||
|
return -1;
|
||||||
|
if (p->h < q->h)
|
||||||
|
return 1;
|
||||||
|
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||||
|
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||||
|
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||||
|
{
|
||||||
|
int i, all_rects_packed = 1;
|
||||||
|
|
||||||
|
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort according to heuristic
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||||
|
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||||
|
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||||
|
} else {
|
||||||
|
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||||
|
if (fr.prev_link) {
|
||||||
|
rects[i].x = (stbrp_coord) fr.x;
|
||||||
|
rects[i].y = (stbrp_coord) fr.y;
|
||||||
|
} else {
|
||||||
|
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsort
|
||||||
|
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||||
|
|
||||||
|
// set was_packed flags and all_rects_packed status
|
||||||
|
for (i=0; i < num_rects; ++i) {
|
||||||
|
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||||
|
if (!rects[i].was_packed)
|
||||||
|
all_rects_packed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the all_rects_packed status
|
||||||
|
return all_rects_packed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
1906
external/stb/stb/stb_sprintf.h
vendored
Normal file
1429
external/stb/stb/stb_textedit.h
vendored
Normal file
4187
external/stb/stb/stb_tilemap_editor.h
vendored
Normal file
5077
external/stb/stb/stb_truetype.h
vendored
Normal file
5584
external/stb/stb/stb_vorbis.c
vendored
Normal file
3807
external/stb/stb/stb_voxel_render.h
vendored
Normal file
12
external/stb/stb/tests/Makefile
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
INCLUDES = -I..
|
||||||
|
CFLAGS = -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast -DSTB_DIVIDE_TEST
|
||||||
|
CPPFLAGS = -Wno-write-strings -DSTB_DIVIDE_TEST
|
||||||
|
|
||||||
|
# Uncomment this line for reproducing OSS-Fuzz bugs with image_fuzzer
|
||||||
|
#CFLAGS += -O -fsanitize=address
|
||||||
|
|
||||||
|
all:
|
||||||
|
$(CC) $(INCLUDES) $(CFLAGS) ../stb_vorbis.c test_c_compilation.c test_c_lexer.c test_dxt.c test_easyfont.c test_image.c test_image_write.c test_perlin.c test_sprintf.c test_truetype.c test_voxel.c -lm
|
||||||
|
$(CC) $(INCLUDES) $(CPPFLAGS) -std=c++0x test_cpp_compilation.cpp -lm -lstdc++
|
||||||
|
$(CC) $(INCLUDES) $(CFLAGS) -DIWT_TEST image_write_test.c -lm -o image_write_test
|
||||||
|
$(CC) $(INCLUDES) $(CFLAGS) fuzz_main.c stbi_read_fuzzer.c -lm -o image_fuzzer
|
50
external/stb/stb/tests/c_lexer_test.c
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#define STB_C_LEX_C_DECIMAL_INTS Y // "0|[1-9][0-9]*" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_HEX_INTS Y // "0x[0-9a-fA-F]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_OCTAL_INTS Y // "[0-7]+" CLEX_intlit
|
||||||
|
#define STB_C_LEX_C_DECIMAL_FLOATS Y // "[0-9]*(.[0-9]*([eE][-+]?[0-9]+)?) CLEX_floatlit
|
||||||
|
#define STB_C_LEX_C99_HEX_FLOATS Y // "0x{hex}+(.{hex}*)?[pP][-+]?{hex}+ CLEX_floatlit
|
||||||
|
#define STB_C_LEX_C_IDENTIFIERS Y // "[_a-zA-Z][_a-zA-Z0-9]*" CLEX_id
|
||||||
|
#define STB_C_LEX_C_DQ_STRINGS Y // double-quote-delimited strings with escapes CLEX_dqstring
|
||||||
|
#define STB_C_LEX_C_SQ_STRINGS Y // single-quote-delimited strings with escapes CLEX_ssstring
|
||||||
|
#define STB_C_LEX_C_CHARS Y // single-quote-delimited character with escape CLEX_charlits
|
||||||
|
#define STB_C_LEX_C_COMMENTS Y // "/* comment */"
|
||||||
|
#define STB_C_LEX_CPP_COMMENTS Y // "// comment to end of line\n"
|
||||||
|
#define STB_C_LEX_C_COMPARISONS Y // "==" CLEX_eq "!=" CLEX_noteq "<=" CLEX_lesseq ">=" CLEX_greatereq
|
||||||
|
#define STB_C_LEX_C_LOGICAL Y // "&&" CLEX_andand "||" CLEX_oror
|
||||||
|
#define STB_C_LEX_C_SHIFTS Y // "<<" CLEX_shl ">>" CLEX_shr
|
||||||
|
#define STB_C_LEX_C_INCREMENTS Y // "++" CLEX_plusplus "--" CLEX_minusminus
|
||||||
|
#define STB_C_LEX_C_ARROW Y // "->" CLEX_arrow
|
||||||
|
#define STB_C_LEX_EQUAL_ARROW Y // "=>" CLEX_eqarrow
|
||||||
|
#define STB_C_LEX_C_BITWISEEQ Y // "&=" CLEX_andeq "|=" CLEX_oreq "^=" CLEX_xoreq
|
||||||
|
#define STB_C_LEX_C_ARITHEQ Y // "+=" CLEX_pluseq "-=" CLEX_minuseq
|
||||||
|
// "*=" CLEX_muleq "/=" CLEX_diveq "%=" CLEX_modeq
|
||||||
|
// if both STB_C_LEX_SHIFTS & STB_C_LEX_ARITHEQ:
|
||||||
|
// "<<=" CLEX_shleq ">>=" CLEX_shreq
|
||||||
|
|
||||||
|
#define STB_C_LEX_PARSE_SUFFIXES Y // letters after numbers are parsed as part of those numbers, and must be in suffix list below
|
||||||
|
#define STB_C_LEX_DECIMAL_SUFFIXES "uUlL" // decimal integer suffixes e.g. "uUlL" -- these are returned as-is in string storage
|
||||||
|
#define STB_C_LEX_HEX_SUFFIXES "lL" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_OCTAL_SUFFIXES "lL" // e.g. "uUlL"
|
||||||
|
#define STB_C_LEX_FLOAT_SUFFIXES "uulL" //
|
||||||
|
|
||||||
|
#define STB_C_LEX_0_IS_EOF N // if Y, ends parsing at '\0'; if N, returns '\0' as token
|
||||||
|
#define STB_C_LEX_INTEGERS_AS_DOUBLES N // parses integers as doubles so they can be larger than 'int', but only if STB_C_LEX_STDLIB==N
|
||||||
|
#define STB_C_LEX_MULTILINE_DSTRINGS Y // allow newlines in double-quoted strings
|
||||||
|
#define STB_C_LEX_MULTILINE_SSTRINGS Y // allow newlines in single-quoted strings
|
||||||
|
#define STB_C_LEX_USE_STDLIB N // use strtod,strtol for parsing #s; otherwise inaccurate hack
|
||||||
|
#define STB_C_LEX_DOLLAR_IDENTIFIER Y // allow $ as an identifier character
|
||||||
|
#define STB_C_LEX_FLOAT_NO_DECIMAL Y // allow floats that have no decimal point if they have an exponent
|
||||||
|
|
||||||
|
#define STB_C_LEX_DEFINE_ALL_TOKEN_NAMES Y // if Y, all CLEX_ token names are defined, even if never returned
|
||||||
|
// leaving it as N should help you catch config bugs
|
||||||
|
|
||||||
|
#define STB_C_LEX_DISCARD_PREPROCESSOR Y // discard C-preprocessor directives (e.g. after prepocess
|
||||||
|
// still have #line, #pragma, etc)
|
||||||
|
|
||||||
|
#define STB_C_LEXER_DEFINITIONS // This line prevents the header file from replacing your definitions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define STB_C_LEXER_IMPLEMENTATION
|
||||||
|
#define STB_C_LEXER_SELF_TEST
|
||||||
|
#include "../stb_c_lexer.h"
|
89
external/stb/stb/tests/c_lexer_test.dsp
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="c_lexer_test" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||||
|
|
||||||
|
CFG=c_lexer_test - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "c_lexer_test.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "c_lexer_test.mak" CFG="c_lexer_test - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "c_lexer_test - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE "c_lexer_test - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "c_lexer_test - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "c_lexer_test - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug\c_lexer_test"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "c_lexer_test - Win32 Release"
|
||||||
|
# Name "c_lexer_test - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\c_lexer_test.c
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
85
external/stb/stb/tests/caveview/README.md
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# FAQ
|
||||||
|
|
||||||
|
### How to run it?
|
||||||
|
|
||||||
|
There's no GUI. Find a directory with Minecraft Anvil files (.mca).
|
||||||
|
Copy a Minecraft "terrain.png" into that directory (do a google
|
||||||
|
image search). Run from that directory.
|
||||||
|
|
||||||
|
### How accurate is this as a Minecraft viewer?
|
||||||
|
|
||||||
|
Not very. Many Minecraft blocks are not handled correctly:
|
||||||
|
|
||||||
|
* No redstone, rails, or other "flat" blocks
|
||||||
|
* No signs, doors, fences, carpets, or other complicated geometry
|
||||||
|
* Stairs are turned into ramps
|
||||||
|
* Upper slabs turn into lower slabs
|
||||||
|
* Wood types only for blocks, not stairs, slabs, etc
|
||||||
|
* Colored glass becomes regular glass
|
||||||
|
* Glass panes become glass blocks
|
||||||
|
* Water is opaque
|
||||||
|
* Water level is incorrect
|
||||||
|
* No biome coloration
|
||||||
|
* Cactus is not shrunk, shows holes
|
||||||
|
* Chests are not shrunk
|
||||||
|
* Double-chests draw as two chests
|
||||||
|
* Pumpkins etc. are not rotated properly
|
||||||
|
* Torches are drawn hackily, do not attach to walls
|
||||||
|
* Incorrect textures for blocks that postdate terrain.png
|
||||||
|
* Transparent textures have black fringes due to non-premultiplied-alpha
|
||||||
|
* Skylight and block light are combined in a single value
|
||||||
|
* Only blocks at y=1..255 are shown (not y=0)
|
||||||
|
* If a 32x32x256 "quad-chunk" needs more than 800K quads, isn't handled (very unlikely)
|
||||||
|
|
||||||
|
Some of these are due to engine limitations, and some of
|
||||||
|
these are because I didn't make the effort since my
|
||||||
|
goal was to make a demo for stb_voxel_render.h, not
|
||||||
|
to make a proper Minecraft viewer.
|
||||||
|
|
||||||
|
|
||||||
|
### Could this be turned into a proper Minecraft viewer?
|
||||||
|
|
||||||
|
Yes and no. Yes, you could do it, but no, it wouldn't
|
||||||
|
really resemble this code that much anymore.
|
||||||
|
|
||||||
|
You could certainly use this engine to
|
||||||
|
render the parts of Minecraft it works for, but many
|
||||||
|
of the things it doesn't handle it can't handle at all
|
||||||
|
(stairs, water, fences, carpets, etc) because it uses
|
||||||
|
low-precision coordinates to store voxel data.
|
||||||
|
|
||||||
|
You would have to render all of the stuff it doesn't
|
||||||
|
handle through another rendering path. In a game (not
|
||||||
|
a viewer) you would need such a path for movable entities
|
||||||
|
like doors and carts anyway, so possibly handling other
|
||||||
|
things that way wouldn't be so bad.
|
||||||
|
|
||||||
|
Rails, ladders, and redstone lines could be implemented by
|
||||||
|
using tex2 to overlay those effects, but you can't rotate
|
||||||
|
tex1 and tex2 independently, so there may be cases where
|
||||||
|
the underlying texture needs a different rotation from the
|
||||||
|
overlaid texture, which would require separate rendering.
|
||||||
|
Handling redstone's brightness being different from underlying
|
||||||
|
block's brightness would require separate rendering.
|
||||||
|
|
||||||
|
You can use the face-color effect to do biome coloration,
|
||||||
|
but the change won't be smooth the way it is in Minecraft.
|
||||||
|
|
||||||
|
|
||||||
|
### Why isn't building the mesh data faster?
|
||||||
|
|
||||||
|
Partly because converting from minecraft data is expensive.
|
||||||
|
|
||||||
|
Here is the approximate breakdown of an older version
|
||||||
|
of this executable and lib that did the building single-threaded.
|
||||||
|
|
||||||
|
* 25% loading & parsing minecraft files (4/5ths of this is my crappy zlib)
|
||||||
|
* 18% converting from minecraft blockids & lighting to stb blockids & lighting
|
||||||
|
* 10% reordering from data[z][y]\[x] (minecraft-style) to data[y][x]\[z] (stb-style)
|
||||||
|
* 40% building mesh data
|
||||||
|
* 7% uploading mesh data to OpenGL
|
||||||
|
|
||||||
|
I did do significant optimizations after the above, so the
|
||||||
|
final breakdown is different, but it should give you some
|
||||||
|
sense of the costs.
|
||||||
|
|
598
external/stb/stb/tests/caveview/cave_main.c
vendored
Normal file
@ -0,0 +1,598 @@
|
|||||||
|
#define _WIN32_WINNT 0x400
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
// stb.h
|
||||||
|
#define STB_DEFINE
|
||||||
|
#include "stb.h"
|
||||||
|
|
||||||
|
// stb_gl.h
|
||||||
|
#define STB_GL_IMPLEMENTATION
|
||||||
|
#define STB_GLEXT_DEFINE "glext_list.h"
|
||||||
|
#include "stb_gl.h"
|
||||||
|
|
||||||
|
// SDL
|
||||||
|
#include "sdl.h"
|
||||||
|
#include "SDL_opengl.h"
|
||||||
|
|
||||||
|
// stb_glprog.h
|
||||||
|
#define STB_GLPROG_IMPLEMENTATION
|
||||||
|
#define STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||||
|
#include "stb_glprog.h"
|
||||||
|
|
||||||
|
// stb_image.h
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
// stb_easy_font.h
|
||||||
|
#include "stb_easy_font.h" // doesn't require an IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "caveview.h"
|
||||||
|
|
||||||
|
char *game_name = "caveview";
|
||||||
|
|
||||||
|
|
||||||
|
#define REVERSE_DEPTH
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void print_string(float x, float y, char *text, float r, float g, float b)
|
||||||
|
{
|
||||||
|
static char buffer[99999];
|
||||||
|
int num_quads;
|
||||||
|
|
||||||
|
num_quads = stb_easy_font_print(x, y, text, NULL, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
glColor3f(r,g,b);
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glVertexPointer(2, GL_FLOAT, 16, buffer);
|
||||||
|
glDrawArrays(GL_QUADS, 0, num_quads*4);
|
||||||
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float text_color[3];
|
||||||
|
static float pos_x = 10;
|
||||||
|
static float pos_y = 10;
|
||||||
|
|
||||||
|
static void print(char *text, ...)
|
||||||
|
{
|
||||||
|
char buffer[999];
|
||||||
|
va_list va;
|
||||||
|
va_start(va, text);
|
||||||
|
vsprintf(buffer, text, va);
|
||||||
|
va_end(va);
|
||||||
|
print_string(pos_x, pos_y, buffer, text_color[0], text_color[1], text_color[2]);
|
||||||
|
pos_y += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
float camang[3], camloc[3] = { 60,22,77 };
|
||||||
|
float player_zoom = 1.0;
|
||||||
|
float rotate_view = 0.0;
|
||||||
|
|
||||||
|
|
||||||
|
void camera_to_worldspace(float world[3], float cam_x, float cam_y, float cam_z)
|
||||||
|
{
|
||||||
|
float vec[3] = { cam_x, cam_y, cam_z };
|
||||||
|
float t[3];
|
||||||
|
float s,c;
|
||||||
|
s = (float) sin(camang[0]*3.141592/180);
|
||||||
|
c = (float) cos(camang[0]*3.141592/180);
|
||||||
|
|
||||||
|
t[0] = vec[0];
|
||||||
|
t[1] = c*vec[1] - s*vec[2];
|
||||||
|
t[2] = s*vec[1] + c*vec[2];
|
||||||
|
|
||||||
|
s = (float) sin(camang[2]*3.141592/180);
|
||||||
|
c = (float) cos(camang[2]*3.141592/180);
|
||||||
|
world[0] = c*t[0] - s*t[1];
|
||||||
|
world[1] = s*t[0] + c*t[1];
|
||||||
|
world[2] = t[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// camera worldspace velocity
|
||||||
|
float cam_vel[3];
|
||||||
|
|
||||||
|
int controls;
|
||||||
|
|
||||||
|
#define MAX_VEL 150.0f // blocks per second
|
||||||
|
#define ACCEL 6.0f
|
||||||
|
#define DECEL 3.0f
|
||||||
|
|
||||||
|
#define STATIC_FRICTION DECEL
|
||||||
|
#define EFFECTIVE_ACCEL (ACCEL+DECEL)
|
||||||
|
|
||||||
|
// dynamic friction:
|
||||||
|
//
|
||||||
|
// if going at MAX_VEL, ACCEL and friction must cancel
|
||||||
|
// EFFECTIVE_ACCEL = DECEL + DYNAMIC_FRIC*MAX_VEL
|
||||||
|
#define DYNAMIC_FRICTION (ACCEL/(float)MAX_VEL)
|
||||||
|
|
||||||
|
float view_x_vel = 0;
|
||||||
|
float view_z_vel = 0;
|
||||||
|
float pending_view_x;
|
||||||
|
float pending_view_z;
|
||||||
|
float pending_view_x;
|
||||||
|
float pending_view_z;
|
||||||
|
|
||||||
|
void process_tick_raw(float dt)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float thrust[3] = { 0,0,0 };
|
||||||
|
float world_thrust[3];
|
||||||
|
|
||||||
|
// choose direction to apply thrust
|
||||||
|
|
||||||
|
thrust[0] = (controls & 3)== 1 ? EFFECTIVE_ACCEL : (controls & 3)== 2 ? -EFFECTIVE_ACCEL : 0;
|
||||||
|
thrust[1] = (controls & 12)== 4 ? EFFECTIVE_ACCEL : (controls & 12)== 8 ? -EFFECTIVE_ACCEL : 0;
|
||||||
|
thrust[2] = (controls & 48)==16 ? EFFECTIVE_ACCEL : (controls & 48)==32 ? -EFFECTIVE_ACCEL : 0;
|
||||||
|
|
||||||
|
// @TODO clamp thrust[0] & thrust[1] vector length to EFFECTIVE_ACCEL
|
||||||
|
|
||||||
|
camera_to_worldspace(world_thrust, thrust[0], thrust[1], 0);
|
||||||
|
world_thrust[2] += thrust[2];
|
||||||
|
|
||||||
|
for (i=0; i < 3; ++i) {
|
||||||
|
float acc = world_thrust[i];
|
||||||
|
cam_vel[i] += acc*dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cam_vel[0] || cam_vel[1] || cam_vel[2])
|
||||||
|
{
|
||||||
|
float vel = (float) sqrt(cam_vel[0]*cam_vel[0] + cam_vel[1]*cam_vel[1] + cam_vel[2]*cam_vel[2]);
|
||||||
|
float newvel = vel;
|
||||||
|
float dec = STATIC_FRICTION + DYNAMIC_FRICTION*vel;
|
||||||
|
newvel = vel - dec*dt;
|
||||||
|
if (newvel < 0)
|
||||||
|
newvel = 0;
|
||||||
|
cam_vel[0] *= newvel/vel;
|
||||||
|
cam_vel[1] *= newvel/vel;
|
||||||
|
cam_vel[2] *= newvel/vel;
|
||||||
|
}
|
||||||
|
|
||||||
|
camloc[0] += cam_vel[0] * dt;
|
||||||
|
camloc[1] += cam_vel[1] * dt;
|
||||||
|
camloc[2] += cam_vel[2] * dt;
|
||||||
|
|
||||||
|
view_x_vel *= (float) pow(0.75, dt);
|
||||||
|
view_z_vel *= (float) pow(0.75, dt);
|
||||||
|
|
||||||
|
view_x_vel += (pending_view_x - view_x_vel)*dt*60;
|
||||||
|
view_z_vel += (pending_view_z - view_z_vel)*dt*60;
|
||||||
|
|
||||||
|
pending_view_x -= view_x_vel * dt;
|
||||||
|
pending_view_z -= view_z_vel * dt;
|
||||||
|
camang[0] += view_x_vel * dt;
|
||||||
|
camang[2] += view_z_vel * dt;
|
||||||
|
camang[0] = stb_clamp(camang[0], -90, 90);
|
||||||
|
camang[2] = (float) fmod(camang[2], 360);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_tick(float dt)
|
||||||
|
{
|
||||||
|
while (dt > 1.0f/60) {
|
||||||
|
process_tick_raw(1.0f/60);
|
||||||
|
dt -= 1.0f/60;
|
||||||
|
}
|
||||||
|
process_tick_raw(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_view(float dx, float dy)
|
||||||
|
{
|
||||||
|
// hard-coded mouse sensitivity, not resolution independent?
|
||||||
|
pending_view_z -= dx*300;
|
||||||
|
pending_view_x -= dy*700;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int screen_x, screen_y;
|
||||||
|
extern int is_synchronous_debug;
|
||||||
|
float render_time;
|
||||||
|
|
||||||
|
extern int chunk_locations, chunks_considered, chunks_in_frustum;
|
||||||
|
extern int quads_considered, quads_rendered;
|
||||||
|
extern int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total;
|
||||||
|
extern int view_dist_in_chunks;
|
||||||
|
extern int num_threads_active, num_meshes_started, num_meshes_uploaded;
|
||||||
|
extern float chunk_server_activity;
|
||||||
|
|
||||||
|
static Uint64 start_time, end_time; // render time
|
||||||
|
|
||||||
|
float chunk_server_status[32];
|
||||||
|
int chunk_server_pos;
|
||||||
|
|
||||||
|
void draw_stats(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
static Uint64 last_frame_time;
|
||||||
|
Uint64 cur_time = SDL_GetPerformanceCounter();
|
||||||
|
float chunk_server=0;
|
||||||
|
float frame_time = (cur_time - last_frame_time) / (float) SDL_GetPerformanceFrequency();
|
||||||
|
last_frame_time = cur_time;
|
||||||
|
|
||||||
|
chunk_server_status[chunk_server_pos] = chunk_server_activity;
|
||||||
|
chunk_server_pos = (chunk_server_pos+1) %32;
|
||||||
|
|
||||||
|
for (i=0; i < 32; ++i)
|
||||||
|
chunk_server += chunk_server_status[i] / 32.0f;
|
||||||
|
|
||||||
|
stb_easy_font_spacing(-0.75);
|
||||||
|
pos_y = 10;
|
||||||
|
text_color[0] = text_color[1] = text_color[2] = 1.0f;
|
||||||
|
print("Frame time: %6.2fms, CPU frame render time: %5.2fms", frame_time*1000, render_time*1000);
|
||||||
|
print("Tris: %4.1fM drawn of %4.1fM in range", 2*quads_rendered/1000000.0f, 2*quads_considered/1000000.0f);
|
||||||
|
print("Vbuf storage: %dMB in frustum of %dMB in range of %dMB in cache", chunk_storage_rendered>>20, chunk_storage_considered>>20, chunk_storage_total>>20);
|
||||||
|
print("Num mesh builds started this frame: %d; num uploaded this frame: %d\n", num_meshes_started, num_meshes_uploaded);
|
||||||
|
print("QChunks: %3d in frustum of %3d valid of %3d in range", chunks_in_frustum, chunks_considered, chunk_locations);
|
||||||
|
print("Mesh worker threads active: %d", num_threads_active);
|
||||||
|
print("View distance: %d blocks", view_dist_in_chunks*16);
|
||||||
|
print("%s", glGetString(GL_RENDERER));
|
||||||
|
|
||||||
|
if (is_synchronous_debug) {
|
||||||
|
text_color[0] = 1.0;
|
||||||
|
text_color[1] = 0.5;
|
||||||
|
text_color[2] = 0.5;
|
||||||
|
print("SLOWNESS: Synchronous debug output is enabled!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_main(void)
|
||||||
|
{
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
#ifdef REVERSE_DEPTH
|
||||||
|
glDepthFunc(GL_GREATER);
|
||||||
|
glClearDepth(0);
|
||||||
|
#else
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
glClearDepth(1);
|
||||||
|
#endif
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
glClearColor(0.6f,0.7f,0.9f,0.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glColor3f(1,1,1);
|
||||||
|
glFrontFace(GL_CW);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
#ifdef REVERSE_DEPTH
|
||||||
|
stbgl_Perspective(player_zoom, 90, 70, 3000, 1.0/16);
|
||||||
|
#else
|
||||||
|
stbgl_Perspective(player_zoom, 90, 70, 1.0/16, 3000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// now compute where the camera should be
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
stbgl_initCamera_zup_facing_y();
|
||||||
|
|
||||||
|
glRotatef(-camang[0],1,0,0);
|
||||||
|
glRotatef(-camang[2],0,0,1);
|
||||||
|
glTranslatef(-camloc[0], -camloc[1], -camloc[2]);
|
||||||
|
|
||||||
|
start_time = SDL_GetPerformanceCounter();
|
||||||
|
render_caves(camloc);
|
||||||
|
end_time = SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
|
render_time = (end_time - start_time) / (float) SDL_GetPerformanceFrequency();
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluOrtho2D(0,screen_x/2,screen_y/2,0);
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
draw_stats();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning(disable:4244; disable:4305; disable:4018)
|
||||||
|
|
||||||
|
#define SCALE 2
|
||||||
|
|
||||||
|
void error(char *s)
|
||||||
|
{
|
||||||
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", s, NULL);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ods(char *fmt, ...)
|
||||||
|
{
|
||||||
|
char buffer[1000];
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
vsprintf(buffer, fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
SDL_Log("%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TICKS_PER_SECOND 60
|
||||||
|
|
||||||
|
static SDL_Window *window;
|
||||||
|
|
||||||
|
extern void draw_main(void);
|
||||||
|
extern void process_tick(float dt);
|
||||||
|
extern void editor_init(void);
|
||||||
|
|
||||||
|
void draw(void)
|
||||||
|
{
|
||||||
|
draw_main();
|
||||||
|
SDL_GL_SwapWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int initialized=0;
|
||||||
|
static float last_dt;
|
||||||
|
|
||||||
|
int screen_x,screen_y;
|
||||||
|
|
||||||
|
float carried_dt = 0;
|
||||||
|
#define TICKRATE 60
|
||||||
|
|
||||||
|
float tex2_alpha = 1.0;
|
||||||
|
|
||||||
|
int raw_level_time;
|
||||||
|
|
||||||
|
float global_timer;
|
||||||
|
int global_hack;
|
||||||
|
|
||||||
|
int loopmode(float dt, int real, int in_client)
|
||||||
|
{
|
||||||
|
if (!initialized) return 0;
|
||||||
|
|
||||||
|
if (!real)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// don't allow more than 6 frames to update at a time
|
||||||
|
if (dt > 0.075) dt = 0.075;
|
||||||
|
|
||||||
|
global_timer += dt;
|
||||||
|
|
||||||
|
carried_dt += dt;
|
||||||
|
while (carried_dt > 1.0/TICKRATE) {
|
||||||
|
if (global_hack) {
|
||||||
|
tex2_alpha += global_hack / 60.0f;
|
||||||
|
if (tex2_alpha < 0) tex2_alpha = 0;
|
||||||
|
if (tex2_alpha > 1) tex2_alpha = 1;
|
||||||
|
}
|
||||||
|
//update_input();
|
||||||
|
// if the player is dead, stop the sim
|
||||||
|
carried_dt -= 1.0/TICKRATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_tick(dt);
|
||||||
|
draw();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int quit;
|
||||||
|
|
||||||
|
extern int controls;
|
||||||
|
|
||||||
|
void active_control_set(int key)
|
||||||
|
{
|
||||||
|
controls |= 1 << key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void active_control_clear(int key)
|
||||||
|
{
|
||||||
|
controls &= ~(1 << key);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void update_view(float dx, float dy);
|
||||||
|
|
||||||
|
void process_sdl_mouse(SDL_Event *e)
|
||||||
|
{
|
||||||
|
update_view((float) e->motion.xrel / screen_x, (float) e->motion.yrel / screen_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_event(SDL_Event *e)
|
||||||
|
{
|
||||||
|
switch (e->type) {
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
process_sdl_mouse(e);
|
||||||
|
break;
|
||||||
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_QUIT:
|
||||||
|
quit = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_WINDOWEVENT:
|
||||||
|
switch (e->window.event) {
|
||||||
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
screen_x = e->window.data1;
|
||||||
|
screen_y = e->window.data2;
|
||||||
|
loopmode(0,1,0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYDOWN: {
|
||||||
|
int k = e->key.keysym.sym;
|
||||||
|
int s = e->key.keysym.scancode;
|
||||||
|
SDL_Keymod mod;
|
||||||
|
mod = SDL_GetModState();
|
||||||
|
if (k == SDLK_ESCAPE)
|
||||||
|
quit = 1;
|
||||||
|
|
||||||
|
if (s == SDL_SCANCODE_D) active_control_set(0);
|
||||||
|
if (s == SDL_SCANCODE_A) active_control_set(1);
|
||||||
|
if (s == SDL_SCANCODE_W) active_control_set(2);
|
||||||
|
if (s == SDL_SCANCODE_S) active_control_set(3);
|
||||||
|
if (k == SDLK_SPACE) active_control_set(4);
|
||||||
|
if (s == SDL_SCANCODE_LCTRL) active_control_set(5);
|
||||||
|
if (s == SDL_SCANCODE_S) active_control_set(6);
|
||||||
|
if (s == SDL_SCANCODE_D) active_control_set(7);
|
||||||
|
if (k == '1') global_hack = !global_hack;
|
||||||
|
if (k == '2') global_hack = -1;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (game_mode == GAME_editor) {
|
||||||
|
switch (k) {
|
||||||
|
case SDLK_RIGHT: editor_key(STBTE_scroll_right); break;
|
||||||
|
case SDLK_LEFT : editor_key(STBTE_scroll_left ); break;
|
||||||
|
case SDLK_UP : editor_key(STBTE_scroll_up ); break;
|
||||||
|
case SDLK_DOWN : editor_key(STBTE_scroll_down ); break;
|
||||||
|
}
|
||||||
|
switch (s) {
|
||||||
|
case SDL_SCANCODE_S: editor_key(STBTE_tool_select); break;
|
||||||
|
case SDL_SCANCODE_B: editor_key(STBTE_tool_brush ); break;
|
||||||
|
case SDL_SCANCODE_E: editor_key(STBTE_tool_erase ); break;
|
||||||
|
case SDL_SCANCODE_R: editor_key(STBTE_tool_rectangle ); break;
|
||||||
|
case SDL_SCANCODE_I: editor_key(STBTE_tool_eyedropper); break;
|
||||||
|
case SDL_SCANCODE_L: editor_key(STBTE_tool_link); break;
|
||||||
|
case SDL_SCANCODE_G: editor_key(STBTE_act_toggle_grid); break;
|
||||||
|
}
|
||||||
|
if ((e->key.keysym.mod & KMOD_CTRL) && !(e->key.keysym.mod & ~KMOD_CTRL)) {
|
||||||
|
switch (s) {
|
||||||
|
case SDL_SCANCODE_X: editor_key(STBTE_act_cut ); break;
|
||||||
|
case SDL_SCANCODE_C: editor_key(STBTE_act_copy ); break;
|
||||||
|
case SDL_SCANCODE_V: editor_key(STBTE_act_paste); break;
|
||||||
|
case SDL_SCANCODE_Z: editor_key(STBTE_act_undo ); break;
|
||||||
|
case SDL_SCANCODE_Y: editor_key(STBTE_act_redo ); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SDL_KEYUP: {
|
||||||
|
int k = e->key.keysym.sym;
|
||||||
|
int s = e->key.keysym.scancode;
|
||||||
|
if (s == SDL_SCANCODE_D) active_control_clear(0);
|
||||||
|
if (s == SDL_SCANCODE_A) active_control_clear(1);
|
||||||
|
if (s == SDL_SCANCODE_W) active_control_clear(2);
|
||||||
|
if (s == SDL_SCANCODE_S) active_control_clear(3);
|
||||||
|
if (k == SDLK_SPACE) active_control_clear(4);
|
||||||
|
if (s == SDL_SCANCODE_LCTRL) active_control_clear(5);
|
||||||
|
if (s == SDL_SCANCODE_S) active_control_clear(6);
|
||||||
|
if (s == SDL_SCANCODE_D) active_control_clear(7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_GLContext *context;
|
||||||
|
|
||||||
|
static float getTimestep(float minimum_time)
|
||||||
|
{
|
||||||
|
float elapsedTime;
|
||||||
|
double thisTime;
|
||||||
|
static double lastTime = -1;
|
||||||
|
|
||||||
|
if (lastTime == -1)
|
||||||
|
lastTime = SDL_GetTicks() / 1000.0 - minimum_time;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
thisTime = SDL_GetTicks() / 1000.0;
|
||||||
|
elapsedTime = (float) (thisTime - lastTime);
|
||||||
|
if (elapsedTime >= minimum_time) {
|
||||||
|
lastTime = thisTime;
|
||||||
|
return elapsedTime;
|
||||||
|
}
|
||||||
|
// @TODO: compute correct delay
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIENTRY gl_debug(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *param)
|
||||||
|
{
|
||||||
|
ods("%s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_synchronous_debug;
|
||||||
|
void enable_synchronous(void)
|
||||||
|
{
|
||||||
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||||
|
is_synchronous_debug = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare_threads(void);
|
||||||
|
|
||||||
|
//void stbwingraph_main(void)
|
||||||
|
int SDL_main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
|
||||||
|
prepare_threads();
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||||
|
|
||||||
|
#ifdef GL_DEBUG
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||||
|
|
||||||
|
screen_x = 1920;
|
||||||
|
screen_y = 1080;
|
||||||
|
|
||||||
|
window = SDL_CreateWindow("caveview", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
screen_x, screen_y,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
|
||||||
|
);
|
||||||
|
if (!window) error("Couldn't create window");
|
||||||
|
|
||||||
|
context = SDL_GL_CreateContext(window);
|
||||||
|
if (!context) error("Couldn't create context");
|
||||||
|
|
||||||
|
SDL_GL_MakeCurrent(window, context); // is this true by default?
|
||||||
|
|
||||||
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
||||||
|
// work around broken behavior in VC6 debugging
|
||||||
|
if (IsDebuggerPresent())
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stbgl_initExtensions();
|
||||||
|
|
||||||
|
#ifdef GL_DEBUG
|
||||||
|
if (glDebugMessageCallbackARB) {
|
||||||
|
glDebugMessageCallbackARB(gl_debug, NULL);
|
||||||
|
|
||||||
|
enable_synchronous();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_GL_SetSwapInterval(1);
|
||||||
|
|
||||||
|
render_init();
|
||||||
|
mesh_init();
|
||||||
|
world_init();
|
||||||
|
|
||||||
|
initialized = 1;
|
||||||
|
|
||||||
|
while (!quit) {
|
||||||
|
SDL_Event e;
|
||||||
|
while (SDL_PollEvent(&e))
|
||||||
|
process_event(&e);
|
||||||
|
|
||||||
|
loopmode(getTimestep(0.0166f/8), 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
933
external/stb/stb/tests/caveview/cave_mesher.c
vendored
Normal file
@ -0,0 +1,933 @@
|
|||||||
|
// This file takes minecraft chunks (decoded by cave_parse) and
|
||||||
|
// uses stb_voxel_render to turn them into vertex buffers.
|
||||||
|
|
||||||
|
#define STB_GLEXT_DECLARE "glext_list.h"
|
||||||
|
#include "stb_gl.h"
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "stb_glprog.h"
|
||||||
|
|
||||||
|
#include "caveview.h"
|
||||||
|
#include "cave_parse.h"
|
||||||
|
#include "stb.h"
|
||||||
|
#include "sdl.h"
|
||||||
|
#include "sdl_thread.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
//#define VHEIGHT_TEST
|
||||||
|
//#define STBVOX_OPTIMIZED_VHEIGHT
|
||||||
|
|
||||||
|
#define STBVOX_CONFIG_MODE 1
|
||||||
|
#define STBVOX_CONFIG_OPENGL_MODELVIEW
|
||||||
|
#define STBVOX_CONFIG_PREFER_TEXBUFFER
|
||||||
|
//#define STBVOX_CONFIG_LIGHTING_SIMPLE
|
||||||
|
#define STBVOX_CONFIG_FOG_SMOOTHSTEP
|
||||||
|
//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA // this doesn't work properly alpha test without next #define
|
||||||
|
//#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better
|
||||||
|
//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
|
||||||
|
#define STBVOX_CONFIG_DISABLE_TEX2
|
||||||
|
//#define STBVOX_CONFIG_DOWN_TEXLERP_PACKED
|
||||||
|
#define STBVOX_CONFIG_ROTATION_IN_LIGHTING
|
||||||
|
|
||||||
|
#define STB_VOXEL_RENDER_IMPLEMENTATION
|
||||||
|
#include "stb_voxel_render.h"
|
||||||
|
|
||||||
|
extern void ods(char *fmt, ...);
|
||||||
|
|
||||||
|
//#define FANCY_LEAVES // nearly 2x the triangles when enabled (if underground is filled)
|
||||||
|
#define FAST_CHUNK
|
||||||
|
#define IN_PLACE
|
||||||
|
|
||||||
|
#define SKIP_TERRAIN 0
|
||||||
|
//#define SKIP_TERRAIN 48 // use to avoid building underground stuff
|
||||||
|
// allows you to see what perf would be like if underground was efficiently culled,
|
||||||
|
// or if you were making a game without underground
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
C_empty,
|
||||||
|
C_solid,
|
||||||
|
C_trans,
|
||||||
|
C_cross,
|
||||||
|
C_water,
|
||||||
|
C_slab,
|
||||||
|
C_stair,
|
||||||
|
C_force,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char geom_map[] =
|
||||||
|
{
|
||||||
|
STBVOX_GEOM_empty,
|
||||||
|
STBVOX_GEOM_solid,
|
||||||
|
STBVOX_GEOM_transp,
|
||||||
|
STBVOX_GEOM_crossed_pair,
|
||||||
|
STBVOX_GEOM_solid,
|
||||||
|
STBVOX_GEOM_slab_lower,
|
||||||
|
STBVOX_GEOM_floor_slope_north_is_top,
|
||||||
|
STBVOX_GEOM_force,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char minecraft_info[256][7] =
|
||||||
|
{
|
||||||
|
{ C_empty, 0,0,0,0,0,0 },
|
||||||
|
{ C_solid, 1,1,1,1,1,1 },
|
||||||
|
{ C_solid, 3,3,3,3,40,2 },
|
||||||
|
{ C_solid, 2,2,2,2,2,2 },
|
||||||
|
{ C_solid, 16,16,16,16,16,16 },
|
||||||
|
{ C_solid, 4,4,4,4,4,4 },
|
||||||
|
{ C_cross, 15,15,15,15 },
|
||||||
|
{ C_solid, 17,17,17,17,17,17 },
|
||||||
|
|
||||||
|
// 8
|
||||||
|
{ C_water, 223,223,223,223,223,223 },
|
||||||
|
{ C_water, 223,223,223,223,223,223 },
|
||||||
|
{ C_solid, 255,255,255,255,255,255 },
|
||||||
|
{ C_solid, 255,255,255,255,255,255 },
|
||||||
|
{ C_solid, 18,18,18,18,18,18 },
|
||||||
|
{ C_solid, 19,19,19,19,19,19 },
|
||||||
|
{ C_solid, 32,32,32,32,32,32 },
|
||||||
|
{ C_solid, 33,33,33,33,33,33 },
|
||||||
|
|
||||||
|
// 16
|
||||||
|
{ C_solid, 34,34,34,34,34,34 },
|
||||||
|
{ C_solid, 20,20,20,20,21,21 },
|
||||||
|
#ifdef FANCY_LEAVES
|
||||||
|
{ C_force, 52,52,52,52,52,52 }, // leaves
|
||||||
|
#else
|
||||||
|
{ C_solid, 53,53,53,53,53,53 }, // leaves
|
||||||
|
#endif
|
||||||
|
{ C_solid, 24,24,24,24,24,24 },
|
||||||
|
{ C_trans, 49,49,49,49,49,49 }, // glass
|
||||||
|
{ C_solid, 160,160,160,160,160,160 },
|
||||||
|
{ C_solid, 144,144,144,144,144,144 },
|
||||||
|
{ C_solid, 46,45,45,45,62,62 },
|
||||||
|
|
||||||
|
// 24
|
||||||
|
{ C_solid, 192,192,192,192, 176,176 },
|
||||||
|
{ C_solid, 74,74,74,74,74,74 },
|
||||||
|
{ C_empty }, // bed
|
||||||
|
{ C_empty }, // powered rail
|
||||||
|
{ C_empty }, // detector rail
|
||||||
|
{ C_solid, 106,108,109,108,108,108 },
|
||||||
|
{ C_empty }, // cobweb=11
|
||||||
|
{ C_cross, 39,39,39,39 },
|
||||||
|
|
||||||
|
// 32
|
||||||
|
{ C_cross, 55,55,55,55,0,0 },
|
||||||
|
{ C_solid, 107,108,109,108,108,108 },
|
||||||
|
{ C_empty }, // piston head
|
||||||
|
{ C_solid, 64,64,64,64,64,64 }, // various colors
|
||||||
|
{ C_empty }, // unused
|
||||||
|
{ C_cross, 13,13,13,13,0,0 },
|
||||||
|
{ C_cross, 12,12,12,12,0,0 },
|
||||||
|
{ C_cross, 29,29,29,29,0,0 },
|
||||||
|
|
||||||
|
// 40
|
||||||
|
{ C_cross, 28,28,28,28,0,0 },
|
||||||
|
{ C_solid, 23,23,23,23,23,23 },
|
||||||
|
{ C_solid, 22,22,22,22,22,22 },
|
||||||
|
{ C_solid, 5,5,5,5,6,6, },
|
||||||
|
{ C_slab , 5,5,5,5,6,6, },
|
||||||
|
{ C_solid, 7,7,7,7,7,7, },
|
||||||
|
{ C_solid, 8,8,8,8,9,10 },
|
||||||
|
{ C_solid, 35,35,35,35,4,4, },
|
||||||
|
|
||||||
|
// 48
|
||||||
|
//{ C_solid, 36,36,36,36,36,36 },
|
||||||
|
{ C_force, 36,36,36,36,36,36 },
|
||||||
|
{ C_solid, 37,37,37,37,37,37 },
|
||||||
|
{ C_cross, 80,80,80,80,80,80 }, // torch
|
||||||
|
{ C_empty }, // fire
|
||||||
|
{ C_trans, 65,65,65,65,65,65 },
|
||||||
|
{ C_stair, 4,4,4,4,4,4 },
|
||||||
|
{ C_solid, 26,26,26,27,25,25 },
|
||||||
|
{ C_empty }, // redstone
|
||||||
|
|
||||||
|
// 56
|
||||||
|
{ C_solid, 50,50,50,50,50,50 },
|
||||||
|
//{ C_force, 50,50,50,50,50,50 },
|
||||||
|
{ C_solid, 26,26,26,26,26,26 },
|
||||||
|
{ C_solid, 60,59,59,59,43,43 },
|
||||||
|
{ C_cross, 95,95,95,95 },
|
||||||
|
{ C_solid, 2,2,2,2,86,2 },
|
||||||
|
{ C_solid, 44,45,45,45,62,62 },
|
||||||
|
{ C_solid, 61,45,45,45,62,62 },
|
||||||
|
{ C_empty }, // sign
|
||||||
|
|
||||||
|
// 64
|
||||||
|
{ C_empty }, // door
|
||||||
|
{ C_empty }, // ladder
|
||||||
|
{ C_empty }, // rail
|
||||||
|
{ C_stair, 16,16,16,16,16,16 }, // cobblestone stairs
|
||||||
|
{ C_empty }, // sign
|
||||||
|
{ C_empty }, // lever
|
||||||
|
{ C_empty }, // stone pressure plate
|
||||||
|
{ C_empty }, // iron door
|
||||||
|
|
||||||
|
// 72
|
||||||
|
{ C_empty }, // wooden pressure
|
||||||
|
{ C_solid, 51,51,51,51,51,51 },
|
||||||
|
{ C_solid, 51,51,51,51,51,51 },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty }, // snow on block below, do as half slab?
|
||||||
|
{ C_solid, 67,67,67,67,67,67 },
|
||||||
|
|
||||||
|
// 80
|
||||||
|
{ C_solid, 66,66,66,66,66,66 },
|
||||||
|
{ C_solid, 70,70,70,70,69,71 },
|
||||||
|
{ C_solid, 72,72,72,72,72,72 },
|
||||||
|
{ C_cross, 73,73,73,73,73,73 },
|
||||||
|
{ C_solid, 74,74,74,74,75,74 },
|
||||||
|
{ C_empty }, // fence
|
||||||
|
{ C_solid,119,118,118,118,102,102 },
|
||||||
|
{ C_solid,103,103,103,103,103,103 },
|
||||||
|
|
||||||
|
// 88
|
||||||
|
{ C_solid, 104,104,104,104,104,104 },
|
||||||
|
{ C_solid, 105,105,105,105,105,105 },
|
||||||
|
{ C_solid, 167,167,167,167,167,167 },
|
||||||
|
{ C_solid, 120,118,118,118,102,102 },
|
||||||
|
{ C_empty }, // cake
|
||||||
|
{ C_empty }, // repeater
|
||||||
|
{ C_empty }, // repeater
|
||||||
|
{ C_solid, 49,49,49,49,49,49 }, // colored glass
|
||||||
|
|
||||||
|
// 96
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_solid, 54,54,54,54,54,54 },
|
||||||
|
{ C_solid, 125,125,125,125,125,125 },
|
||||||
|
{ C_solid, 126,126,126,126,126,126 },
|
||||||
|
{ C_empty }, // bars
|
||||||
|
{ C_trans, 49,49,49,49,49,49 }, // glass pane
|
||||||
|
{ C_solid, 136,136,136,136,137,137 }, // melon
|
||||||
|
|
||||||
|
// 104
|
||||||
|
{ C_empty }, // pumpkin stem
|
||||||
|
{ C_empty }, // melon stem
|
||||||
|
{ C_empty }, // vines
|
||||||
|
{ C_empty }, // gate
|
||||||
|
{ C_stair, 7,7,7,7,7,7, }, // brick stairs
|
||||||
|
{ C_stair, 54,54,54,54,54,54 }, // stone brick stairs
|
||||||
|
{ C_empty }, // mycelium
|
||||||
|
{ C_empty }, // lily pad
|
||||||
|
|
||||||
|
// 112
|
||||||
|
{ C_solid, 224,224,224,224,224,224 },
|
||||||
|
{ C_empty }, // nether brick fence
|
||||||
|
{ C_stair, 224,224,224,224,224,224 }, // nether brick stairs
|
||||||
|
{ C_empty }, // nether wart
|
||||||
|
{ C_solid, 182,182,182,182,166,183 },
|
||||||
|
{ C_empty }, // brewing stand
|
||||||
|
{ C_empty }, // cauldron
|
||||||
|
{ C_empty }, // end portal
|
||||||
|
|
||||||
|
// 120
|
||||||
|
{ C_solid, 159,159,159,159,158,158 },
|
||||||
|
{ C_solid, 175,175,175,175,175,175 },
|
||||||
|
{ C_empty }, // dragon egg
|
||||||
|
{ C_solid, 211,211,211,211,211,211 },
|
||||||
|
{ C_solid, 212,212,212,212,212,212 },
|
||||||
|
{ C_solid, 4,4,4,4,4,4, }, // wood double-slab
|
||||||
|
{ C_slab , 4,4,4,4,4,4, }, // wood slab
|
||||||
|
{ C_empty }, // cocoa
|
||||||
|
|
||||||
|
// 128
|
||||||
|
{ C_solid, 192,192,192,192,176,176 }, // sandstone stairs
|
||||||
|
{ C_solid, 32,32,32,32,32,32 }, // emerald ore
|
||||||
|
{ C_solid, 26,26,26,27,25,25 }, // ender chest
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_solid, 23,23,23,23,23,23 }, // emerald block
|
||||||
|
{ C_solid, 198,198,198,198,198,198 }, // spruce stairs
|
||||||
|
{ C_solid, 214,214,214,214,214,214 }, // birch stairs
|
||||||
|
|
||||||
|
// 136
|
||||||
|
{ C_stair, 199,199,199,199,199,199 }, // jungle stairs
|
||||||
|
{ C_empty }, // command block
|
||||||
|
{ C_empty }, // beacon
|
||||||
|
{ C_slab, 16,16,16,16,16,16 }, // cobblestone wall
|
||||||
|
{ C_empty }, // flower pot
|
||||||
|
{ C_empty }, // carrot
|
||||||
|
{ C_empty }, // potatoes
|
||||||
|
{ C_empty }, // wooden button
|
||||||
|
|
||||||
|
// 144
|
||||||
|
{ C_empty }, // mob head
|
||||||
|
{ C_empty }, // anvil
|
||||||
|
{ C_solid, 26,26,26,27,25,25 }, // trapped chest
|
||||||
|
{ C_empty }, // weighted pressure plate light
|
||||||
|
{ C_empty }, // weighted pressure plat eheavy
|
||||||
|
{ C_empty }, // comparator inactive
|
||||||
|
{ C_empty }, // comparator active
|
||||||
|
{ C_empty }, // daylight sensor
|
||||||
|
|
||||||
|
// 152
|
||||||
|
{ C_solid, 135,135,135,135,135,135 }, // redstone block
|
||||||
|
{ C_solid, 0,0,0,0,0,0, }, // nether quartz ore
|
||||||
|
{ C_empty }, // hopper
|
||||||
|
{ C_solid, 22,22,22,22,22,22 }, // quartz block
|
||||||
|
{ C_stair, 22,22,22,22,22,22 }, // quartz stairs
|
||||||
|
{ C_empty }, // activator rail
|
||||||
|
{ C_solid, 46,45,45,45,62,62 }, // dropper
|
||||||
|
{ C_solid, 72,72,72,72,72,72 }, // stained clay
|
||||||
|
|
||||||
|
// 160
|
||||||
|
{ C_trans, 49,49,49,49,49,49 }, // stained glass pane
|
||||||
|
#ifdef FANCY_LEAVES
|
||||||
|
{ C_force, 52,52,52,52,52,52 }, // leaves
|
||||||
|
#else
|
||||||
|
{ C_solid, 53,53,53,53,53,53 }, // acacia leaves
|
||||||
|
#endif
|
||||||
|
{ C_solid, 20,20,20,20,21,21 }, // acacia tree
|
||||||
|
{ C_solid, 199,199,199,199,199,199 }, // acacia wood stairs
|
||||||
|
{ C_solid, 198,198,198,198,198,198 }, // dark oak stairs
|
||||||
|
{ C_solid, 146,146,146,146,146,146 }, // slime block
|
||||||
|
|
||||||
|
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
|
||||||
|
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
|
||||||
|
|
||||||
|
// 168
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_solid, 72,72,72,72,72,72 }, // hardened clay
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
|
||||||
|
// 176
|
||||||
|
{ C_empty },
|
||||||
|
{ C_empty },
|
||||||
|
{ C_solid, 176,176,176,176,176,176 }, // red sandstone
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char minecraft_tex1_for_blocktype[256][6];
|
||||||
|
unsigned char effective_blocktype[256];
|
||||||
|
unsigned char minecraft_color_for_blocktype[256][6];
|
||||||
|
unsigned char minecraft_geom_for_blocktype[256];
|
||||||
|
|
||||||
|
uint8 build_buffer[BUILD_BUFFER_SIZE];
|
||||||
|
uint8 face_buffer[FACE_BUFFER_SIZE];
|
||||||
|
|
||||||
|
//GLuint vbuf, fbuf, fbuf_tex;
|
||||||
|
|
||||||
|
//unsigned char tex1_for_blocktype[256][6];
|
||||||
|
|
||||||
|
//unsigned char blocktype[34][34][257];
|
||||||
|
//unsigned char lighting[34][34][257];
|
||||||
|
|
||||||
|
// a superchunk is 64x64x256, with the border blocks computed as well,
|
||||||
|
// which means we need 4x4 chunks plus 16 border chunks plus 4 corner chunks
|
||||||
|
|
||||||
|
#define SUPERCHUNK_X 4
|
||||||
|
#define SUPERCHUNK_Y 4
|
||||||
|
|
||||||
|
unsigned char remap_data[16][16];
|
||||||
|
unsigned char remap[256];
|
||||||
|
unsigned char rotate_data[4] = { 1,3,2,0 };
|
||||||
|
|
||||||
|
void convert_fastchunk_inplace(fast_chunk *fc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int num_blocks=0, step=0;
|
||||||
|
unsigned char rot[4096];
|
||||||
|
#ifndef IN_PLACE
|
||||||
|
unsigned char *storage;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(rot, 0, 4096);
|
||||||
|
|
||||||
|
for (i=0; i < 16; ++i)
|
||||||
|
num_blocks += fc->blockdata[i] != NULL;
|
||||||
|
|
||||||
|
#ifndef IN_PLACE
|
||||||
|
storage = malloc(16*16*16*2 * num_blocks);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i=0; i < 16; ++i) {
|
||||||
|
if (fc->blockdata[i]) {
|
||||||
|
int o=0;
|
||||||
|
unsigned char *bd,*dd,*lt,*sky;
|
||||||
|
unsigned char *out, *outb;
|
||||||
|
|
||||||
|
// this ordering allows us to determine which data we can safely overwrite for in-place processing
|
||||||
|
bd = fc->blockdata[i];
|
||||||
|
dd = fc->data[i];
|
||||||
|
lt = fc->light[i];
|
||||||
|
sky = fc->skylight[i];
|
||||||
|
|
||||||
|
#ifdef IN_PLACE
|
||||||
|
out = bd;
|
||||||
|
#else
|
||||||
|
out = storage + 16*16*16*2*step;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// bd is written in place, but also reads from dd
|
||||||
|
for (o=0; o < 16*16*16/2; o += 1) {
|
||||||
|
unsigned char v1,v2;
|
||||||
|
unsigned char d = dd[o];
|
||||||
|
v1 = bd[o*2+0];
|
||||||
|
v2 = bd[o*2+1];
|
||||||
|
|
||||||
|
if (remap[v1])
|
||||||
|
{
|
||||||
|
//unsigned char d = bd[o] & 15;
|
||||||
|
v1 = remap_data[remap[v1]][d&15];
|
||||||
|
rot[o*2+0] = rotate_data[d&3];
|
||||||
|
} else
|
||||||
|
v1 = effective_blocktype[v1];
|
||||||
|
|
||||||
|
if (remap[v2])
|
||||||
|
{
|
||||||
|
//unsigned char d = bd[o] >> 4;
|
||||||
|
v2 = remap_data[remap[v2]][d>>4];
|
||||||
|
rot[o*2+1] = rotate_data[(d>>4)&3];
|
||||||
|
} else
|
||||||
|
v2 = effective_blocktype[v2];
|
||||||
|
|
||||||
|
out[o*2+0] = v1;
|
||||||
|
out[o*2+1] = v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this reads from lt & sky
|
||||||
|
#ifndef IN_PLACE
|
||||||
|
outb = out + 16*16*16;
|
||||||
|
++step;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MC used to write in this order and it makes it possible to compute in-place
|
||||||
|
if (dd < sky && sky < lt) {
|
||||||
|
// @TODO go this path always if !IN_PLACE
|
||||||
|
#ifdef IN_PLACE
|
||||||
|
outb = dd;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (o=0; o < 16*16*16/2; ++o) {
|
||||||
|
int bright;
|
||||||
|
bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16;
|
||||||
|
if (bright > 255) bright = 255;
|
||||||
|
if (bright < 32) bright = 32;
|
||||||
|
outb[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3));
|
||||||
|
|
||||||
|
bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16;
|
||||||
|
if (bright > 255) bright = 255;
|
||||||
|
if (bright < 32) bright = 32;
|
||||||
|
outb[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// @TODO: if blocktype is in between others, this breaks; need to find which side has two pointers, and use that
|
||||||
|
// overwrite rot[] array, then copy out
|
||||||
|
#ifdef IN_PLACE
|
||||||
|
outb = (dd < sky) ? dd : sky;
|
||||||
|
if (lt < outb) lt = outb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (o=0; o < 16*16*16/2; ++o) {
|
||||||
|
int bright;
|
||||||
|
bright = (lt[o]&15)*12 + 15 + (sky[o]&15)*16;
|
||||||
|
if (bright > 255) bright = 255;
|
||||||
|
if (bright < 32) bright = 32;
|
||||||
|
rot[o*2+0] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+0]&3));
|
||||||
|
|
||||||
|
bright = (lt[o]>>4)*12 + 15 + (sky[o]>>4)*16;
|
||||||
|
if (bright > 255) bright = 255;
|
||||||
|
if (bright < 32) bright = 32;
|
||||||
|
rot[o*2+1] = STBVOX_MAKE_LIGHTING_EXT((unsigned char) bright, (rot[o*2+1]&3));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(outb, rot, 4096);
|
||||||
|
fc->data[i] = outb;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef IN_PLACE
|
||||||
|
fc->blockdata[i] = out;
|
||||||
|
fc->data[i] = outb;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef IN_PLACE
|
||||||
|
free(fc->pointer_to_free);
|
||||||
|
fc->pointer_to_free = storage;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_converted_fastchunk(fast_chunk *fc, int x, int y, int segment, uint8 *sv_blocktype, uint8 *sv_lighting)
|
||||||
|
{
|
||||||
|
int z;
|
||||||
|
assert(fc == NULL || (fc->refcount > 0 && fc->refcount < 64));
|
||||||
|
if (fc == NULL || fc->blockdata[segment] == NULL) {
|
||||||
|
for (z=0; z < 16; ++z) {
|
||||||
|
sv_blocktype[z] = C_empty;
|
||||||
|
sv_lighting[z] = 255;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsigned char *block = fc->blockdata[segment];
|
||||||
|
unsigned char *data = fc->data[segment];
|
||||||
|
y = 15-y;
|
||||||
|
for (z=0; z < 16; ++z) {
|
||||||
|
sv_blocktype[z] = block[z*256 + y*16 + x];
|
||||||
|
sv_lighting [z] = data [z*256 + y*16 + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define CHUNK_CACHE 64
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int valid;
|
||||||
|
int chunk_x, chunk_y;
|
||||||
|
fast_chunk *fc;
|
||||||
|
} cached_converted_chunk;
|
||||||
|
|
||||||
|
cached_converted_chunk chunk_cache[CHUNK_CACHE][CHUNK_CACHE];
|
||||||
|
int cache_size = CHUNK_CACHE;
|
||||||
|
|
||||||
|
void reset_cache_size(int size)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
for (j=size; j < cache_size; ++j) {
|
||||||
|
for (i=size; i < cache_size; ++i) {
|
||||||
|
cached_converted_chunk *ccc = &chunk_cache[j][i];
|
||||||
|
if (ccc->valid) {
|
||||||
|
if (ccc->fc) {
|
||||||
|
free(ccc->fc->pointer_to_free);
|
||||||
|
free(ccc->fc);
|
||||||
|
ccc->fc = NULL;
|
||||||
|
}
|
||||||
|
ccc->valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this must be called inside mutex
|
||||||
|
void deref_fastchunk(fast_chunk *fc)
|
||||||
|
{
|
||||||
|
if (fc) {
|
||||||
|
assert(fc->refcount > 0);
|
||||||
|
--fc->refcount;
|
||||||
|
if (fc->refcount == 0) {
|
||||||
|
free(fc->pointer_to_free);
|
||||||
|
free(fc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_mutex * chunk_cache_mutex;
|
||||||
|
SDL_mutex * chunk_get_mutex;
|
||||||
|
|
||||||
|
void lock_chunk_get_mutex(void)
|
||||||
|
{
|
||||||
|
SDL_LockMutex(chunk_get_mutex);
|
||||||
|
}
|
||||||
|
void unlock_chunk_get_mutex(void)
|
||||||
|
{
|
||||||
|
SDL_UnlockMutex(chunk_get_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y)
|
||||||
|
{
|
||||||
|
int slot_x = (chunk_x & (cache_size-1));
|
||||||
|
int slot_y = (chunk_y & (cache_size-1));
|
||||||
|
fast_chunk *fc;
|
||||||
|
cached_converted_chunk *ccc;
|
||||||
|
SDL_LockMutex(chunk_cache_mutex);
|
||||||
|
ccc = &chunk_cache[slot_y][slot_x];
|
||||||
|
if (ccc->valid) {
|
||||||
|
if (ccc->chunk_x == chunk_x && ccc->chunk_y == chunk_y) {
|
||||||
|
fast_chunk *fc = ccc->fc;
|
||||||
|
if (fc)
|
||||||
|
++fc->refcount;
|
||||||
|
SDL_UnlockMutex(chunk_cache_mutex);
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
if (ccc->fc) {
|
||||||
|
deref_fastchunk(ccc->fc);
|
||||||
|
ccc->fc = NULL;
|
||||||
|
ccc->valid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(chunk_cache_mutex);
|
||||||
|
|
||||||
|
fc = get_decoded_fastchunk_uncached(chunk_x, -chunk_y);
|
||||||
|
if (fc)
|
||||||
|
convert_fastchunk_inplace(fc);
|
||||||
|
|
||||||
|
SDL_LockMutex(chunk_cache_mutex);
|
||||||
|
// another thread might have updated it, so before we overwrite it...
|
||||||
|
if (ccc->fc) {
|
||||||
|
deref_fastchunk(ccc->fc);
|
||||||
|
ccc->fc = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fc)
|
||||||
|
fc->refcount = 1; // 1 in the cache
|
||||||
|
|
||||||
|
ccc->chunk_x = chunk_x;
|
||||||
|
ccc->chunk_y = chunk_y;
|
||||||
|
ccc->valid = 1;
|
||||||
|
if (fc)
|
||||||
|
++fc->refcount;
|
||||||
|
ccc->fc = fc;
|
||||||
|
SDL_UnlockMutex(chunk_cache_mutex);
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_map_segment_for_superchunk_preconvert(int chunk_x, int chunk_y, int segment, fast_chunk *fc_table[4][4], uint8 sv_blocktype[34][34][18], uint8 sv_lighting[34][34][18])
|
||||||
|
{
|
||||||
|
int a,b;
|
||||||
|
assert((chunk_x & 1) == 0);
|
||||||
|
assert((chunk_y & 1) == 0);
|
||||||
|
for (b=-1; b < 3; ++b) {
|
||||||
|
for (a=-1; a < 3; ++a) {
|
||||||
|
int xo = a*16+1;
|
||||||
|
int yo = b*16+1;
|
||||||
|
int x,y;
|
||||||
|
fast_chunk *fc = fc_table[b+1][a+1];
|
||||||
|
for (y=0; y < 16; ++y)
|
||||||
|
for (x=0; x < 16; ++x)
|
||||||
|
if (xo+x >= 0 && xo+x < 34 && yo+y >= 0 && yo+y < 34)
|
||||||
|
make_converted_fastchunk(fc,x,y, segment, sv_blocktype[xo+x][yo+y], sv_lighting[xo+x][yo+y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// build 1 mesh covering 2x2 chunks
|
||||||
|
void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm)
|
||||||
|
{
|
||||||
|
int a,b,z;
|
||||||
|
stbvox_input_description *map;
|
||||||
|
|
||||||
|
#ifdef VHEIGHT_TEST
|
||||||
|
unsigned char vheight[34][34][18];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBVOX_CONFIG_DISABLE_TEX2
|
||||||
|
unsigned char tex2_choice[34][34][18];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert((chunk_x & 1) == 0);
|
||||||
|
assert((chunk_y & 1) == 0);
|
||||||
|
|
||||||
|
rm->cx = chunk_x;
|
||||||
|
rm->cy = chunk_y;
|
||||||
|
|
||||||
|
stbvox_set_input_stride(&rm->mm, 34*18, 18);
|
||||||
|
|
||||||
|
assert(rm->mm.input.geometry == NULL);
|
||||||
|
|
||||||
|
map = stbvox_get_input_description(&rm->mm);
|
||||||
|
map->block_tex1_face = minecraft_tex1_for_blocktype;
|
||||||
|
map->block_color_face = minecraft_color_for_blocktype;
|
||||||
|
map->block_geometry = minecraft_geom_for_blocktype;
|
||||||
|
|
||||||
|
stbvox_reset_buffers(&rm->mm);
|
||||||
|
stbvox_set_buffer(&rm->mm, 0, 0, rm->build_buffer, BUILD_BUFFER_SIZE);
|
||||||
|
stbvox_set_buffer(&rm->mm, 0, 1, rm->face_buffer , FACE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
map->blocktype = &rm->sv_blocktype[1][1][1]; // this is (0,0,0), but we need to be able to query off the edges
|
||||||
|
map->lighting = &rm->sv_lighting[1][1][1];
|
||||||
|
|
||||||
|
// fill in the top two rows of the buffer
|
||||||
|
for (a=0; a < 34; ++a) {
|
||||||
|
for (b=0; b < 34; ++b) {
|
||||||
|
rm->sv_blocktype[a][b][16] = 0;
|
||||||
|
rm->sv_lighting [a][b][16] = 255;
|
||||||
|
rm->sv_blocktype[a][b][17] = 0;
|
||||||
|
rm->sv_lighting [a][b][17] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef STBVOX_CONFIG_DISABLE_TEX2
|
||||||
|
for (a=0; a < 34; ++a) {
|
||||||
|
for (b=0; b < 34; ++b) {
|
||||||
|
int px = chunk_x*16 + a - 1;
|
||||||
|
int py = chunk_y*16 + b - 1;
|
||||||
|
float dist = (float) sqrt(px*px + py*py);
|
||||||
|
float s1 = (float) sin(dist / 16), s2, s3;
|
||||||
|
dist = (float) sqrt((px-80)*(px-80) + (py-50)*(py-50));
|
||||||
|
s2 = (float) sin(dist / 11);
|
||||||
|
for (z=0; z < 18; ++z) {
|
||||||
|
s3 = (float) sin(z * 3.141592 / 8);
|
||||||
|
|
||||||
|
s3 = s1*s2*s3;
|
||||||
|
tex2_choice[a][b][z] = 63 & (int) stb_linear_remap(s3,-1,1, -20,83);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (z=256-16; z >= SKIP_TERRAIN; z -= 16)
|
||||||
|
{
|
||||||
|
int z0 = z;
|
||||||
|
int z1 = z+16;
|
||||||
|
if (z1 == 256) z1 = 255;
|
||||||
|
|
||||||
|
make_map_segment_for_superchunk_preconvert(chunk_x, chunk_y, z >> 4, fc_table, rm->sv_blocktype, rm->sv_lighting);
|
||||||
|
|
||||||
|
map->blocktype = &rm->sv_blocktype[1][1][1-z]; // specify location of 0,0,0 so that accessing z0..z1 gets right data
|
||||||
|
map->lighting = &rm->sv_lighting[1][1][1-z];
|
||||||
|
#ifndef STBVOX_CONFIG_DISABLE_TEX2
|
||||||
|
map->tex2 = &tex2_choice[1][1][1-z];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VHEIGHT_TEST
|
||||||
|
// hacky test of vheight
|
||||||
|
for (a=0; a < 34; ++a) {
|
||||||
|
for (b=0; b < 34; ++b) {
|
||||||
|
int c;
|
||||||
|
for (c=0; c < 17; ++c) {
|
||||||
|
if (rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c+1] == 0) {
|
||||||
|
// topmost block
|
||||||
|
vheight[a][b][c] = rand() & 255;
|
||||||
|
rm->sv_blocktype[a][b][c] = 168;
|
||||||
|
} else if (c > 0 && rm->sv_blocktype[a][b][c] != 0 && rm->sv_blocktype[a][b][c-1] == 0) {
|
||||||
|
// bottommost block
|
||||||
|
vheight[a][b][c] = ((rand() % 3) << 6) + ((rand() % 3) << 4) + ((rand() % 3) << 2) + (rand() % 3);
|
||||||
|
rm->sv_blocktype[a][b][c] = 169;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vheight[a][b][c] = STBVOX_MAKE_VHEIGHT(2,2,2,2); // flat top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map->vheight = &vheight[1][1][1-z];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
stbvox_set_input_range(&rm->mm, 0,0,z0, 32,32,z1);
|
||||||
|
stbvox_set_default_mesh(&rm->mm, 0);
|
||||||
|
stbvox_make_mesh(&rm->mm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the bottom two rows of data up to the top
|
||||||
|
for (a=0; a < 34; ++a) {
|
||||||
|
for (b=0; b < 34; ++b) {
|
||||||
|
rm->sv_blocktype[a][b][16] = rm->sv_blocktype[a][b][0];
|
||||||
|
rm->sv_blocktype[a][b][17] = rm->sv_blocktype[a][b][1];
|
||||||
|
rm->sv_lighting [a][b][16] = rm->sv_lighting [a][b][0];
|
||||||
|
rm->sv_lighting [a][b][17] = rm->sv_lighting [a][b][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbvox_set_mesh_coordinates(&rm->mm, chunk_x*16, chunk_y*16, 0);
|
||||||
|
stbvox_get_transform(&rm->mm, rm->transform);
|
||||||
|
|
||||||
|
stbvox_set_input_range(&rm->mm, 0,0,0, 32,32,255);
|
||||||
|
stbvox_get_bounds(&rm->mm, rm->bounds);
|
||||||
|
|
||||||
|
rm->num_quads = stbvox_get_quad_count(&rm->mm, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int next_blocktype = 255;
|
||||||
|
|
||||||
|
unsigned char mc_rot[4] = { 1,3,2,0 };
|
||||||
|
|
||||||
|
// create blocktypes with rotation baked into type...
|
||||||
|
// @TODO we no longer need this now that we store rotations
|
||||||
|
// in lighting
|
||||||
|
void build_stair_rotations(int blocktype, unsigned char *map)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// use the existing block type for floor stairs; allocate a new type for ceil stairs
|
||||||
|
for (i=0; i < 6; ++i) {
|
||||||
|
minecraft_color_for_blocktype[next_blocktype][i] = minecraft_color_for_blocktype[blocktype][i];
|
||||||
|
minecraft_tex1_for_blocktype [next_blocktype][i] = minecraft_tex1_for_blocktype [blocktype][i];
|
||||||
|
}
|
||||||
|
minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_ceil_slope_north_is_bottom, 0, 0);
|
||||||
|
minecraft_geom_for_blocktype[ blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_floor_slope_north_is_top, 0, 0);
|
||||||
|
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
map[0+i+8] = map[0+i] = blocktype;
|
||||||
|
map[4+i+8] = map[4+i] = next_blocktype;
|
||||||
|
}
|
||||||
|
--next_blocktype;
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_wool_variations(int bt, unsigned char *map)
|
||||||
|
{
|
||||||
|
int i,k;
|
||||||
|
unsigned char tex[16] = { 64, 210, 194, 178, 162, 146, 130, 114, 225, 209, 193, 177, 161, 145, 129, 113 };
|
||||||
|
for (i=0; i < 16; ++i) {
|
||||||
|
if (i == 0)
|
||||||
|
map[i] = bt;
|
||||||
|
else {
|
||||||
|
map[i] = next_blocktype;
|
||||||
|
for (k=0; k < 6; ++k) {
|
||||||
|
minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i];
|
||||||
|
}
|
||||||
|
minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt];
|
||||||
|
--next_blocktype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_wood_variations(int bt, unsigned char *map)
|
||||||
|
{
|
||||||
|
int i,k;
|
||||||
|
unsigned char tex[4] = { 5, 198, 214, 199 };
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
if (i == 0)
|
||||||
|
map[i] = bt;
|
||||||
|
else {
|
||||||
|
map[i] = next_blocktype;
|
||||||
|
for (k=0; k < 6; ++k) {
|
||||||
|
minecraft_tex1_for_blocktype[next_blocktype][k] = tex[i];
|
||||||
|
}
|
||||||
|
minecraft_geom_for_blocktype[next_blocktype] = minecraft_geom_for_blocktype[bt];
|
||||||
|
--next_blocktype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map[i] = map[i-1];
|
||||||
|
++i;
|
||||||
|
for (; i < 16; ++i)
|
||||||
|
map[i] = bt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remap_in_place(int bt, int rm)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
remap[bt] = rm;
|
||||||
|
for (i=0; i < 16; ++i)
|
||||||
|
remap_data[rm][i] = bt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mesh_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
chunk_cache_mutex = SDL_CreateMutex();
|
||||||
|
chunk_get_mutex = SDL_CreateMutex();
|
||||||
|
|
||||||
|
for (i=0; i < 256; ++i) {
|
||||||
|
memcpy(minecraft_tex1_for_blocktype[i], minecraft_info[i]+1, 6);
|
||||||
|
effective_blocktype[i] = (minecraft_info[i][0] == C_empty ? 0 : i);
|
||||||
|
minecraft_geom_for_blocktype[i] = geom_map[minecraft_info[i][0]];
|
||||||
|
}
|
||||||
|
//effective_blocktype[50] = 0; // delete torches
|
||||||
|
|
||||||
|
for (i=0; i < 6*256; ++i) {
|
||||||
|
if (minecraft_tex1_for_blocktype[0][i] == 40)
|
||||||
|
minecraft_color_for_blocktype[0][i] = 38 | 64; // apply to tex1
|
||||||
|
if (minecraft_tex1_for_blocktype[0][i] == 39)
|
||||||
|
minecraft_color_for_blocktype[0][i] = 39 | 64; // apply to tex1
|
||||||
|
if (minecraft_tex1_for_blocktype[0][i] == 105)
|
||||||
|
minecraft_color_for_blocktype[0][i] = 63; // emissive
|
||||||
|
if (minecraft_tex1_for_blocktype[0][i] == 212)
|
||||||
|
minecraft_color_for_blocktype[0][i] = 63; // emissive
|
||||||
|
if (minecraft_tex1_for_blocktype[0][i] == 80)
|
||||||
|
minecraft_color_for_blocktype[0][i] = 63; // emissive
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i < 6; ++i) {
|
||||||
|
minecraft_color_for_blocktype[172][i] = 47 | 64; // apply to tex1
|
||||||
|
minecraft_color_for_blocktype[178][i] = 47 | 64; // apply to tex1
|
||||||
|
minecraft_color_for_blocktype[18][i] = 39 | 64; // green
|
||||||
|
minecraft_color_for_blocktype[161][i] = 37 | 64; // green
|
||||||
|
minecraft_color_for_blocktype[10][i] = 63; // emissive lava
|
||||||
|
minecraft_color_for_blocktype[11][i] = 63; // emissive
|
||||||
|
//minecraft_color_for_blocktype[56][i] = 63; // emissive diamond
|
||||||
|
minecraft_color_for_blocktype[48][i] = 63; // emissive dungeon
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VHEIGHT_TEST
|
||||||
|
effective_blocktype[168] = 168;
|
||||||
|
minecraft_tex1_for_blocktype[168][0] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[168][1] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[168][2] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[168][3] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[168][4] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[168][5] = 1;
|
||||||
|
minecraft_geom_for_blocktype[168] = STBVOX_GEOM_floor_vheight_12;
|
||||||
|
effective_blocktype[169] = 169;
|
||||||
|
minecraft_tex1_for_blocktype[169][0] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[169][1] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[169][2] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[169][3] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[169][4] = 1;
|
||||||
|
minecraft_tex1_for_blocktype[169][5] = 1;
|
||||||
|
minecraft_geom_for_blocktype[169] = STBVOX_GEOM_ceil_vheight_03;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
remap[53] = 1;
|
||||||
|
remap[67] = 2;
|
||||||
|
remap[108] = 3;
|
||||||
|
remap[109] = 4;
|
||||||
|
remap[114] = 5;
|
||||||
|
remap[136] = 6;
|
||||||
|
remap[156] = 7;
|
||||||
|
for (i=0; i < 256; ++i)
|
||||||
|
if (remap[i])
|
||||||
|
build_stair_rotations(i, remap_data[remap[i]]);
|
||||||
|
remap[35] = 8;
|
||||||
|
build_wool_variations(35, remap_data[remap[35]]);
|
||||||
|
remap[5] = 11;
|
||||||
|
build_wood_variations(5, remap_data[remap[5]]);
|
||||||
|
|
||||||
|
// set the remap flags for these so they write the rotation values
|
||||||
|
remap_in_place(54, 9);
|
||||||
|
remap_in_place(146, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timing stats while optimizing the single-threaded builder
|
||||||
|
|
||||||
|
// 32..-32, 32..-32, SKIP_TERRAIN=0, !FANCY_LEAVES on 'mcrealm' data set
|
||||||
|
|
||||||
|
// 6.27s - reblocked to do 16 z at a time instead of 256 (still using 66x66x258), 4 meshes in parallel
|
||||||
|
// 5.96s - reblocked to use FAST_CHUNK (no intermediate data structure)
|
||||||
|
// 5.45s - unknown change, or previous measurement was wrong
|
||||||
|
|
||||||
|
// 6.12s - use preconverted data, not in-place
|
||||||
|
// 5.91s - use preconverted, in-place
|
||||||
|
// 5.34s - preconvert, in-place, avoid dependency chain (suggested by ryg)
|
||||||
|
// 5.34s - preconvert, in-place, avoid dependency chain, use bit-table instead of byte-table
|
||||||
|
// 5.50s - preconvert, in-place, branchless
|
||||||
|
|
||||||
|
// 6.42s - non-preconvert, avoid dependency chain (not an error)
|
||||||
|
// 5.40s - non-preconvert, w/dependency chain (same as earlier)
|
||||||
|
|
||||||
|
// 5.50s - non-FAST_CHUNK, reblocked outer loop for better cache reuse
|
||||||
|
// 4.73s - FAST_CHUNK non-preconvert, reblocked outer loop
|
||||||
|
// 4.25s - preconvert, in-place, reblocked outer loop
|
||||||
|
// 4.18s - preconvert, in-place, unrolled again
|
||||||
|
// 4.10s - 34x34 1 mesh instead of 66x66 and 4 meshes (will make it easier to do multiple threads)
|
||||||
|
|
||||||
|
// 4.83s - building bitmasks but not using them (2 bits per block, one if empty, one if solid)
|
||||||
|
|
||||||
|
// 5.16s - using empty bitmasks to early out
|
||||||
|
// 5.01s - using solid & empty bitmasks to early out - "foo"
|
||||||
|
// 4.64s - empty bitmask only, test 8 at a time, then test geom
|
||||||
|
// 4.72s - empty bitmask only, 8 at a time, then test bits
|
||||||
|
// 4.46s - split bitmask building into three loops (each byte is separate)
|
||||||
|
// 4.42s - further optimize computing bitmask
|
||||||
|
|
||||||
|
// 4.58s - using solid & empty bitmasks to early out, same as "foo" but faster bitmask building
|
||||||
|
// 4.12s - using solid & empty bitmasks to efficiently test neighbors
|
||||||
|
// 4.04s - using 16-bit fetches (not endian-independent)
|
||||||
|
// - note this is first place that beats previous best '4.10s - 34x34 1 mesh'
|
||||||
|
|
||||||
|
// 4.30s - current time with bitmasks disabled again (note was 4.10s earlier)
|
||||||
|
// 3.95s - bitmasks enabled again, no other changes
|
||||||
|
// 4.00s - current time with bitmasks disabled again, no other changes -- wide variation that is time dependent?
|
||||||
|
// (note that most of the numbers listed here are median of 3 values already)
|
||||||
|
// 3.98s - bitmasks enabled
|
||||||
|
|
||||||
|
// Bitmasks removed from the code as not worth the complexity increase
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Raw data for Q&A:
|
||||||
|
//
|
||||||
|
// 26% parsing & loading minecraft files (4/5ths of which is zlib decode)
|
||||||
|
// 39% building mesh from stb input format
|
||||||
|
// 18% converting from minecraft blocks to stb blocks
|
||||||
|
// 9% reordering from minecraft axis order to stb axis order
|
||||||
|
// 7% uploading vertex buffer to OpenGL
|
632
external/stb/stb/tests/caveview/cave_parse.c
vendored
Normal file
@ -0,0 +1,632 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define FAST_CHUNK // disabling this enables the old, slower path that deblocks into a regular form
|
||||||
|
|
||||||
|
#include "cave_parse.h"
|
||||||
|
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "stb.h"
|
||||||
|
|
||||||
|
#define NUM_CHUNKS_PER_REGION 32 // only on one axis
|
||||||
|
#define NUM_CHUNKS_PER_REGION_LOG2 5
|
||||||
|
|
||||||
|
#define NUM_COLUMNS_PER_CHUNK 16
|
||||||
|
#define NUM_COLUMNS_PER_CHUNK_LOG2 4
|
||||||
|
|
||||||
|
uint32 read_uint32_be(FILE *f)
|
||||||
|
{
|
||||||
|
unsigned char data[4];
|
||||||
|
fread(data, 1, 4, f);
|
||||||
|
return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8 *data;
|
||||||
|
size_t len;
|
||||||
|
int x,z; // chunk index
|
||||||
|
int refcount; // for multi-threading
|
||||||
|
} compressed_chunk;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int x,z;
|
||||||
|
uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION];
|
||||||
|
} region;
|
||||||
|
|
||||||
|
size_t cached_compressed=0;
|
||||||
|
|
||||||
|
FILE *last_region;
|
||||||
|
int last_region_x;
|
||||||
|
int last_region_z;
|
||||||
|
int opened=0;
|
||||||
|
|
||||||
|
static void open_file(int reg_x, int reg_z)
|
||||||
|
{
|
||||||
|
if (!opened || last_region_x != reg_x || last_region_z != reg_z) {
|
||||||
|
char filename[256];
|
||||||
|
if (last_region != NULL)
|
||||||
|
fclose(last_region);
|
||||||
|
sprintf(filename, "r.%d.%d.mca", reg_x, reg_z);
|
||||||
|
last_region = fopen(filename, "rb");
|
||||||
|
last_region_x = reg_x;
|
||||||
|
last_region_z = reg_z;
|
||||||
|
opened = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static region *load_region(int reg_x, int reg_z)
|
||||||
|
{
|
||||||
|
region *r;
|
||||||
|
int x,z;
|
||||||
|
|
||||||
|
open_file(reg_x, reg_z);
|
||||||
|
|
||||||
|
r = malloc(sizeof(*r));
|
||||||
|
|
||||||
|
if (last_region == NULL) {
|
||||||
|
memset(r, 0, sizeof(*r));
|
||||||
|
} else {
|
||||||
|
fseek(last_region, 0, SEEK_SET);
|
||||||
|
for (z=0; z < NUM_CHUNKS_PER_REGION; ++z)
|
||||||
|
for (x=0; x < NUM_CHUNKS_PER_REGION; ++x)
|
||||||
|
r->sector_data[z][x] = read_uint32_be(last_region);
|
||||||
|
}
|
||||||
|
r->x = reg_x;
|
||||||
|
r->z = reg_z;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_region(region *r)
|
||||||
|
{
|
||||||
|
free(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_MAP_REGIONS 64 // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns
|
||||||
|
region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS];
|
||||||
|
|
||||||
|
static region *get_region(int reg_x, int reg_z)
|
||||||
|
{
|
||||||
|
int slot_x = reg_x & (MAX_MAP_REGIONS-1);
|
||||||
|
int slot_z = reg_z & (MAX_MAP_REGIONS-1);
|
||||||
|
region *r;
|
||||||
|
|
||||||
|
r = regions[slot_z][slot_x];
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
if (r->x == reg_x && r->z == reg_z)
|
||||||
|
return r;
|
||||||
|
free_region(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = load_region(reg_x, reg_z);
|
||||||
|
regions[slot_z][slot_x] = r;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// about one region, so size should be ok
|
||||||
|
#define NUM_CACHED_X 64
|
||||||
|
#define NUM_CACHED_Z 64
|
||||||
|
|
||||||
|
// @TODO: is it really worth caching these? we probably can just
|
||||||
|
// pull them from the disk cache nearly as efficiently.
|
||||||
|
// Can test that by setting to 1x1?
|
||||||
|
compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X];
|
||||||
|
|
||||||
|
static void deref_compressed_chunk(compressed_chunk *cc)
|
||||||
|
{
|
||||||
|
assert(cc->refcount > 0);
|
||||||
|
--cc->refcount;
|
||||||
|
if (cc->refcount == 0) {
|
||||||
|
if (cc->data)
|
||||||
|
free(cc->data);
|
||||||
|
free(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
int slot_x = chunk_x & (NUM_CACHED_X-1);
|
||||||
|
int slot_z = chunk_z & (NUM_CACHED_Z-1);
|
||||||
|
compressed_chunk *cc = cached_chunk[slot_z][slot_x];
|
||||||
|
|
||||||
|
if (cc && cc->x == chunk_x && cc->z == chunk_z)
|
||||||
|
return cc;
|
||||||
|
else {
|
||||||
|
int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2;
|
||||||
|
int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2;
|
||||||
|
region *r = get_region(reg_x, reg_z);
|
||||||
|
if (cc) {
|
||||||
|
deref_compressed_chunk(cc);
|
||||||
|
cached_chunk[slot_z][slot_x] = NULL;
|
||||||
|
}
|
||||||
|
cc = malloc(sizeof(*cc));
|
||||||
|
cc->x = chunk_x;
|
||||||
|
cc->z = chunk_z;
|
||||||
|
{
|
||||||
|
int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1);
|
||||||
|
int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1);
|
||||||
|
uint32 code = r->sector_data[subchunk_z][subchunk_x];
|
||||||
|
|
||||||
|
if (code & 255) {
|
||||||
|
open_file(reg_x, reg_z);
|
||||||
|
fseek(last_region, (code>>8)*4096, SEEK_SET);
|
||||||
|
cc->len = (code&255)*4096;
|
||||||
|
cc->data = malloc(cc->len);
|
||||||
|
fread(cc->data, 1, cc->len, last_region);
|
||||||
|
} else {
|
||||||
|
cc->len = 0;
|
||||||
|
cc->data = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cc->refcount = 1;
|
||||||
|
cached_chunk[slot_z][slot_x] = cc;
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NBT parser -- can automatically parse stuff we don't
|
||||||
|
// have definitions for, but want to explicitly parse
|
||||||
|
// stuff we do have definitions for.
|
||||||
|
//
|
||||||
|
// option 1: auto-parse everything into data structures,
|
||||||
|
// then read those
|
||||||
|
//
|
||||||
|
// option 2: have a "parse next object" which
|
||||||
|
// doesn't resolve whether it expands its children
|
||||||
|
// yet, and then the user either says "expand" or
|
||||||
|
// "skip" after looking at the name. Anything with
|
||||||
|
// "children" without names can't go through this
|
||||||
|
// interface.
|
||||||
|
//
|
||||||
|
// Let's try option 2.
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char *buffer_start;
|
||||||
|
unsigned char *buffer_end;
|
||||||
|
unsigned char *cur;
|
||||||
|
int nesting;
|
||||||
|
char temp_buffer[256];
|
||||||
|
} nbt;
|
||||||
|
|
||||||
|
enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4,
|
||||||
|
TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8,
|
||||||
|
TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 };
|
||||||
|
|
||||||
|
static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize)
|
||||||
|
{
|
||||||
|
int len = data[0]*256 + data[1];
|
||||||
|
int i;
|
||||||
|
for (i=0; i < len && i+1 < (int) bufsize; ++i)
|
||||||
|
buffer[i] = (char) data[i+2];
|
||||||
|
buffer[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *nbt_peek(nbt *n)
|
||||||
|
{
|
||||||
|
unsigned char type = *n->cur;
|
||||||
|
if (type == TAG_End)
|
||||||
|
return NULL;
|
||||||
|
nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer));
|
||||||
|
return n->temp_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32 nbt_parse_uint32(unsigned char *buffer)
|
||||||
|
{
|
||||||
|
return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_skip(nbt *n);
|
||||||
|
|
||||||
|
// skip an item that doesn't have an id or name prefix (usable in lists)
|
||||||
|
static void nbt_skip_raw(nbt *n, unsigned char type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case TAG_Byte : n->cur += 1; break;
|
||||||
|
case TAG_Short : n->cur += 2; break;
|
||||||
|
case TAG_Int : n->cur += 4; break;
|
||||||
|
case TAG_Long : n->cur += 8; break;
|
||||||
|
case TAG_Float : n->cur += 4; break;
|
||||||
|
case TAG_Double: n->cur += 8; break;
|
||||||
|
case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break;
|
||||||
|
case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break;
|
||||||
|
case TAG_String : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break;
|
||||||
|
case TAG_List : {
|
||||||
|
unsigned char list_type = *n->cur++;
|
||||||
|
unsigned int list_len = nbt_parse_uint32(n->cur);
|
||||||
|
unsigned int i;
|
||||||
|
n->cur += 4; // list_len
|
||||||
|
for (i=0; i < list_len; ++i)
|
||||||
|
nbt_skip_raw(n, list_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TAG_Compound : {
|
||||||
|
while (*n->cur != TAG_End)
|
||||||
|
nbt_skip(n);
|
||||||
|
nbt_skip(n); // skip the TAG_end
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(n->cur <= n->buffer_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_skip(nbt *n)
|
||||||
|
{
|
||||||
|
unsigned char type = *n->cur++;
|
||||||
|
if (type == TAG_End)
|
||||||
|
return;
|
||||||
|
// skip name
|
||||||
|
n->cur += (n->cur[0]*256 + n->cur[1]) + 2;
|
||||||
|
nbt_skip_raw(n, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// byteswap
|
||||||
|
static void nbt_swap(unsigned char *ptr, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < (len>>1); ++i) {
|
||||||
|
unsigned char t = ptr[i];
|
||||||
|
ptr[i] = ptr[len-1-i];
|
||||||
|
ptr[len-1-i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass in the expected type, fail if doesn't match
|
||||||
|
// returns a pointer to the data, byteswapped if appropriate
|
||||||
|
static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len)
|
||||||
|
{
|
||||||
|
unsigned char *ptr;
|
||||||
|
assert(type != TAG_Compound);
|
||||||
|
assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh
|
||||||
|
if (len) *len = 1;
|
||||||
|
ptr = n->cur;
|
||||||
|
switch (type) {
|
||||||
|
case TAG_Byte : break;
|
||||||
|
|
||||||
|
case TAG_Short : nbt_swap(ptr, 2); break;
|
||||||
|
case TAG_Int : nbt_swap(ptr, 4); break;
|
||||||
|
case TAG_Long : nbt_swap(ptr, 8); break;
|
||||||
|
case TAG_Float : nbt_swap(ptr, 4); break;
|
||||||
|
case TAG_Double: nbt_swap(ptr, 8); break;
|
||||||
|
|
||||||
|
case TAG_Byte_Array:
|
||||||
|
*len = nbt_parse_uint32(ptr);
|
||||||
|
ptr += 4;
|
||||||
|
break;
|
||||||
|
case TAG_Int_Array: {
|
||||||
|
int i;
|
||||||
|
*len = nbt_parse_uint32(ptr);
|
||||||
|
ptr += 4;
|
||||||
|
for (i=0; i < *len; ++i)
|
||||||
|
nbt_swap(ptr + 4*i, 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: assert(0); // unhandled case
|
||||||
|
}
|
||||||
|
nbt_skip_raw(n, type);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *nbt_get(nbt *n, unsigned char type, int *len)
|
||||||
|
{
|
||||||
|
assert(n->cur[0] == type);
|
||||||
|
n->cur += 3 + (n->cur[1]*256+n->cur[2]);
|
||||||
|
return nbt_get_fromlist(n, type, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_begin_compound(nbt *n) // start a compound
|
||||||
|
{
|
||||||
|
assert(*n->cur == TAG_Compound);
|
||||||
|
// skip header
|
||||||
|
n->cur += 3 + (n->cur[1]*256 + n->cur[2]);
|
||||||
|
++n->nesting;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_begin_compound_in_list(nbt *n) // start a compound
|
||||||
|
{
|
||||||
|
++n->nesting;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_end_compound(nbt *n) // end a compound
|
||||||
|
{
|
||||||
|
assert(*n->cur == TAG_End);
|
||||||
|
assert(n->nesting != 0);
|
||||||
|
++n->cur;
|
||||||
|
--n->nesting;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO no interface to get lists from lists
|
||||||
|
static int nbt_begin_list(nbt *n, unsigned char type)
|
||||||
|
{
|
||||||
|
uint32 len;
|
||||||
|
unsigned char *ptr;
|
||||||
|
|
||||||
|
ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]);
|
||||||
|
if (ptr[0] != type)
|
||||||
|
return -1;
|
||||||
|
n->cur = ptr;
|
||||||
|
len = nbt_parse_uint32(n->cur+1);
|
||||||
|
assert(n->cur[0] == type);
|
||||||
|
// @TODO keep a stack with the count to make sure they do it right
|
||||||
|
++n->nesting;
|
||||||
|
n->cur += 5;
|
||||||
|
return (int) len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nbt_end_list(nbt *n)
|
||||||
|
{
|
||||||
|
--n->nesting;
|
||||||
|
}
|
||||||
|
|
||||||
|
// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB
|
||||||
|
//
|
||||||
|
// if we want to process 64x64x256 at a time, that will be:
|
||||||
|
// 4*4*256KB => 4MB per area in raw_block
|
||||||
|
//
|
||||||
|
// (plus we maybe need to decode adjacent regions)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef FAST_CHUNK
|
||||||
|
typedef fast_chunk parse_chunk;
|
||||||
|
#else
|
||||||
|
typedef chunk parse_chunk;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
parse_chunk *c = NULL;
|
||||||
|
|
||||||
|
nbt n_store, *n = &n_store;
|
||||||
|
n->buffer_start = data;
|
||||||
|
n->buffer_end = data + len;
|
||||||
|
n->cur = n->buffer_start;
|
||||||
|
n->nesting = 0;
|
||||||
|
|
||||||
|
nbt_begin_compound(n);
|
||||||
|
while ((s = nbt_peek(n)) != NULL) {
|
||||||
|
if (!strcmp(s, "Level")) {
|
||||||
|
int *height;
|
||||||
|
c = malloc(sizeof(*c));
|
||||||
|
#ifdef FAST_CHUNK
|
||||||
|
memset(c, 0, sizeof(*c));
|
||||||
|
c->pointer_to_free = data;
|
||||||
|
#else
|
||||||
|
c->rb[15][15][255].block = 0;
|
||||||
|
#endif
|
||||||
|
c->max_y = 0;
|
||||||
|
|
||||||
|
nbt_begin_compound(n);
|
||||||
|
while ((s = nbt_peek(n)) != NULL) {
|
||||||
|
if (!strcmp(s, "xPos"))
|
||||||
|
c->xpos = *(int *) nbt_get(n, TAG_Int, 0);
|
||||||
|
else if (!strcmp(s, "zPos"))
|
||||||
|
c->zpos = *(int *) nbt_get(n, TAG_Int, 0);
|
||||||
|
else if (!strcmp(s, "Sections")) {
|
||||||
|
int count = nbt_begin_list(n, TAG_Compound), i;
|
||||||
|
if (count == -1) {
|
||||||
|
// this not-a-list case happens in The End and I'm not sure
|
||||||
|
// what it means... possibly one of those silly encodings
|
||||||
|
// where it's not encoded as a list if there's only one?
|
||||||
|
// not worth figuring out
|
||||||
|
nbt_skip(n);
|
||||||
|
count = -1;
|
||||||
|
}
|
||||||
|
for (i=0; i < count; ++i) {
|
||||||
|
int yi, len;
|
||||||
|
uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL;
|
||||||
|
nbt_begin_compound_in_list(n);
|
||||||
|
while ((s = nbt_peek(n)) != NULL) {
|
||||||
|
if (!strcmp(s, "Y"))
|
||||||
|
yi = * (uint8 *) nbt_get(n, TAG_Byte, 0);
|
||||||
|
else if (!strcmp(s, "BlockLight")) {
|
||||||
|
light = nbt_get(n, TAG_Byte_Array, &len);
|
||||||
|
assert(len == 2048);
|
||||||
|
} else if (!strcmp(s, "Blocks")) {
|
||||||
|
blocks = nbt_get(n, TAG_Byte_Array, &len);
|
||||||
|
assert(len == 4096);
|
||||||
|
} else if (!strcmp(s, "Data")) {
|
||||||
|
data = nbt_get(n, TAG_Byte_Array, &len);
|
||||||
|
assert(len == 2048);
|
||||||
|
} else if (!strcmp(s, "SkyLight")) {
|
||||||
|
skylight = nbt_get(n, TAG_Byte_Array, &len);
|
||||||
|
assert(len == 2048);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nbt_end_compound(n);
|
||||||
|
|
||||||
|
assert(yi < 16);
|
||||||
|
|
||||||
|
#ifndef FAST_CHUNK
|
||||||
|
|
||||||
|
// clear data below current max_y
|
||||||
|
{
|
||||||
|
int x,z;
|
||||||
|
while (c->max_y < yi*16) {
|
||||||
|
for (x=0; x < 16; ++x)
|
||||||
|
for (z=0; z < 16; ++z)
|
||||||
|
c->rb[z][x][c->max_y].block = 0;
|
||||||
|
++c->max_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now assemble the data
|
||||||
|
{
|
||||||
|
int x,y,z, o2=0,o4=0;
|
||||||
|
for (y=0; y < 16; ++y) {
|
||||||
|
for (z=0; z < 16; ++z) {
|
||||||
|
for (x=0; x < 16; x += 2) {
|
||||||
|
raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis
|
||||||
|
rb[0].block = blocks[o4];
|
||||||
|
rb[0].light = light[o2] & 15;
|
||||||
|
rb[0].data = data[o2] & 15;
|
||||||
|
rb[0].skylight = skylight[o2] & 15;
|
||||||
|
|
||||||
|
rb[256].block = blocks[o4+1];
|
||||||
|
rb[256].light = light[o2] >> 4;
|
||||||
|
rb[256].data = data[o2] >> 4;
|
||||||
|
rb[256].skylight = skylight[o2] >> 4;
|
||||||
|
|
||||||
|
o2 += 1;
|
||||||
|
o4 += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c->max_y += 16;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
c->blockdata[yi] = blocks;
|
||||||
|
c->data [yi] = data;
|
||||||
|
c->light [yi] = light;
|
||||||
|
c->skylight [yi] = skylight;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
//nbt_end_list(n);
|
||||||
|
} else if (!strcmp(s, "HeightMap")) {
|
||||||
|
height = nbt_get(n, TAG_Int_Array, &len);
|
||||||
|
assert(len == 256);
|
||||||
|
} else
|
||||||
|
nbt_skip(n);
|
||||||
|
}
|
||||||
|
nbt_end_compound(n);
|
||||||
|
|
||||||
|
} else
|
||||||
|
nbt_skip(n);
|
||||||
|
}
|
||||||
|
nbt_end_compound(n);
|
||||||
|
assert(n->cur == n->buffer_end);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_DECODED_CHUNK_X 64
|
||||||
|
#define MAX_DECODED_CHUNK_Z 64
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int cx,cz;
|
||||||
|
fast_chunk *fc;
|
||||||
|
int valid;
|
||||||
|
} decoded_buffer;
|
||||||
|
|
||||||
|
static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X];
|
||||||
|
void lock_chunk_get_mutex(void);
|
||||||
|
void unlock_chunk_get_mutex(void);
|
||||||
|
|
||||||
|
#ifdef FAST_CHUNK
|
||||||
|
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
unsigned char *decoded;
|
||||||
|
compressed_chunk *cc;
|
||||||
|
int inlen;
|
||||||
|
int len;
|
||||||
|
fast_chunk *fc;
|
||||||
|
|
||||||
|
lock_chunk_get_mutex();
|
||||||
|
cc = get_compressed_chunk(chunk_x, chunk_z);
|
||||||
|
if (cc->len != 0)
|
||||||
|
++cc->refcount;
|
||||||
|
unlock_chunk_get_mutex();
|
||||||
|
|
||||||
|
if (cc->len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert(cc != NULL);
|
||||||
|
|
||||||
|
assert(cc->data[4] == 2);
|
||||||
|
|
||||||
|
inlen = nbt_parse_uint32(cc->data);
|
||||||
|
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len);
|
||||||
|
assert(decoded != NULL);
|
||||||
|
assert(len != 0);
|
||||||
|
|
||||||
|
lock_chunk_get_mutex();
|
||||||
|
deref_compressed_chunk(cc);
|
||||||
|
unlock_chunk_get_mutex();
|
||||||
|
|
||||||
|
#ifdef FAST_CHUNK
|
||||||
|
fc = minecraft_chunk_parse(decoded, len);
|
||||||
|
#else
|
||||||
|
fc = NULL;
|
||||||
|
#endif
|
||||||
|
if (fc == NULL)
|
||||||
|
free(decoded);
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)];
|
||||||
|
if (db->valid) {
|
||||||
|
if (db->cx == chunk_x && db->cz == chunk_z)
|
||||||
|
return db;
|
||||||
|
if (db->fc) {
|
||||||
|
free(db->fc->pointer_to_free);
|
||||||
|
free(db->fc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db->cx = chunk_x;
|
||||||
|
db->cz = chunk_z;
|
||||||
|
db->valid = 1;
|
||||||
|
db->fc = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z);
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z);
|
||||||
|
return db->fc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FAST_CHUNK
|
||||||
|
chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
unsigned char *decoded;
|
||||||
|
compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z);
|
||||||
|
assert(cc != NULL);
|
||||||
|
if (cc->len == 0)
|
||||||
|
return NULL;
|
||||||
|
else {
|
||||||
|
chunk *ch;
|
||||||
|
int inlen = nbt_parse_uint32(cc->data);
|
||||||
|
int len;
|
||||||
|
assert(cc->data[4] == 2);
|
||||||
|
decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len);
|
||||||
|
assert(decoded != NULL);
|
||||||
|
#ifdef FAST_CHUNK
|
||||||
|
ch = NULL;
|
||||||
|
#else
|
||||||
|
ch = minecraft_chunk_parse(decoded, len);
|
||||||
|
#endif
|
||||||
|
free(decoded);
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X];
|
||||||
|
chunk *get_decoded_chunk(int chunk_x, int chunk_z)
|
||||||
|
{
|
||||||
|
chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)];
|
||||||
|
if (c && c->xpos == chunk_x && c->zpos == chunk_z)
|
||||||
|
return c;
|
||||||
|
if (c) free(c);
|
||||||
|
c = get_decoded_chunk_raw(chunk_x, chunk_z);
|
||||||
|
decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
#endif
|
41
external/stb/stb/tests/caveview/cave_parse.h
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef INCLUDE_CAVE_PARSE_H
|
||||||
|
#define INCLUDE_CAVE_PARSE_H
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char block;
|
||||||
|
unsigned char data;
|
||||||
|
unsigned char light:4;
|
||||||
|
unsigned char skylight:4;
|
||||||
|
} raw_block;
|
||||||
|
|
||||||
|
// this is the old fully-decoded chunk
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int xpos, zpos, max_y;
|
||||||
|
int height[16][16];
|
||||||
|
raw_block rb[16][16][256]; // [z][x][y] which becomes [y][x][z] in stb
|
||||||
|
} chunk;
|
||||||
|
|
||||||
|
chunk *get_decoded_chunk(int chunk_x, int chunk_z);
|
||||||
|
|
||||||
|
#define NUM_SEGMENTS 16
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int max_y, xpos, zpos;
|
||||||
|
|
||||||
|
unsigned char *blockdata[NUM_SEGMENTS];
|
||||||
|
unsigned char *data[NUM_SEGMENTS];
|
||||||
|
unsigned char *skylight[NUM_SEGMENTS];
|
||||||
|
unsigned char *light[NUM_SEGMENTS];
|
||||||
|
|
||||||
|
void *pointer_to_free;
|
||||||
|
|
||||||
|
int refcount; // this allows multi-threaded building without wrapping in ANOTHER struct
|
||||||
|
} fast_chunk;
|
||||||
|
|
||||||
|
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z); // cache, never call free()
|
||||||
|
|
||||||
|
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z);
|
||||||
|
|
||||||
|
#endif
|
951
external/stb/stb/tests/caveview/cave_render.c
vendored
Normal file
@ -0,0 +1,951 @@
|
|||||||
|
// This file renders vertex buffers, converts raw meshes
|
||||||
|
// to GL meshes, and manages threads that do the raw-mesh
|
||||||
|
// building (found in cave_mesher.c)
|
||||||
|
|
||||||
|
|
||||||
|
#include "stb_voxel_render.h"
|
||||||
|
|
||||||
|
#define STB_GLEXT_DECLARE "glext_list.h"
|
||||||
|
#include "stb_gl.h"
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "stb_glprog.h"
|
||||||
|
|
||||||
|
#include "caveview.h"
|
||||||
|
#include "cave_parse.h"
|
||||||
|
#include "stb.h"
|
||||||
|
#include "sdl.h"
|
||||||
|
#include "sdl_thread.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
|
||||||
|
|
||||||
|
|
||||||
|
// currently no dynamic way to set mesh cache size or view distance
|
||||||
|
//#define SHORTVIEW
|
||||||
|
|
||||||
|
|
||||||
|
stbvox_mesh_maker g_mesh_maker;
|
||||||
|
|
||||||
|
GLuint main_prog;
|
||||||
|
GLint uniform_locations[64];
|
||||||
|
|
||||||
|
//#define MAX_QUADS_PER_DRAW (65536 / 4) // assuming 16-bit indices, 4 verts per quad
|
||||||
|
//#define FIXED_INDEX_BUFFER_SIZE (MAX_QUADS_PER_DRAW * 6 * 2) // 16*1024 * 12 == ~192KB
|
||||||
|
|
||||||
|
// while uploading texture data, this holds our each texture
|
||||||
|
#define TEX_SIZE 64
|
||||||
|
uint32 texture[TEX_SIZE][TEX_SIZE];
|
||||||
|
|
||||||
|
GLuint voxel_tex[2];
|
||||||
|
|
||||||
|
// chunk state
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STATE_invalid,
|
||||||
|
STATE_needed,
|
||||||
|
STATE_requested,
|
||||||
|
STATE_abandoned,
|
||||||
|
STATE_valid,
|
||||||
|
};
|
||||||
|
|
||||||
|
// mesh is 32x32x255 ... this is hardcoded in that
|
||||||
|
// a mesh covers 2x2 minecraft chunks, no #defines for it
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int state;
|
||||||
|
int chunk_x, chunk_y;
|
||||||
|
int num_quads;
|
||||||
|
float priority;
|
||||||
|
int vbuf_size, fbuf_size;
|
||||||
|
|
||||||
|
float transform[3][3];
|
||||||
|
float bounds[2][3];
|
||||||
|
|
||||||
|
GLuint vbuf;// vbuf_tex;
|
||||||
|
GLuint fbuf, fbuf_tex;
|
||||||
|
|
||||||
|
} chunk_mesh;
|
||||||
|
|
||||||
|
void scale_texture(unsigned char *src, int x, int y, int w, int h)
|
||||||
|
{
|
||||||
|
int i,j,k;
|
||||||
|
assert(w == 256 && h == 256);
|
||||||
|
for (j=0; j < TEX_SIZE; ++j) {
|
||||||
|
for (i=0; i < TEX_SIZE; ++i) {
|
||||||
|
uint32 val=0;
|
||||||
|
for (k=0; k < 4; ++k) {
|
||||||
|
val >>= 8;
|
||||||
|
val += src[ 4*(x+(i>>2)) + 4*w*(y+(j>>2)) + k]<<24;
|
||||||
|
}
|
||||||
|
texture[j][i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_base_texture(int n)
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
uint32 color = stb_rand() | 0x808080;
|
||||||
|
for (y=0; y<TEX_SIZE; ++y)
|
||||||
|
for (x=0; x<TEX_SIZE; ++x) {
|
||||||
|
texture[y][x] = (color + (stb_rand()&0x1f1f1f))|0xff000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_overlay_texture(int n)
|
||||||
|
{
|
||||||
|
int x,y;
|
||||||
|
uint32 color = stb_rand();
|
||||||
|
if (color & 16)
|
||||||
|
color = 0xff000000;
|
||||||
|
else
|
||||||
|
color = 0xffffffff;
|
||||||
|
for (y=0; y<TEX_SIZE; ++y)
|
||||||
|
for (x=0; x<TEX_SIZE; ++x) {
|
||||||
|
texture[y][x] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y=0; y < TEX_SIZE/8; ++y) {
|
||||||
|
for (x=0; x < TEX_SIZE; ++x) {
|
||||||
|
texture[y][x] = color;
|
||||||
|
texture[TEX_SIZE-1-y][x] = color;
|
||||||
|
texture[x][y] = color;
|
||||||
|
texture[x][TEX_SIZE-1-y] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// view radius of about 1024 = 2048 columns / 32 columns-per-mesh = 2^11 / 2^5 = 64x64
|
||||||
|
// so we need bigger than 64x64 so we can precache, which means we have to be
|
||||||
|
// non-power-of-two, or we have to be pretty huge
|
||||||
|
#define CACHED_MESH_NUM_X 128
|
||||||
|
#define CACHED_MESH_NUM_Y 128
|
||||||
|
|
||||||
|
|
||||||
|
chunk_mesh cached_chunk_mesh[CACHED_MESH_NUM_Y][CACHED_MESH_NUM_X];
|
||||||
|
|
||||||
|
void free_chunk(int slot_x, int slot_y)
|
||||||
|
{
|
||||||
|
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
|
||||||
|
if (cm->state == STATE_valid) {
|
||||||
|
glDeleteTextures(1, &cm->fbuf_tex);
|
||||||
|
glDeleteBuffersARB(1, &cm->vbuf);
|
||||||
|
glDeleteBuffersARB(1, &cm->fbuf);
|
||||||
|
cached_chunk_mesh[slot_y][slot_x].state = STATE_invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void upload_mesh(chunk_mesh *cm, uint8 *build_buffer, uint8 *face_buffer)
|
||||||
|
{
|
||||||
|
glGenBuffersARB(1, &cm->vbuf);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB, cm->num_quads*4*sizeof(uint32), build_buffer, GL_STATIC_DRAW_ARB);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
|
||||||
|
glGenBuffersARB(1, &cm->fbuf);
|
||||||
|
glBindBufferARB(GL_TEXTURE_BUFFER_ARB, cm->fbuf);
|
||||||
|
glBufferDataARB(GL_TEXTURE_BUFFER_ARB, cm->num_quads*sizeof(uint32), face_buffer , GL_STATIC_DRAW_ARB);
|
||||||
|
glBindBufferARB(GL_TEXTURE_BUFFER_ARB, 0);
|
||||||
|
|
||||||
|
glGenTextures(1, &cm->fbuf_tex);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex);
|
||||||
|
glTexBufferARB(GL_TEXTURE_BUFFER_ARB, GL_RGBA8UI, cm->fbuf);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upload_mesh_data(raw_mesh *rm)
|
||||||
|
{
|
||||||
|
int cx = rm->cx;
|
||||||
|
int cy = rm->cy;
|
||||||
|
int slot_x = (cx >> 1) & (CACHED_MESH_NUM_X-1);
|
||||||
|
int slot_y = (cy >> 1) & (CACHED_MESH_NUM_Y-1);
|
||||||
|
chunk_mesh *cm;
|
||||||
|
|
||||||
|
free_chunk(slot_x, slot_y);
|
||||||
|
|
||||||
|
cm = &cached_chunk_mesh[slot_y][slot_x];
|
||||||
|
cm->num_quads = rm->num_quads;
|
||||||
|
|
||||||
|
upload_mesh(cm, rm->build_buffer, rm->face_buffer);
|
||||||
|
cm->vbuf_size = rm->num_quads*4*sizeof(uint32);
|
||||||
|
cm->fbuf_size = rm->num_quads*sizeof(uint32);
|
||||||
|
cm->priority = 100000;
|
||||||
|
cm->chunk_x = cx;
|
||||||
|
cm->chunk_y = cy;
|
||||||
|
|
||||||
|
memcpy(cm->bounds, rm->bounds, sizeof(cm->bounds));
|
||||||
|
memcpy(cm->transform, rm->transform, sizeof(cm->transform));
|
||||||
|
|
||||||
|
// write barrier here
|
||||||
|
cm->state = STATE_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint uniform_loc[16];
|
||||||
|
float table3[128][3];
|
||||||
|
float table4[64][4];
|
||||||
|
GLint tablei[2];
|
||||||
|
|
||||||
|
float step=0;
|
||||||
|
|
||||||
|
#ifdef SHORTVIEW
|
||||||
|
int view_dist_in_chunks = 50;
|
||||||
|
#else
|
||||||
|
int view_dist_in_chunks = 80;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setup_uniforms(float pos[3])
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
step += 1.0f/60.0f;
|
||||||
|
for (i=0; i < STBVOX_UNIFORM_count; ++i) {
|
||||||
|
stbvox_uniform_info raw, *ui=&raw;
|
||||||
|
stbvox_get_uniform_info(&raw, i);
|
||||||
|
uniform_loc[i] = -1;
|
||||||
|
|
||||||
|
if (i == STBVOX_UNIFORM_texscale || i == STBVOX_UNIFORM_texgen || i == STBVOX_UNIFORM_color_table)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ui) {
|
||||||
|
void *data = ui->default_value;
|
||||||
|
uniform_loc[i] = stbgl_find_uniform(main_prog, ui->name);
|
||||||
|
switch (i) {
|
||||||
|
case STBVOX_UNIFORM_face_data:
|
||||||
|
tablei[0] = 2;
|
||||||
|
data = tablei;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBVOX_UNIFORM_tex_array:
|
||||||
|
glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]);
|
||||||
|
glActiveTextureARB(GL_TEXTURE1_ARB);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
|
||||||
|
glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||||
|
tablei[0] = 0;
|
||||||
|
tablei[1] = 1;
|
||||||
|
data = tablei;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBVOX_UNIFORM_color_table:
|
||||||
|
data = ui->default_value;
|
||||||
|
((float *)data)[63*4+3] = 2.0f; // emissive
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBVOX_UNIFORM_camera_pos:
|
||||||
|
data = table3[0];
|
||||||
|
table3[0][0] = pos[0];
|
||||||
|
table3[0][1] = pos[1];
|
||||||
|
table3[0][2] = pos[2];
|
||||||
|
table3[0][3] = stb_max(0,(float)sin(step*2)*0.125f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBVOX_UNIFORM_ambient: {
|
||||||
|
float bright = 1.0;
|
||||||
|
//float bright = 0.75;
|
||||||
|
float amb[3][3];
|
||||||
|
|
||||||
|
// ambient direction is sky-colored upwards
|
||||||
|
// "ambient" lighting is from above
|
||||||
|
table4[0][0] = 0.3f;
|
||||||
|
table4[0][1] = -0.5f;
|
||||||
|
table4[0][2] = 0.9f;
|
||||||
|
|
||||||
|
amb[1][0] = 0.3f; amb[1][1] = 0.3f; amb[1][2] = 0.3f; // dark-grey
|
||||||
|
amb[2][0] = 1.0; amb[2][1] = 1.0; amb[2][2] = 1.0; // white
|
||||||
|
|
||||||
|
// convert so (table[1]*dot+table[2]) gives
|
||||||
|
// above interpolation
|
||||||
|
// lerp((dot+1)/2, amb[1], amb[2])
|
||||||
|
// amb[1] + (amb[2] - amb[1]) * (dot+1)/2
|
||||||
|
// amb[1] + (amb[2] - amb[1]) * dot/2 + (amb[2]-amb[1])/2
|
||||||
|
|
||||||
|
for (j=0; j < 3; ++j) {
|
||||||
|
table4[1][j] = (amb[2][j] - amb[1][j])/2 * bright;
|
||||||
|
table4[2][j] = (amb[1][j] + amb[2][j])/2 * bright;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fog color
|
||||||
|
table4[3][0] = 0.6f, table4[3][1] = 0.7f, table4[3][2] = 0.9f;
|
||||||
|
table4[3][3] = 1.0f / (view_dist_in_chunks * 16);
|
||||||
|
table4[3][3] *= table4[3][3];
|
||||||
|
|
||||||
|
data = table4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ui->type) {
|
||||||
|
case STBVOX_UNIFORM_TYPE_sampler: stbglUniform1iv(uniform_loc[i], ui->array_length, data); break;
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec2: stbglUniform2fv(uniform_loc[i], ui->array_length, data); break;
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec3: stbglUniform3fv(uniform_loc[i], ui->array_length, data); break;
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec4: stbglUniform4fv(uniform_loc[i], ui->array_length, data); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint unitex[64], unibuf[64];
|
||||||
|
void make_texture_buffer_for_uniform(int uniform, int slot)
|
||||||
|
{
|
||||||
|
GLenum type;
|
||||||
|
stbvox_uniform_info raw, *ui=&raw;
|
||||||
|
GLint uloc;
|
||||||
|
|
||||||
|
stbvox_get_uniform_info(ui, uniform);
|
||||||
|
uloc = stbgl_find_uniform(main_prog, ui->name);
|
||||||
|
|
||||||
|
if (uniform == STBVOX_UNIFORM_color_table)
|
||||||
|
((float *)ui->default_value)[63*4+3] = 2.0f; // emissive
|
||||||
|
|
||||||
|
glGenBuffersARB(1, &unibuf[uniform]);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, unibuf[uniform]);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ui->array_length * ui->bytes_per_element, ui->default_value, GL_STATIC_DRAW_ARB);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
|
||||||
|
glGenTextures(1, &unitex[uniform]);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]);
|
||||||
|
switch (ui->type) {
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec2: type = GL_RG32F; break;
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec3: type = GL_RGB32F; break;
|
||||||
|
case STBVOX_UNIFORM_TYPE_vec4: type = GL_RGBA32F; break;
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
glTexBufferARB(GL_TEXTURE_BUFFER_ARB, type, unibuf[uniform]);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, 0);
|
||||||
|
|
||||||
|
glActiveTextureARB(GL_TEXTURE0 + slot);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, unitex[uniform]);
|
||||||
|
glActiveTextureARB(GL_TEXTURE0);
|
||||||
|
|
||||||
|
stbglUseProgram(main_prog);
|
||||||
|
stbglUniform1i(uloc, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_MESH_WORKERS 8
|
||||||
|
#define MAX_CHUNK_LOAD_WORKERS 2
|
||||||
|
|
||||||
|
int num_mesh_workers;
|
||||||
|
int num_chunk_load_workers;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int state;
|
||||||
|
int request_cx;
|
||||||
|
int request_cy;
|
||||||
|
int padding[13];
|
||||||
|
|
||||||
|
SDL_sem * request_received;
|
||||||
|
|
||||||
|
SDL_sem * chunk_server_done_processing;
|
||||||
|
int chunk_action;
|
||||||
|
int chunk_request_x;
|
||||||
|
int chunk_request_y;
|
||||||
|
fast_chunk *chunks[4][4];
|
||||||
|
|
||||||
|
int padding2[16];
|
||||||
|
raw_mesh rm;
|
||||||
|
int padding3[16];
|
||||||
|
|
||||||
|
uint8 *build_buffer;
|
||||||
|
uint8 *face_buffer ;
|
||||||
|
} mesh_worker;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
WSTATE_idle,
|
||||||
|
WSTATE_requested,
|
||||||
|
WSTATE_running,
|
||||||
|
WSTATE_mesh_ready,
|
||||||
|
};
|
||||||
|
|
||||||
|
mesh_worker mesh_data[MAX_MESH_WORKERS];
|
||||||
|
int num_meshes_started; // stats
|
||||||
|
|
||||||
|
int request_chunk(int chunk_x, int chunk_y);
|
||||||
|
void update_meshes_from_render_thread(void);
|
||||||
|
|
||||||
|
unsigned char tex2_data[64][4];
|
||||||
|
|
||||||
|
void init_tex2_gradient(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < 16; ++i) {
|
||||||
|
tex2_data[i+ 0][0] = 64 + 12*i;
|
||||||
|
tex2_data[i+ 0][1] = 32;
|
||||||
|
tex2_data[i+ 0][2] = 64;
|
||||||
|
|
||||||
|
tex2_data[i+16][0] = 255;
|
||||||
|
tex2_data[i+16][1] = 32 + 8*i;
|
||||||
|
tex2_data[i+16][2] = 64;
|
||||||
|
|
||||||
|
tex2_data[i+32][0] = 255;
|
||||||
|
tex2_data[i+32][1] = 160;
|
||||||
|
tex2_data[i+32][2] = 64 + 12*i;
|
||||||
|
|
||||||
|
tex2_data[i+48][0] = 255;
|
||||||
|
tex2_data[i+48][1] = 160 + 6*i;
|
||||||
|
tex2_data[i+48][2] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_tex2_alpha(float fa)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int a = (int) stb_lerp(fa, 0, 255);
|
||||||
|
if (a < 0) a = 0; else if (a > 255) a = 255;
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
|
||||||
|
for (i=0; i < 64; ++i) {
|
||||||
|
tex2_data[i][3] = a;
|
||||||
|
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, 1,1,1, GL_RGBA, GL_UNSIGNED_BYTE, tex2_data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *binds[] = { "attr_vertex", "attr_face", NULL };
|
||||||
|
char *vertex;
|
||||||
|
char *fragment;
|
||||||
|
int w=0,h=0;
|
||||||
|
|
||||||
|
unsigned char *texdata = stbi_load("terrain.png", &w, &h, NULL, 4);
|
||||||
|
|
||||||
|
stbvox_init_mesh_maker(&g_mesh_maker);
|
||||||
|
for (i=0; i < num_mesh_workers; ++i) {
|
||||||
|
stbvox_init_mesh_maker(&mesh_data[i].rm.mm);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex = stbvox_get_vertex_shader();
|
||||||
|
fragment = stbvox_get_fragment_shader();
|
||||||
|
|
||||||
|
{
|
||||||
|
char error_buffer[1024];
|
||||||
|
char *main_vertex[] = { vertex, NULL };
|
||||||
|
char *main_fragment[] = { fragment, NULL };
|
||||||
|
main_prog = stbgl_create_program(main_vertex, main_fragment, binds, error_buffer, sizeof(error_buffer));
|
||||||
|
if (main_prog == 0) {
|
||||||
|
ods("Compile error for main shader: %s\n", error_buffer);
|
||||||
|
assert(0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//init_index_buffer();
|
||||||
|
|
||||||
|
make_texture_buffer_for_uniform(STBVOX_UNIFORM_texscale , 3);
|
||||||
|
make_texture_buffer_for_uniform(STBVOX_UNIFORM_texgen , 4);
|
||||||
|
make_texture_buffer_for_uniform(STBVOX_UNIFORM_color_table , 5);
|
||||||
|
|
||||||
|
glGenTextures(2, voxel_tex);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[0]);
|
||||||
|
glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA,
|
||||||
|
TEX_SIZE,TEX_SIZE,256,
|
||||||
|
0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
|
||||||
|
for (i=0; i < 256; ++i) {
|
||||||
|
if (texdata)
|
||||||
|
scale_texture(texdata, (i&15)*w/16, (h/16)*(i>>4), w,h);
|
||||||
|
else
|
||||||
|
build_base_texture(i);
|
||||||
|
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]);
|
||||||
|
}
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
|
||||||
|
#ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, voxel_tex[1]);
|
||||||
|
glTexImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, GL_RGBA,
|
||||||
|
1,1,64,
|
||||||
|
0,GL_RGBA,GL_UNSIGNED_BYTE,NULL);
|
||||||
|
init_tex2_gradient();
|
||||||
|
set_tex2_alpha(0.0);
|
||||||
|
#if 0
|
||||||
|
for (i=0; i < 128; ++i) {
|
||||||
|
//build_overlay_texture(i);
|
||||||
|
glTexSubImage3DEXT(GL_TEXTURE_2D_ARRAY_EXT, 0, 0,0,i, TEX_SIZE,TEX_SIZE,1, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void world_init(void)
|
||||||
|
{
|
||||||
|
int a,b,x,y;
|
||||||
|
|
||||||
|
Uint64 start_time, end_time;
|
||||||
|
#ifdef NDEBUG
|
||||||
|
int range = 32;
|
||||||
|
#else
|
||||||
|
int range = 12;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
start_time = SDL_GetPerformanceCounter();
|
||||||
|
|
||||||
|
// iterate in 8x8 clusters of qchunks at a time to get better converted-chunk-cache reuse
|
||||||
|
// than a purely row-by-row ordering is (single-threaded this is a bigger win than
|
||||||
|
// any of the above optimizations were, since it halves zlib/mc-conversion costs)
|
||||||
|
for (x=-range; x <= range; x += 16)
|
||||||
|
for (y=-range; y <= range; y += 16)
|
||||||
|
for (b=y; b < y+16 && b <= range; b += 2)
|
||||||
|
for (a=x; a < x+16 && a <= range; a += 2)
|
||||||
|
while (!request_chunk(a, b)) { // if request fails, all threads are busy
|
||||||
|
update_meshes_from_render_thread();
|
||||||
|
SDL_Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait until all the workers are done,
|
||||||
|
// (this is only needed if we want to time
|
||||||
|
// when the build finishes, or when we want to reset the
|
||||||
|
// cache size; otherwise we could just go ahead and
|
||||||
|
// start rendering whatever we've got)
|
||||||
|
for(;;) {
|
||||||
|
int i;
|
||||||
|
update_meshes_from_render_thread();
|
||||||
|
for (i=0; i < num_mesh_workers; ++i)
|
||||||
|
if (mesh_data[i].state != WSTATE_idle)
|
||||||
|
break;
|
||||||
|
if (i == num_mesh_workers)
|
||||||
|
break;
|
||||||
|
SDL_Delay(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
end_time = SDL_GetPerformanceCounter();
|
||||||
|
ods("Build time: %7.2fs\n", (end_time - start_time) / (float) SDL_GetPerformanceFrequency());
|
||||||
|
|
||||||
|
// don't waste lots of storage on chunk caches once it's finished starting-up;
|
||||||
|
// this was only needed to be this large because we worked in large blocks
|
||||||
|
// to maximize sharing
|
||||||
|
reset_cache_size(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern SDL_mutex * chunk_cache_mutex;
|
||||||
|
|
||||||
|
int mesh_worker_handler(void *data)
|
||||||
|
{
|
||||||
|
mesh_worker *mw = data;
|
||||||
|
mw->face_buffer = malloc(FACE_BUFFER_SIZE);
|
||||||
|
mw->build_buffer = malloc(BUILD_BUFFER_SIZE);
|
||||||
|
|
||||||
|
// this loop only works because the compiler can't
|
||||||
|
// tell that the SDL_calls don't access mw->state;
|
||||||
|
// really we should barrier that stuff
|
||||||
|
for(;;) {
|
||||||
|
int i,j;
|
||||||
|
int cx,cy;
|
||||||
|
|
||||||
|
// wait for a chunk request
|
||||||
|
SDL_SemWait(mw->request_received);
|
||||||
|
|
||||||
|
// analyze the chunk request
|
||||||
|
assert(mw->state == WSTATE_requested);
|
||||||
|
cx = mw->request_cx;
|
||||||
|
cy = mw->request_cy;
|
||||||
|
|
||||||
|
// this is inaccurate as it can block while another thread has the cache locked
|
||||||
|
mw->state = WSTATE_running;
|
||||||
|
|
||||||
|
// get the chunks we need (this takes a lock and caches them)
|
||||||
|
for (j=0; j < 4; ++j)
|
||||||
|
for (i=0; i < 4; ++i)
|
||||||
|
mw->chunks[j][i] = get_converted_fastchunk(cx-1 + i, cy-1 + j);
|
||||||
|
|
||||||
|
// build the mesh based on the chunks
|
||||||
|
mw->rm.build_buffer = mw->build_buffer;
|
||||||
|
mw->rm.face_buffer = mw->face_buffer;
|
||||||
|
build_chunk(cx, cy, mw->chunks, &mw->rm);
|
||||||
|
mw->state = WSTATE_mesh_ready;
|
||||||
|
// don't need to notify of this, because it gets polled
|
||||||
|
|
||||||
|
// when done, free the chunks
|
||||||
|
|
||||||
|
// for efficiency we just take the mutex once around the whole thing,
|
||||||
|
// though this spreads the mutex logic over two files
|
||||||
|
SDL_LockMutex(chunk_cache_mutex);
|
||||||
|
for (j=0; j < 4; ++j)
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
deref_fastchunk(mw->chunks[j][i]);
|
||||||
|
mw->chunks[j][i] = NULL;
|
||||||
|
}
|
||||||
|
SDL_UnlockMutex(chunk_cache_mutex);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int request_chunk(int chunk_x, int chunk_y)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < num_mesh_workers; ++i) {
|
||||||
|
mesh_worker *mw = &mesh_data[i];
|
||||||
|
if (mw->state == WSTATE_idle) {
|
||||||
|
mw->request_cx = chunk_x;
|
||||||
|
mw->request_cy = chunk_y;
|
||||||
|
mw->state = WSTATE_requested;
|
||||||
|
SDL_SemPost(mw->request_received);
|
||||||
|
++num_meshes_started;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepare_threads(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int num_proc = SDL_GetCPUCount();
|
||||||
|
|
||||||
|
if (num_proc > 6)
|
||||||
|
num_mesh_workers = num_proc/2;
|
||||||
|
else if (num_proc > 4)
|
||||||
|
num_mesh_workers = 4;
|
||||||
|
else
|
||||||
|
num_mesh_workers = num_proc-1;
|
||||||
|
|
||||||
|
// @TODO
|
||||||
|
// Thread usage is probably pretty terrible; need to make a
|
||||||
|
// separate queue of needed chunks, instead of just generating
|
||||||
|
// one request per thread per frame, and a separate queue of
|
||||||
|
// results. (E.g. If it takes 1.5 frames to build mesh, thread
|
||||||
|
// is idle for 0.5 frames.) To fake this for now, I've just
|
||||||
|
// doubled the number of threads to let those serve as a 'queue',
|
||||||
|
// but that's dumb.
|
||||||
|
|
||||||
|
num_mesh_workers *= 2; // try to get better thread usage
|
||||||
|
|
||||||
|
if (num_mesh_workers > MAX_MESH_WORKERS)
|
||||||
|
num_mesh_workers = MAX_MESH_WORKERS;
|
||||||
|
|
||||||
|
for (i=0; i < num_mesh_workers; ++i) {
|
||||||
|
mesh_worker *data = &mesh_data[i];
|
||||||
|
data->request_received = SDL_CreateSemaphore(0);
|
||||||
|
data->chunk_server_done_processing = SDL_CreateSemaphore(0);
|
||||||
|
SDL_CreateThread(mesh_worker_handler, "mesh worker", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "better" buffer uploading
|
||||||
|
#if 0
|
||||||
|
if (glBufferStorage) {
|
||||||
|
glDeleteBuffersARB(1, &vb->vbuf);
|
||||||
|
glGenBuffersARB(1, &vb->vbuf);
|
||||||
|
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf);
|
||||||
|
glBufferStorage(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, 0);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
} else {
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vb->vbuf);
|
||||||
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(build_buffer), build_buffer, GL_STATIC_DRAW_ARB);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float x,y,z,w;
|
||||||
|
} plane;
|
||||||
|
|
||||||
|
static plane frustum[6];
|
||||||
|
|
||||||
|
static void matd_mul(double out[4][4], double src1[4][4], double src2[4][4])
|
||||||
|
{
|
||||||
|
int i,j,k;
|
||||||
|
for (j=0; j < 4; ++j) {
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
double t=0;
|
||||||
|
for (k=0; k < 4; ++k)
|
||||||
|
t += src1[k][i] * src2[j][k];
|
||||||
|
out[i][j] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://fgiesen.wordpress.com/2012/08/31/frustum-planes-from-the-projection-matrix/
|
||||||
|
static void compute_frustum(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
GLdouble mv[4][4],proj[4][4], mvproj[4][4];
|
||||||
|
glGetDoublev(GL_MODELVIEW_MATRIX , mv[0]);
|
||||||
|
glGetDoublev(GL_PROJECTION_MATRIX, proj[0]);
|
||||||
|
matd_mul(mvproj, proj, mv);
|
||||||
|
for (i=0; i < 4; ++i) {
|
||||||
|
(&frustum[0].x)[i] = (float) (mvproj[3][i] + mvproj[0][i]);
|
||||||
|
(&frustum[1].x)[i] = (float) (mvproj[3][i] - mvproj[0][i]);
|
||||||
|
(&frustum[2].x)[i] = (float) (mvproj[3][i] + mvproj[1][i]);
|
||||||
|
(&frustum[3].x)[i] = (float) (mvproj[3][i] - mvproj[1][i]);
|
||||||
|
(&frustum[4].x)[i] = (float) (mvproj[3][i] + mvproj[2][i]);
|
||||||
|
(&frustum[5].x)[i] = (float) (mvproj[3][i] - mvproj[2][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_plane(plane *p, float x0, float y0, float z0, float x1, float y1, float z1)
|
||||||
|
{
|
||||||
|
// return false if the box is entirely behind the plane
|
||||||
|
float d=0;
|
||||||
|
assert(x0 <= x1 && y0 <= y1 && z0 <= z1);
|
||||||
|
if (p->x > 0) d += x1*p->x; else d += x0*p->x;
|
||||||
|
if (p->y > 0) d += y1*p->y; else d += y0*p->y;
|
||||||
|
if (p->z > 0) d += z1*p->z; else d += z0*p->z;
|
||||||
|
return d + p->w >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_box_in_frustum(float *bmin, float *bmax)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < 6; ++i)
|
||||||
|
if (!test_plane(&frustum[i], bmin[0], bmin[1], bmin[2], bmax[0], bmax[1], bmax[2]))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float compute_priority(int cx, int cy, float x, float y)
|
||||||
|
{
|
||||||
|
float distx, disty, dist2;
|
||||||
|
distx = (cx*16+8) - x;
|
||||||
|
disty = (cy*16+8) - y;
|
||||||
|
dist2 = distx*distx + disty*disty;
|
||||||
|
return view_dist_in_chunks*view_dist_in_chunks * 16 * 16 - dist2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunk_locations, chunks_considered, chunks_in_frustum;
|
||||||
|
int quads_considered, quads_rendered;
|
||||||
|
int chunk_storage_rendered, chunk_storage_considered, chunk_storage_total;
|
||||||
|
int update_frustum = 1;
|
||||||
|
|
||||||
|
#ifdef SHORTVIEW
|
||||||
|
int max_chunk_storage = 450 << 20;
|
||||||
|
int min_chunk_storage = 350 << 20;
|
||||||
|
#else
|
||||||
|
int max_chunk_storage = 900 << 20;
|
||||||
|
int min_chunk_storage = 800 << 20;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float min_priority = -500; // this really wants to be in unit space, not squared space
|
||||||
|
|
||||||
|
int num_meshes_uploaded;
|
||||||
|
|
||||||
|
void update_meshes_from_render_thread(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i < num_mesh_workers; ++i) {
|
||||||
|
mesh_worker *mw = &mesh_data[i];
|
||||||
|
if (mw->state == WSTATE_mesh_ready) {
|
||||||
|
upload_mesh_data(&mw->rm);
|
||||||
|
++num_meshes_uploaded;
|
||||||
|
mw->state = WSTATE_idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern float tex2_alpha;
|
||||||
|
extern int global_hack;
|
||||||
|
int num_threads_active;
|
||||||
|
float chunk_server_activity;
|
||||||
|
|
||||||
|
void render_caves(float campos[3])
|
||||||
|
{
|
||||||
|
float x = campos[0], y = campos[1];
|
||||||
|
int qchunk_x, qchunk_y;
|
||||||
|
int cam_x, cam_y;
|
||||||
|
int i,j, rad;
|
||||||
|
|
||||||
|
compute_frustum();
|
||||||
|
|
||||||
|
chunk_locations = chunks_considered = chunks_in_frustum = 0;
|
||||||
|
quads_considered = quads_rendered = 0;
|
||||||
|
chunk_storage_total = chunk_storage_considered = chunk_storage_rendered = 0;
|
||||||
|
|
||||||
|
cam_x = (int) floor(x+0.5);
|
||||||
|
cam_y = (int) floor(y+0.5);
|
||||||
|
|
||||||
|
qchunk_x = (((int) floor(x)+16) >> 5) << 1;
|
||||||
|
qchunk_y = (((int) floor(y)+16) >> 5) << 1;
|
||||||
|
|
||||||
|
glEnable(GL_ALPHA_TEST);
|
||||||
|
glAlphaFunc(GL_GREATER, 0.5);
|
||||||
|
|
||||||
|
stbglUseProgram(main_prog);
|
||||||
|
setup_uniforms(campos); // set uniforms to default values inefficiently
|
||||||
|
glActiveTextureARB(GL_TEXTURE2_ARB);
|
||||||
|
stbglEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
{
|
||||||
|
float lighting[2][3] = { { campos[0],campos[1],campos[2] }, { 0.75,0.75,0.65f } };
|
||||||
|
float bright = 8;
|
||||||
|
lighting[1][0] *= bright;
|
||||||
|
lighting[1][1] *= bright;
|
||||||
|
lighting[1][2] *= bright;
|
||||||
|
stbglUniform3fv(stbgl_find_uniform(main_prog, "light_source"), 2, lighting[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global_hack)
|
||||||
|
set_tex2_alpha(tex2_alpha);
|
||||||
|
|
||||||
|
num_meshes_uploaded = 0;
|
||||||
|
update_meshes_from_render_thread();
|
||||||
|
|
||||||
|
// traverse all in-range chunks and analyze them
|
||||||
|
for (j=-view_dist_in_chunks; j <= view_dist_in_chunks; j += 2) {
|
||||||
|
for (i=-view_dist_in_chunks; i <= view_dist_in_chunks; i += 2) {
|
||||||
|
float priority;
|
||||||
|
int cx = qchunk_x + i;
|
||||||
|
int cy = qchunk_y + j;
|
||||||
|
|
||||||
|
priority = compute_priority(cx, cy, x, y);
|
||||||
|
if (priority >= min_priority) {
|
||||||
|
int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1);
|
||||||
|
int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1);
|
||||||
|
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
|
||||||
|
++chunk_locations;
|
||||||
|
if (cm->state == STATE_valid && priority >= 0) {
|
||||||
|
// check if chunk pos actually matches
|
||||||
|
if (cm->chunk_x != cx || cm->chunk_y != cy) {
|
||||||
|
// we have a stale chunk we need to recreate
|
||||||
|
free_chunk(slot_x, slot_y); // it probably will have already gotten freed, but just in case
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cm->state == STATE_invalid) {
|
||||||
|
cm->chunk_x = cx;
|
||||||
|
cm->chunk_y = cy;
|
||||||
|
cm->state = STATE_needed;
|
||||||
|
}
|
||||||
|
cm->priority = priority;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw front-to-back
|
||||||
|
for (rad = 0; rad <= view_dist_in_chunks; rad += 2) {
|
||||||
|
for (j=-rad; j <= rad; j += 2) {
|
||||||
|
// if j is +- rad, then iterate i through all values
|
||||||
|
// if j isn't +-rad, then i should be only -rad & rad
|
||||||
|
int step = 2;
|
||||||
|
if (abs(j) != rad)
|
||||||
|
step = 2*rad;
|
||||||
|
for (i=-rad; i <= rad; i += step) {
|
||||||
|
int cx = qchunk_x + i;
|
||||||
|
int cy = qchunk_y + j;
|
||||||
|
int slot_x = (cx>>1) & (CACHED_MESH_NUM_X-1);
|
||||||
|
int slot_y = (cy>>1) & (CACHED_MESH_NUM_Y-1);
|
||||||
|
chunk_mesh *cm = &cached_chunk_mesh[slot_y][slot_x];
|
||||||
|
if (cm->state == STATE_valid && cm->priority >= 0) {
|
||||||
|
++chunks_considered;
|
||||||
|
quads_considered += cm->num_quads;
|
||||||
|
if (is_box_in_frustum(cm->bounds[0], cm->bounds[1])) {
|
||||||
|
++chunks_in_frustum;
|
||||||
|
|
||||||
|
// @TODO if in range
|
||||||
|
stbglUniform3fv(uniform_loc[STBVOX_UNIFORM_transform], 3, cm->transform[0]);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, cm->vbuf);
|
||||||
|
glVertexAttribIPointer(0, 1, GL_UNSIGNED_INT, 4, (void*) 0);
|
||||||
|
glBindTexture(GL_TEXTURE_BUFFER_ARB, cm->fbuf_tex);
|
||||||
|
glDrawArrays(GL_QUADS, 0, cm->num_quads*4);
|
||||||
|
quads_rendered += cm->num_quads;
|
||||||
|
|
||||||
|
chunk_storage_rendered += cm->vbuf_size + cm->fbuf_size;
|
||||||
|
}
|
||||||
|
chunk_storage_considered += cm->vbuf_size + cm->fbuf_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbglDisableVertexAttribArray(0);
|
||||||
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
||||||
|
glActiveTextureARB(GL_TEXTURE0_ARB);
|
||||||
|
|
||||||
|
stbglUseProgram(0);
|
||||||
|
num_meshes_started = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
#define MAX_QUEUE 8
|
||||||
|
float highest_priority[MAX_QUEUE];
|
||||||
|
int highest_i[MAX_QUEUE], highest_j[MAX_QUEUE];
|
||||||
|
float lowest_priority = view_dist_in_chunks * view_dist_in_chunks * 16 * 16.0f;
|
||||||
|
int lowest_i = -1, lowest_j = -1;
|
||||||
|
|
||||||
|
for (i=0; i < MAX_QUEUE; ++i) {
|
||||||
|
highest_priority[i] = min_priority;
|
||||||
|
highest_i[i] = -1;
|
||||||
|
highest_j[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=0; j < CACHED_MESH_NUM_Y; ++j) {
|
||||||
|
for (i=0; i < CACHED_MESH_NUM_X; ++i) {
|
||||||
|
chunk_mesh *cm = &cached_chunk_mesh[j][i];
|
||||||
|
if (cm->state == STATE_valid) {
|
||||||
|
cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y);
|
||||||
|
chunk_storage_total += cm->vbuf_size + cm->fbuf_size;
|
||||||
|
if (cm->priority < lowest_priority) {
|
||||||
|
lowest_priority = cm->priority;
|
||||||
|
lowest_i = i;
|
||||||
|
lowest_j = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cm->state == STATE_needed) {
|
||||||
|
cm->priority = compute_priority(cm->chunk_x, cm->chunk_y, x, y);
|
||||||
|
if (cm->priority < min_priority)
|
||||||
|
cm->state = STATE_invalid;
|
||||||
|
else if (cm->priority > highest_priority[0]) {
|
||||||
|
int k;
|
||||||
|
highest_priority[0] = cm->priority;
|
||||||
|
highest_i[0] = i;
|
||||||
|
highest_j[0] = j;
|
||||||
|
// bubble this up to right place
|
||||||
|
for (k=0; k < MAX_QUEUE-1; ++k) {
|
||||||
|
if (highest_priority[k] > highest_priority[k+1]) {
|
||||||
|
highest_priority[k] = highest_priority[k+1];
|
||||||
|
highest_priority[k+1] = cm->priority;
|
||||||
|
highest_i[k] = highest_i[k+1];
|
||||||
|
highest_i[k+1] = i;
|
||||||
|
highest_j[k] = highest_j[k+1];
|
||||||
|
highest_j[k+1] = j;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// I couldn't find any straightforward logic that avoids
|
||||||
|
// the hysteresis problem of continually creating & freeing
|
||||||
|
// a block on the margin, so I just don't free a block until
|
||||||
|
// it's out of range, but this doesn't actually correctly
|
||||||
|
// handle when the cache is too small for the given range
|
||||||
|
if (chunk_storage_total >= min_chunk_storage && lowest_i >= 0) {
|
||||||
|
if (cached_chunk_mesh[lowest_j][lowest_i].priority < -1200) // -1000? 0?
|
||||||
|
free_chunk(lowest_i, lowest_j);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk_storage_total < max_chunk_storage && highest_i[0] >= 0) {
|
||||||
|
for (j=MAX_QUEUE-1; j >= 0; --j) {
|
||||||
|
if (highest_j[0] >= 0) {
|
||||||
|
chunk_mesh *cm = &cached_chunk_mesh[highest_j[j]][highest_i[j]];
|
||||||
|
if (request_chunk(cm->chunk_x, cm->chunk_y)) {
|
||||||
|
cm->state = STATE_requested;
|
||||||
|
} else {
|
||||||
|
// if we couldn't queue this one, skip the remainder
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_meshes_from_render_thread();
|
||||||
|
|
||||||
|
num_threads_active = 0;
|
||||||
|
for (i=0; i < num_mesh_workers; ++i) {
|
||||||
|
num_threads_active += (mesh_data[i].state == WSTATE_running);
|
||||||
|
}
|
||||||
|
}
|
157
external/stb/stb/tests/caveview/caveview.dsp
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="caveview" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Application" 0x0101
|
||||||
|
|
||||||
|
CFG=caveview - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "caveview.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "caveview.mak" CFG="caveview - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "caveview - Win32 Release" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE "caveview - Win32 Debug" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "caveview - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /MD /W3 /WX /GX /Zd /O2 /I "../.." /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386
|
||||||
|
# SUBTRACT LINK32 /map
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "caveview - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /MDd /W3 /WX /Gm /GX /Zi /Od /I "../.." /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
|
||||||
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl2.lib opengl32.lib glu32.lib winmm.lib sdl2_mixer.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "caveview - Win32 Release"
|
||||||
|
# Name "caveview - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cave_main.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cave_mesher.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cave_parse.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cave_parse.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\cave_render.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\caveview.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\glext.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\glext_list.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\README.md
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\win32\SDL_windows_main.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\stb.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\stb_easy_font.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\stb_gl.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\stb_glprog.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\stb_image.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\..\stb_voxel_render.h
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
29
external/stb/stb/tests/caveview/caveview.dsw
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||||
|
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Project: "caveview"=.\caveview.dsp - Package Owner=<4>
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<4>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Global:
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<3>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
50
external/stb/stb/tests/caveview/caveview.h
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef INCLUDE_CAVEVIEW_H
|
||||||
|
#define INCLUDE_CAVEVIEW_H
|
||||||
|
|
||||||
|
#include "stb.h"
|
||||||
|
|
||||||
|
#include "stb_voxel_render.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int cx,cy;
|
||||||
|
|
||||||
|
stbvox_mesh_maker mm;
|
||||||
|
|
||||||
|
uint8 *build_buffer;
|
||||||
|
uint8 *face_buffer;
|
||||||
|
|
||||||
|
int num_quads;
|
||||||
|
float transform[3][3];
|
||||||
|
float bounds[2][3];
|
||||||
|
|
||||||
|
uint8 sv_blocktype[34][34][18];
|
||||||
|
uint8 sv_lighting [34][34][18];
|
||||||
|
} raw_mesh;
|
||||||
|
|
||||||
|
// a 3D checkerboard of empty,solid would be: 32x32x255x6/2 ~= 800000
|
||||||
|
// an all-leaf qchunk would be: 32 x 32 x 255 x 6 ~= 1,600,000
|
||||||
|
|
||||||
|
#define BUILD_QUAD_MAX 400000
|
||||||
|
#define BUILD_BUFFER_SIZE (4*4*BUILD_QUAD_MAX) // 4 bytes per vertex, 4 vertices per quad
|
||||||
|
#define FACE_BUFFER_SIZE ( 4*BUILD_QUAD_MAX) // 4 bytes per quad
|
||||||
|
|
||||||
|
|
||||||
|
extern void mesh_init(void);
|
||||||
|
extern void render_init(void);
|
||||||
|
extern void world_init(void);
|
||||||
|
extern void ods(char *fmt, ...); // output debug string
|
||||||
|
extern void reset_cache_size(int size);
|
||||||
|
|
||||||
|
|
||||||
|
extern void render_caves(float pos[3]);
|
||||||
|
|
||||||
|
|
||||||
|
#include "cave_parse.h" // fast_chunk
|
||||||
|
|
||||||
|
extern fast_chunk *get_converted_fastchunk(int chunk_x, int chunk_y);
|
||||||
|
extern void build_chunk(int chunk_x, int chunk_y, fast_chunk *fc_table[4][4], raw_mesh *rm);
|
||||||
|
extern void reset_cache_size(int size);
|
||||||
|
extern void deref_fastchunk(fast_chunk *fc);
|
||||||
|
|
||||||
|
#endif
|
11124
external/stb/stb/tests/caveview/glext.h
vendored
Normal file
34
external/stb/stb/tests/caveview/glext_list.h
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
GLARB(ActiveTexture,ACTIVETEXTURE)
|
||||||
|
GLARB(ClientActiveTexture,CLIENTACTIVETEXTURE)
|
||||||
|
GLARB(MultiTexCoord2f,MULTITEXCOORD2F)
|
||||||
|
GLEXT(TexImage3D,TEXIMAGE3D)
|
||||||
|
GLEXT(TexSubImage3D,TEXSUBIMAGE3D)
|
||||||
|
GLEXT(GenerateMipmap,GENERATEMIPMAP)
|
||||||
|
GLARB(DebugMessageCallback,DEBUGMESSAGECALLBACK)
|
||||||
|
|
||||||
|
GLCORE(VertexAttribIPointer,VERTEXATTRIBIPOINTER)
|
||||||
|
|
||||||
|
GLEXT(BindFramebuffer,BINDFRAMEBUFFER)
|
||||||
|
GLEXT(DeleteFramebuffers,DELETEFRAMEBUFFERS)
|
||||||
|
GLEXT(GenFramebuffers,GENFRAMEBUFFERS)
|
||||||
|
GLEXT(CheckFramebufferStatus,CHECKFRAMEBUFFERSTATUS)
|
||||||
|
GLEXT(FramebufferTexture2D,FRAMEBUFFERTEXTURE2D)
|
||||||
|
GLEXT(BindRenderBuffer,BINDRENDERBUFFER)
|
||||||
|
GLEXT(RenderbufferStorage,RENDERBUFFERSTORAGE)
|
||||||
|
GLEXT(GenRenderbuffers,GENRENDERBUFFERS)
|
||||||
|
GLEXT(BindRenderbuffer,BINDRENDERBUFFER)
|
||||||
|
GLEXT(FramebufferRenderbuffer,FRAMEBUFFERRENDERBUFFER)
|
||||||
|
GLEXT(GenerateMipmap,GENERATEMIPMAP)
|
||||||
|
|
||||||
|
GLARB(BindBuffer ,BINDBUFFER,)
|
||||||
|
GLARB(GenBuffers ,GENBUFFERS )
|
||||||
|
GLARB(DeleteBuffers,DELETEBUFFERS)
|
||||||
|
GLARB(BufferData ,BUFFERDATA )
|
||||||
|
GLARB(BufferSubData,BUFFERSUBDATA)
|
||||||
|
GLARB(MapBuffer ,MAPBUFFER )
|
||||||
|
GLARB(UnmapBuffer ,UNMAPBUFFER )
|
||||||
|
GLARB(TexBuffer ,TEXBUFFER )
|
||||||
|
|
||||||
|
GLEXT(NamedBufferStorage,NAMEDBUFFERSTORAGE)
|
||||||
|
GLE(BufferStorage,BUFFERSTORAGE)
|
||||||
|
GLE(GetStringi,GETSTRINGI)
|
0
external/stb/stb/tests/caveview/main.c
vendored
Normal file
1103
external/stb/stb/tests/caveview/stb_gl.h
vendored
Normal file
504
external/stb/stb/tests/caveview/stb_glprog.h
vendored
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
// stb_glprog v0.02 public domain functions to reduce GLSL boilerplate
|
||||||
|
// http://nothings.org/stb/stb_glprog.h especially with GL1 + ARB extensions
|
||||||
|
//
|
||||||
|
// Following defines *before* including have following effects:
|
||||||
|
//
|
||||||
|
// STB_GLPROG_IMPLEMENTATION
|
||||||
|
// creates the implementation
|
||||||
|
//
|
||||||
|
// STB_GLPROG_STATIC
|
||||||
|
// forces the implementation to be static (private to file that creates it)
|
||||||
|
//
|
||||||
|
// STB_GLPROG_ARB
|
||||||
|
// uses ARB extension names for GLSL functions and enumerants instead of core names
|
||||||
|
//
|
||||||
|
// STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||||
|
// instantiates function pointers needed, static to implementing file
|
||||||
|
// to avoid collisions (but will collide if implementing file also
|
||||||
|
// defines any; best to isolate this to its own file in this case).
|
||||||
|
// This will try to automatically #include glext.h, but if it's not
|
||||||
|
// in the default include directories you'll need to include it
|
||||||
|
// yourself and define the next macro.
|
||||||
|
//
|
||||||
|
// STB_GLPROG_SUPPRESS_GLEXT_INCLUDE
|
||||||
|
// disables the automatic #include of glext.h which is normally
|
||||||
|
// forced by STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||||
|
//
|
||||||
|
// So, e.g., sample usage on an old Windows compiler:
|
||||||
|
//
|
||||||
|
// #define STB_GLPROG_IMPLEMENTATION
|
||||||
|
// #define STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||||
|
// #include <windows.h>
|
||||||
|
// #include "gl/gl.h"
|
||||||
|
// #include "stb_glprog.h"
|
||||||
|
//
|
||||||
|
// Note though that the header-file version of this (when you don't define
|
||||||
|
// STB_GLPROG_IMPLEMENTATION) still uses GLint and such, so you basically
|
||||||
|
// can only include it in places where you're already including GL, especially
|
||||||
|
// on Windows where including "gl.h" requires (some of) "windows.h".
|
||||||
|
//
|
||||||
|
// See following comment blocks for function documentation.
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
// 2013-12-08 v0.02 slightly simplified API and reduced GL resource usage (@rygorous)
|
||||||
|
// 2013-12-08 v0.01 initial release
|
||||||
|
|
||||||
|
|
||||||
|
// header file section starts here
|
||||||
|
#if !defined(INCLUDE_STB_GLPROG_H)
|
||||||
|
#define INCLUDE_STB_GLPROG_H
|
||||||
|
|
||||||
|
#ifndef STB_GLPROG_STATIC
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////// SHADER CREATION
|
||||||
|
|
||||||
|
|
||||||
|
/// EASY API
|
||||||
|
|
||||||
|
extern GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen);
|
||||||
|
// This function returns a compiled program or 0 if there's an error.
|
||||||
|
// To free the created program, call stbgl_delete_program.
|
||||||
|
//
|
||||||
|
// stbgl_create_program(
|
||||||
|
// char **vertex_source, // NULL or one or more strings with the vertex shader source, with a final NULL
|
||||||
|
// char **frag_source, // NULL or one or more strings with the fragment shader source, with a final NULL
|
||||||
|
// char **binds, // NULL or zero or more strings with attribute bind names, with a final NULL
|
||||||
|
// char *error, // output location where compile error message is placed
|
||||||
|
// int error_buflen) // length of error output buffer
|
||||||
|
//
|
||||||
|
// Returns a GLuint with the GL program object handle.
|
||||||
|
//
|
||||||
|
// If an individual bind string is "", no name is bound to that slot (this
|
||||||
|
// allows you to create binds that aren't continuous integers starting at 0).
|
||||||
|
//
|
||||||
|
// If the vertex shader is NULL, then fixed-function vertex pipeline
|
||||||
|
// is used, if that's legal in your version of GL.
|
||||||
|
//
|
||||||
|
// If the fragment shader is NULL, then fixed-function fragment pipeline
|
||||||
|
// is used, if that's legal in your version of GL.
|
||||||
|
|
||||||
|
extern void stgbl_delete_program(GLuint program);
|
||||||
|
// deletes a program created by stbgl_create_program or stbgl_link_program
|
||||||
|
|
||||||
|
|
||||||
|
/// FLEXIBLE API
|
||||||
|
|
||||||
|
extern GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen);
|
||||||
|
// compiles a shader. returns the shader on success or 0 on failure.
|
||||||
|
//
|
||||||
|
// type either: GL_VERTEX_SHADER or GL_FRAGMENT_SHADER
|
||||||
|
// or GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB
|
||||||
|
// or STBGL_VERTEX_SHADER or STBGL_FRAGMENT_SHADER
|
||||||
|
// sources array of strings containing the shader source
|
||||||
|
// num_sources number of string in sources, or -1 meaning sources is NULL-terminated
|
||||||
|
// error string to output compiler error to
|
||||||
|
// error_buflen length of error buffer in chars
|
||||||
|
|
||||||
|
extern GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen);
|
||||||
|
// links a shader. returns the linked program on success or 0 on failure.
|
||||||
|
//
|
||||||
|
// vertex_shader a compiled vertex shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
|
||||||
|
// fragment_shader a compiled fragment shader from stbgl_compile_shader, or 0 for fixed-function (if legal)
|
||||||
|
//
|
||||||
|
|
||||||
|
extern void stbgl_delete_shader(GLuint shader);
|
||||||
|
// deletes a shader created by stbgl_compile_shader
|
||||||
|
|
||||||
|
|
||||||
|
///////////// RENDERING WITH SHADERS
|
||||||
|
|
||||||
|
extern GLint stbgl_find_uniform(GLuint prog, char *uniform);
|
||||||
|
|
||||||
|
extern void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms);
|
||||||
|
// Given the locations array that is num_uniforms long, fills out
|
||||||
|
// the locations of each of those uniforms for the specified program.
|
||||||
|
// If num_uniforms is -1, then uniforms[] must be NULL-terminated
|
||||||
|
|
||||||
|
// the following functions just wrap the difference in naming between GL2+ and ARB,
|
||||||
|
// so you don't need them unless you're using both ARB and GL2+ in the same codebase,
|
||||||
|
// or you're relying on this lib to provide the extensions
|
||||||
|
extern void stbglUseProgram(GLuint program);
|
||||||
|
extern void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer);
|
||||||
|
extern void stbglEnableVertexAttribArray(GLuint index);
|
||||||
|
extern void stbglDisableVertexAttribArray(GLuint index);
|
||||||
|
extern void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v);
|
||||||
|
extern void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v);
|
||||||
|
extern void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v);
|
||||||
|
extern void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v);
|
||||||
|
extern void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v);
|
||||||
|
extern void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v);
|
||||||
|
extern void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v);
|
||||||
|
extern void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v);
|
||||||
|
extern void stbglUniform1f(GLint loc, float v0);
|
||||||
|
extern void stbglUniform2f(GLint loc, float v0, float v1);
|
||||||
|
extern void stbglUniform3f(GLint loc, float v0, float v1, float v2);
|
||||||
|
extern void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3);
|
||||||
|
extern void stbglUniform1i(GLint loc, GLint v0);
|
||||||
|
extern void stbglUniform2i(GLint loc, GLint v0, GLint v1);
|
||||||
|
extern void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2);
|
||||||
|
extern void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3);
|
||||||
|
|
||||||
|
|
||||||
|
////////////// END OF FUNCTIONS
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // STB_GLPROG_STATIC
|
||||||
|
|
||||||
|
#ifdef STB_GLPROG_ARB
|
||||||
|
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER_ARB
|
||||||
|
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB
|
||||||
|
#else
|
||||||
|
#define STBGL_VERTEX_SHADER GL_VERTEX_SHADER
|
||||||
|
#define STBGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // INCLUDE_STB_GLPROG_H
|
||||||
|
|
||||||
|
|
||||||
|
///////// header file section ends here
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef STB_GLPROG_IMPLEMENTATION
|
||||||
|
#include <string.h> // strncpy
|
||||||
|
|
||||||
|
#ifdef STB_GLPROG_STATIC
|
||||||
|
#define STB_GLPROG_DECLARE static
|
||||||
|
#else
|
||||||
|
#define STB_GLPROG_DECLARE extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// check if user wants this file to define the GL extensions itself
|
||||||
|
#ifdef STB_GLPROG_ARB_DEFINE_EXTENSIONS
|
||||||
|
#define STB_GLPROG_ARB // make sure later code uses the extensions
|
||||||
|
|
||||||
|
#ifndef STB_GLPROG_SUPPRESS_GLEXT_INCLUDE
|
||||||
|
#include "glext.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STB_GLPROG_EXTENSIONS \
|
||||||
|
STB_GLPROG_FUNC(ATTACHOBJECT , AttachObject ) \
|
||||||
|
STB_GLPROG_FUNC(BINDATTRIBLOCATION , BindAttribLocation ) \
|
||||||
|
STB_GLPROG_FUNC(COMPILESHADER , CompileShader ) \
|
||||||
|
STB_GLPROG_FUNC(CREATEPROGRAMOBJECT , CreateProgramObject ) \
|
||||||
|
STB_GLPROG_FUNC(CREATESHADEROBJECT , CreateShaderObject ) \
|
||||||
|
STB_GLPROG_FUNC(DELETEOBJECT , DeleteObject ) \
|
||||||
|
STB_GLPROG_FUNC(DETACHOBJECT , DetachObject ) \
|
||||||
|
STB_GLPROG_FUNC(DISABLEVERTEXATTRIBARRAY, DisableVertexAttribArray) \
|
||||||
|
STB_GLPROG_FUNC(ENABLEVERTEXATTRIBARRAY, EnableVertexAttribArray ) \
|
||||||
|
STB_GLPROG_FUNC(GETATTACHEDOBJECTS , GetAttachedObjects ) \
|
||||||
|
STB_GLPROG_FUNC(GETOBJECTPARAMETERIV, GetObjectParameteriv) \
|
||||||
|
STB_GLPROG_FUNC(GETINFOLOG , GetInfoLog ) \
|
||||||
|
STB_GLPROG_FUNC(GETUNIFORMLOCATION , GetUniformLocation ) \
|
||||||
|
STB_GLPROG_FUNC(LINKPROGRAM , LinkProgram ) \
|
||||||
|
STB_GLPROG_FUNC(SHADERSOURCE , ShaderSource ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM1F , Uniform1f ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM2F , Uniform2f ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM3F , Uniform3f ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM4F , Uniform4f ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM1I , Uniform1i ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM2I , Uniform2i ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM3I , Uniform3i ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM4I , Uniform4i ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM1FV , Uniform1fv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM2FV , Uniform2fv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM3FV , Uniform3fv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM4FV , Uniform4fv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM1IV , Uniform1iv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM2IV , Uniform2iv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM3IV , Uniform3iv ) \
|
||||||
|
STB_GLPROG_FUNC(UNIFORM4IV , Uniform4iv ) \
|
||||||
|
STB_GLPROG_FUNC(USEPROGRAMOBJECT , UseProgramObject ) \
|
||||||
|
STB_GLPROG_FUNC(VERTEXATTRIBPOINTER , VertexAttribPointer )
|
||||||
|
|
||||||
|
// define the static function pointers
|
||||||
|
|
||||||
|
#define STB_GLPROG_FUNC(x,y) static PFNGL##x##ARBPROC gl##y##ARB;
|
||||||
|
STB_GLPROG_EXTENSIONS
|
||||||
|
#undef STB_GLPROG_FUNC
|
||||||
|
|
||||||
|
// define the GetProcAddress
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifndef WINGDIAPI
|
||||||
|
#ifndef STB__HAS_WGLPROC
|
||||||
|
typedef int (__stdcall *stbgl__voidfunc)(void);
|
||||||
|
static __declspec(dllimport) stbgl__voidfunc wglGetProcAddress(char *);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#define STBGL__GET_FUNC(x) wglGetProcAddress(x)
|
||||||
|
#else
|
||||||
|
#error "need to define how this platform gets extensions"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// create a function that fills out the function pointers
|
||||||
|
|
||||||
|
static void stb_glprog_init(void)
|
||||||
|
{
|
||||||
|
static int initialized = 0; // not thread safe!
|
||||||
|
if (initialized) return;
|
||||||
|
#define STB_GLPROG_FUNC(x,y) gl##y##ARB = (PFNGL##x##ARBPROC) STBGL__GET_FUNC("gl" #y "ARB");
|
||||||
|
STB_GLPROG_EXTENSIONS
|
||||||
|
#undef STB_GLPROG_FUNC
|
||||||
|
}
|
||||||
|
#undef STB_GLPROG_EXTENSIONS
|
||||||
|
|
||||||
|
#else
|
||||||
|
static void stb_glprog_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// define generic names for many of the gl functions or extensions for later use;
|
||||||
|
// note that in some cases there are two functions in core and one function in ARB
|
||||||
|
#ifdef STB_GLPROG_ARB
|
||||||
|
#define stbglCreateShader glCreateShaderObjectARB
|
||||||
|
#define stbglDeleteShader glDeleteObjectARB
|
||||||
|
#define stbglAttachShader glAttachObjectARB
|
||||||
|
#define stbglDetachShader glDetachObjectARB
|
||||||
|
#define stbglShaderSource glShaderSourceARB
|
||||||
|
#define stbglCompileShader glCompileShaderARB
|
||||||
|
#define stbglGetShaderStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_COMPILE_STATUS_ARB, b)
|
||||||
|
#define stbglGetShaderInfoLog glGetInfoLogARB
|
||||||
|
#define stbglCreateProgram glCreateProgramObjectARB
|
||||||
|
#define stbglDeleteProgram glDeleteObjectARB
|
||||||
|
#define stbglLinkProgram glLinkProgramARB
|
||||||
|
#define stbglGetProgramStatus(a,b) glGetObjectParameterivARB(a, GL_OBJECT_LINK_STATUS_ARB, b)
|
||||||
|
#define stbglGetProgramInfoLog glGetInfoLogARB
|
||||||
|
#define stbglGetAttachedShaders glGetAttachedObjectsARB
|
||||||
|
#define stbglBindAttribLocation glBindAttribLocationARB
|
||||||
|
#define stbglGetUniformLocation glGetUniformLocationARB
|
||||||
|
#define stbgl_UseProgram glUseProgramObjectARB
|
||||||
|
#else
|
||||||
|
#define stbglCreateShader glCreateShader
|
||||||
|
#define stbglDeleteShader glDeleteShader
|
||||||
|
#define stbglAttachShader glAttachShader
|
||||||
|
#define stbglDetachShader glDetachShader
|
||||||
|
#define stbglShaderSource glShaderSource
|
||||||
|
#define stbglCompileShader glCompileShader
|
||||||
|
#define stbglGetShaderStatus(a,b) glGetShaderiv(a, GL_COMPILE_STATUS, b)
|
||||||
|
#define stbglGetShaderInfoLog glGetShaderInfoLog
|
||||||
|
#define stbglCreateProgram glCreateProgram
|
||||||
|
#define stbglDeleteProgram glDeleteProgram
|
||||||
|
#define stbglLinkProgram glLinkProgram
|
||||||
|
#define stbglGetProgramStatus(a,b) glGetProgramiv(a, GL_LINK_STATUS, b)
|
||||||
|
#define stbglGetProgramInfoLog glGetProgramInfoLog
|
||||||
|
#define stbglGetAttachedShaders glGetAttachedShaders
|
||||||
|
#define stbglBindAttribLocation glBindAttribLocation
|
||||||
|
#define stbglGetUniformLocation glGetUniformLocation
|
||||||
|
#define stbgl_UseProgram glUseProgram
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// perform a safe strcat of 3 strings, given that we can't rely on portable snprintf
|
||||||
|
// if you need to break on error, this is the best place to place a breakpoint
|
||||||
|
static void stb_glprog_error(char *error, int error_buflen, char *str1, char *str2, char *str3)
|
||||||
|
{
|
||||||
|
int n = strlen(str1);
|
||||||
|
strncpy(error, str1, error_buflen);
|
||||||
|
if (n < error_buflen && str2) {
|
||||||
|
strncpy(error+n, str2, error_buflen - n);
|
||||||
|
n += strlen(str2);
|
||||||
|
if (n < error_buflen && str3) {
|
||||||
|
strncpy(error+n, str3, error_buflen - n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error[error_buflen-1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE GLuint stbgl_compile_shader(GLenum type, char const **sources, int num_sources, char *error, int error_buflen)
|
||||||
|
{
|
||||||
|
char *typename = (type == STBGL_VERTEX_SHADER ? "vertex" : "fragment");
|
||||||
|
int len;
|
||||||
|
GLint result;
|
||||||
|
GLuint shader;
|
||||||
|
|
||||||
|
// initialize the extensions if we haven't already
|
||||||
|
stb_glprog_init();
|
||||||
|
|
||||||
|
// allocate
|
||||||
|
|
||||||
|
shader = stbglCreateShader(type);
|
||||||
|
if (!shader) {
|
||||||
|
stb_glprog_error(error, error_buflen, "Couldn't allocate shader object in stbgl_compile_shader for ", typename, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile
|
||||||
|
|
||||||
|
// if num_sources is negative, assume source is NULL-terminated and count the non-NULL ones
|
||||||
|
if (num_sources < 0)
|
||||||
|
for (num_sources = 0; sources[num_sources] != NULL; ++num_sources)
|
||||||
|
;
|
||||||
|
stbglShaderSource(shader, num_sources, sources, NULL);
|
||||||
|
stbglCompileShader(shader);
|
||||||
|
stbglGetShaderStatus(shader, &result);
|
||||||
|
if (result)
|
||||||
|
return shader;
|
||||||
|
|
||||||
|
// errors
|
||||||
|
|
||||||
|
stb_glprog_error(error, error_buflen, "Compile error for ", typename, " shader: ");
|
||||||
|
len = strlen(error);
|
||||||
|
if (len < error_buflen)
|
||||||
|
stbglGetShaderInfoLog(shader, error_buflen-len, NULL, error+len);
|
||||||
|
|
||||||
|
stbglDeleteShader(shader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE GLuint stbgl_link_program(GLuint vertex_shader, GLuint fragment_shader, char const **binds, int num_binds, char *error, int error_buflen)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
GLint result;
|
||||||
|
|
||||||
|
// allocate
|
||||||
|
|
||||||
|
GLuint prog = stbglCreateProgram();
|
||||||
|
if (!prog) {
|
||||||
|
stb_glprog_error(error, error_buflen, "Couldn't allocate program object in stbgl_link_program", NULL, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attach
|
||||||
|
|
||||||
|
if (vertex_shader)
|
||||||
|
stbglAttachShader(prog, vertex_shader);
|
||||||
|
if (fragment_shader)
|
||||||
|
stbglAttachShader(prog, fragment_shader);
|
||||||
|
|
||||||
|
// attribute binds
|
||||||
|
|
||||||
|
if (binds) {
|
||||||
|
int i;
|
||||||
|
// if num_binds is negative, then it is NULL terminated
|
||||||
|
if (num_binds < 0)
|
||||||
|
for (num_binds=0; binds[num_binds]; ++num_binds)
|
||||||
|
;
|
||||||
|
for (i=0; i < num_binds; ++i)
|
||||||
|
if (binds[i] && binds[i][0]) // empty binds can be NULL or ""
|
||||||
|
stbglBindAttribLocation(prog, i, binds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// link
|
||||||
|
|
||||||
|
stbglLinkProgram(prog);
|
||||||
|
|
||||||
|
// detach
|
||||||
|
|
||||||
|
if (vertex_shader)
|
||||||
|
stbglDetachShader(prog, vertex_shader);
|
||||||
|
if (fragment_shader)
|
||||||
|
stbglDetachShader(prog, fragment_shader);
|
||||||
|
|
||||||
|
// errors
|
||||||
|
|
||||||
|
stbglGetProgramStatus(prog, &result);
|
||||||
|
if (result)
|
||||||
|
return prog;
|
||||||
|
|
||||||
|
stb_glprog_error(error, error_buflen, "Link error: ", NULL, NULL);
|
||||||
|
len = strlen(error);
|
||||||
|
if (len < error_buflen)
|
||||||
|
stbglGetProgramInfoLog(prog, error_buflen-len, NULL, error+len);
|
||||||
|
|
||||||
|
stbglDeleteProgram(prog);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE GLuint stbgl_create_program(char const **vertex_source, char const **frag_source, char const **binds, char *error, int error_buflen)
|
||||||
|
{
|
||||||
|
GLuint vertex, fragment, prog=0;
|
||||||
|
vertex = stbgl_compile_shader(STBGL_VERTEX_SHADER, vertex_source, -1, error, error_buflen);
|
||||||
|
if (vertex) {
|
||||||
|
fragment = stbgl_compile_shader(STBGL_FRAGMENT_SHADER, frag_source, -1, error, error_buflen);
|
||||||
|
if (fragment)
|
||||||
|
prog = stbgl_link_program(vertex, fragment, binds, -1, error, error_buflen);
|
||||||
|
if (fragment)
|
||||||
|
stbglDeleteShader(fragment);
|
||||||
|
stbglDeleteShader(vertex);
|
||||||
|
}
|
||||||
|
return prog;
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbgl_delete_shader(GLuint shader)
|
||||||
|
{
|
||||||
|
stbglDeleteShader(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stgbl_delete_program(GLuint program)
|
||||||
|
{
|
||||||
|
stbglDeleteProgram(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint stbgl_find_uniform(GLuint prog, char *uniform)
|
||||||
|
{
|
||||||
|
return stbglGetUniformLocation(prog, uniform);
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbgl_find_uniforms(GLuint prog, GLint *locations, char const **uniforms, int num_uniforms)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (num_uniforms < 0)
|
||||||
|
num_uniforms = 999999;
|
||||||
|
for (i=0; i < num_uniforms && uniforms[i]; ++i)
|
||||||
|
locations[i] = stbglGetUniformLocation(prog, uniforms[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglUseProgram(GLuint program)
|
||||||
|
{
|
||||||
|
stbgl_UseProgram(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef STB_GLPROG_ARB
|
||||||
|
#define STBGL_ARBIFY(name) name##ARB
|
||||||
|
#else
|
||||||
|
#define STBGL_ARBIFY(name) name
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
|
||||||
|
{
|
||||||
|
STBGL_ARBIFY(glVertexAttribPointer)(index, size, type, normalized, stride, pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglEnableVertexAttribArray (GLuint index) { STBGL_ARBIFY(glEnableVertexAttribArray )(index); }
|
||||||
|
STB_GLPROG_DECLARE void stbglDisableVertexAttribArray(GLuint index) { STBGL_ARBIFY(glDisableVertexAttribArray)(index); }
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform1fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform1fv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform2fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform2fv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform3fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform3fv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform4fv(GLint loc, GLsizei count, const GLfloat *v) { STBGL_ARBIFY(glUniform4fv)(loc,count,v); }
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform1iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform1iv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform2iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform2iv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform3iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform3iv)(loc,count,v); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform4iv(GLint loc, GLsizei count, const GLint *v) { STBGL_ARBIFY(glUniform4iv)(loc,count,v); }
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform1f(GLint loc, float v0)
|
||||||
|
{ STBGL_ARBIFY(glUniform1f)(loc,v0); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform2f(GLint loc, float v0, float v1)
|
||||||
|
{ STBGL_ARBIFY(glUniform2f)(loc,v0,v1); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform3f(GLint loc, float v0, float v1, float v2)
|
||||||
|
{ STBGL_ARBIFY(glUniform3f)(loc,v0,v1,v2); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform4f(GLint loc, float v0, float v1, float v2, float v3)
|
||||||
|
{ STBGL_ARBIFY(glUniform4f)(loc,v0,v1,v2,v3); }
|
||||||
|
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform1i(GLint loc, GLint v0)
|
||||||
|
{ STBGL_ARBIFY(glUniform1i)(loc,v0); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform2i(GLint loc, GLint v0, GLint v1)
|
||||||
|
{ STBGL_ARBIFY(glUniform2i)(loc,v0,v1); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform3i(GLint loc, GLint v0, GLint v1, GLint v2)
|
||||||
|
{ STBGL_ARBIFY(glUniform3i)(loc,v0,v1,v2); }
|
||||||
|
STB_GLPROG_DECLARE void stbglUniform4i(GLint loc, GLint v0, GLint v1, GLint v2, GLint v3)
|
||||||
|
{ STBGL_ARBIFY(glUniform4i)(loc,v0,v1,v2,v3); }
|
||||||
|
|
||||||
|
#endif
|
224
external/stb/stb/tests/caveview/win32/SDL_windows_main.c
vendored
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98
|
||||||
|
|
||||||
|
The WinMain function -- calls your program's main() function
|
||||||
|
*/
|
||||||
|
#include "SDL_config.h"
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
|
||||||
|
//#include "../../core/windows/SDL_windows.h"
|
||||||
|
|
||||||
|
/* Include this so we define UNICODE properly */
|
||||||
|
#if defined(__WIN32__)
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define STRICT
|
||||||
|
#ifndef UNICODE
|
||||||
|
#define UNICODE 1
|
||||||
|
#endif
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x501 /* Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
/* Routines to convert from UTF8 to native Windows text */
|
||||||
|
#if UNICODE
|
||||||
|
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "UTF-16LE", (char *)(S), (SDL_wcslen(S)+1)*sizeof(WCHAR))
|
||||||
|
#define WIN_UTF8ToString(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (char *)(S), SDL_strlen(S)+1)
|
||||||
|
#else
|
||||||
|
/* !!! FIXME: UTF8ToString() can just be a SDL_strdup() here. */
|
||||||
|
#define WIN_StringToUTF8(S) SDL_iconv_string("UTF-8", "ASCII", (char *)(S), (SDL_strlen(S)+1))
|
||||||
|
#define WIN_UTF8ToString(S) SDL_iconv_string("ASCII", "UTF-8", (char *)(S), SDL_strlen(S)+1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Sets an error message based on a given HRESULT */
|
||||||
|
extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
|
||||||
|
|
||||||
|
/* Sets an error message based on GetLastError(). Always return -1. */
|
||||||
|
extern int WIN_SetError(const char *prefix);
|
||||||
|
|
||||||
|
/* Wrap up the oddities of CoInitialize() into a common function. */
|
||||||
|
extern HRESULT WIN_CoInitialize(void);
|
||||||
|
extern void WIN_CoUninitialize(void);
|
||||||
|
|
||||||
|
/* Returns SDL_TRUE if we're running on Windows Vista and newer */
|
||||||
|
extern BOOL WIN_IsWindowsVistaOrGreater();
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Include the SDL main definition header */
|
||||||
|
#include "SDL.h"
|
||||||
|
#include "SDL_main.h"
|
||||||
|
|
||||||
|
#ifdef main
|
||||||
|
# undef main
|
||||||
|
#endif /* main */
|
||||||
|
|
||||||
|
static void
|
||||||
|
UnEscapeQuotes(char *arg)
|
||||||
|
{
|
||||||
|
char *last = NULL;
|
||||||
|
|
||||||
|
while (*arg) {
|
||||||
|
if (*arg == '"' && (last != NULL && *last == '\\')) {
|
||||||
|
char *c_curr = arg;
|
||||||
|
char *c_last = last;
|
||||||
|
|
||||||
|
while (*c_curr) {
|
||||||
|
*c_last = *c_curr;
|
||||||
|
c_last = c_curr;
|
||||||
|
c_curr++;
|
||||||
|
}
|
||||||
|
*c_last = '\0';
|
||||||
|
}
|
||||||
|
last = arg;
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a command line buffer into arguments */
|
||||||
|
static int
|
||||||
|
ParseCommandLine(char *cmdline, char **argv)
|
||||||
|
{
|
||||||
|
char *bufp;
|
||||||
|
char *lastp = NULL;
|
||||||
|
int argc, last_argc;
|
||||||
|
|
||||||
|
argc = last_argc = 0;
|
||||||
|
for (bufp = cmdline; *bufp;) {
|
||||||
|
/* Skip leading whitespace */
|
||||||
|
while (SDL_isspace(*bufp)) {
|
||||||
|
++bufp;
|
||||||
|
}
|
||||||
|
/* Skip over argument */
|
||||||
|
if (*bufp == '"') {
|
||||||
|
++bufp;
|
||||||
|
if (*bufp) {
|
||||||
|
if (argv) {
|
||||||
|
argv[argc] = bufp;
|
||||||
|
}
|
||||||
|
++argc;
|
||||||
|
}
|
||||||
|
/* Skip over word */
|
||||||
|
lastp = bufp;
|
||||||
|
while (*bufp && (*bufp != '"' || *lastp == '\\')) {
|
||||||
|
lastp = bufp;
|
||||||
|
++bufp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (*bufp) {
|
||||||
|
if (argv) {
|
||||||
|
argv[argc] = bufp;
|
||||||
|
}
|
||||||
|
++argc;
|
||||||
|
}
|
||||||
|
/* Skip over word */
|
||||||
|
while (*bufp && !SDL_isspace(*bufp)) {
|
||||||
|
++bufp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*bufp) {
|
||||||
|
if (argv) {
|
||||||
|
*bufp = '\0';
|
||||||
|
}
|
||||||
|
++bufp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strip out \ from \" sequences */
|
||||||
|
if (argv && last_argc != argc) {
|
||||||
|
UnEscapeQuotes(argv[last_argc]);
|
||||||
|
}
|
||||||
|
last_argc = argc;
|
||||||
|
}
|
||||||
|
if (argv) {
|
||||||
|
argv[argc] = NULL;
|
||||||
|
}
|
||||||
|
return (argc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show an error message */
|
||||||
|
static void
|
||||||
|
ShowError(const char *title, const char *message)
|
||||||
|
{
|
||||||
|
/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
|
||||||
|
#ifdef USE_MESSAGEBOX
|
||||||
|
MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "%s: %s\n", title, message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pop up an out of memory message, returns to Windows */
|
||||||
|
static BOOL
|
||||||
|
OutOfMemory(void)
|
||||||
|
{
|
||||||
|
ShowError("Fatal Error", "Out of memory - aborting");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
/* The VC++ compiler needs main defined */
|
||||||
|
#define console_main main
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is where execution begins [console apps] */
|
||||||
|
int
|
||||||
|
console_main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
SDL_SetMainReady();
|
||||||
|
|
||||||
|
/* Run the application main() code */
|
||||||
|
status = SDL_main(argc, argv);
|
||||||
|
|
||||||
|
/* Exit cleanly, calling atexit() functions */
|
||||||
|
exit(status);
|
||||||
|
|
||||||
|
/* Hush little compiler, don't you cry... */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where execution begins [windowed apps] */
|
||||||
|
int WINAPI
|
||||||
|
WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
|
||||||
|
{
|
||||||
|
char **argv;
|
||||||
|
int argc;
|
||||||
|
char *cmdline;
|
||||||
|
|
||||||
|
/* Grab the command line */
|
||||||
|
TCHAR *text = GetCommandLine();
|
||||||
|
#if UNICODE
|
||||||
|
cmdline = SDL_iconv_string("UTF-8", "UCS-2-INTERNAL", (char *)(text), (SDL_wcslen(text)+1)*sizeof(WCHAR));
|
||||||
|
#else
|
||||||
|
cmdline = SDL_strdup(text);
|
||||||
|
#endif
|
||||||
|
if (cmdline == NULL) {
|
||||||
|
return OutOfMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse it into argv and argc */
|
||||||
|
argc = ParseCommandLine(cmdline, NULL);
|
||||||
|
argv = SDL_stack_alloc(char *, argc + 1);
|
||||||
|
if (argv == NULL) {
|
||||||
|
return OutOfMemory();
|
||||||
|
}
|
||||||
|
ParseCommandLine(cmdline, argv);
|
||||||
|
|
||||||
|
/* Run the main program */
|
||||||
|
console_main(argc, argv);
|
||||||
|
|
||||||
|
SDL_stack_free(argv);
|
||||||
|
|
||||||
|
SDL_free(cmdline);
|
||||||
|
|
||||||
|
/* Hush little compiler, don't you cry... */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __WIN32__ */
|
||||||
|
|
||||||
|
/* vi: set ts=4 sw=4 expandtab: */
|
54
external/stb/stb/tests/fuzz_main.c
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* fuzz target entry point, works without libFuzzer */
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *buf = NULL;
|
||||||
|
long siz_buf;
|
||||||
|
|
||||||
|
if(argc < 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "no input file\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(argv[1], "rb");
|
||||||
|
if(f == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error opening input file %s\n", argv[1]);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
|
||||||
|
siz_buf = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
if(siz_buf < 1) goto err;
|
||||||
|
|
||||||
|
buf = (char*)malloc((size_t)siz_buf);
|
||||||
|
if(buf == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "malloc() failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fread(buf, (size_t)siz_buf, 1, f) != 1)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "fread() failed\n");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)LLVMFuzzerTestOneInput((uint8_t*)buf, (size_t)siz_buf);
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
363
external/stb/stb/tests/grid_reachability.c
vendored
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
#define STB_CONNECTED_COMPONENTS_IMPLEMENTATION
|
||||||
|
#define STBCC_GRID_COUNT_X_LOG2 10
|
||||||
|
#define STBCC_GRID_COUNT_Y_LOG2 10
|
||||||
|
#include "stb_connected_components.h"
|
||||||
|
|
||||||
|
#ifdef GRID_TEST
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <direct.h>
|
||||||
|
|
||||||
|
//#define STB_DEFINE
|
||||||
|
#include "stb.h"
|
||||||
|
|
||||||
|
//#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
//#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16 x,y;
|
||||||
|
} point;
|
||||||
|
|
||||||
|
point leader[1024][1024];
|
||||||
|
uint32 color[1024][1024];
|
||||||
|
|
||||||
|
point find(int x, int y)
|
||||||
|
{
|
||||||
|
point p,q;
|
||||||
|
p = leader[y][x];
|
||||||
|
if (p.x == x && p.y == y)
|
||||||
|
return p;
|
||||||
|
q = find(p.x, p.y);
|
||||||
|
leader[y][x] = q;
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onion(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
point p = find(x1,y1);
|
||||||
|
point q = find(x2,y2);
|
||||||
|
|
||||||
|
if (p.x == q.x && p.y == q.y)
|
||||||
|
return;
|
||||||
|
|
||||||
|
leader[p.y][p.x] = q;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reference(uint8 *map, int w, int h)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
leader[j][i].x = i;
|
||||||
|
leader[j][i].y = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=1; j < h-1; ++j) {
|
||||||
|
for (i=1; i < w-1; ++i) {
|
||||||
|
if (map[j*w+i] == 255) {
|
||||||
|
if (map[(j+1)*w+i] == 255) onion(i,j, i,j+1);
|
||||||
|
if (map[(j)*w+i+1] == 255) onion(i,j, i+1,j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
uint32 c = 0xff000000;
|
||||||
|
if (leader[j][i].x == i && leader[j][i].y == j) {
|
||||||
|
if (map[j*w+i] == 255)
|
||||||
|
c = stb_randLCG() | 0xff404040;
|
||||||
|
}
|
||||||
|
color[j][i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
if (leader[j][i].x != i || leader[j][i].y != j) {
|
||||||
|
point p = find(i,j);
|
||||||
|
color[j][i] = color[p.y][p.x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_map(stbcc_grid *g, int w, int h, char *filename)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
unsigned int c;
|
||||||
|
c = stbcc_get_unique_id(g,i,j);
|
||||||
|
c = stb_rehash_improved(c)&0xffffff;
|
||||||
|
if (c == STBCC_NULL_UNIQUE_ID)
|
||||||
|
c = 0xff000000;
|
||||||
|
else
|
||||||
|
c = (~c)^0x555555;
|
||||||
|
if (i % 32 == 0 || j %32 == 0) {
|
||||||
|
int r = (c >> 16) & 255;
|
||||||
|
int g = (c >> 8) & 255;
|
||||||
|
int b = c & 255;
|
||||||
|
r = (r+130)/2;
|
||||||
|
g = (g+130)/2;
|
||||||
|
b = (b+130)/2;
|
||||||
|
c = 0xff000000 + (r<<16) + (g<<8) + b;
|
||||||
|
}
|
||||||
|
color[j][i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stbi_write_png(filename, w, h, 4, color, 4*w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_connected(stbcc_grid *g)
|
||||||
|
{
|
||||||
|
int n = stbcc_query_grid_node_connection(g, 512, 90, 512, 871);
|
||||||
|
//printf("%d ", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *message;
|
||||||
|
LARGE_INTEGER start;
|
||||||
|
|
||||||
|
void start_timer(char *s)
|
||||||
|
{
|
||||||
|
message = s;
|
||||||
|
QueryPerformanceCounter(&start);
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_timer(void)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER end, freq;
|
||||||
|
double tm;
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&end);
|
||||||
|
QueryPerformanceFrequency(&freq);
|
||||||
|
|
||||||
|
tm = (end.QuadPart - start.QuadPart) / (double) freq.QuadPart;
|
||||||
|
printf("%6.4lf ms: %s\n", tm * 1000, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void quicktest(void);
|
||||||
|
|
||||||
|
int loc[5000][2];
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stbcc_grid *g;
|
||||||
|
|
||||||
|
int w,h, i,j,k=0, count=0, r;
|
||||||
|
uint8 *map = stbi_load("data/map_03.png", &w, &h, 0, 1);
|
||||||
|
|
||||||
|
assert(map);
|
||||||
|
quicktest();
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j)
|
||||||
|
for (i=0; i < w; ++i)
|
||||||
|
map[j*w+i] = ~map[j*w+i];
|
||||||
|
|
||||||
|
for (i=0; i < w; ++i)
|
||||||
|
for (j=0; j < h; ++j)
|
||||||
|
//map[j*w+i] = (((i+1) ^ (j+1)) >> 1) & 1 ? 255 : 0;
|
||||||
|
map[j*w+i] = stb_max(abs(i-w/2),abs(j-h/2)) & 1 ? 255 : 0;
|
||||||
|
//map[j*w+i] = (((i ^ j) >> 5) ^ (i ^ j)) & 1 ? 255 : 0;
|
||||||
|
//map[j*w+i] = stb_rand() & 1 ? 255 : 0;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
for (i=0; i < 100000; ++i)
|
||||||
|
map[(stb_rand()%h)*w + stb_rand()%w] ^= 255;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_mkdir("tests/output/stbcc");
|
||||||
|
|
||||||
|
stbi_write_png("tests/output/stbcc/reference.png", w, h, 1, map, 0);
|
||||||
|
|
||||||
|
//reference(map, w, h);
|
||||||
|
|
||||||
|
g = malloc(stbcc_grid_sizeof());
|
||||||
|
printf("Size: %d\n", stbcc_grid_sizeof());
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
memset(map, 0, w*h);
|
||||||
|
stbcc_init_grid(g, map, w, h);
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char **s = stb_stringfile("c:/x/clockwork_update.txt", &n);
|
||||||
|
write_map(g, w, h, "tests/output/stbcc/base.png");
|
||||||
|
for (i=1; i < n; i += 1) {
|
||||||
|
int x,y,t;
|
||||||
|
sscanf(s[i], "%d %d %d", &x, &y, &t);
|
||||||
|
if (i == 571678)
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_good.png", i));
|
||||||
|
stbcc_update_grid(g, x, y, t);
|
||||||
|
if (i == 571678)
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_bad.png", i));
|
||||||
|
//if (i > 571648 && i <= 571712)
|
||||||
|
//write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i));
|
||||||
|
}
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/clockwork_%06d.png", i-1));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
start_timer("init");
|
||||||
|
stbcc_init_grid(g, map, w, h);
|
||||||
|
end_timer();
|
||||||
|
//g = stb_file("c:/x/clockwork_path.bin", 0);
|
||||||
|
write_map(g, w, h, "tests/output/stbcc/base.png");
|
||||||
|
|
||||||
|
for (i=0; i < 5000;) {
|
||||||
|
loc[i][0] = stb_rand() % w;
|
||||||
|
loc[i][1] = stb_rand() % h;
|
||||||
|
if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
start_timer("reachable");
|
||||||
|
for (i=0; i < 2000; ++i) {
|
||||||
|
for (j=0; j < 2000; ++j) {
|
||||||
|
int x1 = loc[i][0], y1 = loc[i][1];
|
||||||
|
int x2 = loc[2000+j][0], y2 = loc[2000+j][1];
|
||||||
|
r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
printf("%d reachable\n", r);
|
||||||
|
|
||||||
|
printf("Cluster size: %d,%d\n", STBCC__CLUSTER_SIZE_X, STBCC__CLUSTER_SIZE_Y);
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
for (j=0; j < 10; ++j) {
|
||||||
|
for (i=0; i < 5000; ++i) {
|
||||||
|
loc[i][0] = stb_rand() % w;
|
||||||
|
loc[i][1] = stb_rand() % h;
|
||||||
|
}
|
||||||
|
start_timer("updating 2500");
|
||||||
|
for (i=0; i < 2500; ++i) {
|
||||||
|
if (stbcc_query_grid_open(g, loc[i][0], loc[i][1]))
|
||||||
|
stbcc_update_grid(g, loc[i][0], loc[i][1], 1);
|
||||||
|
else
|
||||||
|
stbcc_update_grid(g, loc[i][0], loc[i][1], 0);
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/update_random_%d.png", j*i));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
start_timer("removing");
|
||||||
|
count = 0;
|
||||||
|
for (i=0; i < 1800; ++i) {
|
||||||
|
int x,y,a,b;
|
||||||
|
x = stb_rand() % (w-32);
|
||||||
|
y = stb_rand() % (h-32);
|
||||||
|
|
||||||
|
if (i & 1) {
|
||||||
|
for (a=0; a < 32; ++a)
|
||||||
|
for (b=0; b < 1; ++b)
|
||||||
|
if (stbcc_query_grid_open(g, x+a, y+b)) {
|
||||||
|
stbcc_update_grid(g, x+a, y+b, 1);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (a=0; a < 1; ++a)
|
||||||
|
for (b=0; b < 32; ++b)
|
||||||
|
if (stbcc_query_grid_open(g, x+a, y+b)) {
|
||||||
|
stbcc_update_grid(g, x+a, y+b, 1);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i+1));
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
printf("Removed %d grid spaces\n", count);
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_random_%d.png", i));
|
||||||
|
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
start_timer("reachable");
|
||||||
|
for (i=0; i < 1000; ++i) {
|
||||||
|
for (j=0; j < 1000; ++j) {
|
||||||
|
int x1 = loc[i][0], y1 = loc[i][1];
|
||||||
|
int x2 = loc[j][0], y2 = loc[j][1];
|
||||||
|
r += stbcc_query_grid_node_connection(g, x1,y1, x2,y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
printf("%d reachable\n", r);
|
||||||
|
|
||||||
|
start_timer("adding");
|
||||||
|
count = 0;
|
||||||
|
for (i=0; i < 1800; ++i) {
|
||||||
|
int x,y,a,b;
|
||||||
|
x = stb_rand() % (w-32);
|
||||||
|
y = stb_rand() % (h-32);
|
||||||
|
|
||||||
|
if (i & 1) {
|
||||||
|
for (a=0; a < 32; ++a)
|
||||||
|
for (b=0; b < 1; ++b)
|
||||||
|
if (!stbcc_query_grid_open(g, x+a, y+b)) {
|
||||||
|
stbcc_update_grid(g, x+a, y+b, 0);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (a=0; a < 1; ++a)
|
||||||
|
for (b=0; b < 32; ++b)
|
||||||
|
if (!stbcc_query_grid_open(g, x+a, y+b)) {
|
||||||
|
stbcc_update_grid(g, x+a, y+b, 0);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (i % 100 == 0) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i+1));
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_random_%d.png", i));
|
||||||
|
printf("Added %d grid spaces\n", count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if 0 // for map_02.png
|
||||||
|
start_timer("process");
|
||||||
|
for (k=0; k < 20; ++k) {
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
int any=0;
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
if (map[j*w+i] > 10 && map[j*w+i] < 250) {
|
||||||
|
//start_timer(stb_sprintf("open %d,%d", i,j));
|
||||||
|
stbcc_update_grid(g, i, j, 0);
|
||||||
|
test_connected(g);
|
||||||
|
//end_timer();
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/open_row_%04d.png", j));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j=0; j < h; ++j) {
|
||||||
|
int any=0;
|
||||||
|
for (i=0; i < w; ++i) {
|
||||||
|
if (map[j*w+i] > 10 && map[j*w+i] < 250) {
|
||||||
|
//start_timer(stb_sprintf("close %d,%d", i,j));
|
||||||
|
stbcc_update_grid(g, i, j, 1);
|
||||||
|
test_connected(g);
|
||||||
|
//end_timer();
|
||||||
|
any = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (any) write_map(g, w, h, stb_sprintf("tests/output/stbcc/close_row_%04d.png", j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end_timer();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
95
external/stb/stb/tests/herringbone.dsp
vendored
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="herringbone" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||||
|
|
||||||
|
CFG=herringbone - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "herringbone.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "herringbone.mak" CFG="herringbone - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "herringbone - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE "herringbone - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "herringbone - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "herringbone - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "herringbone___Win32_Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "herringbone___Win32_Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "herringbone - Win32 Release"
|
||||||
|
# Name "herringbone - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\herringbone_generator.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\stb_herringbone_wang_tile.h
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
87
external/stb/stb/tests/herringbone_generator.c
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
|
||||||
|
#include "stb_herringbone_wang_tile.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
// e 12 1 1 1 1 1 1 4 4
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
stbhw_config c = { 0 };
|
||||||
|
int w,h, num_colors,i;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
if (argc == 1) goto usage;
|
||||||
|
if (argc < 3) goto error;
|
||||||
|
|
||||||
|
switch (argv[2][0]) {
|
||||||
|
case 'c':
|
||||||
|
if (argc < 8 || argc > 10)
|
||||||
|
goto error;
|
||||||
|
num_colors = 4;
|
||||||
|
c.is_corner = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
if (argc < 10 || argc > 12)
|
||||||
|
goto error;
|
||||||
|
num_colors = 6;
|
||||||
|
c.is_corner = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.short_side_len = atoi(argv[3]);
|
||||||
|
for (i=0; i < num_colors; ++i)
|
||||||
|
c.num_color[i] = atoi(argv[4+i]);
|
||||||
|
|
||||||
|
c.num_vary_x = 1;
|
||||||
|
c.num_vary_y = 1;
|
||||||
|
|
||||||
|
if (argc > 4+i)
|
||||||
|
c.num_vary_x = atoi(argv[4+i]);
|
||||||
|
if (argc > 5+i)
|
||||||
|
c.num_vary_y = atoi(argv[5+i]);
|
||||||
|
|
||||||
|
stbhw_get_template_size(&c, &w, &h);
|
||||||
|
|
||||||
|
data = (unsigned char *) malloc(w*h*3);
|
||||||
|
|
||||||
|
if (stbhw_make_template(&c, data, w, h, w*3))
|
||||||
|
stbi_write_png(argv[1], w, h, 3, data, w*3);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
fputs("Invalid command-line arguments\n\n", stderr);
|
||||||
|
usage:
|
||||||
|
fputs("Usage (see source for corner & edge type definitions):\n\n", stderr);
|
||||||
|
fputs("herringbone_generator {outfile} c {sidelen} {c0} {c1} {c2} {c3} [{vx} {vy}]\n"
|
||||||
|
" {outfile} -- filename that template will be written to as PNG\n"
|
||||||
|
" {sidelen} -- length of short side of rectangle in pixels\n"
|
||||||
|
" {c0} -- number of colors for corner type 0\n"
|
||||||
|
" {c1} -- number of colors for corner type 1\n"
|
||||||
|
" {c2} -- number of colors for corner type 2\n"
|
||||||
|
" {c3} -- number of colors for corner type 3\n"
|
||||||
|
" {vx} -- number of color-duplicating variations horizontally in template\n"
|
||||||
|
" {vy} -- number of color-duplicating variations vertically in template\n"
|
||||||
|
"\n"
|
||||||
|
, stderr);
|
||||||
|
fputs("herringbone_generator {outfile} e {sidelen} {e0} {e1} {e2} {e3} {e4} {e5} [{vx} {vy}]\n"
|
||||||
|
" {outfile} -- filename that template will be written to as PNG\n"
|
||||||
|
" {sidelen} -- length of short side of rectangle in pixels\n"
|
||||||
|
" {e0} -- number of colors for edge type 0\n"
|
||||||
|
" {e1} -- number of colors for edge type 1\n"
|
||||||
|
" {e2} -- number of colors for edge type 2\n"
|
||||||
|
" {e3} -- number of colors for edge type 3\n"
|
||||||
|
" {e4} -- number of colors for edge type 4\n"
|
||||||
|
" {e5} -- number of colors for edge type 5\n"
|
||||||
|
" {vx} -- number of color-duplicating variations horizontally in template\n"
|
||||||
|
" {vy} -- number of color-duplicating variations vertically in template\n"
|
||||||
|
, stderr);
|
||||||
|
return 1;
|
||||||
|
}
|
83
external/stb/stb/tests/herringbone_map.c
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define STB_HBWANG_MAX_X 500
|
||||||
|
#define STB_HBWANG_MAX_Y 500
|
||||||
|
|
||||||
|
#define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION
|
||||||
|
#include "stb_herringbone_wang_tile.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 5) {
|
||||||
|
fprintf(stderr, "Usage: herringbone_map {inputfile} {output-width} {output-height} {outputfile}\n");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
char *filename = argv[1];
|
||||||
|
int out_w = atoi(argv[2]);
|
||||||
|
int out_h = atoi(argv[3]);
|
||||||
|
char *outfile = argv[4];
|
||||||
|
|
||||||
|
unsigned char *pixels, *out_pixels;
|
||||||
|
stbhw_tileset ts;
|
||||||
|
int w,h;
|
||||||
|
|
||||||
|
pixels = stbi_load(filename, &w, &h, 0, 3);
|
||||||
|
if (pixels == 0) {
|
||||||
|
fprintf(stderr, "Couldn't open input file '%s'\n", filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stbhw_build_tileset_from_image(&ts, pixels, w*3, w, h)) {
|
||||||
|
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pixels);
|
||||||
|
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
{
|
||||||
|
int i,j,k;
|
||||||
|
// add blue borders to top-left edges of the tiles
|
||||||
|
int hstride = (ts.short_side_len*2)*3;
|
||||||
|
int vstride = (ts.short_side_len )*3;
|
||||||
|
for (i=0; i < ts.num_h_tiles; ++i) {
|
||||||
|
unsigned char *pix = ts.h_tiles[i]->pixels;
|
||||||
|
for (j=0; j < ts.short_side_len*2; ++j)
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5;
|
||||||
|
for (j=1; j < ts.short_side_len; ++j)
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
pix[j*hstride+k] = (pix[j*hstride+k]*0.5+100+k*75)/1.5;
|
||||||
|
}
|
||||||
|
for (i=0; i < ts.num_v_tiles; ++i) {
|
||||||
|
unsigned char *pix = ts.v_tiles[i]->pixels;
|
||||||
|
for (j=0; j < ts.short_side_len; ++j)
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
pix[j*3+k] = (pix[j*3+k]*0.5+100+k*75)/1.5;
|
||||||
|
for (j=1; j < ts.short_side_len*2; ++j)
|
||||||
|
for (k=0; k < 3; ++k)
|
||||||
|
pix[j*vstride+k] = (pix[j*vstride+k]*0.5+100+k*75)/1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
out_pixels = malloc(out_w * out_h * 3);
|
||||||
|
|
||||||
|
if (!stbhw_generate_image(&ts, NULL, out_pixels, out_w*3, out_w, out_h)) {
|
||||||
|
fprintf(stderr, "Error: %s\n", stbhw_get_last_error());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_write_png(argv[4], out_w, out_h, 3, out_pixels, out_w*3);
|
||||||
|
free(out_pixels);
|
||||||
|
|
||||||
|
stbhw_free_tileset(&ts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
94
external/stb/stb/tests/herringbone_map.dsp
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="herringbone_map" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||||
|
|
||||||
|
CFG=herringbone_map - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "herringbone_map.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "herringbone_map.mak" CFG="herringbone_map - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "herringbone_map - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE "herringbone_map - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "herringbone_map - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "herringbone_map - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "herringbone_map___Win32_Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "herringbone_map___Win32_Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "herringbone_map - Win32 Release"
|
||||||
|
# Name "herringbone_map - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\herringbone_map.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\stb_herringbone_wang_tile.h
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
173
external/stb/stb/tests/image_test.c
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#define STBI_WINDOWS_UTF8
|
||||||
|
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_DEFINE
|
||||||
|
#include "stb.h"
|
||||||
|
|
||||||
|
//#define PNGSUITE_PRIMARY
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void test_ycbcr(void)
|
||||||
|
{
|
||||||
|
STBI_SIMD_ALIGN(unsigned char, y[256]);
|
||||||
|
STBI_SIMD_ALIGN(unsigned char, cb[256]);
|
||||||
|
STBI_SIMD_ALIGN(unsigned char, cr[256]);
|
||||||
|
STBI_SIMD_ALIGN(unsigned char, out1[256][4]);
|
||||||
|
STBI_SIMD_ALIGN(unsigned char, out2[256][4]);
|
||||||
|
|
||||||
|
int i,j,k;
|
||||||
|
int count = 0, bigcount=0, total=0;
|
||||||
|
|
||||||
|
for (i=0; i < 256; ++i) {
|
||||||
|
for (j=0; j < 256; ++j) {
|
||||||
|
for (k=0; k < 256; ++k) {
|
||||||
|
y [k] = k;
|
||||||
|
cb[k] = j;
|
||||||
|
cr[k] = i;
|
||||||
|
}
|
||||||
|
stbi__YCbCr_to_RGB_row(out1[0], y, cb, cr, 256, 4);
|
||||||
|
stbi__YCbCr_to_RGB_sse2(out2[0], y, cb, cr, 256, 4);
|
||||||
|
for (k=0; k < 256; ++k) {
|
||||||
|
// inaccurate proxy for values outside of RGB cube
|
||||||
|
if (out1[k][0] == 0 || out1[k][1] == 0 || out1[k][2] == 0 || out1[k][0] == 255 || out1[k][1] == 255 || out1[k][2] == 255)
|
||||||
|
continue;
|
||||||
|
++total;
|
||||||
|
if (out1[k][0] != out2[k][0] || out1[k][1] != out2[k][1] || out1[k][2] != out2[k][2]) {
|
||||||
|
int dist1 = abs(out1[k][0] - out2[k][0]);
|
||||||
|
int dist2 = abs(out1[k][1] - out2[k][1]);
|
||||||
|
int dist3 = abs(out1[k][2] - out2[k][2]);
|
||||||
|
++count;
|
||||||
|
if (out1[k][1] > out2[k][1])
|
||||||
|
++bigcount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("So far: %d (%d big) of %d\n", count, bigcount, total);
|
||||||
|
}
|
||||||
|
printf("Final: %d (%d big) of %d\n", count, bigcount, total);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float hdr_data[200][200][3];
|
||||||
|
|
||||||
|
void dummy_write(void *context, void *data, int len)
|
||||||
|
{
|
||||||
|
static char dummy[1024];
|
||||||
|
if (len > 1024) len = 1024;
|
||||||
|
memcpy(dummy, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void image_write_test(void);
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int w,h;
|
||||||
|
//test_ycbcr();
|
||||||
|
|
||||||
|
image_write_test();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// test hdr asserts
|
||||||
|
for (h=0; h < 100; h += 2)
|
||||||
|
for (w=0; w < 200; ++w)
|
||||||
|
hdr_data[h][w][0] = (float) rand(),
|
||||||
|
hdr_data[h][w][1] = (float) rand(),
|
||||||
|
hdr_data[h][w][2] = (float) rand();
|
||||||
|
|
||||||
|
stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
for (i=1; i < argc; ++i) {
|
||||||
|
int res;
|
||||||
|
int w2,h2,n2;
|
||||||
|
unsigned char *data;
|
||||||
|
printf("%s\n", argv[i]);
|
||||||
|
res = stbi_info(argv[i], &w2, &h2, &n2);
|
||||||
|
data = stbi_load(argv[i], &w, &h, &n, 0); if (data) free(data); else printf("Failed &n\n");
|
||||||
|
data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
|
||||||
|
data = stbi_load(argv[i], &w, &h, 0, 1); if (data) free(data); else printf("Failed 1\n");
|
||||||
|
data = stbi_load(argv[i], &w, &h, 0, 2); if (data) free(data); else printf("Failed 2\n");
|
||||||
|
data = stbi_load(argv[i], &w, &h, 0, 3); if (data) free(data); else printf("Failed 3\n");
|
||||||
|
data = stbi_load(argv[i], &w, &h, &n, 4);
|
||||||
|
assert(data);
|
||||||
|
assert(w == w2 && h == h2 && n == n2);
|
||||||
|
assert(res);
|
||||||
|
if (data) {
|
||||||
|
char fname[512];
|
||||||
|
stb_splitpath(fname, argv[i], STB_FILE);
|
||||||
|
stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
|
||||||
|
stbi_write_bmp(stb_sprintf("output/%s.bmp", fname), w, h, 4, data);
|
||||||
|
stbi_write_tga(stb_sprintf("output/%s.tga", fname), w, h, 4, data);
|
||||||
|
stbi_write_png_to_func(dummy_write,0, w, h, 4, data, w*4);
|
||||||
|
stbi_write_bmp_to_func(dummy_write,0, w, h, 4, data);
|
||||||
|
stbi_write_tga_to_func(dummy_write,0, w, h, 4, data);
|
||||||
|
free(data);
|
||||||
|
} else
|
||||||
|
printf("FAILED 4\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
#ifdef PNGSUITE_PRIMARY
|
||||||
|
char **files = stb_readdir_files("pngsuite/primary");
|
||||||
|
#else
|
||||||
|
char **files = stb_readdir_files("images");
|
||||||
|
#endif
|
||||||
|
for (i=0; i < stb_arr_len(files); ++i) {
|
||||||
|
int n;
|
||||||
|
char **failed = NULL;
|
||||||
|
unsigned char *data;
|
||||||
|
printf(".");
|
||||||
|
//printf("%s\n", files[i]);
|
||||||
|
data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
|
||||||
|
data = stbi_load(files[i], &w, &h, 0, 1); if (data) free(data); else stb_arr_push(failed, "1");
|
||||||
|
data = stbi_load(files[i], &w, &h, 0, 2); if (data) free(data); else stb_arr_push(failed, "2");
|
||||||
|
data = stbi_load(files[i], &w, &h, 0, 3); if (data) free(data); else stb_arr_push(failed, "3");
|
||||||
|
data = stbi_load(files[i], &w, &h, 0, 4); if (data) ; else stb_arr_push(failed, "4");
|
||||||
|
if (data) {
|
||||||
|
char fname[512];
|
||||||
|
|
||||||
|
#ifdef PNGSUITE_PRIMARY
|
||||||
|
int w2,h2;
|
||||||
|
unsigned char *data2;
|
||||||
|
stb_splitpath(fname, files[i], STB_FILE_EXT);
|
||||||
|
data2 = stbi_load(stb_sprintf("pngsuite/primary_check/%s", fname), &w2, &h2, 0, 4);
|
||||||
|
if (!data2)
|
||||||
|
printf("FAILED: couldn't load 'pngsuite/primary_check/%s\n", fname);
|
||||||
|
else {
|
||||||
|
if (w != w2 || h != w2 || 0 != memcmp(data, data2, w*h*4)) {
|
||||||
|
int x,y,c;
|
||||||
|
if (w == w2 && h == h2)
|
||||||
|
for (y=0; y < h; ++y)
|
||||||
|
for (x=0; x < w; ++x)
|
||||||
|
for (c=0; c < 4; ++c)
|
||||||
|
assert(data[y*w*4+x*4+c] == data2[y*w*4+x*4+c]);
|
||||||
|
printf("FAILED: %s loaded but didn't match PRIMARY_check 32-bit version\n", files[i]);
|
||||||
|
}
|
||||||
|
free(data2);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
stb_splitpath(fname, files[i], STB_FILE);
|
||||||
|
stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
|
||||||
|
#endif
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
if (failed) {
|
||||||
|
int j;
|
||||||
|
printf("FAILED: ");
|
||||||
|
for (j=0; j < stb_arr_len(failed); ++j)
|
||||||
|
printf("%s ", failed[j]);
|
||||||
|
printf(" -- %s\n", files[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Tested %d files.\n", i);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
103
external/stb/stb/tests/image_test.dsp
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="image_test" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Console Application" 0x0103
|
||||||
|
|
||||||
|
CFG=image_test - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "image_test.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "image_test.mak" CFG="image_test - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "image_test - Win32 Release" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE "image_test - Win32 Debug" (based on "Win32 (x86) Console Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "image_test - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /GX /Zi /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "image_test - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug\image_test"
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "image_test - Win32 Release"
|
||||||
|
# Name "image_test - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\image_test.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\image_write_test.c
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\stb_image.h
|
||||||
|
# End Source File
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=..\stb_image_write.h
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
60
external/stb/stb/tests/image_write_test.c
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifdef __clang__
|
||||||
|
#define STBIWDEF static inline
|
||||||
|
#endif
|
||||||
|
#define STB_IMAGE_WRITE_STATIC
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
|
// using an 'F' since it has no rotational symmetries, and 6x5
|
||||||
|
// because it's a small, atypical size likely to trigger edge cases.
|
||||||
|
//
|
||||||
|
// conveniently, it's also small enough to fully fit inside a typical
|
||||||
|
// directory listing thumbnail, which simplifies checking at a glance.
|
||||||
|
static const char img6x5_template[] =
|
||||||
|
".****."
|
||||||
|
".*...."
|
||||||
|
".***.."
|
||||||
|
".*...."
|
||||||
|
".*....";
|
||||||
|
|
||||||
|
void image_write_test(void)
|
||||||
|
{
|
||||||
|
// make a RGB version of the template image
|
||||||
|
// use red on blue to detect R<->B swaps
|
||||||
|
unsigned char img6x5_rgb[6*5*3];
|
||||||
|
float img6x5_rgbf[6*5*3];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 6*5; i++) {
|
||||||
|
int on = img6x5_template[i] == '*';
|
||||||
|
img6x5_rgb[i*3 + 0] = on ? 255 : 0;
|
||||||
|
img6x5_rgb[i*3 + 1] = 0;
|
||||||
|
img6x5_rgb[i*3 + 2] = on ? 0 : 255;
|
||||||
|
|
||||||
|
img6x5_rgbf[i*3 + 0] = on ? 1.0f : 0.0f;
|
||||||
|
img6x5_rgbf[i*3 + 1] = 0.0f;
|
||||||
|
img6x5_rgbf[i*3 + 2] = on ? 0.0f : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_write_png("output/wr6x5_regular.png", 6, 5, 3, img6x5_rgb, 6*3);
|
||||||
|
stbi_write_bmp("output/wr6x5_regular.bmp", 6, 5, 3, img6x5_rgb);
|
||||||
|
stbi_write_tga("output/wr6x5_regular.tga", 6, 5, 3, img6x5_rgb);
|
||||||
|
stbi_write_jpg("output/wr6x5_regular.jpg", 6, 5, 3, img6x5_rgb, 95);
|
||||||
|
stbi_write_hdr("output/wr6x5_regular.hdr", 6, 5, 3, img6x5_rgbf);
|
||||||
|
|
||||||
|
stbi_flip_vertically_on_write(1);
|
||||||
|
|
||||||
|
stbi_write_png("output/wr6x5_flip.png", 6, 5, 3, img6x5_rgb, 6*3);
|
||||||
|
stbi_write_bmp("output/wr6x5_flip.bmp", 6, 5, 3, img6x5_rgb);
|
||||||
|
stbi_write_tga("output/wr6x5_flip.tga", 6, 5, 3, img6x5_rgb);
|
||||||
|
stbi_write_jpg("output/wr6x5_flip.jpg", 6, 5, 3, img6x5_rgb, 95);
|
||||||
|
stbi_write_hdr("output/wr6x5_flip.hdr", 6, 5, 3, img6x5_rgbf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IWT_TEST
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
image_write_test();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
29
external/stb/stb/tests/ossfuzz.sh
vendored
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash -eu
|
||||||
|
# This script is meant to be run by
|
||||||
|
# https://github.com/google/oss-fuzz/blob/master/projects/stb/Dockerfile
|
||||||
|
|
||||||
|
$CXX $CXXFLAGS -std=c++11 -I. -DSTBI_ONLY_PNG \
|
||||||
|
$SRC/stb/tests/stbi_read_fuzzer.c \
|
||||||
|
-o $OUT/stb_png_read_fuzzer $LIB_FUZZING_ENGINE
|
||||||
|
|
||||||
|
$CXX $CXXFLAGS -std=c++11 -I. \
|
||||||
|
$SRC/stb/tests/stbi_read_fuzzer.c \
|
||||||
|
-o $OUT/stbi_read_fuzzer $LIB_FUZZING_ENGINE
|
||||||
|
|
||||||
|
find $SRC/stb/tests/pngsuite -name "*.png" | \
|
||||||
|
xargs zip $OUT/stb_png_read_fuzzer_seed_corpus.zip
|
||||||
|
|
||||||
|
cp $SRC/stb/tests/stb_png.dict $OUT/stb_png_read_fuzzer.dict
|
||||||
|
|
||||||
|
tar xvzf $SRC/stbi/jpg.tar.gz --directory $SRC/stb/tests
|
||||||
|
tar xvzf $SRC/stbi/gif.tar.gz --directory $SRC/stb/tests
|
||||||
|
unzip $SRC/stbi/bmp.zip -d $SRC/stb/tests
|
||||||
|
unzip $SRC/stbi/tga.zip -d $SRC/stb/tests
|
||||||
|
|
||||||
|
find $SRC/stb/tests -name "*.png" -o -name "*.jpg" -o -name "*.gif" \
|
||||||
|
-o -name "*.bmp" -o -name "*.tga" -o -name "*.TGA" \
|
||||||
|
-o -name "*.ppm" -o -name "*.pgm" \
|
||||||
|
| xargs zip $OUT/stbi_read_fuzzer_seed_corpus.zip
|
||||||
|
|
||||||
|
echo "" >> $SRC/stbi/gif.dict
|
||||||
|
cat $SRC/stbi/gif.dict $SRC/stb/tests/stb_png.dict > $OUT/stbi_read_fuzzer.dict
|
94
external/stb/stb/tests/oversample/README.md
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
# Font character oversampling for rendering from atlas textures
|
||||||
|
|
||||||
|
TL,DR: Run oversample.exe on a windows machine to see the
|
||||||
|
benefits of oversampling. It will try to use arial.ttf from the
|
||||||
|
Windows font directory unless you type the name of a .ttf file as
|
||||||
|
a command-line argument.
|
||||||
|
|
||||||
|
## Benefits of oversampling
|
||||||
|
|
||||||
|
Oversampling is a mechanism for improving subpixel rendering of characters.
|
||||||
|
|
||||||
|
Improving subpixel has a few benefits:
|
||||||
|
|
||||||
|
* With horizontal-oversampling, text can remain sharper while still being sub-pixel positioned for better kerning
|
||||||
|
* Horizontally-oversampled text significantly reduces aliasing when text animates horizontally
|
||||||
|
* Vertically-oversampled text significantly reduces aliasing when text animates vertically
|
||||||
|
* Text oversampled in both directions significantly reduces aliasing when text rotates
|
||||||
|
|
||||||
|
## What text oversampling is
|
||||||
|
|
||||||
|
A common strategy for rendering text is to cache character bitmaps
|
||||||
|
and reuse them. For hinted characters, every instance of a given
|
||||||
|
character is always identical, so this works fine. However, stb_truetype
|
||||||
|
doesn't do hinting.
|
||||||
|
|
||||||
|
For anti-aliased characters, you can actually position the characters
|
||||||
|
with subpixel precision, and get different bitmaps based on that positioning
|
||||||
|
if you re-render the vector data.
|
||||||
|
|
||||||
|
However, if you simply cache a single version of the bitmap and
|
||||||
|
draw it at different subpixel positions with a GPU, you will get
|
||||||
|
either the exact same result (if you use point-sampling on the
|
||||||
|
texture) or linear filtering. Linear filtering will cause a sub-pixel
|
||||||
|
positioned bitmap to blur further, causing a visible de-sharpening
|
||||||
|
of the character. (And, since the character wasn't hinted, it was
|
||||||
|
already blurrier than a hinted one would be, and now it gets even
|
||||||
|
more blurry.)
|
||||||
|
|
||||||
|
You can avoid this by caching multiple variants of a character which
|
||||||
|
were rendered independently from the vector data. For example, you
|
||||||
|
might cache 3 versions of a char, at 0, 1/3, and 2/3rds of a pixel
|
||||||
|
horizontal offset, and always require characters to fall on integer
|
||||||
|
positions vertically.
|
||||||
|
|
||||||
|
When creating a texture atlas for use on GPUs, which support bilinear
|
||||||
|
filtering, there is a better approach than caching several independent
|
||||||
|
positions, which is to allow lerping between the versions to allow
|
||||||
|
finer subpixel positioning. You can achieve these by interleaving
|
||||||
|
each of the cached bitmaps, but this turns out to be mathematically
|
||||||
|
equivalent to a simpler operation: oversampling and prefiltering the
|
||||||
|
characters.
|
||||||
|
|
||||||
|
So, setting oversampling of 2x2 in stb_truetype is equivalent to caching
|
||||||
|
each character in 4 different variations, 1 for each subpixel position
|
||||||
|
in a 2x2 set.
|
||||||
|
|
||||||
|
An advantage of this formulation is that no changes are required to
|
||||||
|
the rendering code; the exact same quad-rendering code works, it just
|
||||||
|
uses different texture coordinates. (Note this does potentially increase
|
||||||
|
texture bandwidth for text rendering since we end up minifying the texture
|
||||||
|
without using mipmapping, but you probably are not going to be fill-bound
|
||||||
|
by your text rendering.)
|
||||||
|
|
||||||
|
## What about gamma?
|
||||||
|
|
||||||
|
Gamma-correction for fonts just doesn't work. This doesn't seem to make
|
||||||
|
much sense -- it's physically correct, it simulates what we'd see if you
|
||||||
|
shrunk a font down really far, right?
|
||||||
|
|
||||||
|
But you can play with it in the oversample.exe app. If you turn it on,
|
||||||
|
white-on-black fonts become too thick (i.e. they become too bright), and
|
||||||
|
black-on-white fonts become too thin (i.e. they are insufficiently dark). There is
|
||||||
|
no way to adjust the font's inherent thickness (i.e. by switching to
|
||||||
|
bold) to fix this for both; making the font thicker will make white
|
||||||
|
text worse, and making the font thinner will make black text worse.
|
||||||
|
Obviously you could use different fonts for light and dark cases, but
|
||||||
|
this doesn't seem like a very good way for fonts to work.
|
||||||
|
|
||||||
|
Multiple people who have experimented with this independently (me,
|
||||||
|
Fabian Giesen,and Maxim Shemanarev of Anti-Grain Geometry) have all
|
||||||
|
concluded that correct gamma-correction does not produce the best
|
||||||
|
results for fonts. Font rendering just generally looks better without
|
||||||
|
gamma correction (or possibly with some arbitrary power stuck in
|
||||||
|
there, but it's not really correcting for gamma at that point). Maybe
|
||||||
|
this is in part a product of how we're used to fonts being on screens
|
||||||
|
which has changed how we expect them to look (e.g. perhaps hinting
|
||||||
|
oversharpens them and prevents the real-world thinning you'd see in
|
||||||
|
a black-on-white text).
|
||||||
|
|
||||||
|
(AGG link on text rendering, including mention of gamma:
|
||||||
|
http://www.antigrain.com/research/font_rasterization/ )
|
||||||
|
|
||||||
|
Nevertheless, even if you turn on gamma-correction, you will find that
|
||||||
|
oversampling still helps in many cases for small fonts.
|
332
external/stb/stb/tests/oversample/main.c
vendored
Normal file
@ -0,0 +1,332 @@
|
|||||||
|
#pragma warning(disable:4244; disable:4305; disable:4018)
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define STB_WINMAIN
|
||||||
|
#include "stb_wingraph.h"
|
||||||
|
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#include "stb_rect_pack.h"
|
||||||
|
#include "stb_truetype.h"
|
||||||
|
|
||||||
|
#ifndef WINGDIAPI
|
||||||
|
#define CALLBACK __stdcall
|
||||||
|
#define WINGDIAPI __declspec(dllimport)
|
||||||
|
#define APIENTRY __stdcall
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gl/gl.h>
|
||||||
|
#include <gl/glu.h>
|
||||||
|
|
||||||
|
#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9
|
||||||
|
|
||||||
|
#define SIZE_X 1024
|
||||||
|
#define SIZE_Y 768
|
||||||
|
|
||||||
|
stbtt_packedchar chardata[6][128];
|
||||||
|
|
||||||
|
int sx=SIZE_X, sy=SIZE_Y;
|
||||||
|
|
||||||
|
#define BITMAP_W 512
|
||||||
|
#define BITMAP_H 512
|
||||||
|
unsigned char temp_bitmap[BITMAP_W][BITMAP_H];
|
||||||
|
unsigned char ttf_buffer[1 << 25];
|
||||||
|
GLuint font_tex;
|
||||||
|
|
||||||
|
float scale[2] = { 24.0f, 14.0f };
|
||||||
|
|
||||||
|
int sf[6] = { 0,1,2, 0,1,2 };
|
||||||
|
|
||||||
|
void load_fonts(void)
|
||||||
|
{
|
||||||
|
stbtt_pack_context pc;
|
||||||
|
int i;
|
||||||
|
FILE *f;
|
||||||
|
char filename[256];
|
||||||
|
char *win = getenv("windir");
|
||||||
|
if (win == NULL) win = getenv("SystemRoot");
|
||||||
|
|
||||||
|
f = fopen(stb_wingraph_commandline, "rb");
|
||||||
|
if (!f) {
|
||||||
|
if (win == NULL)
|
||||||
|
sprintf(filename, "arial.ttf", win);
|
||||||
|
else
|
||||||
|
sprintf(filename, "%s/fonts/arial.ttf", win);
|
||||||
|
f = fopen(filename, "rb");
|
||||||
|
if (!f) exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(ttf_buffer, 1, 1<<25, f);
|
||||||
|
|
||||||
|
stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL);
|
||||||
|
for (i=0; i < 2; ++i) {
|
||||||
|
stbtt_PackSetOversampling(&pc, 1, 1);
|
||||||
|
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32);
|
||||||
|
stbtt_PackSetOversampling(&pc, 2, 2);
|
||||||
|
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32);
|
||||||
|
stbtt_PackSetOversampling(&pc, 3, 1);
|
||||||
|
stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32);
|
||||||
|
}
|
||||||
|
stbtt_PackEnd(&pc);
|
||||||
|
|
||||||
|
glGenTextures(1, &font_tex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, font_tex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int black_on_white;
|
||||||
|
|
||||||
|
void draw_init(void)
|
||||||
|
{
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glDisable(GL_TEXTURE_2D);
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
glViewport(0,0,sx,sy);
|
||||||
|
if (black_on_white)
|
||||||
|
glClearColor(255,255,255,0);
|
||||||
|
else
|
||||||
|
glClearColor(0,0,0,0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluOrtho2D(0,sx,sy,0);
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void drawBoxTC(float x0, float y0, float x1, float y1, float s0, float t0, float s1, float t1)
|
||||||
|
{
|
||||||
|
glTexCoord2f(s0,t0); glVertex2f(x0,y0);
|
||||||
|
glTexCoord2f(s1,t0); glVertex2f(x1,y0);
|
||||||
|
glTexCoord2f(s1,t1); glVertex2f(x1,y1);
|
||||||
|
glTexCoord2f(s0,t1); glVertex2f(x0,y1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int integer_align;
|
||||||
|
|
||||||
|
void print(float x, float y, int font, char *text)
|
||||||
|
{
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, font_tex);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
while (*text) {
|
||||||
|
stbtt_aligned_quad q;
|
||||||
|
stbtt_GetPackedQuad(chardata[font], BITMAP_W, BITMAP_H, *text++, &x, &y, &q, font ? 0 : integer_align);
|
||||||
|
drawBoxTC(q.x0,q.y0,q.x1,q.y1, q.s0,q.t0,q.s1,q.t1);
|
||||||
|
}
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
int font=3;
|
||||||
|
int translating;
|
||||||
|
int rotating=0;
|
||||||
|
int srgb=0;
|
||||||
|
float rotate_t, translate_t;
|
||||||
|
int show_tex;
|
||||||
|
|
||||||
|
void draw_world(void)
|
||||||
|
{
|
||||||
|
int sfont = sf[font];
|
||||||
|
float x = 20;
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
if (black_on_white)
|
||||||
|
glColor3f(0,0,0);
|
||||||
|
else
|
||||||
|
glColor3f(1,1,1);
|
||||||
|
|
||||||
|
|
||||||
|
print(80, 30, sfont, "Controls:");
|
||||||
|
print(100, 60, sfont, "S: toggle font size");
|
||||||
|
print(100, 85, sfont, "O: toggle oversampling");
|
||||||
|
print(100,110, sfont, "T: toggle translation");
|
||||||
|
print(100,135, sfont, "R: toggle rotation");
|
||||||
|
print(100,160, sfont, "P: toggle pixel-snap (only non-oversampled)");
|
||||||
|
print(100,185, sfont, "G: toggle srgb gamma-correction");
|
||||||
|
if (black_on_white)
|
||||||
|
print(100,210, sfont, "B: toggle to white-on-black");
|
||||||
|
else
|
||||||
|
print(100,210, sfont, "B: toggle to black-on-white");
|
||||||
|
print(100,235, sfont, "V: view font texture");
|
||||||
|
|
||||||
|
print(80, 300, sfont, "Current font:");
|
||||||
|
|
||||||
|
if (!show_tex) {
|
||||||
|
if (font < 3)
|
||||||
|
print(100, 350, sfont, "Font height: 24 pixels");
|
||||||
|
else
|
||||||
|
print(100, 350, sfont, "Font height: 14 pixels");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font%3==1)
|
||||||
|
print(100, 325, sfont, "2x2 oversampled text at 1:1");
|
||||||
|
else if (font%3 == 2)
|
||||||
|
print(100, 325, sfont, "3x1 oversampled text at 1:1");
|
||||||
|
else if (integer_align)
|
||||||
|
print(100, 325, sfont, "1:1 text, one texel = one pixel, snapped to integer coordinates");
|
||||||
|
else
|
||||||
|
print(100, 325, sfont, "1:1 text, one texel = one pixel");
|
||||||
|
|
||||||
|
if (show_tex) {
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
drawBoxTC(200,400, 200+BITMAP_W,300+BITMAP_H, 0,0,1,1);
|
||||||
|
glEnd();
|
||||||
|
} else {
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glTranslatef(200,350,0);
|
||||||
|
|
||||||
|
if (translating)
|
||||||
|
x += fmod(translate_t*8,30);
|
||||||
|
|
||||||
|
if (rotating) {
|
||||||
|
glTranslatef(100,150,0);
|
||||||
|
glRotatef(rotate_t*2,0,0,1);
|
||||||
|
glTranslatef(-100,-150,0);
|
||||||
|
}
|
||||||
|
print(x,100, font, "This is a test");
|
||||||
|
print(x,130, font, "Now is the time for all good men to come to the aid of their country.");
|
||||||
|
print(x,160, font, "The quick brown fox jumps over the lazy dog.");
|
||||||
|
print(x,190, font, "0123456789");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void)
|
||||||
|
{
|
||||||
|
draw_init();
|
||||||
|
draw_world();
|
||||||
|
stbwingraph_SwapBuffers(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int initialized=0;
|
||||||
|
static float last_dt;
|
||||||
|
|
||||||
|
int move[4];
|
||||||
|
int raw_mouse_x, raw_mouse_y;
|
||||||
|
|
||||||
|
int loopmode(float dt, int real, int in_client)
|
||||||
|
{
|
||||||
|
float actual_dt = dt;
|
||||||
|
|
||||||
|
if (!initialized) return 0;
|
||||||
|
|
||||||
|
rotate_t += dt;
|
||||||
|
translate_t += dt;
|
||||||
|
|
||||||
|
// music_sim();
|
||||||
|
if (!real)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (dt > 0.25) dt = 0.25;
|
||||||
|
if (dt < 0.01) dt = 0.01;
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int winproc(void *data, stbwingraph_event *e)
|
||||||
|
{
|
||||||
|
switch (e->type) {
|
||||||
|
case STBWGE_create:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBWGE_char:
|
||||||
|
switch(e->key) {
|
||||||
|
case 27:
|
||||||
|
stbwingraph_ShowCursor(NULL,1);
|
||||||
|
return STBWINGRAPH_winproc_exit;
|
||||||
|
break;
|
||||||
|
case 'o': case 'O':
|
||||||
|
font = (font+1) % 3 + (font/3)*3;
|
||||||
|
break;
|
||||||
|
case 's': case 'S':
|
||||||
|
font = (font+3) % 6;
|
||||||
|
break;
|
||||||
|
case 't': case 'T':
|
||||||
|
translating = !translating;
|
||||||
|
translate_t = 0;
|
||||||
|
break;
|
||||||
|
case 'r': case 'R':
|
||||||
|
rotating = !rotating;
|
||||||
|
rotate_t = 0;
|
||||||
|
break;
|
||||||
|
case 'p': case 'P':
|
||||||
|
integer_align = !integer_align;
|
||||||
|
break;
|
||||||
|
case 'g': case 'G':
|
||||||
|
srgb = !srgb;
|
||||||
|
if (srgb)
|
||||||
|
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
|
||||||
|
else
|
||||||
|
glDisable(GL_FRAMEBUFFER_SRGB_EXT);
|
||||||
|
break;
|
||||||
|
case 'v': case 'V':
|
||||||
|
show_tex = !show_tex;
|
||||||
|
break;
|
||||||
|
case 'b': case 'B':
|
||||||
|
black_on_white = !black_on_white;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBWGE_mousemove:
|
||||||
|
raw_mouse_x = e->mx;
|
||||||
|
raw_mouse_y = e->my;
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
case STBWGE_mousewheel: do_mouse(e,0,0); break;
|
||||||
|
case STBWGE_leftdown: do_mouse(e, 1,0); break;
|
||||||
|
case STBWGE_leftup: do_mouse(e,-1,0); break;
|
||||||
|
case STBWGE_rightdown: do_mouse(e,0, 1); break;
|
||||||
|
case STBWGE_rightup: do_mouse(e,0,-1); break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case STBWGE_keydown:
|
||||||
|
if (e->key == VK_RIGHT) move[0] = 1;
|
||||||
|
if (e->key == VK_LEFT) move[1] = 1;
|
||||||
|
if (e->key == VK_UP) move[2] = 1;
|
||||||
|
if (e->key == VK_DOWN) move[3] = 1;
|
||||||
|
break;
|
||||||
|
case STBWGE_keyup:
|
||||||
|
if (e->key == VK_RIGHT) move[0] = 0;
|
||||||
|
if (e->key == VK_LEFT) move[1] = 0;
|
||||||
|
if (e->key == VK_UP) move[2] = 0;
|
||||||
|
if (e->key == VK_DOWN) move[3] = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBWGE_size:
|
||||||
|
sx = e->width;
|
||||||
|
sy = e->height;
|
||||||
|
loopmode(0,1,0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STBWGE_draw:
|
||||||
|
if (initialized)
|
||||||
|
loopmode(0,1,0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return STBWINGRAPH_unprocessed;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_main(void)
|
||||||
|
{
|
||||||
|
stbwingraph_Priority(2);
|
||||||
|
stbwingraph_CreateWindow(1, winproc, NULL, "tt", SIZE_X,SIZE_Y, 0, 1, 0, 0);
|
||||||
|
stbwingraph_ShowCursor(NULL, 0);
|
||||||
|
load_fonts();
|
||||||
|
initialized = 1;
|
||||||
|
stbwingraph_MainLoop(loopmode, 0.016f); // 30 fps = 0.33
|
||||||
|
}
|
||||||
|
|
97
external/stb/stb/tests/oversample/oversample.dsp
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Microsoft Developer Studio Project File - Name="oversample" - Package Owner=<4>
|
||||||
|
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||||
|
# ** DO NOT EDIT **
|
||||||
|
|
||||||
|
# TARGTYPE "Win32 (x86) Application" 0x0101
|
||||||
|
|
||||||
|
CFG=oversample - Win32 Debug
|
||||||
|
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||||
|
!MESSAGE use the Export Makefile command and run
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "oversample.mak".
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE You can specify a configuration when running NMAKE
|
||||||
|
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE NMAKE /f "oversample.mak" CFG="oversample - Win32 Debug"
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE Possible choices for configuration are:
|
||||||
|
!MESSAGE
|
||||||
|
!MESSAGE "oversample - Win32 Release" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE "oversample - Win32 Debug" (based on "Win32 (x86) Application")
|
||||||
|
!MESSAGE
|
||||||
|
|
||||||
|
# Begin Project
|
||||||
|
# PROP AllowPerConfigDependencies 0
|
||||||
|
# PROP Scc_ProjName ""
|
||||||
|
# PROP Scc_LocalPath ""
|
||||||
|
CPP=cl.exe
|
||||||
|
MTL=midl.exe
|
||||||
|
RSC=rc.exe
|
||||||
|
|
||||||
|
!IF "$(CFG)" == "oversample - Win32 Release"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 0
|
||||||
|
# PROP BASE Output_Dir "Release"
|
||||||
|
# PROP BASE Intermediate_Dir "Release"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 0
|
||||||
|
# PROP Output_Dir "Release"
|
||||||
|
# PROP Intermediate_Dir "Release"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
|
||||||
|
# ADD CPP /nologo /W3 /WX /GX /O2 /I "c:\sean\prj\stb" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
|
||||||
|
# SUBTRACT CPP /YX
|
||||||
|
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
|
||||||
|
# SUBTRACT LINK32 /map /debug
|
||||||
|
|
||||||
|
!ELSEIF "$(CFG)" == "oversample - Win32 Debug"
|
||||||
|
|
||||||
|
# PROP BASE Use_MFC 0
|
||||||
|
# PROP BASE Use_Debug_Libraries 1
|
||||||
|
# PROP BASE Output_Dir "Debug"
|
||||||
|
# PROP BASE Intermediate_Dir "Debug"
|
||||||
|
# PROP BASE Target_Dir ""
|
||||||
|
# PROP Use_MFC 0
|
||||||
|
# PROP Use_Debug_Libraries 1
|
||||||
|
# PROP Output_Dir "Debug"
|
||||||
|
# PROP Intermediate_Dir "Debug"
|
||||||
|
# PROP Ignore_Export_Lib 0
|
||||||
|
# PROP Target_Dir ""
|
||||||
|
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
|
||||||
|
# ADD CPP /nologo /W3 /WX /Gm /GX /Zi /Od /I "c:\sean\prj\stb" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FD /GZ /c
|
||||||
|
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||||
|
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||||
|
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||||
|
BSC32=bscmake.exe
|
||||||
|
# ADD BASE BSC32 /nologo
|
||||||
|
# ADD BSC32 /nologo
|
||||||
|
LINK32=link.exe
|
||||||
|
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
|
||||||
|
# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib winspool.lib comdlg32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
|
||||||
|
|
||||||
|
!ENDIF
|
||||||
|
|
||||||
|
# Begin Target
|
||||||
|
|
||||||
|
# Name "oversample - Win32 Release"
|
||||||
|
# Name "oversample - Win32 Debug"
|
||||||
|
# Begin Source File
|
||||||
|
|
||||||
|
SOURCE=.\main.c
|
||||||
|
# End Source File
|
||||||
|
# End Target
|
||||||
|
# End Project
|
29
external/stb/stb/tests/oversample/oversample.dsw
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Microsoft Developer Studio Workspace File, Format Version 6.00
|
||||||
|
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Project: "oversample"=.\oversample.dsp - Package Owner=<4>
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<4>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
Global:
|
||||||
|
|
||||||
|
Package=<5>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
Package=<3>
|
||||||
|
{{{
|
||||||
|
}}}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
BIN
external/stb/stb/tests/oversample/oversample.exe
vendored
Normal file
829
external/stb/stb/tests/oversample/stb_wingraph.h
vendored
Normal file
@ -0,0 +1,829 @@
|
|||||||
|
// stb_wingraph.h v0.01 - public domain windows graphics programming
|
||||||
|
// wraps WinMain, ChoosePixelFormat, ChangeDisplayResolution, etc. for
|
||||||
|
// doing OpenGL graphics
|
||||||
|
//
|
||||||
|
// in ONE source file, put '#define STB_DEFINE' before including this
|
||||||
|
// OR put '#define STB_WINMAIN' to define a WinMain that calls stbwingraph_main(void)
|
||||||
|
//
|
||||||
|
// @TODO:
|
||||||
|
// 2d rendering interface (that can be done easily in software)
|
||||||
|
// STB_WINGRAPH_SOFTWARE -- 2d software rendering only
|
||||||
|
// STB_WINGRAPH_OPENGL -- OpenGL only
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef INCLUDE_STB_WINGRAPH_H
|
||||||
|
#define INCLUDE_STB_WINGRAPH_H
|
||||||
|
|
||||||
|
#ifdef STB_WINMAIN
|
||||||
|
#ifndef STB_DEFINE
|
||||||
|
#define STB_DEFINE
|
||||||
|
#define STB_WINGRAPH_DISABLE_DEFINE_AT_END
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_DEFINE
|
||||||
|
#pragma comment(lib, "opengl32.lib")
|
||||||
|
#pragma comment(lib, "glu32.lib")
|
||||||
|
#pragma comment(lib, "winmm.lib")
|
||||||
|
#pragma comment(lib, "gdi32.lib")
|
||||||
|
#pragma comment(lib, "user32.lib")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define STB_EXTERN extern "C"
|
||||||
|
#else
|
||||||
|
#define STB_EXTERN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_DEFINE
|
||||||
|
#ifndef _WINDOWS_
|
||||||
|
#ifdef APIENTRY
|
||||||
|
#undef APIENTRY
|
||||||
|
#endif
|
||||||
|
#ifdef WINGDIAPI
|
||||||
|
#undef WINGDIAPI
|
||||||
|
#endif
|
||||||
|
#define _WIN32_WINNT 0x0400 // WM_MOUSEWHEEL
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void * stbwingraph_hwnd;
|
||||||
|
typedef void * stbwingraph_hinstance;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
STBWINGRAPH_unprocessed = -(1 << 24),
|
||||||
|
STBWINGRAPH_do_not_show,
|
||||||
|
STBWINGRAPH_winproc_exit,
|
||||||
|
STBWINGRAPH_winproc_update,
|
||||||
|
STBWINGRAPH_update_exit,
|
||||||
|
STBWINGRAPH_update_pause,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
STBWGE__none=0,
|
||||||
|
|
||||||
|
STBWGE_create,
|
||||||
|
STBWGE_create_postshow,
|
||||||
|
STBWGE_draw,
|
||||||
|
STBWGE_destroy,
|
||||||
|
STBWGE_char,
|
||||||
|
STBWGE_keydown,
|
||||||
|
STBWGE_syskeydown,
|
||||||
|
STBWGE_keyup,
|
||||||
|
STBWGE_syskeyup,
|
||||||
|
STBWGE_deactivate,
|
||||||
|
STBWGE_activate,
|
||||||
|
STBWGE_size,
|
||||||
|
|
||||||
|
STBWGE_mousemove ,
|
||||||
|
STBWGE_leftdown , STBWGE_leftup ,
|
||||||
|
STBWGE_middledown, STBWGE_middleup,
|
||||||
|
STBWGE_rightdown , STBWGE_rightup ,
|
||||||
|
STBWGE_mousewheel,
|
||||||
|
} stbwingraph_event_type;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
stbwingraph_event_type type;
|
||||||
|
|
||||||
|
// for input events (mouse, keyboard)
|
||||||
|
int mx,my; // mouse x & y
|
||||||
|
int dx,dy;
|
||||||
|
int shift, ctrl, alt;
|
||||||
|
|
||||||
|
// for keyboard events
|
||||||
|
int key;
|
||||||
|
|
||||||
|
// for STBWGE_size:
|
||||||
|
int width, height;
|
||||||
|
|
||||||
|
// for STBWGE_crate
|
||||||
|
int did_share_lists; // if true, wglShareLists succeeded
|
||||||
|
|
||||||
|
void *handle;
|
||||||
|
|
||||||
|
} stbwingraph_event;
|
||||||
|
|
||||||
|
typedef int (*stbwingraph_window_proc)(void *data, stbwingraph_event *event);
|
||||||
|
|
||||||
|
extern stbwingraph_hinstance stbwingraph_app;
|
||||||
|
extern stbwingraph_hwnd stbwingraph_primary_window;
|
||||||
|
extern int stbwingraph_request_fullscreen;
|
||||||
|
extern int stbwingraph_request_windowed;
|
||||||
|
|
||||||
|
STB_EXTERN void stbwingraph_ods(char *str, ...);
|
||||||
|
STB_EXTERN int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type,
|
||||||
|
char *caption, char *text, ...);
|
||||||
|
STB_EXTERN int stbwingraph_ChangeResolution(unsigned int w, unsigned int h,
|
||||||
|
unsigned int bits, int use_message_box);
|
||||||
|
STB_EXTERN int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits,
|
||||||
|
int alpha_bits, int depth_bits, int stencil_bits, int accum_bits);
|
||||||
|
STB_EXTERN int stbwingraph_DefineClass(void *hinstance, char *iconname);
|
||||||
|
STB_EXTERN void stbwingraph_SwapBuffers(void *win);
|
||||||
|
STB_EXTERN void stbwingraph_Priority(int n);
|
||||||
|
|
||||||
|
STB_EXTERN void stbwingraph_MakeFonts(void *window, int font_base);
|
||||||
|
STB_EXTERN void stbwingraph_ShowWindow(void *window);
|
||||||
|
STB_EXTERN void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text, int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil);
|
||||||
|
STB_EXTERN void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height);
|
||||||
|
STB_EXTERN void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh);
|
||||||
|
STB_EXTERN void stbwingraph_DestroyWindow(void *window);
|
||||||
|
STB_EXTERN void stbwingraph_ShowCursor(void *window, int visible);
|
||||||
|
STB_EXTERN float stbwingraph_GetTimestep(float minimum_time);
|
||||||
|
STB_EXTERN void stbwingraph_SetGLWindow(void *win);
|
||||||
|
typedef int (*stbwingraph_update)(float timestep, int real, int in_client);
|
||||||
|
STB_EXTERN int stbwingraph_MainLoop(stbwingraph_update func, float mintime);
|
||||||
|
|
||||||
|
#ifdef STB_DEFINE
|
||||||
|
stbwingraph_hinstance stbwingraph_app;
|
||||||
|
stbwingraph_hwnd stbwingraph_primary_window;
|
||||||
|
int stbwingraph_request_fullscreen;
|
||||||
|
int stbwingraph_request_windowed;
|
||||||
|
|
||||||
|
void stbwingraph_ods(char *str, ...)
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
va_list v;
|
||||||
|
va_start(v,str);
|
||||||
|
vsprintf(buffer, str, v);
|
||||||
|
va_end(v);
|
||||||
|
OutputDebugString(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbwingraph_MessageBox(stbwingraph_hwnd win, unsigned int type, char *caption, char *text, ...)
|
||||||
|
{
|
||||||
|
va_list v;
|
||||||
|
char buffer[1024];
|
||||||
|
va_start(v, text);
|
||||||
|
vsprintf(buffer, text, v);
|
||||||
|
va_end(v);
|
||||||
|
return MessageBox(win, buffer, caption, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_Priority(int n)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
switch (n) {
|
||||||
|
case -1: p = THREAD_PRIORITY_BELOW_NORMAL; break;
|
||||||
|
case 0: p = THREAD_PRIORITY_NORMAL; break;
|
||||||
|
case 1: p = THREAD_PRIORITY_ABOVE_NORMAL; break;
|
||||||
|
default:
|
||||||
|
if (n < 0) p = THREAD_PRIORITY_LOWEST;
|
||||||
|
else p = THREAD_PRIORITY_HIGHEST;
|
||||||
|
}
|
||||||
|
SetThreadPriority(GetCurrentThread(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stbwingraph_ResetResolution(void)
|
||||||
|
{
|
||||||
|
ChangeDisplaySettings(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stbwingraph_RegisterResetResolution(void)
|
||||||
|
{
|
||||||
|
static int done=0;
|
||||||
|
if (!done) {
|
||||||
|
done = 1;
|
||||||
|
atexit(stbwingraph_ResetResolution);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbwingraph_ChangeResolution(unsigned int w, unsigned int h, unsigned int bits, int use_message_box)
|
||||||
|
{
|
||||||
|
DEVMODE mode;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
int i, tries=0;
|
||||||
|
for (i=0; ; ++i) {
|
||||||
|
int success = EnumDisplaySettings(NULL, i, &mode);
|
||||||
|
if (!success) break;
|
||||||
|
if (mode.dmBitsPerPel == bits && mode.dmPelsWidth == w && mode.dmPelsHeight == h) {
|
||||||
|
++tries;
|
||||||
|
success = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||||
|
if (success == DISP_CHANGE_SUCCESSFUL) {
|
||||||
|
stbwingraph_RegisterResetResolution();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tries) {
|
||||||
|
if (use_message_box)
|
||||||
|
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we tried but failed, so try explicitly doing it without specifying refresh rate
|
||||||
|
|
||||||
|
// Win95 support logic
|
||||||
|
mode.dmBitsPerPel = bits;
|
||||||
|
mode.dmPelsWidth = w;
|
||||||
|
mode.dmPelsHeight = h;
|
||||||
|
mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
||||||
|
|
||||||
|
res = ChangeDisplaySettings(&mode, CDS_FULLSCREEN);
|
||||||
|
|
||||||
|
switch (res) {
|
||||||
|
case DISP_CHANGE_SUCCESSFUL:
|
||||||
|
stbwingraph_RegisterResetResolution();
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
case DISP_CHANGE_RESTART:
|
||||||
|
if (use_message_box)
|
||||||
|
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "Please set your desktop to %d-bit color and then try again.");
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case DISP_CHANGE_FAILED:
|
||||||
|
if (use_message_box)
|
||||||
|
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The hardware failed to change modes.");
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case DISP_CHANGE_BADMODE:
|
||||||
|
if (use_message_box)
|
||||||
|
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "The resolution %d x %d x %d-bits is not supported.", w, h, bits);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (use_message_box)
|
||||||
|
stbwingraph_MessageBox(stbwingraph_primary_window, MB_ICONERROR, NULL, "An unknown error prevented a change to a %d x %d x %d-bit display.", w, h, bits);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbwingraph_SetPixelFormat(stbwingraph_hwnd win, int color_bits, int alpha_bits, int depth_bits, int stencil_bits, int accum_bits)
|
||||||
|
{
|
||||||
|
HDC dc = GetDC(win);
|
||||||
|
PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
|
||||||
|
int pixel_format;
|
||||||
|
|
||||||
|
pfd.nVersion = 1;
|
||||||
|
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
||||||
|
pfd.dwLayerMask = PFD_MAIN_PLANE;
|
||||||
|
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||||
|
pfd.cColorBits = color_bits;
|
||||||
|
pfd.cAlphaBits = alpha_bits;
|
||||||
|
pfd.cDepthBits = depth_bits;
|
||||||
|
pfd.cStencilBits = stencil_bits;
|
||||||
|
pfd.cAccumBits = accum_bits;
|
||||||
|
|
||||||
|
pixel_format = ChoosePixelFormat(dc, &pfd);
|
||||||
|
if (!pixel_format) return FALSE;
|
||||||
|
|
||||||
|
if (!DescribePixelFormat(dc, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd))
|
||||||
|
return FALSE;
|
||||||
|
SetPixelFormat(dc, pixel_format, &pfd);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// app data
|
||||||
|
stbwingraph_window_proc func;
|
||||||
|
void *data;
|
||||||
|
// creation parameters
|
||||||
|
int color, alpha, depth, stencil, accum;
|
||||||
|
HWND share_window;
|
||||||
|
HWND window;
|
||||||
|
// internal data
|
||||||
|
HGLRC rc;
|
||||||
|
HDC dc;
|
||||||
|
int hide_mouse;
|
||||||
|
int in_client;
|
||||||
|
int active;
|
||||||
|
int did_share_lists;
|
||||||
|
int mx,my; // last mouse positions
|
||||||
|
} stbwingraph__window;
|
||||||
|
|
||||||
|
static void stbwingraph__inclient(stbwingraph__window *win, int state)
|
||||||
|
{
|
||||||
|
if (state != win->in_client) {
|
||||||
|
win->in_client = state;
|
||||||
|
if (win->hide_mouse)
|
||||||
|
ShowCursor(!state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stbwingraph__key(stbwingraph_event *e, int type, int key, stbwingraph__window *z)
|
||||||
|
{
|
||||||
|
e->type = type;
|
||||||
|
e->key = key;
|
||||||
|
e->shift = (GetKeyState(VK_SHIFT) < 0);
|
||||||
|
e->ctrl = (GetKeyState(VK_CONTROL) < 0);
|
||||||
|
e->alt = (GetKeyState(VK_MENU) < 0);
|
||||||
|
if (z) {
|
||||||
|
e->mx = z->mx;
|
||||||
|
e->my = z->my;
|
||||||
|
} else {
|
||||||
|
e->mx = e->my = 0;
|
||||||
|
}
|
||||||
|
e->dx = e->dy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stbwingraph__mouse(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
|
||||||
|
{
|
||||||
|
static int captured = 0;
|
||||||
|
e->type = type;
|
||||||
|
e->mx = (short) LOWORD(lparam);
|
||||||
|
e->my = (short) HIWORD(lparam);
|
||||||
|
if (!z || z->mx == -(1 << 30)) {
|
||||||
|
e->dx = e->dy = 0;
|
||||||
|
} else {
|
||||||
|
e->dx = e->mx - z->mx;
|
||||||
|
e->dy = e->my - z->my;
|
||||||
|
}
|
||||||
|
e->shift = (wparam & MK_SHIFT) != 0;
|
||||||
|
e->ctrl = (wparam & MK_CONTROL) != 0;
|
||||||
|
e->alt = (wparam & MK_ALT) != 0;
|
||||||
|
if (z) {
|
||||||
|
z->mx = e->mx;
|
||||||
|
z->my = e->my;
|
||||||
|
}
|
||||||
|
if (capture) {
|
||||||
|
if (!captured && capture == 1)
|
||||||
|
SetCapture(wnd);
|
||||||
|
captured += capture;
|
||||||
|
if (!captured && capture == -1)
|
||||||
|
ReleaseCapture();
|
||||||
|
if (captured < 0) captured = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stbwingraph__mousewheel(stbwingraph_event *e, int type, WPARAM wparam, LPARAM lparam, int capture, void *wnd, stbwingraph__window *z)
|
||||||
|
{
|
||||||
|
// lparam seems bogus!
|
||||||
|
static int captured = 0;
|
||||||
|
e->type = type;
|
||||||
|
if (z) {
|
||||||
|
e->mx = z->mx;
|
||||||
|
e->my = z->my;
|
||||||
|
}
|
||||||
|
e->dx = e->dy = 0;
|
||||||
|
e->shift = (wparam & MK_SHIFT) != 0;
|
||||||
|
e->ctrl = (wparam & MK_CONTROL) != 0;
|
||||||
|
e->alt = (GetKeyState(VK_MENU) < 0);
|
||||||
|
e->key = ((int) wparam >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbwingraph_force_update;
|
||||||
|
static int WINAPI stbwingraph_WinProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
{
|
||||||
|
int allow_default = TRUE;
|
||||||
|
stbwingraph_event e = { STBWGE__none };
|
||||||
|
// the following line is wrong for 64-bit windows, but VC6 doesn't have GetWindowLongPtr
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(wnd, GWL_USERDATA);
|
||||||
|
|
||||||
|
switch (msg) {
|
||||||
|
|
||||||
|
case WM_CREATE:
|
||||||
|
{
|
||||||
|
LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lparam;
|
||||||
|
assert(z == NULL);
|
||||||
|
z = (stbwingraph__window *) lpcs->lpCreateParams;
|
||||||
|
SetWindowLong(wnd, GWL_USERDATA, (LONG) z);
|
||||||
|
z->dc = GetDC(wnd);
|
||||||
|
if (stbwingraph_SetPixelFormat(wnd, z->color, z->alpha, z->depth, z->stencil, z->accum)) {
|
||||||
|
z->rc = wglCreateContext(z->dc);
|
||||||
|
if (z->rc) {
|
||||||
|
e.type = STBWGE_create;
|
||||||
|
z->did_share_lists = FALSE;
|
||||||
|
if (z->share_window) {
|
||||||
|
stbwingraph__window *y = (stbwingraph__window *) GetWindowLong(z->share_window, GWL_USERDATA);
|
||||||
|
if (wglShareLists(z->rc, y->rc))
|
||||||
|
z->did_share_lists = TRUE;
|
||||||
|
}
|
||||||
|
wglMakeCurrent(z->dc, z->rc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_PAINT: {
|
||||||
|
PAINTSTRUCT ps;
|
||||||
|
HDC hdc = BeginPaint(wnd, &ps);
|
||||||
|
SelectObject(hdc, GetStockObject(NULL_BRUSH));
|
||||||
|
e.type = STBWGE_draw;
|
||||||
|
e.handle = wnd;
|
||||||
|
z->func(z->data, &e);
|
||||||
|
EndPaint(wnd, &ps);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_DESTROY:
|
||||||
|
e.type = STBWGE_destroy;
|
||||||
|
e.handle = wnd;
|
||||||
|
if (z && z->func)
|
||||||
|
z->func(z->data, &e);
|
||||||
|
wglMakeCurrent(NULL, NULL) ;
|
||||||
|
if (z) {
|
||||||
|
if (z->rc) wglDeleteContext(z->rc);
|
||||||
|
z->dc = 0;
|
||||||
|
z->rc = 0;
|
||||||
|
}
|
||||||
|
if (wnd == stbwingraph_primary_window)
|
||||||
|
PostQuitMessage (0);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WM_CHAR: stbwingraph__key(&e, STBWGE_char , wparam, z); break;
|
||||||
|
case WM_KEYDOWN: stbwingraph__key(&e, STBWGE_keydown, wparam, z); break;
|
||||||
|
case WM_KEYUP: stbwingraph__key(&e, STBWGE_keyup , wparam, z); break;
|
||||||
|
|
||||||
|
case WM_NCMOUSEMOVE: stbwingraph__inclient(z,0); break;
|
||||||
|
case WM_MOUSEMOVE: stbwingraph__inclient(z,1); stbwingraph__mouse(&e, STBWGE_mousemove, wparam, lparam,0,wnd, z); break;
|
||||||
|
case WM_LBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_leftdown, wparam, lparam,1,wnd, z); break;
|
||||||
|
case WM_MBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_middledown, wparam, lparam,1,wnd, z); break;
|
||||||
|
case WM_RBUTTONDOWN: stbwingraph__mouse(&e, STBWGE_rightdown, wparam, lparam,1,wnd, z); break;
|
||||||
|
case WM_LBUTTONUP: stbwingraph__mouse(&e, STBWGE_leftup, wparam, lparam,-1,wnd, z); break;
|
||||||
|
case WM_MBUTTONUP: stbwingraph__mouse(&e, STBWGE_middleup, wparam, lparam,-1,wnd, z); break;
|
||||||
|
case WM_RBUTTONUP: stbwingraph__mouse(&e, STBWGE_rightup, wparam, lparam,-1,wnd, z); break;
|
||||||
|
case WM_MOUSEWHEEL: stbwingraph__mousewheel(&e, STBWGE_mousewheel, wparam, lparam,0,wnd, z); break;
|
||||||
|
|
||||||
|
case WM_ACTIVATE:
|
||||||
|
allow_default = FALSE;
|
||||||
|
if (LOWORD(wparam)==WA_INACTIVE ) {
|
||||||
|
wglMakeCurrent(z->dc, NULL);
|
||||||
|
e.type = STBWGE_deactivate;
|
||||||
|
z->active = FALSE;
|
||||||
|
} else {
|
||||||
|
wglMakeCurrent(z->dc, z->rc);
|
||||||
|
e.type = STBWGE_activate;
|
||||||
|
z->active = TRUE;
|
||||||
|
}
|
||||||
|
e.handle = wnd;
|
||||||
|
z->func(z->data, &e);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WM_SIZE: {
|
||||||
|
RECT rect;
|
||||||
|
allow_default = FALSE;
|
||||||
|
GetClientRect(wnd, &rect);
|
||||||
|
e.type = STBWGE_size;
|
||||||
|
e.width = rect.right;
|
||||||
|
e.height = rect.bottom;
|
||||||
|
e.handle = wnd;
|
||||||
|
z->func(z->data, &e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DefWindowProc (wnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.type != STBWGE__none) {
|
||||||
|
int n;
|
||||||
|
e.handle = wnd;
|
||||||
|
n = z->func(z->data, &e);
|
||||||
|
if (n == STBWINGRAPH_winproc_exit) {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
if (n == STBWINGRAPH_winproc_update) {
|
||||||
|
stbwingraph_force_update = TRUE;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (n != STBWINGRAPH_unprocessed)
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return DefWindowProc (wnd, msg, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
int stbwingraph_DefineClass(HINSTANCE hInstance, char *iconname)
|
||||||
|
{
|
||||||
|
WNDCLASSEX wndclass;
|
||||||
|
|
||||||
|
stbwingraph_app = hInstance;
|
||||||
|
|
||||||
|
wndclass.cbSize = sizeof(wndclass);
|
||||||
|
wndclass.style = CS_OWNDC;
|
||||||
|
wndclass.lpfnWndProc = (WNDPROC) stbwingraph_WinProc;
|
||||||
|
wndclass.cbClsExtra = 0;
|
||||||
|
wndclass.cbWndExtra = 0;
|
||||||
|
wndclass.hInstance = hInstance;
|
||||||
|
wndclass.hIcon = LoadIcon(hInstance, iconname);
|
||||||
|
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
|
||||||
|
wndclass.hbrBackground = GetStockObject(NULL_BRUSH);
|
||||||
|
wndclass.lpszMenuName = "zwingraph";
|
||||||
|
wndclass.lpszClassName = "zwingraph";
|
||||||
|
wndclass.hIconSm = NULL;
|
||||||
|
|
||||||
|
if (!RegisterClassEx(&wndclass))
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_ShowWindow(void *window)
|
||||||
|
{
|
||||||
|
stbwingraph_event e = { STBWGE_create_postshow };
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
|
||||||
|
ShowWindow(window, SW_SHOWNORMAL);
|
||||||
|
InvalidateRect(window, NULL, TRUE);
|
||||||
|
UpdateWindow(window);
|
||||||
|
e.handle = window;
|
||||||
|
z->func(z->data, &e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *stbwingraph_CreateWindow(int primary, stbwingraph_window_proc func, void *data, char *text,
|
||||||
|
int width, int height, int fullscreen, int resizeable, int dest_alpha, int stencil)
|
||||||
|
{
|
||||||
|
HWND win;
|
||||||
|
DWORD dwstyle;
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) malloc(sizeof(*z));
|
||||||
|
|
||||||
|
if (z == NULL) return NULL;
|
||||||
|
memset(z, 0, sizeof(*z));
|
||||||
|
z->color = 24;
|
||||||
|
z->depth = 24;
|
||||||
|
z->alpha = dest_alpha;
|
||||||
|
z->stencil = stencil;
|
||||||
|
z->func = func;
|
||||||
|
z->data = data;
|
||||||
|
z->mx = -(1 << 30);
|
||||||
|
z->my = 0;
|
||||||
|
|
||||||
|
if (primary) {
|
||||||
|
if (stbwingraph_request_windowed)
|
||||||
|
fullscreen = FALSE;
|
||||||
|
else if (stbwingraph_request_fullscreen)
|
||||||
|
fullscreen = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullscreen) {
|
||||||
|
#ifdef STB_SIMPLE
|
||||||
|
stbwingraph_ChangeResolution(width, height, 32, 1);
|
||||||
|
#else
|
||||||
|
if (!stbwingraph_ChangeResolution(width, height, 32, 0))
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
dwstyle = WS_POPUP | WS_CLIPSIBLINGS;
|
||||||
|
} else {
|
||||||
|
RECT rect;
|
||||||
|
dwstyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||||
|
if (resizeable)
|
||||||
|
dwstyle |= WS_SIZEBOX | WS_MAXIMIZEBOX;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.left = 0;
|
||||||
|
rect.right = width;
|
||||||
|
rect.bottom = height;
|
||||||
|
AdjustWindowRect(&rect, dwstyle, FALSE);
|
||||||
|
width = rect.right - rect.left;
|
||||||
|
height = rect.bottom - rect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
win = CreateWindow("zwingraph", text ? text : "sample", dwstyle,
|
||||||
|
CW_USEDEFAULT,0, width, height,
|
||||||
|
NULL, NULL, stbwingraph_app, z);
|
||||||
|
|
||||||
|
if (win == NULL) return win;
|
||||||
|
|
||||||
|
if (primary) {
|
||||||
|
if (stbwingraph_primary_window)
|
||||||
|
stbwingraph_DestroyWindow(stbwingraph_primary_window);
|
||||||
|
stbwingraph_primary_window = win;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stbwingraph_event e = { STBWGE_create };
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
|
||||||
|
z->window = win;
|
||||||
|
e.did_share_lists = z->did_share_lists;
|
||||||
|
e.handle = win;
|
||||||
|
if (z->func(z->data, &e) != STBWINGRAPH_do_not_show)
|
||||||
|
stbwingraph_ShowWindow(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *stbwingraph_CreateWindowSimple(stbwingraph_window_proc func, int width, int height)
|
||||||
|
{
|
||||||
|
int fullscreen = 0;
|
||||||
|
#ifndef _DEBUG
|
||||||
|
if (width == 640 && height == 480) fullscreen = 1;
|
||||||
|
if (width == 800 && height == 600) fullscreen = 1;
|
||||||
|
if (width == 1024 && height == 768) fullscreen = 1;
|
||||||
|
if (width == 1280 && height == 1024) fullscreen = 1;
|
||||||
|
if (width == 1600 && height == 1200) fullscreen = 1;
|
||||||
|
//@TODO: widescreen widths
|
||||||
|
#endif
|
||||||
|
return stbwingraph_CreateWindow(1, func, NULL, NULL, width, height, fullscreen, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *stbwingraph_CreateWindowSimpleFull(stbwingraph_window_proc func, int fullscreen, int ww, int wh, int fw, int fh)
|
||||||
|
{
|
||||||
|
if (fullscreen == -1) {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
fullscreen = 0;
|
||||||
|
#else
|
||||||
|
fullscreen = 1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullscreen) {
|
||||||
|
if (fw) ww = fw;
|
||||||
|
if (fh) wh = fh;
|
||||||
|
}
|
||||||
|
return stbwingraph_CreateWindow(1, func, NULL, NULL, ww, wh, fullscreen, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_DestroyWindow(void *window)
|
||||||
|
{
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(window, GWL_USERDATA);
|
||||||
|
DestroyWindow(window);
|
||||||
|
free(z);
|
||||||
|
if (stbwingraph_primary_window == window)
|
||||||
|
stbwingraph_primary_window = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_ShowCursor(void *window, int visible)
|
||||||
|
{
|
||||||
|
int hide;
|
||||||
|
stbwingraph__window *win;
|
||||||
|
if (!window)
|
||||||
|
window = stbwingraph_primary_window;
|
||||||
|
win = (stbwingraph__window *) GetWindowLong((HWND) window, GWL_USERDATA);
|
||||||
|
hide = !visible;
|
||||||
|
if (hide != win->hide_mouse) {
|
||||||
|
win->hide_mouse = hide;
|
||||||
|
if (!hide)
|
||||||
|
ShowCursor(TRUE);
|
||||||
|
else if (win->in_client)
|
||||||
|
ShowCursor(FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float stbwingraph_GetTimestep(float minimum_time)
|
||||||
|
{
|
||||||
|
float elapsedTime;
|
||||||
|
double thisTime;
|
||||||
|
static double lastTime = -1;
|
||||||
|
|
||||||
|
if (lastTime == -1)
|
||||||
|
lastTime = timeGetTime() / 1000.0 - minimum_time;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
thisTime = timeGetTime() / 1000.0;
|
||||||
|
elapsedTime = (float) (thisTime - lastTime);
|
||||||
|
if (elapsedTime >= minimum_time) {
|
||||||
|
lastTime = thisTime;
|
||||||
|
return elapsedTime;
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
Sleep(2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_SetGLWindow(void *win)
|
||||||
|
{
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
|
||||||
|
if (z)
|
||||||
|
wglMakeCurrent(z->dc, z->rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_MakeFonts(void *window, int font_base)
|
||||||
|
{
|
||||||
|
wglUseFontBitmaps(GetDC(window ? window : stbwingraph_primary_window), 0, 256, font_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns 1 if WM_QUIT, 0 if 'func' returned 0
|
||||||
|
int stbwingraph_MainLoop(stbwingraph_update func, float mintime)
|
||||||
|
{
|
||||||
|
int needs_drawing = FALSE;
|
||||||
|
MSG msg;
|
||||||
|
|
||||||
|
int is_animating = TRUE;
|
||||||
|
if (mintime <= 0) mintime = 0.01f;
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
int n;
|
||||||
|
|
||||||
|
is_animating = TRUE;
|
||||||
|
// wait for a message if: (a) we're animating and there's already a message
|
||||||
|
// or (b) we're not animating
|
||||||
|
if (!is_animating || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
||||||
|
stbwingraph_force_update = FALSE;
|
||||||
|
if (GetMessage(&msg, NULL, 0, 0)) {
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
} else {
|
||||||
|
return 1; // WM_QUIT
|
||||||
|
}
|
||||||
|
|
||||||
|
// only force a draw for certain messages...
|
||||||
|
// if I don't do this, we peg at 50% for some reason... must
|
||||||
|
// be a bug somewhere, because we peg at 100% when rendering...
|
||||||
|
// very weird... looks like NVIDIA is pumping some messages
|
||||||
|
// through our pipeline? well, ok, I guess if we can get
|
||||||
|
// non-user-generated messages we have to do this
|
||||||
|
if (!stbwingraph_force_update) {
|
||||||
|
switch (msg.message) {
|
||||||
|
case WM_MOUSEMOVE:
|
||||||
|
case WM_NCMOUSEMOVE:
|
||||||
|
break;
|
||||||
|
case WM_CHAR:
|
||||||
|
case WM_KEYDOWN:
|
||||||
|
case WM_KEYUP:
|
||||||
|
case WM_LBUTTONDOWN:
|
||||||
|
case WM_MBUTTONDOWN:
|
||||||
|
case WM_RBUTTONDOWN:
|
||||||
|
case WM_LBUTTONUP:
|
||||||
|
case WM_MBUTTONUP:
|
||||||
|
case WM_RBUTTONUP:
|
||||||
|
case WM_TIMER:
|
||||||
|
case WM_SIZE:
|
||||||
|
case WM_ACTIVATE:
|
||||||
|
needs_drawing = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
needs_drawing = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if another message, process that first
|
||||||
|
// @TODO: i don't think this is working, because I can't key ahead
|
||||||
|
// in the SVT demo app
|
||||||
|
if (PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// and now call update
|
||||||
|
if (needs_drawing || is_animating) {
|
||||||
|
int real=1, in_client=1;
|
||||||
|
if (stbwingraph_primary_window) {
|
||||||
|
stbwingraph__window *z = (stbwingraph__window *) GetWindowLong(stbwingraph_primary_window, GWL_USERDATA);
|
||||||
|
if (z && !z->active) {
|
||||||
|
real = 0;
|
||||||
|
}
|
||||||
|
if (z)
|
||||||
|
in_client = z->in_client;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stbwingraph_primary_window)
|
||||||
|
stbwingraph_SetGLWindow(stbwingraph_primary_window);
|
||||||
|
n = func(stbwingraph_GetTimestep(mintime), real, in_client);
|
||||||
|
if (n == STBWINGRAPH_update_exit)
|
||||||
|
return 0; // update_quit
|
||||||
|
|
||||||
|
is_animating = (n != STBWINGRAPH_update_pause);
|
||||||
|
|
||||||
|
needs_drawing = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stbwingraph_SwapBuffers(void *win)
|
||||||
|
{
|
||||||
|
stbwingraph__window *z;
|
||||||
|
if (win == NULL) win = stbwingraph_primary_window;
|
||||||
|
z = (stbwingraph__window *) GetWindowLong(win, GWL_USERDATA);
|
||||||
|
if (z && z->dc)
|
||||||
|
SwapBuffers(z->dc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_WINMAIN
|
||||||
|
void stbwingraph_main(void);
|
||||||
|
|
||||||
|
char *stb_wingraph_commandline;
|
||||||
|
|
||||||
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
// add spaces to either side of the string
|
||||||
|
buffer[0] = ' ';
|
||||||
|
strcpy(buffer+1, lpCmdLine);
|
||||||
|
strcat(buffer, " ");
|
||||||
|
if (strstr(buffer, " -reset ")) {
|
||||||
|
ChangeDisplaySettings(NULL, 0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (strstr(buffer, " -window ") || strstr(buffer, " -windowed "))
|
||||||
|
stbwingraph_request_windowed = TRUE;
|
||||||
|
else if (strstr(buffer, " -full ") || strstr(buffer, " -fullscreen "))
|
||||||
|
stbwingraph_request_fullscreen = TRUE;
|
||||||
|
}
|
||||||
|
stb_wingraph_commandline = lpCmdLine;
|
||||||
|
|
||||||
|
stbwingraph_DefineClass(hInstance, "appicon");
|
||||||
|
stbwingraph_main();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef STB_EXTERN
|
||||||
|
#ifdef STB_WINGRAPH_DISABLE_DEFINE_AT_END
|
||||||
|
#undef STB_DEFINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // INCLUDE_STB_WINGRAPH_H
|