-
Notifications
You must be signed in to change notification settings - Fork 36
/
ogt_vox.h
3207 lines (2868 loc) · 173 KB
/
ogt_vox.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
opengametools vox file reader/writer/merger - v0.997 - MIT license - Justin Paver, Oct 2019
This is a single-header-file library that provides easy-to-use
support for reading MagicaVoxel .vox files into structures that
are easy to dereference and extract information from. It also
supports writing back out to .vox file from those structures.
Please see the MIT license information at the end of this file.
Also, please consider sharing any improvements you make.
For more information and more tools, visit:
https://github.com/jpaver/opengametools
HOW TO COMPILE THIS LIBRARY
1. To compile this library, do this in *one* C or C++ file:
#define OGT_VOX_IMPLEMENTATION
#include "ogt_vox.h"
2. From any other module, it is sufficient to just #include this as usual:
#include "ogt_vox.h"
HOW TO READ A VOX SCENE (See demo_vox.cpp)
1. load a .vox file off disk into a memory buffer.
2. construct a scene from the memory buffer:
ogt_vox_scene* scene = ogt_vox_read_scene(buffer, buffer_size);
3. use the scene members to extract the information you need. eg.
printf("# of layers: %u\n", scene->num_layers );
4. destroy the scene:
ogt_vox_destroy_scene(scene);
HOW TO MERGE MULTIPLE VOX SCENES (See merge_vox.cpp)
1. construct multiple scenes from files you want to merge.
// read buffer1/buffer_size1 from "test1.vox"
// read buffer2/buffer_size2 from "test2.vox"
// read buffer3/buffer_size3 from "test3.vox"
ogt_vox_scene* scene1 = ogt_vox_read_scene(buffer1, buffer_size1);
ogt_vox_scene* scene2 = ogt_vox_read_scene(buffer2, buffer_size2);
ogt_vox_scene* scene3 = ogt_vox_read_scene(buffer3, buffer_size3);
2. construct a merged scene
const ogt_vox_scene* scenes[] = {scene1, scene2, scene3};
ogt_vox_scene* merged_scene = ogt_vox_merge_scenes(scenes, 3, NULL, 0);
3. save out the merged scene
uint8_t* out_buffer = ogt_vox_write_scene(merged_scene, &out_buffer_size);
// save out_buffer to disk as a .vox file (it has length out_buffer_size)
4. destroy the merged scene:
ogt_vox_destroy_scene(merged_scene);
EXPLANATION OF SCENE ELEMENTS:
A ogt_vox_scene comprises primarily a set of instances, models, layers and a palette.
A ogt_vox_palette contains a set of 256 colors that is used for the scene.
Each color is represented by a 4-tuple called an ogt_vox_rgba which contains red,
green, blue and alpha values for the color.
A ogt_vox_model is a 3-dimensional grid of voxels, where each of those voxels
is represented by an 8-bit color index. Voxels are arranged in order of increasing
X then increasing Y then increasing Z.
Given the x,y,z values for a voxel within the model dimensions, the voxels index
in the grid can be obtained as follows:
voxel_index = x + (y * model->size_x) + (z * model->size_x * model->size_y)
The index is only valid if the coordinate x,y,z satisfy the following conditions:
0 <= x < model->size_x -AND-
0 <= y < model->size_y -AND-
0 <= z < model->size_z
A voxels color index can be obtained as follows:
uint8_t color_index = model->voxel_data[voxel_index];
If color_index == 0, the voxel is not solid and can be skipped,
If color_index != 0, the voxel is solid and can be used to lookup the color in the palette:
ogt_vox_rgba color = scene->palette.color[ color_index]
A ogt_vox_instance is an individual placement of a voxel model within the scene. Each
instance has a transform that determines its position and orientation within the scene,
but it also has an index that specifies which model the instance uses for its shape. It
is expected that there is a many-to-one mapping of instances to models.
An ogt_vox_layer is used to conceptually group instances. Each instance indexes the
layer that it belongs to, but the layer itself has its own name and hidden/shown state.
EXPLANATION OF MERGED SCENES:
A merged scene contains all the models and all the scene instances from
each of the scenes that were passed into it.
The merged scene will have a combined palette of all the source scene
palettes by trying to match existing colors exactly, and falling back
to an RGB-distance matched color when all 256 colors in the merged
scene palette has been allocated.
You can explicitly control up to 255 merge palette colors by providing
those colors to ogt_vox_merge_scenes in the required_colors parameters eg.
const ogt_vox_palette palette; // load this via .vox or procedurally or whatever
const ogt_vox_scene* scenes[] = {scene1, scene2, scene3};
// palette.color[0] is always the empty color which is why we pass 255 colors starting from index 1 only:
ogt_vox_scene* merged_scene = ogt_vox_merge_scenes(scenes, 3, &palette.color[1], 255);
EXPLANATION OF MODEL PIVOTS
If a voxel model grid has dimension size.xyz in terms of number of voxels, the centre pivot
for that model is located at floor( size.xyz / 2).
eg. for a 3x4x1 voxel model, the pivot would be at (1,2,0), or the X in the below ascii art.
4 +-----+-----+-----+
| . | . | . |
3 +-----+-----+-----+
| . | . | . |
2 +-----X-----+-----+
| . | . | . |
1 +-----+-----+-----+
| . | . | . |
0 +-----+-----+-----+
0 1 2 3
An example model in this grid form factor might look like this:
4 +-----+-----+-----+
| . | . | . |
3 +-----+-----+-----+
| . |
2 X-----+
| . |
1 +-----+
| . |
0 +-----+
0 1 2 3
If you were to generate a mesh from this, clearly each vertex and each face would be on an integer
coordinate eg. 1, 2, 3 etc. while the centre of each grid location (ie. the . in the above diagram)
will be on a coordinate that is halfway between integer coordinates. eg. 1.5, 2.5, 3.5 etc.
To ensure your mesh is properly centered such that instance transforms are correctly applied, you
want the pivot to be treated as if it were (0,0,0) in model space. To achieve this, simply
subtract the pivot from any geometry that is generated (eg. vertices in a mesh).
For the 3x4x1 voxel model above, doing this would look like this:
2 +-----+-----+-----+
| . | . | . |
1 +-----+-----+-----+
| . |
0 X-----+
| . |
-1 +-----+
| . |
-2 +-----+
-1 0 1 2
To replace asserts within this library with your own implementation, simply #define ogt_assert before defining your implementation
eg.
#include "my_assert.h"
#define ogt_assert(condition, message_str) my_assert(condition, message_str)
#define OGT_VOX_IMPLEMENTATION
#include "path/to/ogt_vox.h"
ogt_vox is little-endian by default, but it does support big-endian if OGT_VOX_BIGENDIAN_SWAP32(x) is #defined
to a function that can swap byte order within a uint32_t word before the implementation. eg.
#define OGT_VOX_BIGENDIAN_SWAP32(x) __builtin_swap32(x) // linux/gcc
#define OGT_VOX_IMPLEMENTATION
#include "path/to/ogt_vox.h"
*/
#ifndef OGT_VOX_H__
#define OGT_VOX_H__
#if _MSC_VER == 1400
// VS2005 doesn't have inttypes or stdint so we just define what we need here.
typedef unsigned char uint8_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
#ifndef UINT32_MAX
#define UINT32_MAX ((uint32_t)0xFFFFFFFF)
#endif
#ifndef INT32_MAX
#define INT32_MAX ((int32_t)0x7FFFFFFF)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX ((uint8_t)0xFF)
#endif
#elif defined(_MSC_VER)
// general VS*
#include <inttypes.h>
#elif __APPLE__
// general Apple compiler
#include <stdint.h>
#include <limits.h>
#include <stdlib.h> // for size_t
#elif defined(__GNUC__)
// any GCC*
#include <inttypes.h>
#include <stdlib.h> // for size_t
#else
#error some fixup needed for this platform?
#endif
#ifdef OGT_VOX_BIGENDIAN_SWAP32
// host is big-endian, so we byte-swap
#define _vox_htole32(x) OGT_VOX_BIGENDIAN_SWAP32((x))
#define _vox_le32toh(x) OGT_VOX_BIGENDIAN_SWAP32((x))
#else
// host is little-endian (default)
#define _vox_htole32(x) (x)
#define _vox_le32toh(x) (x)
#endif
// denotes an invalid group index. Usually this is only applicable to the scene's root group's parent.
static const uint32_t k_invalid_group_index = UINT32_MAX;
// color
typedef struct ogt_vox_rgba
{
uint8_t r,g,b,a; // red, green, blue and alpha components of a color.
} ogt_vox_rgba;
// column-major 4x4 matrix
typedef struct ogt_vox_transform
{
float m00, m01, m02, m03; // column 0 of 4x4 matrix, 1st three elements = x axis vector, last element always 0.0
float m10, m11, m12, m13; // column 1 of 4x4 matrix, 1st three elements = y axis vector, last element always 0.0
float m20, m21, m22, m23; // column 2 of 4x4 matrix, 1st three elements = z axis vector, last element always 0.0
float m30, m31, m32, m33; // column 3 of 4x4 matrix. 1st three elements = translation vector, last element always 1.0
} ogt_vox_transform;
ogt_vox_transform ogt_vox_transform_get_identity();
ogt_vox_transform ogt_vox_transform_multiply(const ogt_vox_transform & a, const ogt_vox_transform & b);
// a palette of colors
typedef struct ogt_vox_palette
{
ogt_vox_rgba color[256]; // palette of colors. use the voxel indices to lookup color from the palette.
} ogt_vox_palette;
// Extended Material Chunk MATL types
enum ogt_matl_type
{
ogt_matl_type_diffuse = 0, // diffuse is default
ogt_matl_type_metal = 1,
ogt_matl_type_glass = 2,
ogt_matl_type_emit = 3,
ogt_matl_type_blend = 4,
ogt_matl_type_media = 5,
};
enum ogt_cam_mode
{
ogt_cam_mode_perspective = 0,
ogt_cam_mode_free = 1,
ogt_cam_mode_pano = 2,
ogt_cam_mode_orthographic = 3,
ogt_cam_mode_isometric = 4,
ogt_cam_mode_unknown = 5
};
// Content Flags for ogt_vox_matl values for a given material
static const uint32_t k_ogt_vox_matl_have_metal = 1 << 0;
static const uint32_t k_ogt_vox_matl_have_rough = 1 << 1;
static const uint32_t k_ogt_vox_matl_have_spec = 1 << 2;
static const uint32_t k_ogt_vox_matl_have_ior = 1 << 3;
static const uint32_t k_ogt_vox_matl_have_att = 1 << 4;
static const uint32_t k_ogt_vox_matl_have_flux = 1 << 5;
static const uint32_t k_ogt_vox_matl_have_emit = 1 << 6;
static const uint32_t k_ogt_vox_matl_have_ldr = 1 << 7;
static const uint32_t k_ogt_vox_matl_have_trans = 1 << 8;
static const uint32_t k_ogt_vox_matl_have_alpha = 1 << 9;
static const uint32_t k_ogt_vox_matl_have_d = 1 << 10;
static const uint32_t k_ogt_vox_matl_have_sp = 1 << 11;
static const uint32_t k_ogt_vox_matl_have_g = 1 << 12;
static const uint32_t k_ogt_vox_matl_have_media = 1 << 13;
// Extended Material Chunk MATL information
typedef struct ogt_vox_matl
{
uint32_t content_flags; // set of k_ogt_vox_matl_* OR together to denote contents available
ogt_matl_type type;
float metal;
float rough;
float spec;
float ior;
float att;
float flux;
float emit;
float ldr;
float trans;
float alpha;
float d;
float sp;
float g;
float media;
} ogt_vox_matl;
// Extended Material Chunk MATL array of materials
typedef struct ogt_vox_matl_array
{
ogt_vox_matl matl[256]; // extended material information from Material Chunk MATL
} ogt_vox_matl_array;
typedef struct ogt_vox_cam
{
uint32_t camera_id;
ogt_cam_mode mode;
float focus[3]; // the target position
float angle[3]; // rotation in degree
float radius;
float frustum;
int fov; // angle in degree
} ogt_vox_cam;
// a 3-dimensional model of voxels
typedef struct ogt_vox_model
{
uint32_t size_x; // number of voxels in the local x dimension
uint32_t size_y; // number of voxels in the local y dimension
uint32_t size_z; // number of voxels in the local z dimension
uint32_t voxel_hash; // hash of the content of the grid.
const uint8_t* voxel_data; // grid of voxel data comprising color indices in x -> y -> z order. a color index of 0 means empty, all other indices mean solid and can be used to index the scene's palette to obtain the color for the voxel.
} ogt_vox_model;
// a keyframe for animation of a transform
typedef struct ogt_vox_keyframe_transform {
uint32_t frame_index;
ogt_vox_transform transform;
} ogt_vox_keyframe_transform;
// a keyframe for animation of a model
typedef struct ogt_vox_keyframe_model {
uint32_t frame_index;
uint32_t model_index;
} ogt_vox_keyframe_model;
// an animated transform
typedef struct ogt_vox_anim_transform {
const ogt_vox_keyframe_transform* keyframes;
uint32_t num_keyframes;
bool loop;
} ogt_vox_anim_transform;
// an animated model
typedef struct ogt_vox_anim_model {
const ogt_vox_keyframe_model* keyframes;
uint32_t num_keyframes;
bool loop;
} ogt_vox_anim_model;
// an instance of a model within the scene
typedef struct ogt_vox_instance
{
const char* name; // name of the instance if there is one, will be NULL otherwise.
ogt_vox_transform transform; // orientation and position of this instance on first frame of the scene. This is relative to its group local transform if group_index is not 0
uint32_t model_index; // index of the model used by this instance on the first frame of the scene. used to lookup the model in the scene's models[] array.
uint32_t layer_index; // index of the layer used by this instance. used to lookup the layer in the scene's layers[] array.
uint32_t group_index; // this will be the index of the group in the scene's groups[] array. If group is zero it will be the scene root group and the instance transform will be a world-space transform, otherwise the transform is relative to the group.
bool hidden; // whether this instance is individually hidden or not. Note: the instance can also be hidden when its layer is hidden, or if it belongs to a group that is hidden.
ogt_vox_anim_transform transform_anim; // animation for the transform
ogt_vox_anim_model model_anim; // animation for the model_index
} ogt_vox_instance;
// describes a layer within the scene
typedef struct ogt_vox_layer
{
const char* name; // name of this layer if there is one, will be NULL otherwise.
ogt_vox_rgba color; // color of the layer.
bool hidden; // whether this layer is hidden or not.
} ogt_vox_layer;
// describes a group within the scene
typedef struct ogt_vox_group
{
const char* name; // name of the group if there is one, will be NULL otherwise
ogt_vox_transform transform; // transform of this group relative to its parent group (if any), otherwise this will be relative to world-space.
uint32_t parent_group_index; // if this group is parented to another group, this will be the index of its parent in the scene's groups[] array, otherwise this group will be the scene root group and this value will be k_invalid_group_index
uint32_t layer_index; // which layer this group belongs to. used to lookup the layer in the scene's layers[] array.
bool hidden; // whether this group is hidden or not.
ogt_vox_anim_transform transform_anim; // animated transform data
} ogt_vox_group;
// the scene parsed from a .vox file.
typedef struct ogt_vox_scene
{
uint32_t num_models; // number of models within the scene.
uint32_t num_instances; // number of instances in the scene (on anim frame 0)
uint32_t num_layers; // number of layers in the scene
uint32_t num_groups; // number of groups in the scene
const ogt_vox_model** models; // array of models. size is num_models
const ogt_vox_instance* instances; // array of instances. size is num_instances
const ogt_vox_layer* layers; // array of layers. size is num_layers
const ogt_vox_group* groups; // array of groups. size is num_groups
ogt_vox_palette palette; // the palette for this scene
ogt_vox_matl_array materials; // the extended materials for this scene
uint32_t num_cameras; // number of cameras for this scene
const ogt_vox_cam* cameras; // the cameras for this scene
} ogt_vox_scene;
// allocate memory function interface. pass in size, and get a pointer to memory with at least that size available.
typedef void* (*ogt_vox_alloc_func)(size_t size);
// free memory function interface. pass in a pointer previously allocated and it will be released back to the system managing memory.
typedef void (*ogt_vox_free_func)(void* ptr);
// override the default scene memory allocator if you need to control memory precisely.
void ogt_vox_set_memory_allocator(ogt_vox_alloc_func alloc_func, ogt_vox_free_func free_func);
void* ogt_vox_malloc(size_t size);
void ogt_vox_free(void* mem);
// progress feedback function with option to cancel a ogt_vox_write_scene operation. Percentage complete is approximately given by: 100.0f * progress.
typedef bool (*ogt_vox_progress_callback_func)(float progress, void* user_data);
// set the progress callback function and user data to pass to it
void ogt_vox_set_progress_callback_func(ogt_vox_progress_callback_func progress_callback_func, void* user_data);
// flags for ogt_vox_read_scene_with_flags
static const uint32_t k_read_scene_flags_groups = 1 << 0; // if not specified, all instance transforms will be flattened into world space. If specified, will read group information and keep all transforms as local transform relative to the group they are in.
static const uint32_t k_read_scene_flags_keyframes = 1 << 1; // if specified, all instances and groups will contain keyframe data.
static const uint32_t k_read_scene_flags_keep_empty_models_instances = 1 << 2; // if specified, all empty models and instances referencing those will be kept rather than culled.
static const uint32_t k_read_scene_flags_keep_duplicate_models = 1 << 3; // if specified, we do not de-duplicate models.
// creates a scene from a vox file within a memory buffer of a given size.
// you can destroy the input buffer once you have the scene as this function will allocate separate memory for the scene objecvt.
const ogt_vox_scene* ogt_vox_read_scene(const uint8_t* buffer, uint32_t buffer_size);
// just like ogt_vox_read_scene, but you can additionally pass a union of k_read_scene_flags
const ogt_vox_scene* ogt_vox_read_scene_with_flags(const uint8_t* buffer, uint32_t buffer_size, uint32_t read_flags);
// destroys a scene object to release its memory.
void ogt_vox_destroy_scene(const ogt_vox_scene* scene);
// writes the scene to a new buffer and returns the buffer size. free the buffer with ogt_vox_free
uint8_t* ogt_vox_write_scene(const ogt_vox_scene* scene, uint32_t* buffer_size);
// merges the specified scenes together to create a bigger scene. Merged scene can be destroyed using ogt_vox_destroy_scene
// If you require specific colors in the merged scene palette, provide up to and including 255 of them via required_colors/required_color_count.
ogt_vox_scene* ogt_vox_merge_scenes(const ogt_vox_scene** scenes, uint32_t scene_count, const ogt_vox_rgba* required_colors, const uint32_t required_color_count);
// sample the model index for a given instance at the given frame
uint32_t ogt_vox_sample_instance_model(const ogt_vox_instance* instance, uint32_t frame_index);
// samples the transform for an instance at a given frame.
// ogt_vox_sample_instance_transform_global returns the transform in world space (aka global)
// ogt_vox_sample_instance_transform_local returns the transform relative to its parent group
ogt_vox_transform ogt_vox_sample_instance_transform_global(const ogt_vox_instance* instance, uint32_t frame_index, const ogt_vox_scene* scene);
ogt_vox_transform ogt_vox_sample_instance_transform_local(const ogt_vox_instance* instance, uint32_t frame_index);
// sample the transform for a group at a given frame
// ogt_vox_sample_group_transform_global returns the transform in world space (aka global)
// ogt_vox_sample_group_transform_local returns the transform relative to its parent group
ogt_vox_transform ogt_vox_sample_group_transform_global(const ogt_vox_group* group, uint32_t frame_index, const ogt_vox_scene* scene);
ogt_vox_transform ogt_vox_sample_group_transform_local(const ogt_vox_group* group, uint32_t frame_index);
#endif // OGT_VOX_H__
//-----------------------------------------------------------------------------------------------------------------
//
// If you're only interested in using this library, everything you need is above this point.
// If you're interested in how this library works, everything you need is below this point.
//
//-----------------------------------------------------------------------------------------------------------------
#ifdef OGT_VOX_IMPLEMENTATION
// callers can override asserts in ogt_vox by defining their own macro before the implementation
#ifndef ogt_assert
#include <assert.h>
#define ogt_assert(x, msg_str) do { assert((x) && (msg_str)); } while(0)
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// MAKE_VOX_CHUNK_ID: used to construct a literal to describe a chunk in a .vox file.
#define MAKE_VOX_CHUNK_ID(c0,c1,c2,c3) ( (c0<<0) | (c1<<8) | (c2<<16) | (c3<<24) )
static const uint32_t CHUNK_ID_VOX_ = MAKE_VOX_CHUNK_ID('V','O','X',' ');
static const uint32_t CHUNK_ID_MAIN = MAKE_VOX_CHUNK_ID('M','A','I','N');
static const uint32_t CHUNK_ID_SIZE = MAKE_VOX_CHUNK_ID('S','I','Z','E');
static const uint32_t CHUNK_ID_XYZI = MAKE_VOX_CHUNK_ID('X','Y','Z','I');
static const uint32_t CHUNK_ID_RGBA = MAKE_VOX_CHUNK_ID('R','G','B','A');
static const uint32_t CHUNK_ID_nTRN = MAKE_VOX_CHUNK_ID('n','T','R','N');
static const uint32_t CHUNK_ID_nGRP = MAKE_VOX_CHUNK_ID('n','G','R','P');
static const uint32_t CHUNK_ID_nSHP = MAKE_VOX_CHUNK_ID('n','S','H','P');
static const uint32_t CHUNK_ID_IMAP = MAKE_VOX_CHUNK_ID('I','M','A','P');
static const uint32_t CHUNK_ID_LAYR = MAKE_VOX_CHUNK_ID('L','A','Y','R');
static const uint32_t CHUNK_ID_MATL = MAKE_VOX_CHUNK_ID('M','A','T','L');
static const uint32_t CHUNK_ID_MATT = MAKE_VOX_CHUNK_ID('M','A','T','T');
static const uint32_t CHUNK_ID_rOBJ = MAKE_VOX_CHUNK_ID('r','O','B','J');
static const uint32_t CHUNK_ID_rCAM = MAKE_VOX_CHUNK_ID('r','C','A','M');
static const uint32_t NAME_MAX_LEN = 256; // max name len = 255 plus 1 for null terminator
static const uint32_t CHUNK_HEADER_LEN = 12; // 4 bytes for each of: chunk_id, chunk_size, chunk_child_size
// Some older .vox files will not store a palette, in which case the following palette will be used!
static const uint8_t k_default_vox_palette[256 * 4] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xff, 0xff, 0xff, 0x99, 0xff, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x33, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xcc, 0xff, 0xff, 0xff, 0xcc, 0xcc, 0xff,
0xff, 0xcc, 0x99, 0xff, 0xff, 0xcc, 0x66, 0xff, 0xff, 0xcc, 0x33, 0xff, 0xff, 0xcc, 0x00, 0xff, 0xff, 0x99, 0xff, 0xff, 0xff, 0x99, 0xcc, 0xff, 0xff, 0x99, 0x99, 0xff, 0xff, 0x99, 0x66, 0xff,
0xff, 0x99, 0x33, 0xff, 0xff, 0x99, 0x00, 0xff, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xcc, 0xff, 0xff, 0x66, 0x99, 0xff, 0xff, 0x66, 0x66, 0xff, 0xff, 0x66, 0x33, 0xff, 0xff, 0x66, 0x00, 0xff,
0xff, 0x33, 0xff, 0xff, 0xff, 0x33, 0xcc, 0xff, 0xff, 0x33, 0x99, 0xff, 0xff, 0x33, 0x66, 0xff, 0xff, 0x33, 0x33, 0xff, 0xff, 0x33, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xcc, 0xff,
0xff, 0x00, 0x99, 0xff, 0xff, 0x00, 0x66, 0xff, 0xff, 0x00, 0x33, 0xff, 0xff, 0x00, 0x00, 0xff, 0xcc, 0xff, 0xff, 0xff, 0xcc, 0xff, 0xcc, 0xff, 0xcc, 0xff, 0x99, 0xff, 0xcc, 0xff, 0x66, 0xff,
0xcc, 0xff, 0x33, 0xff, 0xcc, 0xff, 0x00, 0xff, 0xcc, 0xcc, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0xff, 0xcc, 0xcc, 0x99, 0xff, 0xcc, 0xcc, 0x66, 0xff, 0xcc, 0xcc, 0x33, 0xff, 0xcc, 0xcc, 0x00, 0xff,
0xcc, 0x99, 0xff, 0xff, 0xcc, 0x99, 0xcc, 0xff, 0xcc, 0x99, 0x99, 0xff, 0xcc, 0x99, 0x66, 0xff, 0xcc, 0x99, 0x33, 0xff, 0xcc, 0x99, 0x00, 0xff, 0xcc, 0x66, 0xff, 0xff, 0xcc, 0x66, 0xcc, 0xff,
0xcc, 0x66, 0x99, 0xff, 0xcc, 0x66, 0x66, 0xff, 0xcc, 0x66, 0x33, 0xff, 0xcc, 0x66, 0x00, 0xff, 0xcc, 0x33, 0xff, 0xff, 0xcc, 0x33, 0xcc, 0xff, 0xcc, 0x33, 0x99, 0xff, 0xcc, 0x33, 0x66, 0xff,
0xcc, 0x33, 0x33, 0xff, 0xcc, 0x33, 0x00, 0xff, 0xcc, 0x00, 0xff, 0xff, 0xcc, 0x00, 0xcc, 0xff, 0xcc, 0x00, 0x99, 0xff, 0xcc, 0x00, 0x66, 0xff, 0xcc, 0x00, 0x33, 0xff, 0xcc, 0x00, 0x00, 0xff,
0x99, 0xff, 0xff, 0xff, 0x99, 0xff, 0xcc, 0xff, 0x99, 0xff, 0x99, 0xff, 0x99, 0xff, 0x66, 0xff, 0x99, 0xff, 0x33, 0xff, 0x99, 0xff, 0x00, 0xff, 0x99, 0xcc, 0xff, 0xff, 0x99, 0xcc, 0xcc, 0xff,
0x99, 0xcc, 0x99, 0xff, 0x99, 0xcc, 0x66, 0xff, 0x99, 0xcc, 0x33, 0xff, 0x99, 0xcc, 0x00, 0xff, 0x99, 0x99, 0xff, 0xff, 0x99, 0x99, 0xcc, 0xff, 0x99, 0x99, 0x99, 0xff, 0x99, 0x99, 0x66, 0xff,
0x99, 0x99, 0x33, 0xff, 0x99, 0x99, 0x00, 0xff, 0x99, 0x66, 0xff, 0xff, 0x99, 0x66, 0xcc, 0xff, 0x99, 0x66, 0x99, 0xff, 0x99, 0x66, 0x66, 0xff, 0x99, 0x66, 0x33, 0xff, 0x99, 0x66, 0x00, 0xff,
0x99, 0x33, 0xff, 0xff, 0x99, 0x33, 0xcc, 0xff, 0x99, 0x33, 0x99, 0xff, 0x99, 0x33, 0x66, 0xff, 0x99, 0x33, 0x33, 0xff, 0x99, 0x33, 0x00, 0xff, 0x99, 0x00, 0xff, 0xff, 0x99, 0x00, 0xcc, 0xff,
0x99, 0x00, 0x99, 0xff, 0x99, 0x00, 0x66, 0xff, 0x99, 0x00, 0x33, 0xff, 0x99, 0x00, 0x00, 0xff, 0x66, 0xff, 0xff, 0xff, 0x66, 0xff, 0xcc, 0xff, 0x66, 0xff, 0x99, 0xff, 0x66, 0xff, 0x66, 0xff,
0x66, 0xff, 0x33, 0xff, 0x66, 0xff, 0x00, 0xff, 0x66, 0xcc, 0xff, 0xff, 0x66, 0xcc, 0xcc, 0xff, 0x66, 0xcc, 0x99, 0xff, 0x66, 0xcc, 0x66, 0xff, 0x66, 0xcc, 0x33, 0xff, 0x66, 0xcc, 0x00, 0xff,
0x66, 0x99, 0xff, 0xff, 0x66, 0x99, 0xcc, 0xff, 0x66, 0x99, 0x99, 0xff, 0x66, 0x99, 0x66, 0xff, 0x66, 0x99, 0x33, 0xff, 0x66, 0x99, 0x00, 0xff, 0x66, 0x66, 0xff, 0xff, 0x66, 0x66, 0xcc, 0xff,
0x66, 0x66, 0x99, 0xff, 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x33, 0xff, 0x66, 0x66, 0x00, 0xff, 0x66, 0x33, 0xff, 0xff, 0x66, 0x33, 0xcc, 0xff, 0x66, 0x33, 0x99, 0xff, 0x66, 0x33, 0x66, 0xff,
0x66, 0x33, 0x33, 0xff, 0x66, 0x33, 0x00, 0xff, 0x66, 0x00, 0xff, 0xff, 0x66, 0x00, 0xcc, 0xff, 0x66, 0x00, 0x99, 0xff, 0x66, 0x00, 0x66, 0xff, 0x66, 0x00, 0x33, 0xff, 0x66, 0x00, 0x00, 0xff,
0x33, 0xff, 0xff, 0xff, 0x33, 0xff, 0xcc, 0xff, 0x33, 0xff, 0x99, 0xff, 0x33, 0xff, 0x66, 0xff, 0x33, 0xff, 0x33, 0xff, 0x33, 0xff, 0x00, 0xff, 0x33, 0xcc, 0xff, 0xff, 0x33, 0xcc, 0xcc, 0xff,
0x33, 0xcc, 0x99, 0xff, 0x33, 0xcc, 0x66, 0xff, 0x33, 0xcc, 0x33, 0xff, 0x33, 0xcc, 0x00, 0xff, 0x33, 0x99, 0xff, 0xff, 0x33, 0x99, 0xcc, 0xff, 0x33, 0x99, 0x99, 0xff, 0x33, 0x99, 0x66, 0xff,
0x33, 0x99, 0x33, 0xff, 0x33, 0x99, 0x00, 0xff, 0x33, 0x66, 0xff, 0xff, 0x33, 0x66, 0xcc, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x66, 0xff, 0x33, 0x66, 0x33, 0xff, 0x33, 0x66, 0x00, 0xff,
0x33, 0x33, 0xff, 0xff, 0x33, 0x33, 0xcc, 0xff, 0x33, 0x33, 0x99, 0xff, 0x33, 0x33, 0x66, 0xff, 0x33, 0x33, 0x33, 0xff, 0x33, 0x33, 0x00, 0xff, 0x33, 0x00, 0xff, 0xff, 0x33, 0x00, 0xcc, 0xff,
0x33, 0x00, 0x99, 0xff, 0x33, 0x00, 0x66, 0xff, 0x33, 0x00, 0x33, 0xff, 0x33, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xcc, 0xff, 0x00, 0xff, 0x99, 0xff, 0x00, 0xff, 0x66, 0xff,
0x00, 0xff, 0x33, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xcc, 0xff, 0xff, 0x00, 0xcc, 0xcc, 0xff, 0x00, 0xcc, 0x99, 0xff, 0x00, 0xcc, 0x66, 0xff, 0x00, 0xcc, 0x33, 0xff, 0x00, 0xcc, 0x00, 0xff,
0x00, 0x99, 0xff, 0xff, 0x00, 0x99, 0xcc, 0xff, 0x00, 0x99, 0x99, 0xff, 0x00, 0x99, 0x66, 0xff, 0x00, 0x99, 0x33, 0xff, 0x00, 0x99, 0x00, 0xff, 0x00, 0x66, 0xff, 0xff, 0x00, 0x66, 0xcc, 0xff,
0x00, 0x66, 0x99, 0xff, 0x00, 0x66, 0x66, 0xff, 0x00, 0x66, 0x33, 0xff, 0x00, 0x66, 0x00, 0xff, 0x00, 0x33, 0xff, 0xff, 0x00, 0x33, 0xcc, 0xff, 0x00, 0x33, 0x99, 0xff, 0x00, 0x33, 0x66, 0xff,
0x00, 0x33, 0x33, 0xff, 0x00, 0x33, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xcc, 0xff, 0x00, 0x00, 0x99, 0xff, 0x00, 0x00, 0x66, 0xff, 0x00, 0x00, 0x33, 0xff, 0xee, 0x00, 0x00, 0xff,
0xdd, 0x00, 0x00, 0xff, 0xbb, 0x00, 0x00, 0xff, 0xaa, 0x00, 0x00, 0xff, 0x88, 0x00, 0x00, 0xff, 0x77, 0x00, 0x00, 0xff, 0x55, 0x00, 0x00, 0xff, 0x44, 0x00, 0x00, 0xff, 0x22, 0x00, 0x00, 0xff,
0x11, 0x00, 0x00, 0xff, 0x00, 0xee, 0x00, 0xff, 0x00, 0xdd, 0x00, 0xff, 0x00, 0xbb, 0x00, 0xff, 0x00, 0xaa, 0x00, 0xff, 0x00, 0x88, 0x00, 0xff, 0x00, 0x77, 0x00, 0xff, 0x00, 0x55, 0x00, 0xff,
0x00, 0x44, 0x00, 0xff, 0x00, 0x22, 0x00, 0xff, 0x00, 0x11, 0x00, 0xff, 0x00, 0x00, 0xee, 0xff, 0x00, 0x00, 0xdd, 0xff, 0x00, 0x00, 0xbb, 0xff, 0x00, 0x00, 0xaa, 0xff, 0x00, 0x00, 0x88, 0xff,
0x00, 0x00, 0x77, 0xff, 0x00, 0x00, 0x55, 0xff, 0x00, 0x00, 0x44, 0xff, 0x00, 0x00, 0x22, 0xff, 0x00, 0x00, 0x11, 0xff, 0xee, 0xee, 0xee, 0xff, 0xdd, 0xdd, 0xdd, 0xff, 0xbb, 0xbb, 0xbb, 0xff,
0xaa, 0xaa, 0xaa, 0xff, 0x88, 0x88, 0x88, 0xff, 0x77, 0x77, 0x77, 0xff, 0x55, 0x55, 0x55, 0xff, 0x44, 0x44, 0x44, 0xff, 0x22, 0x22, 0x22, 0xff, 0x11, 0x11, 0x11, 0xff, 0x00, 0x00, 0x00, 0xff,
};
// internal math/helper utilities
static inline uint32_t _vox_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
static inline uint32_t _vox_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}
// string utilities
#ifdef _MSC_VER
#define _vox_str_scanf(str,...) sscanf_s(str,__VA_ARGS__)
#define _vox_strcasecmp(a,b) _stricmp(a,b)
#define _vox_strcmp(a,b) strcmp(a,b)
#define _vox_strlen(a) strlen(a)
#define _vox_sprintf(str,str_max,fmt,...) sprintf_s(str, str_max, fmt, __VA_ARGS__)
#else
#define _vox_str_scanf(str,...) sscanf(str,__VA_ARGS__)
#define _vox_strcasecmp(a,b) strcasecmp(a,b)
#define _vox_strcmp(a,b) strcmp(a,b)
#define _vox_strlen(a) strlen(a)
#define _vox_sprintf(str,str_max,fmt,...) snprintf(str, str_max, fmt, __VA_ARGS__)
#endif
// Like strcpy, but will only copy the amount of characters from src that can fit in dst.
// Will assert if too many characters are in src.
template <size_t SIZE>
inline void _vox_strcpy_static(char (&dst)[SIZE], const char* src){
size_t storage_needed = _vox_strlen(src) + 1; // +1 for zero terminator
ogt_assert(storage_needed <= SIZE, "strcpy of src into dst would exceed storage in dst");
memcpy(dst, src, storage_needed <= SIZE ? storage_needed : SIZE);
dst[SIZE-1] = 0; // make sure we're always zero terminated.
}
// 3d vector utilities
struct vec3 {
float x, y, z;
};
static inline vec3 vec3_make(float x, float y, float z) { vec3 v; v.x = x; v.y = y; v.z = z; return v; }
static inline vec3 vec3_negate(const vec3& v) { vec3 r; r.x = -v.x; r.y = -v.y; r.z = -v.z; return r; }
// API for emulating file transactions on an in-memory buffer of data.
struct _vox_file {
const uint8_t* buffer; // source buffer data
const uint32_t buffer_size; // size of the data in the buffer
uint32_t offset; // current offset in the buffer data.
};
static uint32_t _vox_file_bytes_remaining(const _vox_file* fp) {
if (fp->offset < fp->buffer_size) {
return fp->buffer_size - fp->offset;
} else {
return 0;
}
}
static bool _vox_file_read(_vox_file* fp, void* data, uint32_t data_size) {
size_t data_to_read = _vox_min(_vox_file_bytes_remaining(fp), data_size);
memcpy(data, &fp->buffer[fp->offset], data_to_read);
fp->offset += data_size;
return data_to_read == data_size;
}
static bool _vox_file_read_uint32(_vox_file* fp, uint32_t* data) {
bool ret = _vox_file_read(fp, data, sizeof(*data));
if (ret) {
*data = _vox_le32toh(*data);
}
return ret;
}
static bool _vox_file_read_int32(_vox_file* fp, int32_t* data) {
bool ret = _vox_file_read(fp, data, sizeof(*data));
if (ret) {
*data = _vox_le32toh(*data);
}
return ret;
}
static bool _vox_file_read_float(_vox_file* fp, float* data) {
bool ret = _vox_file_read(fp, data, sizeof(*data));
if (ret) {
union {
uint32_t u;
float f;
} bs;
bs.f = *data;
bs.u = _vox_le32toh(bs.u);
*data = bs.f;
}
return ret;
}
static void _vox_file_seek_forwards(_vox_file* fp, uint32_t offset) {
fp->offset += _vox_min(offset, _vox_file_bytes_remaining(fp));
}
static const void* _vox_file_data_pointer(const _vox_file* fp) {
return &fp->buffer[fp->offset];
}
// hash utilities
static uint32_t _vox_hash(const uint8_t* data, uint32_t data_size) {
uint32_t hash = 0;
for (uint32_t i = 0; i < data_size; i++)
hash = data[i] + (hash * 65559);
return hash;
}
// memory allocation utils.
static void* _ogt_priv_alloc_default(size_t size) { return malloc(size); }
static void _ogt_priv_free_default(void* ptr) { free(ptr); }
static ogt_vox_alloc_func g_alloc_func = _ogt_priv_alloc_default; // default function for allocating
static ogt_vox_free_func g_free_func = _ogt_priv_free_default; // default function for freeing.
// set the provided allocate/free functions if they are non-null, otherwise reset to default allocate/free functions
void ogt_vox_set_memory_allocator(ogt_vox_alloc_func alloc_func, ogt_vox_free_func free_func)
{
ogt_assert((alloc_func && free_func) || // both alloc/free must be non-NULL -OR-
(!alloc_func && !free_func), "mixed alloc/free functions"); // both alloc/free must be NULL. No mixing 'n matching.
if (alloc_func && free_func) {
g_alloc_func = alloc_func;
g_free_func = free_func;
}
else {
// reset to default allocate/free functions.
g_alloc_func = _ogt_priv_alloc_default;
g_free_func = _ogt_priv_free_default;
}
}
static void* _vox_malloc(size_t size) {
return size ? g_alloc_func(size) : NULL;
}
static void* _vox_calloc(size_t size) {
void* pMem = _vox_malloc(size);
if (pMem)
memset(pMem, 0, size);
return pMem;
}
static void _vox_free(void* old_ptr) {
if (old_ptr)
g_free_func(old_ptr);
}
static void* _vox_realloc(void* old_ptr, size_t old_size, size_t new_size) {
// early out if new size is non-zero and no resize is required.
if (new_size && old_size >= new_size)
return old_ptr;
// memcpy from the old ptr only if both sides are valid.
void* new_ptr = _vox_malloc(new_size);
if (new_ptr) {
// copy any existing elements over
if (old_ptr && old_size)
memcpy(new_ptr, old_ptr, old_size);
// zero out any new tail elements
ogt_assert(new_size > old_size, "_vox_realloc error"); // this should be guaranteed by the _vox_realloc early out case above.
uintptr_t new_tail_ptr = (uintptr_t)new_ptr + old_size;
memset((void*)new_tail_ptr, 0, new_size - old_size);
}
if (old_ptr)
_vox_free(old_ptr);
return new_ptr;
}
// std::vector-style allocator, which use client-provided allocation functions.
template <class T> struct _vox_array {
_vox_array() : data(NULL), capacity(0), count(0) { }
~_vox_array() {
_vox_free(data);
data = NULL;
count = 0;
capacity = 0;
}
void reserve(size_t new_capacity) {
data = (T*)_vox_realloc(data, capacity * sizeof(T), new_capacity * sizeof(T));
capacity = new_capacity;
}
void grow_to_fit_index(size_t index) {
if (index >= count)
resize(index + 1);
}
void resize(size_t new_count) {
if (new_count > capacity)
{
size_t new_capacity = capacity ? (capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements.
new_capacity = new_count > new_capacity ? new_count : new_capacity; // ensure fits new_count
reserve(new_capacity);
ogt_assert(capacity >= new_count, "failed to resize array");
}
count = new_count;
}
void push_back(const T & new_element) {
if (count == capacity) {
size_t new_capacity = capacity ? (capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements.
reserve(new_capacity);
ogt_assert(capacity > count, "failed to push_back in array");
}
data[count++] = new_element;
}
void pop_back() {
ogt_assert(count > 0, "cannot pop_back on empty array");
count--;
}
const T & peek_back(size_t back_offset = 0) const {
ogt_assert(back_offset < count, "can't peek back further than the number of elements in an array");
size_t index = count - 1 - back_offset;
return data[index];
}
void push_back_many(const T * new_elements, size_t num_elements) {
if (count + num_elements > capacity) {
size_t new_capacity = capacity + num_elements;
new_capacity = new_capacity ? (new_capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements.
reserve(new_capacity);
ogt_assert(capacity >= (count + num_elements), "failed to push_back_many in array");
}
for (size_t i = 0; i < num_elements; i++)
data[count + i] = new_elements[i];
count += num_elements;
}
T* alloc_many(size_t num_elements) {
if (count + num_elements > capacity) {
size_t new_capacity = capacity + num_elements;
new_capacity = new_capacity ? (new_capacity * 3) >> 1 : 2; // grow by 50% each time, otherwise start at 2 elements.
reserve(new_capacity);
ogt_assert(capacity >= (count + num_elements), "failed to push_back_many in array");
}
T* ret = &data[count];
count += num_elements;
return ret;
}
// returns the index that it was inserted at.
uint32_t insert_unique_sorted( const T & value, uint32_t start_index ) {
for (uint32_t i = start_index; i < (uint32_t)count; i++) {
if (data[i] == value)
return i;
if (data[i] >= value) {
resize(count+1);
for (size_t j = count-1; j > i; j--)
data[j] = data[j-1];
data[i] = value;
return i;
}
}
push_back(value);
return (uint32_t)(count-1);
}
size_t size() const {
return count;
}
T& operator[](size_t index) {
ogt_assert(index < count, "index out of bounds");
return data[index];
}
const T& operator[](size_t index) const {
ogt_assert(index < count, "index out of bounds");
return data[index];
}
T* data; // data for the array
size_t capacity; // capacity of the array
size_t count; // size of the array
};
// a growing array where data is suballocated.
class _vox_suballoc_array {
public:
_vox_suballoc_array() {
data.reserve(1024);
// push a sentinel character into this array. This allows clients to keep an
// offset rather than a pointer and still allow an offset of 0 to mean invalid.
data.push_back('X');
}
void reserve(size_t new_capacity) {
data.reserve(new_capacity);
}
size_t size() const {
return data.size();
}
// gets the offset of a pointer that was allocated within this array.
size_t offset_of(void* ptr) const {
size_t unaligned_data = (size_t)&data[0];
size_t unaligned_ptr = (size_t)ptr;
ogt_assert(unaligned_ptr >= unaligned_data && unaligned_ptr < (unaligned_data + data.size()), "provided ptr is out of bounds in this array");
return unaligned_ptr - unaligned_data;
}
// gets a typed pointer given an offset into the block
template <class T>
const T * get_ptr(size_t offset) const {
ogt_assert(0 == offset % sizeof(T), "offset is not properly aligned for this datatype");
return (T*)&data[offset];
}
// gets a mutable typed pointer given an offset into the block
template <class T>
T * get_ptr(size_t offset) {
ogt_assert(0 == offset % sizeof(T), "offset is not properly aligned for this datatype");
return (T*)&data[offset];
}
// allocates num_bytes of memory with optionally specified base offset alignment.
void* alloc(size_t num_bytes, size_t align=0) {
if (align > 1 && 0 != (data.size() % align)) {
size_t padding = align - (data.size() % align);
data.alloc_many(padding);
}
return data.alloc_many(num_bytes);
}
// allocates and returns a pointer to many elements of the specified type.
// if align != 0, will use that alignment, otherwise will align to the size of the type T.
template <class T>
T* alloc_many(size_t num_elements, size_t align=0) {
// if alignment is specified use that, otherwise default alignment to the size fo the type T
align = align == 0 ? sizeof(T) : align;
return (T*)alloc(sizeof(T) * num_elements, align);
}
// returns the offset in the memory blob that the data was pushed to.
template <class T>
size_t push_back_many(const T * new_elements, size_t num_elements, size_t align=0) {
T * mem = alloc_many<T>(num_elements, align);
if (!mem)
return 0;
memcpy(mem, new_elements, sizeof(T)*num_elements);
return offset_of(mem);
}
// returns the offset in the memory blob that the string was pushed to.
size_t push_string(const char* str) {
size_t str_size = _vox_strlen(str) + 1; // +1 for terminator
return push_back_many(str, str_size);
}
private:
_vox_array<char> data;
};
// progress callback function.
static ogt_vox_progress_callback_func g_progress_callback_func = NULL;
static void* g_progress_callback_user_data = NULL;
// set the progress callback function.
void ogt_vox_set_progress_callback_func(ogt_vox_progress_callback_func progress_callback_func, void* user_data)
{
g_progress_callback_func = progress_callback_func;
g_progress_callback_user_data = user_data;
}
// matrix utilities
ogt_vox_transform ogt_vox_transform_get_identity() {
ogt_vox_transform t;
t.m00 = 1.0f; t.m01 = 0.0f; t.m02 = 0.0f; t.m03 = 0.0f;
t.m10 = 0.0f; t.m11 = 1.0f; t.m12 = 0.0f; t.m13 = 0.0f;
t.m20 = 0.0f; t.m21 = 0.0f; t.m22 = 1.0f; t.m23 = 0.0f;
t.m30 = 0.0f; t.m31 = 0.0f; t.m32 = 0.0f; t.m33 = 1.0f;
return t;
}
ogt_vox_transform ogt_vox_transform_multiply(const ogt_vox_transform& a, const ogt_vox_transform& b) {
ogt_vox_transform r;
r.m00 = (a.m00 * b.m00) + (a.m01 * b.m10) + (a.m02 * b.m20) + (a.m03 * b.m30);
r.m01 = (a.m00 * b.m01) + (a.m01 * b.m11) + (a.m02 * b.m21) + (a.m03 * b.m31);
r.m02 = (a.m00 * b.m02) + (a.m01 * b.m12) + (a.m02 * b.m22) + (a.m03 * b.m32);
r.m03 = (a.m00 * b.m03) + (a.m01 * b.m13) + (a.m02 * b.m23) + (a.m03 * b.m33);
r.m10 = (a.m10 * b.m00) + (a.m11 * b.m10) + (a.m12 * b.m20) + (a.m13 * b.m30);
r.m11 = (a.m10 * b.m01) + (a.m11 * b.m11) + (a.m12 * b.m21) + (a.m13 * b.m31);
r.m12 = (a.m10 * b.m02) + (a.m11 * b.m12) + (a.m12 * b.m22) + (a.m13 * b.m32);
r.m13 = (a.m10 * b.m03) + (a.m11 * b.m13) + (a.m12 * b.m23) + (a.m13 * b.m33);
r.m20 = (a.m20 * b.m00) + (a.m21 * b.m10) + (a.m22 * b.m20) + (a.m23 * b.m30);
r.m21 = (a.m20 * b.m01) + (a.m21 * b.m11) + (a.m22 * b.m21) + (a.m23 * b.m31);
r.m22 = (a.m20 * b.m02) + (a.m21 * b.m12) + (a.m22 * b.m22) + (a.m23 * b.m32);
r.m23 = (a.m20 * b.m03) + (a.m21 * b.m13) + (a.m22 * b.m23) + (a.m23 * b.m33);
r.m30 = (a.m30 * b.m00) + (a.m31 * b.m10) + (a.m32 * b.m20) + (a.m33 * b.m30);
r.m31 = (a.m30 * b.m01) + (a.m31 * b.m11) + (a.m32 * b.m21) + (a.m33 * b.m31);
r.m32 = (a.m30 * b.m02) + (a.m31 * b.m12) + (a.m32 * b.m22) + (a.m33 * b.m32);
r.m33 = (a.m30 * b.m03) + (a.m31 * b.m13) + (a.m32 * b.m23) + (a.m33 * b.m33);
return r;
}
// dictionary utilities
static const uint32_t k_vox_max_dict_buffer_size = 4096;
static const uint32_t k_vox_max_dict_key_value_pairs = 256;
struct _vox_dictionary {
const char* keys[k_vox_max_dict_key_value_pairs];
const char* values[k_vox_max_dict_key_value_pairs];
uint32_t num_key_value_pairs;
char buffer[k_vox_max_dict_buffer_size + 4]; // max 4096, +4 for safety
uint32_t buffer_mem_used;
};
static bool _vox_file_read_dict(_vox_dictionary * dict, _vox_file * fp) {
uint32_t num_pairs_to_read = 0;
_vox_file_read_uint32(fp, &num_pairs_to_read);
ogt_assert(num_pairs_to_read <= k_vox_max_dict_key_value_pairs, "max key/value pairs exceeded in dictionary");
dict->buffer_mem_used = 0;
dict->num_key_value_pairs = 0;
for (uint32_t i = 0; i < num_pairs_to_read; i++) {
// get the size of the key string
uint32_t key_string_size = 0;
if (!_vox_file_read_uint32(fp, &key_string_size))
return false;
// allocate space for the key, and read it in.
if (dict->buffer_mem_used + key_string_size > k_vox_max_dict_buffer_size)
return false;
char* key = &dict->buffer[dict->buffer_mem_used];
dict->buffer_mem_used += key_string_size + 1; // + 1 for zero terminator
if (!_vox_file_read(fp, key, key_string_size))
return false;
key[key_string_size] = 0; // zero-terminate
ogt_assert(_vox_strlen(key) == key_string_size, "key size sanity check failed");
// get the size of the value string
uint32_t value_string_size = 0;
if (!_vox_file_read_uint32(fp, &value_string_size))
return false;
// allocate space for the value, and read it in.
if (dict->buffer_mem_used + value_string_size > k_vox_max_dict_buffer_size)
return false;
char* value = &dict->buffer[dict->buffer_mem_used];
dict->buffer_mem_used += value_string_size + 1; // + 1 for zero terminator
if (!_vox_file_read(fp, value, value_string_size))
return false;
value[value_string_size] = 0; // zero-terminate
ogt_assert(_vox_strlen(value) == value_string_size, "value size sanity check failed");
// now assign it in the dictionary
dict->keys[dict->num_key_value_pairs] = key;
dict->values[dict->num_key_value_pairs] = value;
dict->num_key_value_pairs++;
}
return true;
}
// helper for looking up in the dictionary
static const char* _vox_dict_get_value_as_string(const _vox_dictionary* dict, const char* key_to_find, const char* default_value = NULL) {
for (uint32_t i = 0; i < dict->num_key_value_pairs; i++)
if (_vox_strcasecmp(dict->keys[i], key_to_find) == 0)
return dict->values[i];
return default_value;
}
static bool _vox_dict_get_value_as_bool(const _vox_dictionary* dict, const char* key_to_find, bool default_value) {
const char* str = _vox_dict_get_value_as_string(dict, key_to_find, NULL);