-
Notifications
You must be signed in to change notification settings - Fork 0
/
ogt_vox.h
2110 lines (1917 loc) · 115 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 - v0.6 - 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);
*/
#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
#elif defined(__GNUC__)
// any GCC*
#include <inttypes.h>
#include <stdlib.h> // for size_t
#else
#error some fixup needed for this platform?
#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;
// 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,
};
// 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;
// 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;
// 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 within 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. 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_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.
bool hidden; // whether this layer is hidden or not.
} ogt_vox_layer;
// describes a group within the scene
typedef struct ogt_vox_group
{
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_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
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
} 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);
// 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.
// 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);
#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
#include <assert.h>
#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');
// 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_strcpy_static(dst,src) strcpy_s(dst,src)
#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_strcpy_static(dst,src) strcpy(dst,src)
#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
// 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 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)
{
assert((alloc_func && free_func) || // both alloc/free must be non-NULL -OR-
(!alloc_func && !free_func)); // 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
assert(new_size > old_size); // 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)
reserve(new_count);
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);
assert(capacity > count);
}
data[count++] = new_element;
}
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);
assert(capacity >= (count + num_elements));
}
for (size_t i = 0; i < num_elements; i++)
data[count + i] = new_elements[i];
count += num_elements;
}
size_t size() const {
return count;
}
T& operator[](size_t index) {
assert(index < count);
return data[index];
}
const T& operator[](size_t index) const {
assert(index < count);
return data[index];
}
T* data; // data for the array
size_t capacity; // capacity of the array
size_t count; // size of the array
};
// matrix utilities
static ogt_vox_transform _vox_transform_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;
}
static ogt_vox_transform _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(fp, &num_pairs_to_read, sizeof(uint32_t));
assert(num_pairs_to_read <= k_vox_max_dict_key_value_pairs);
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(fp, &key_string_size, sizeof(uint32_t)))
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
assert(_vox_strlen(key) == key_string_size); // sanity check
// get the size of the value string
uint32_t value_string_size = 0;
if (!_vox_file_read(fp, &value_string_size, sizeof(uint32_t)))
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
assert(_vox_strlen(value) == value_string_size); // sanity check
// 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;
}
// lookup table for _vox_make_transform_from_dict_strings
static const vec3 k_vectors[4] = {
vec3_make(1.0f, 0.0f, 0.0f),
vec3_make(0.0f, 1.0f, 0.0f),
vec3_make(0.0f, 0.0f, 1.0f),
vec3_make(0.0f, 0.0f, 0.0f) // invalid!
};
// lookup table for _vox_make_transform_from_dict_strings
static const uint32_t k_row2_index[] = { UINT32_MAX, UINT32_MAX, UINT32_MAX, 2, UINT32_MAX, 1, 0, UINT32_MAX };
static ogt_vox_transform _vox_make_transform_from_dict_strings(const char* rotation_string, const char* translation_string) {
ogt_vox_transform transform = _vox_transform_identity();
if (rotation_string != NULL) {
// compute the per-row indexes into k_vectors[] array.
// unpack rotation bits.
// bits : meaning
// 0 - 1 : index of the non-zero entry in the first row
// 2 - 3 : index of the non-zero entry in the second row
uint32_t packed_rotation_bits = atoi(rotation_string);
uint32_t row0_vec_index = (packed_rotation_bits >> 0) & 3;
uint32_t row1_vec_index = (packed_rotation_bits >> 2) & 3;
uint32_t row2_vec_index = k_row2_index[(1 << row0_vec_index) | (1 << row1_vec_index)]; // process of elimination to determine row 2 index based on row0/row1 being one of {0,1,2} choose 2.
assert(row2_vec_index != UINT32_MAX); // if you hit this, you probably have invalid indices for row0_vec_index/row1_vec_index.
// unpack rotation bits for vector signs
// bits : meaning
// 4 : the sign in the first row (0 : positive; 1 : negative)
// 5 : the sign in the second row (0 : positive; 1 : negative)
// 6 : the sign in the third row (0 : positive; 1 : negative)
vec3 row0 = k_vectors[row0_vec_index];
vec3 row1 = k_vectors[row1_vec_index];
vec3 row2 = k_vectors[row2_vec_index];
if (packed_rotation_bits & (1 << 4))
row0 = vec3_negate(row0);
if (packed_rotation_bits & (1 << 5))
row1 = vec3_negate(row1);
if (packed_rotation_bits & (1 << 6))
row2 = vec3_negate(row2);
// magicavoxel stores rows, we need columns, so we do the swizzle here into columns
transform.m00 = row0.x; transform.m01 = row1.x; transform.m02 = row2.x;
transform.m10 = row0.y; transform.m11 = row1.y; transform.m12 = row2.y;
transform.m20 = row0.z; transform.m21 = row1.z; transform.m22 = row2.z;
}
if (translation_string != NULL) {
int32_t x = 0;
int32_t y = 0;
int32_t z = 0;
_vox_str_scanf(translation_string, "%i %i %i", &x, &y, &z);
transform.m30 = (float)x;
transform.m31 = (float)y;
transform.m32 = (float)z;
}
return transform;
}
enum _vox_scene_node_type
{
k_nodetype_invalid = 0, // has not been parsed yet.
k_nodetype_group = 1,
k_nodetype_transform = 2,
k_nodetype_shape = 3,
};
struct _vox_scene_node_ {
_vox_scene_node_type node_type; // only gets assigned when this has been parsed, otherwise will be k_nodetype_invalid
union {
// used only when node_type == k_nodetype_transform
struct {
char name[65]; // max name size is 64 plus 1 for null terminator
ogt_vox_transform transform;
uint32_t child_node_id;
uint32_t layer_id;
bool hidden;
} transform;
// used only when node_type == k_nodetype_group
struct {
uint32_t first_child_node_id_index; // the index of the first child node ID within the ChildNodeID array
uint32_t num_child_nodes; // number of child node IDs starting at the first index
} group;
// used only when node_type == k_nodetype_shape
struct {
uint32_t model_id; // will be UINT32_MAX if there is no model. Unlikely, there should always be a model.
} shape;
} u;
};
static void generate_instances_for_node(
const _vox_array<_vox_scene_node_> & nodes, uint32_t node_index, const _vox_array<uint32_t> & child_id_array, uint32_t layer_index,
const ogt_vox_transform& transform, const _vox_array<ogt_vox_model*> & model_ptrs, const char* transform_last_name, bool transform_last_hidden,
_vox_array<ogt_vox_instance> & instances, _vox_array<char> & string_data, _vox_array<ogt_vox_group>& groups, uint32_t group_index, bool generate_groups)
{
const _vox_scene_node_* node = &nodes[node_index];
assert(node);
switch (node->node_type)
{
case k_nodetype_transform:
{
ogt_vox_transform new_transform = (generate_groups) ? node->u.transform.transform // don't multiply by the parent transform. caller wants the group-relative transform
: _vox_transform_multiply(node->u.transform.transform, transform); // flatten the transform if we're not generating groups: child transform * parent transform
const char* new_transform_name = node->u.transform.name[0] ? node->u.transform.name : NULL;
transform_last_name = new_transform_name ? new_transform_name : transform_last_name; // if this node has a name, use it instead of our parent name
generate_instances_for_node(nodes, node->u.transform.child_node_id, child_id_array, node->u.transform.layer_id, new_transform, model_ptrs, transform_last_name, node->u.transform.hidden, instances, string_data, groups, group_index, generate_groups);
break;
}
case k_nodetype_group:
{
// create a new group only if we're generating groups.
uint32_t next_group_index = 0;
if (generate_groups) {
next_group_index = (uint32_t)groups.size();
ogt_vox_group group;
group.parent_group_index = group_index;
group.transform = transform;
group.hidden = transform_last_hidden;
group.layer_index = layer_index;
groups.push_back(group);
}
// child nodes will only be hidden if their immediate transform is hidden.
transform_last_hidden = false;
const uint32_t* child_node_ids = (const uint32_t*)& child_id_array[node->u.group.first_child_node_id_index];
for (uint32_t i = 0; i < node->u.group.num_child_nodes; i++) {
generate_instances_for_node(nodes, child_node_ids[i], child_id_array, layer_index, transform, model_ptrs, transform_last_name, transform_last_hidden, instances, string_data, groups, next_group_index, generate_groups);
}
break;
}
case k_nodetype_shape:
{
assert(node->u.shape.model_id < model_ptrs.size());
if (node->u.shape.model_id < model_ptrs.size() && // model ID is valid
model_ptrs[node->u.shape.model_id] != NULL ) // model is non-NULL.
{
assert(generate_groups || group_index == 0); // if we're not generating groups, group_index should be zero to map to the root group.
ogt_vox_instance new_instance;
new_instance.model_index = node->u.shape.model_id;
new_instance.transform = transform;
new_instance.layer_index = layer_index;
new_instance.group_index = group_index;
new_instance.hidden = transform_last_hidden;
// if we got a transform name, allocate space in string_data for it and keep track of the index
// within string data. This will be patched to a real pointer at the very end.
new_instance.name = 0;
if (transform_last_name && transform_last_name[0]) {
new_instance.name = (const char*)(string_data.size());
size_t name_size = _vox_strlen(transform_last_name) + 1; // +1 for terminator
string_data.push_back_many(transform_last_name, name_size);
}
// create the instance
instances.push_back(new_instance);
}
break;
}
default:
{
assert(0); // unhandled node type!
}
}
}
// ensure instances are ordered in order of increasing model_index
static int _vox_ordered_compare_instance(const void* _lhs, const void* _rhs) {
const ogt_vox_instance* lhs = (const ogt_vox_instance*)_lhs;
const ogt_vox_instance* rhs = (const ogt_vox_instance*)_rhs;
return lhs->model_index < rhs->model_index ? -1 :
lhs->model_index > rhs->model_index ? 1 : 0;
}
// returns true if the 2 models are content-wise identical.
static bool _vox_models_are_equal(const ogt_vox_model* lhs, const ogt_vox_model* rhs) {
// early out: if hashes don't match, they can't be equal
// if hashes match, they might be equal OR there might be a hash collision.
if (lhs->voxel_hash != rhs->voxel_hash)
return false;
// early out: if number of voxels in the model's grid don't match, they can't be equal.
uint32_t num_voxels_lhs = lhs->size_x * lhs->size_y * lhs->size_z;
uint32_t num_voxels_rhs = rhs->size_x * rhs->size_y * rhs->size_z;
if (num_voxels_lhs != num_voxels_rhs)
return false;
// Finally, we know their hashes are the same, and their dimensions are the same
// but they are only equal if they have exactly the same voxel data.
return memcmp(lhs->voxel_data, rhs->voxel_data, num_voxels_lhs) == 0 ? true : false;
}
const ogt_vox_scene* ogt_vox_read_scene_with_flags(const uint8_t * buffer, uint32_t buffer_size, uint32_t read_flags) {
_vox_file file = { buffer, buffer_size, 0 };
_vox_file* fp = &file;
// parsing state/context
_vox_array<ogt_vox_model*> model_ptrs;
_vox_array<_vox_scene_node_> nodes;
_vox_array<ogt_vox_instance> instances;
_vox_array<char> string_data;
_vox_array<ogt_vox_layer> layers;
_vox_array<ogt_vox_group> groups;
_vox_array<uint32_t> child_ids;
ogt_vox_palette palette;
ogt_vox_matl_array materials;
_vox_dictionary dict;
uint32_t size_x = 0;
uint32_t size_y = 0;
uint32_t size_z = 0;
uint8_t index_map[256];
bool found_index_map_chunk = false;
// size some of our arrays to prevent resizing during the parsing for smallish cases.
model_ptrs.reserve(64);
instances.reserve(256);
child_ids.reserve(256);
nodes.reserve(16);
layers.reserve(8);
groups.reserve(0);
string_data.reserve(256);
// push a sentinel character into these datastructures. This allows us to keep indexes
// rather than pointers into data-structures that grow, and still allow an index of 0
// to means invalid
string_data.push_back('X');
child_ids.push_back(UINT32_MAX);
// copy the default palette into the scene. It may get overwritten by a palette chunk later
memcpy(&palette, k_default_vox_palette, sizeof(ogt_vox_palette));
// zero initialize materials (this sets valid defaults)
memset(&materials, 0, sizeof(materials));
// load and validate fileheader and file version.
uint32_t file_header = 0;
uint32_t file_version = 0;
_vox_file_read(fp, &file_header, sizeof(uint32_t));
_vox_file_read(fp, &file_version, sizeof(uint32_t));
if (file_header != CHUNK_ID_VOX_ || file_version != 150)
return NULL;
// parse chunks until we reach the end of the file/buffer
while (_vox_file_bytes_remaining(fp) >= sizeof(uint32_t) * 3)
{
// read the fields common to all chunks
uint32_t chunk_id = 0;
uint32_t chunk_size = 0;
uint32_t chunk_child_size = 0;
_vox_file_read(fp, &chunk_id, sizeof(uint32_t));
_vox_file_read(fp, &chunk_size, sizeof(uint32_t));
_vox_file_read(fp, &chunk_child_size, sizeof(uint32_t));
// process the chunk.
switch (chunk_id)
{
case CHUNK_ID_MAIN:
{
assert(chunk_size == 0);
break;
}
case CHUNK_ID_SIZE:
{
assert(chunk_size == 12 && chunk_child_size == 0);
_vox_file_read(fp, &size_x, sizeof(uint32_t));
_vox_file_read(fp, &size_y, sizeof(uint32_t));
_vox_file_read(fp, &size_z, sizeof(uint32_t));
break;
}
case CHUNK_ID_XYZI:
{
assert(chunk_child_size == 0 && size_x && size_y && size_z); // must have read a SIZE chunk prior to XYZI.
// read the number of voxels to process for this moodel
uint32_t num_voxels_in_chunk = 0;
_vox_file_read(fp, &num_voxels_in_chunk, sizeof(uint32_t));
if (num_voxels_in_chunk != 0) {
uint32_t voxel_count = size_x * size_y * size_z;
ogt_vox_model * model = (ogt_vox_model*)_vox_calloc(sizeof(ogt_vox_model) + voxel_count); // 1 byte for each voxel
if (!model)
return NULL;
uint8_t * voxel_data = (uint8_t*)&model[1];
// insert the model into the model array
model_ptrs.push_back(model);
// now setup the model
model->size_x = size_x;
model->size_y = size_y;
model->size_z = size_z;
model->voxel_data = voxel_data;
// setup some strides for computing voxel index based on x/y/z
const uint32_t k_stride_x = 1;
const uint32_t k_stride_y = size_x;
const uint32_t k_stride_z = size_x * size_y;
// read this many voxels and store it in voxel data.
const uint8_t * packed_voxel_data = (const uint8_t*)_vox_file_data_pointer(fp);
const uint32_t voxels_to_read = _vox_min(_vox_file_bytes_remaining(fp) / 4, num_voxels_in_chunk);
for (uint32_t i = 0; i < voxels_to_read; i++) {
uint8_t x = packed_voxel_data[i * 4 + 0];
uint8_t y = packed_voxel_data[i * 4 + 1];
uint8_t z = packed_voxel_data[i * 4 + 2];
uint8_t color_index = packed_voxel_data[i * 4 + 3];
assert(x < size_x && y < size_y && z < size_z);
voxel_data[(x * k_stride_x) + (y * k_stride_y) + (z * k_stride_z)] = color_index;
}
_vox_file_seek_forwards(fp, num_voxels_in_chunk * 4);
// compute the hash of the voxels in this model-- used to accelerate duplicate models checking.
model->voxel_hash = _vox_hash(voxel_data, size_x * size_y * size_z);
}
else {
model_ptrs.push_back(NULL);
}
break;
}
case CHUNK_ID_RGBA:
{
assert(chunk_size == sizeof(palette));
_vox_file_read(fp, &palette, sizeof(palette));
break;
}
case CHUNK_ID_nTRN:
{
uint32_t node_id = 0;
_vox_file_read(fp, &node_id, sizeof(node_id));
// Parse the node dictionary, which can contain:
// _name: string
// _hidden: 0/1
char node_name[65];
bool hidden = false;
node_name[0] = 0;
{
_vox_file_read_dict(&dict, fp);
const char* name_string = _vox_dict_get_value_as_string(&dict, "_name");
if (name_string)
_vox_strcpy_static(node_name, name_string);
// if we got a hidden attribute - assign it now.
const char* hidden_string = _vox_dict_get_value_as_string(&dict, "_hidden", "0");
if (hidden_string)
hidden = (hidden_string[0] == '1' ? true : false);
}
// get other properties.
uint32_t child_node_id = 0, reserved_id = 0, layer_id = 0, num_frames = 0;
_vox_file_read(fp, &child_node_id, sizeof(child_node_id));
_vox_file_read(fp, &reserved_id, sizeof(reserved_id));
_vox_file_read(fp, &layer_id, sizeof(layer_id));
_vox_file_read(fp, &num_frames, sizeof(num_frames));
assert(reserved_id == UINT32_MAX && num_frames == 1); // must be these values according to the spec
// Parse the frame dictionary that contains:
// _r : int8 ROTATION (c)
// _t : int32x3 translation
// and extract a transform
ogt_vox_transform frame_transform;
{
_vox_file_read_dict(&dict, fp);
const char* rotation_value = _vox_dict_get_value_as_string(&dict, "_r");
const char* translation_value = _vox_dict_get_value_as_string(&dict, "_t");
frame_transform = _vox_make_transform_from_dict_strings(rotation_value, translation_value);
}
// setup the transform node.
{
nodes.grow_to_fit_index(node_id);
_vox_scene_node_* transform_node = &nodes[node_id];
assert(transform_node);
transform_node->node_type = k_nodetype_transform;
transform_node->u.transform.child_node_id = child_node_id;
transform_node->u.transform.layer_id = layer_id;
transform_node->u.transform.transform = frame_transform;
transform_node->u.transform.hidden = hidden;
// assign the name
_vox_strcpy_static(transform_node->u.transform.name, node_name);
}
break;
}
case CHUNK_ID_nGRP:
{
uint32_t node_id = 0;