diff --git a/Extras/readblend/LICENSE.txt b/Extras/readblend/LICENSE.txt
new file mode 100644
index 000000000..07b85e91c
--- /dev/null
+++ b/Extras/readblend/LICENSE.txt
@@ -0,0 +1,24 @@
+
+Copyright (C) 2003-2006 Adam D. Moss (the "Author"). All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
diff --git a/Extras/readblend/Makefile b/Extras/readblend/Makefile
new file mode 100644
index 000000000..bf2358e51
--- /dev/null
+++ b/Extras/readblend/Makefile
@@ -0,0 +1,36 @@
+
+CC = gcc
+COPTS = -O2 -Wall
+
+#CC = gcc31
+#COPTS = -O2 -Wall -ggdb -lm
+
+#COPTS = -O3 -g -fno-inline-functions -ffast-math -pg -Wall
+#COPTS = -ggdb -Wall
+
+CFLAGS = $(COPTS) $(INCS) -lm
+
+EXEC = testblend
+
+OBJ_SRCS = testblend.c readblend.c
+OBJ_OBJS = $(OBJ_SRCS:.c=.o)
+
+%.o: %.c
+ ${CC} ${CFLAGS} -c $< -o $@
+
+all: $(EXEC)
+
+clean:
+ rm -f $(EXEC) *.o gmon.out
+
+#dist:
+# tar -hzvcf flynn-`cat VERSION`.tar.gz `cat MANIFEST`
+# ls -la flynn-`cat VERSION`.tar.gz
+
+#testdist:
+# tar -zvcf flynn-testdir.tar.gz test
+# ls -la flynn-testdir.tar.gz
+
+$(EXEC): $(OBJ_OBJS)
+ $(CC) $(CFLAGS) $^ -o $@
+
diff --git a/Extras/readblend/README.blendstruct b/Extras/readblend/README.blendstruct
new file mode 100644
index 000000000..597bb787f
--- /dev/null
+++ b/Extras/readblend/README.blendstruct
@@ -0,0 +1,26 @@
+ReadBlend, a data extraction API for Blender's .blend files
+
+quick notes on the logical .blend file format as presented
+by readblend:
+
+
+BLENDFILE
+ |
+ |--BLENDFILE_VERSION
+ |
+ |--NUM_BLOCKS ... number of blocks in the file
+ |--BLOCK[NUM_BLOCKS] ... array of blocks
+ |
+ |--BLOCK_TAG ... general 'DATA', otherwise specialized type
+ |
+ |--OBJECT_TYPE ... the block is an array of objects of this type
+ |--OBJECT_COUNT ... this is the number of objects in the block
+ |--OBJECT[OBJECT_COUNT] ... array of objects
+ |
+ |--OBJECT
+ an OBJECT...
+ = ATOMIC type (uchar, float, etc)
+ or
+ = STRUCTURE (array of assorted OBJECTs, nested)
+ or
+ = POINTER (a reference to another BLOCK)
diff --git a/Extras/readblend/ReadBlend.sln b/Extras/readblend/ReadBlend.sln
new file mode 100644
index 000000000..fe04c7d28
--- /dev/null
+++ b/Extras/readblend/ReadBlend.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReadBlend", "ReadBlend.vcproj", "{74CA6BF4-60C4-4FE6-BAB5-31F31FA2A0B3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {74CA6BF4-60C4-4FE6-BAB5-31F31FA2A0B3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {74CA6BF4-60C4-4FE6-BAB5-31F31FA2A0B3}.Debug|Win32.Build.0 = Debug|Win32
+ {74CA6BF4-60C4-4FE6-BAB5-31F31FA2A0B3}.Release|Win32.ActiveCfg = Release|Win32
+ {74CA6BF4-60C4-4FE6-BAB5-31F31FA2A0B3}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Extras/readblend/ReadBlend.vcproj b/Extras/readblend/ReadBlend.vcproj
new file mode 100644
index 000000000..fbb49b582
--- /dev/null
+++ b/Extras/readblend/ReadBlend.vcproj
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Extras/readblend/abs-file.h b/Extras/readblend/abs-file.h
new file mode 100644
index 000000000..1f0e89f7b
--- /dev/null
+++ b/Extras/readblend/abs-file.h
@@ -0,0 +1,226 @@
+/*
+ * stdio/physfs abstraction layer 2004-02-06
+ *
+ * Adam D. Moss
+ *
+ * These wrapper macros and functions are designed to allow a program
+ * to perform file I/O with identical semantics and syntax regardless
+ * of whether PhysicsFS is being used or not.
+ */
+#ifndef _ABS_FILE_H
+#define _ABS_FILE_H
+
+ typedef unsigned char uint8_t;
+ typedef unsigned long int uint64_t;
+ typedef unsigned int uint32_t;
+ typedef int int32_t;
+ typedef unsigned short uint16_t;
+ typedef short int16_t;
+
+
+/*
+PLEASE NOTE: This license applies to abs-file.h ONLY; The version of
+PhysicsFS itself which you are using may have been released under a
+license with additional restrictions.
+
+Copyright (C) 2002-2004 Adam D. Moss (the "Author"). All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+*/
+
+#include
+#include
+
+/*
+ * API:
+ *
+ * Macro/function use like stdio equivalent...
+ * -------------- ----------------------------
+ * MY_FILETYPE FILE
+ * MY_OPEN_FOR_READ fopen(..., "rb")
+ * MY_READ fread(...)
+ * MY_GETC fgetc(...)
+ * MY_GETS fgets(...)
+ * MY_OPEN_FOR_WRITE fopen(..., "wb")
+ * MY_WRITE fwrite(...)
+ * MY_PUTC fputc(...)
+ * MY_PUTS fputs(...)
+ * MY_CLOSE fclose(...)
+ * MY_ATEOF feof(...)
+ * MY_TELL ftell(...)
+ * MY_SEEK fseek(..., SEEK_SET)
+ * MY_REWIND rewind(...)
+ * MY_SETBUFFER (not a standard for stdio, does nothing there)
+ * MY_FILELENGTH (not a standard for stdio, but implemented anyway)
+ */
+
+/*
+ * Important DEFINEs:
+ *
+ * It is important to define these consistantly across the various
+ * compilation modules of your program if you wish to exchange file
+ * handles between them.
+ *
+ * USE_PHYSFS: Define USE_PHYSFS if PhysicsFS is being used; note that if
+ * you do intend to use PhysicsFS then you will still need to initialize
+ * PhysicsFS yourself and set up its search-paths.
+ *
+ * Optional DEFINEs:
+ *
+ * PHYSFS_DEFAULT_BUFFER_SIZE : If set then abs-file.h sets the
+ * PhysicsFS buffer size to this value whenever you open a file. You
+ * may over-ride this on a per-filehandle basis by using the
+ * MY_SETBUFFER() macro (which simply does nothing when not using
+ * PhysicsFS). If you have not defined this value explicitly then
+ * abs-file.h will default to the same default buffer size as used by
+ * stdio if it can be determined, or 8192 bytes otherwise.
+ */
+
+#ifndef PHYSFS_DEFAULT_BUFFER_SIZE
+#ifdef BUFSIZ
+#define PHYSFS_DEFAULT_BUFFER_SIZE BUFSIZ
+#else
+#define PHYSFS_DEFAULT_BUFFER_SIZE 8192
+#endif
+#endif
+
+#ifdef USE_PHYSFS
+
+#include
+#define MY_FILETYPE PHYSFS_file
+#define MY_SETBUFFER(fp,size) PHYSFS_setBuffer(fp,size)
+#define MY_READ(p,s,n,fp) PHYSFS_read(fp,p,s,n)
+#define MY_WRITE(p,s,n,fp) PHYSFS_write(fp,p,s,n)
+#if PHYSFS_DEFAULT_BUFFER_SIZE
+static MY_FILETYPE* MY_OPEN_FOR_READ(const char *const filename)
+{
+ MY_FILETYPE *const file = PHYSFS_openRead(filename);
+ if (file) {
+ MY_SETBUFFER(file, PHYSFS_DEFAULT_BUFFER_SIZE);
+ }
+ return file;
+}
+static MY_FILETYPE* MY_OPEN_FOR_WRITE(const char *const filename)
+{
+ MY_FILETYPE *const file = PHYSFS_openWrite(filename);
+ if (file) {
+ MY_SETBUFFER(file, PHYSFS_DEFAULT_BUFFER_SIZE);
+ }
+ return file;
+}
+#else
+#define MY_OPEN_FOR_READ(fn) PHYSFS_openRead(fn)
+#define MY_OPEN_FOR_WRITE(fn) PHYSFS_openWrite(fn)
+#endif
+static int MY_GETC(MY_FILETYPE *const fp) {
+ unsigned char c;
+ /*if (PHYSFS_eof(fp)) {
+ return EOF;
+ }
+ MY_READ(&c, 1, 1, fp);*/
+ if (MY_READ(&c, 1, 1, fp) != 1) {
+ return EOF;
+ }
+ return c;
+}
+static char * MY_GETS(char * const str, const int size,
+ MY_FILETYPE *const fp) {
+ int i = 0;
+ int c;
+ do {
+ if (i == size-1) {
+ break;
+ }
+ c = MY_GETC(fp);
+ if (c == EOF) {
+ break;
+ }
+ str[i++] = c;
+ } while (c != '\0' &&
+ c != -1 &&
+ c != '\n');
+ str[i] = '\0';
+ if (i == 0) {
+ return NULL;
+ }
+ return str;
+}
+static int MY_PUTC(int c, MY_FILETYPE *const fp) {
+ unsigned char cc = (unsigned char)c;
+ if (MY_WRITE(&cc, 1, 1, fp) != 1) {
+ return EOF;
+ }
+ return (int)cc;
+}
+static int MY_PUTS(const char *s, MY_FILETYPE *const fp) {
+ int i = 0;
+ while (s[i] != '\0') {
+ if (MY_PUTC(s[i], fp) == EOF) {
+ return EOF;
+ }
+ ++i;
+ }
+ if (MY_PUTC('\n', fp) == EOF) {
+ return EOF;
+ }
+ return 0;
+}
+#define MY_CLOSE(fp) (0==PHYSFS_close(fp))
+#define MY_ATEOF(fp) PHYSFS_eof(fp)
+#define MY_TELL(fp) PHYSFS_tell(fp)
+#define MY_SEEK(fp,o) (0==PHYSFS_seek(fp,o))
+#define MY_REWIND(fp) MY_SEEK(fp,0)
+#define MY_FILELENGTH(fp) PHYSFS_fileLength(fp)
+
+#else /* !USE_PHYSFS */
+
+#define MY_FILETYPE FILE
+#define MY_READ(p,s,n,fp) fread(p,s,n,fp)
+#define MY_WRITE(p,s,n,fp) fwrite(p,s,n,fp)
+#define MY_OPEN_FOR_READ(n) fopen(n, "rb")
+#define MY_OPEN_FOR_WRITE(n) fopen(n, "wb")
+#define MY_GETC(fp) fgetc(fp)
+#define MY_GETS(str,size,fp) fgets(str,size,fp)
+#define MY_PUTC(c,fp) fputc(c,fp)
+#define MY_PUTS(str,fp) fput(str,fp)
+#define MY_CLOSE(fp) fclose(fp)
+#define MY_ATEOF(fp) feof(fp)
+#define MY_TELL(fp) ftell(fp)
+#define MY_SEEK(fp,o) fseek(fp,o, SEEK_SET)
+#define MY_REWIND(fp) rewind(fp)
+#define MY_SETBUFFER(fp,size)
+/* a TODO from ryan: "seeking to the end followed by an ftell() is probably
+more portable, but you can also use fileno() to get a handle from a
+(FILE *) to use with fstat()...on supported platforms, that is, most
+platforms, this is probably faster." */
+static long MY_FILELENGTH(FILE *fp) {
+ long currentpos = ftell(fp); /* save current cursor position */
+ long newpos;
+ fseek(fp, 0, SEEK_END); /* seek to end */
+ newpos = ftell(fp); /* find position of end -- this is the length */
+ fseek(fp, currentpos, SEEK_SET); /* restore previous cursor position */
+ return newpos;
+}
+#endif /* USE_PHYSFS */
+
+#endif /* _ABS_FILE_H */
diff --git a/Extras/readblend/readblend.c b/Extras/readblend/readblend.c
new file mode 100644
index 000000000..9a229d7cb
--- /dev/null
+++ b/Extras/readblend/readblend.c
@@ -0,0 +1,2397 @@
+
+#include
+//#include
+#include
+#include
+
+#include "abs-file.h"
+
+
+#define B_DEBUG
+
+
+#ifdef B_DEBUG
+# ifndef dprintf
+# include
+# define dprintf fprintf
+# endif
+#else
+# define dprintf
+#endif
+
+
+/* the types extracted from the Blender file */
+typedef struct _BlendType BlendType;
+struct _BlendType {
+ char* name;
+ int size;
+
+ int is_struct;
+
+ /* if is_struct... this defines the types of each of the structure fields */
+ int fieldtypes_count;
+ int* fieldtypes; /* type indices */
+ int fieldnames_count;
+ int* fieldnames; /* name indices */
+};
+
+
+typedef struct _BlendField BlendField;
+struct _BlendField {
+ char* field_bytes;
+ int field_bytes_count;
+
+ /* the offset into field_bytes at which each field of a
+ structure begins. this is so we can keep the data aligned
+ correctly for various architectures. a non-structure type
+ is equivalent to a structure with a single field.
+ */
+ int* field_offsets;
+ int field_offsets_count;
+};
+
+
+typedef struct _BlendBlock BlendBlock;
+struct _BlendBlock {
+ char tag[5];
+ uint32_t blender_pointer;
+ /*void* fixed_pointer;*/
+
+ int type_index;
+ /* a block is simply an array of its type as defined by type_index.
+ array_entries is an array of pointers to each entry in the block's
+ array.
+ */
+ BlendField *array_entries;
+ int array_entries_count;
+};
+
+
+/* the opaque BlendFile structure */
+typedef struct {
+ BlendType* types;
+ int types_count;
+
+ char* *names;
+ int names_count;
+
+ int* strc_indices;
+ int strc_indices_count;
+
+ BlendBlock *blocks;
+ int blocks_count;
+
+ int name_undef; /* index of the name we add specially for top blocks */
+
+} IBlendFile;
+
+
+#define BlendFile IBlendFile
+#include "readblend.h"
+
+
+/* endianness conversion macros */
+#define BGETLEUINT16(p) ((uint16_t) ( \
+ ( ((uint16_t)*( 1 + (uint8_t*)(p) ))<<8 ) | \
+ ( ((uint16_t)*( 0 + (uint8_t*)(p) ))<<0 ) \
+ ) )
+#define BGETLEINT16(p) ((int16_t) BGETLEUINT16(p))
+#define BGETLEUINT32(p) ((uint32_t) ( \
+ ( ((uint32_t)*( 3 + (uint8_t*)(p) ))<<24 ) | \
+ ( ((uint32_t)*( 2 + (uint8_t*)(p) ))<<16 ) | \
+ ( ((uint32_t)*( 1 + (uint8_t*)(p) ))<<8 ) | \
+ ( ((uint32_t)*( 0 + (uint8_t*)(p) ))<<0 ) \
+ ) )
+#define BGETLEINT32(p) ((int32_t) BGETLEUINT32(p))
+#define BGETLEUINT64(p) ((uint64_t) ( \
+ ( ((uint64_t)*( 7 + (uint8_t*)(p) ))<<56 ) | \
+ ( ((uint64_t)*( 6 + (uint8_t*)(p) ))<<48 ) | \
+ ( ((uint64_t)*( 5 + (uint8_t*)(p) ))<<40 ) | \
+ ( ((uint64_t)*( 4 + (uint8_t*)(p) ))<<32 ) | \
+ ( ((uint64_t)*( 3 + (uint8_t*)(p) ))<<24 ) | \
+ ( ((uint64_t)*( 2 + (uint8_t*)(p) ))<<16 ) | \
+ ( ((uint64_t)*( 1 + (uint8_t*)(p) ))<<8 ) | \
+ ( ((uint64_t)*( 0 + (uint8_t*)(p) ))<<0 ) \
+ ) )
+#define BGETLEINT64(p) ((int64_t) BGETLEUINT64(p))
+static float BGETLEFLOAT32(char *p) {
+ union { /* use type-punning, or aliasing optimization will kick our arse */
+ uint32_t u32;
+ float f32;
+ } punner;
+ punner.u32 = BGETLEUINT32(p);
+ return punner.f32;
+}
+static double BGETLEDOUBLE64(char *p) {
+ union { /* use type-punning, or aliasing optimization will kick our arse */
+ uint64_t u64;
+ float f64;
+ } punner;
+ punner.u64 = BGETLEUINT64(p);
+ return punner.f64;
+}
+
+
+static BlendFile*
+bf_new(void)
+{
+ BlendFile *rtn = malloc(sizeof(BlendFile));
+ if (!rtn) {
+ dprintf(stderr, "out of mem making bf handle\n");
+ return NULL;
+ }
+ rtn->types = NULL;
+ rtn->types_count = 0;
+ rtn->names = NULL;
+ rtn->names_count = 0;
+ rtn->blocks = NULL;
+ rtn->blocks_count = 0;
+ rtn->strc_indices = NULL;
+ rtn->strc_indices_count = 0;
+ rtn->name_undef = -1;
+ return rtn;
+}
+
+
+static long
+seek_past_string(MY_FILETYPE* file, const char *str) {
+ const int match_max = strlen(str);
+ int match_now = 0;
+
+ do {
+ const char c = MY_GETC(file);
+ if (c == str[match_now]) {
+ ++match_now;
+ } else {
+ match_now = 0;
+ }
+ } while(match_now < match_max &&
+ !MY_ATEOF(file));
+
+ if (MY_ATEOF(file)) {
+ return -1;
+ }
+
+ return MY_TELL(file) - match_max;
+}
+
+#define EXPANDO_MULTIPLE(ARRAY,ADDITIONPTR,NUMBER) \
+do { \
+ (ARRAY) = realloc((ARRAY), sizeof((ARRAY)[0]) * (NUMBER +(ARRAY##_count))); \
+ memcpy(&(ARRAY)[ARRAY##_count], ADDITIONPTR, sizeof((ARRAY)[0]) * NUMBER); \
+ (ARRAY##_count) += NUMBER; \
+} while(0)
+
+#define EXPANDO(ARRAY,ADDITION) \
+do { \
+ (ARRAY) = realloc((ARRAY), sizeof((ARRAY)[0]) * (1 + (ARRAY##_count))); \
+ (ARRAY)[ARRAY##_count] = (ADDITION); \
+ ++(ARRAY##_count); \
+} while(0)
+
+
+static unsigned short
+read_ushort(MY_FILETYPE* file) {
+ unsigned char c[2];
+ if (MY_READ(c, 2, 1, file) == 1) {
+ return c[0] | (c[1]<<8);
+ } else {
+ return 0xFFFF;
+ }
+}
+
+
+static long
+read_long(MY_FILETYPE* file) {
+ unsigned char c[4];
+ if (MY_READ(c, 4, 1, file) == 1) {
+ return ((unsigned long)c[0] | (c[1]<<8) | (c[2]<<16) | (c[3]<<24));
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+
+static unsigned long
+read_ulong(MY_FILETYPE* file) {
+ unsigned char c[4];
+ if (MY_READ(c, 4, 1, file) == 1) {
+ return c[0] | (c[1]<<8) | (c[2]<<16) | (c[3]<<24);
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+
+static int
+name_is_pointer(char* name) {
+ int len = strlen(name);
+ /*fprintf(stderr,"[%s]",name);*/
+ if (len >= 1) {
+ if (name[0] == '*')
+ return 1;
+ }
+ if (len >= 2) {
+ if (name[1] == '*')
+ return 1;
+ }
+ return 0;
+}
+
+
+/* note: we only deal with 1d or 2d arrays at the moment. haven't
+ seen any arrays of a higher order from Blender yet. */
+static int
+name_is_array(char* name, int* dim1, int* dim2) {
+ int len = strlen(name);
+ /*fprintf(stderr,"[%s]",name);*/
+ /*if (len >= 1) {
+ if (name[len-1] != ']')
+ return 1;
+ }
+ return 0;*/
+ char *bp;
+ int num;
+ if (dim1) {
+ *dim1 = 1;
+ }
+ if (dim2) {
+ *dim2 = 1;
+ }
+ bp = strchr(name, '[');
+ if (!bp) {
+ return 0;
+ }
+ num = 0;
+ while (++bp < name+len-1) {
+ const char c = *bp;
+ if (c == ']') {
+ break;
+ }
+ if (c <= '9' && c >= '0') {
+ num *= 10;
+ num += (c - '0');
+ } else {
+ dprintf(stderr, "array parse error.\n");
+ return 0;
+ }
+ }
+ if (dim2) {
+ *dim2 = num;
+ }
+
+ /* find second dim, if any. */
+ bp = strchr(bp, '[');
+ if (!bp) {
+ return 1; /* at least we got the first dim. */
+ }
+ num = 0;
+ while (++bp < name+len-1) {
+ const char c = *bp;
+ if (c == ']') {
+ break;
+ }
+ if (c <= '9' && c >= '0') {
+ num *= 10;
+ num += (c - '0');
+ } else {
+ dprintf(stderr, "array2 parse error.\n");
+ return 1;
+ }
+ }
+ if (dim1) {
+ if (dim2) {
+ *dim1 = *dim2;
+ *dim2 = num;
+ } else {
+ *dim1 = num;
+ }
+ }
+
+ return 1;
+}
+
+
+#define SIZE_ROUNDUP(SIZE) (((SIZE) + sizeof(int) - 1) & ~(sizeof(int) - 1))
+
+
+static void
+recursively_read_type(MY_FILETYPE* file, BlendFile* bf,
+ unsigned long section_type,
+ BlendField* field)
+{
+ char *new_data = NULL;
+ int new_data_size = 0;
+ int dim1, dim2;
+
+ if (bf->types[section_type].is_struct) {
+ int i;
+ /*fprintf(stderr, "type%d(%s)is_struct(%d) ", section_type, bf->types[section_type].name, bf->types[section_type].size);*/
+ for (i=0; itypes[section_type].fieldtypes_count; ++i) {
+ /*fprintf(stderr, "@%d ", i);*/
+ int j,k;
+#if 0
+ if (name_is_array(bf->names[bf->types[section_type].fieldnames[i]],
+ &j,&k) || 1) {
+ dprintf(stderr, " %s/%s=[%d][%d] ",
+ bf->types[bf->types[section_type].fieldtypes[i]].name,
+ bf->names[bf->types[section_type].fieldnames[i]], j, k);
+ }
+#endif
+ if (name_is_pointer(bf->names[bf->types[section_type].fieldnames[i]])) {
+ /*fprintf(stderr, "*(4) ");*/
+ name_is_array(bf->names[bf->types[section_type].fieldnames[i]],
+ &dim1,&dim2);
+ new_data_size = SIZE_ROUNDUP(4);
+ new_data = malloc(new_data_size);
+ /*fprintf(stderr, " ");*/
+ for (j=0; jfield_offsets, field->field_bytes_count);
+ EXPANDO_MULTIPLE(field->field_bytes, new_data, new_data_size);
+ /*fprintf(stderr, "N*(%d) ", new_data_size);*/
+ }
+ }
+ free(new_data);
+ } else {
+ name_is_array(bf->names[bf->types[section_type].fieldnames[i]],
+ &dim1,&dim2);
+ /*fprintf(stderr, " ");*/
+ for (j=0; jtypes[section_type].fieldtypes[i],
+ field);
+ }
+ }
+ }
+ }
+ } else {
+ /*fprintf(stderr, "type%d(%s)plain(%d) ", section_type, bf->types[section_type].name, bf->types[section_type].size); */
+ new_data_size = SIZE_ROUNDUP(bf->types[section_type].size);
+ /*fprintf(stderr, "%d... ", bf->types[section_type].size);
+ if (bf->types[section_type].size > 4) {
+ fprintf(stderr, "%d ", field->field_bytes_count);
+ }*/
+ if (new_data_size) {
+ new_data = malloc(new_data_size);
+ MY_READ(new_data, 1, bf->types[section_type].size, file);
+ EXPANDO(field->field_offsets, field->field_bytes_count);
+ EXPANDO_MULTIPLE(field->field_bytes, new_data, new_data_size);
+ /*fprintf(stderr, "ND(%d) ", new_data_size); */
+ free(new_data);
+ } else {
+ dprintf(stderr, " ",
+ bf->types[section_type].size);
+ }
+ }
+
+}
+
+
+static BlendField
+read_type(MY_FILETYPE* file, BlendFile* bf,
+ unsigned long section_type)
+{
+ BlendField rtn;
+
+ rtn.field_bytes = NULL;
+ rtn.field_bytes_count = 0;
+ rtn.field_offsets = NULL;
+ rtn.field_offsets_count = 0;
+
+ recursively_read_type(file, bf, section_type, &rtn);
+
+ return rtn;
+}
+
+
+static int
+blend_read_data(MY_FILETYPE* file, BlendFile* bf)
+{
+ long next_block_start = 12;
+ int finished_extracting = 0;
+ char section_name[5] = {0,0, 0,0, 0};
+
+ /* slurp up the whole file block by block! */
+
+ do {
+ unsigned long section_size;
+ unsigned long section_pointer;
+ unsigned long section_type;
+ unsigned long section_ents;
+
+ MY_SEEK(file, next_block_start);
+
+ MY_READ(section_name, 4, 1, file);
+
+ if (strcmp(section_name, "DNA1") != 0) {
+ int i;
+ BlendBlock block;
+
+ section_size = read_ulong(file);
+ section_pointer = read_ulong(file);
+ section_type = read_ulong(file);
+ section_type = bf->strc_indices[section_type];
+ section_ents = read_ulong(file);
+
+ memcpy(block.tag, section_name, 4);
+ block.tag[4] = '\0';
+ block.type_index = section_type;
+ block.blender_pointer = section_pointer;
+ /*block.fixed_pointer = NULL;*/
+ block.array_entries = NULL;
+ block.array_entries_count = 0;
+
+ /*dprintf(stderr, "\nsizeof(%s)=%ld: %s[%ld]\n", section_name, section_size, bf->types[section_type].name, section_ents); */
+
+ for (i=0; iblocks, block);
+
+ next_block_start += 4+4+4+4+4 + section_size;
+
+#ifdef B_DEBUG
+ if (MY_TELL(file) > next_block_start) {
+ dprintf(stderr, " **OVER-READ(%ld,%ld)** ",
+ MY_TELL(file), next_block_start);
+ if (strcmp(bf->types[section_type].name, "Link") == 0 &&
+ MY_TELL(file) - next_block_start == 4) {
+ dprintf(stderr, "<- don't panic, known Link struct weirdness.");
+ } else {
+ dprintf(stderr, "<- okay, PANIC!");
+ }
+ dprintf(stderr, "\n");
+ } else if (MY_TELL(file) < next_block_start) {
+ /*dprintf(stderr, " **under-READ(%ld,%ld)** ",
+ MY_TELL(file), next_block_start);*/
+ } else {
+ /*dprintf(stderr, " :) ");*/
+ }
+#endif
+
+ } else {
+ finished_extracting = 1;
+ }
+
+ } while (!finished_extracting);
+
+ return 1;
+}
+
+
+BlendFile*
+blend_read(MY_FILETYPE* file)
+{
+ char blender_mark[8] = {0,0,0,0, 0,0,0,0};
+ BlendFile *bf;
+ long sdnaname_offs, type_offs, tlen_offs, strc_offs, endb_offs;
+ long sdnaname_size, type_size, tlen_size, strc_size;
+ long sdnaname_ents, type_ents, tlen_ents, strc_ents;
+
+ MY_REWIND(file);
+
+ /* Check file signature */
+
+ MY_READ(blender_mark, 1, 7, file);
+ if (strcmp(blender_mark, "BLENDER") != 0) {
+ dprintf(stderr, "Not a Blender file.\n");
+ return NULL;
+ }
+
+ /* Alloc a handle to return */
+
+ bf = bf_new();
+
+ /* Scan the whole file (!) for file section markers */
+
+ sdnaname_offs = seek_past_string(file, "SDNANAME");
+ sdnaname_ents = read_long(file);
+ type_offs = seek_past_string(file, "TYPE");
+ type_ents = read_long(file);
+ tlen_offs = seek_past_string(file, "TLEN");
+ tlen_ents = type_ents;
+ strc_offs = seek_past_string(file, "STRC");
+ strc_ents = read_long(file);
+ endb_offs = seek_past_string(file, "ENDB");
+
+ if (sdnaname_offs == -1 || type_offs == -1 || tlen_offs == -1 ||
+ strc_offs == -1 || endb_offs == -1) {
+ dprintf(stderr, "Couldn't find all necessary file markers. :(\n");
+ return NULL;
+ }
+
+ /* Move marker offsets to point to the start of each one's block */
+
+ sdnaname_offs += 8 + 4;
+ sdnaname_size = type_offs - sdnaname_offs;
+
+ type_offs += 4 + 4;
+ type_size = tlen_offs - type_offs;
+
+ tlen_offs += 4;
+ tlen_size = strc_offs - tlen_offs;
+
+ strc_offs += 4 + 4;
+ strc_size = endb_offs - strc_offs;
+
+ /* read the NAME table */
+
+ MY_SEEK(file, sdnaname_offs);
+ {
+ long offs = 0;
+ int i;
+ char *top_block_name;
+
+ for (i=0; inames, this_name_chars);
+ }
+
+ /* our top-block name */
+#define BLEND_TOP_BLOCK_NAME "READBLEND_TOP_BLOCK"
+ top_block_name = calloc(1, 1+strlen(BLEND_TOP_BLOCK_NAME));
+ strcpy(top_block_name, BLEND_TOP_BLOCK_NAME);
+ bf->name_undef = bf->names_count;
+ EXPANDO(bf->names, top_block_name);
+ }
+
+ /* read the TYPE table */
+
+ MY_SEEK(file, type_offs);
+ {
+ long offs = 0;
+ int i;
+
+ for (i=0; itypes, bt);
+ }
+ }
+
+ /* read the TLEN table */
+
+ MY_SEEK(file, tlen_offs);
+ {
+ int i;
+ for (i=0; itypes_count; ++i) {
+ unsigned short len = read_ushort(file);
+ bf->types[i].size = len;
+ /*fprintf(stderr, "sizeof(%s)=%d ", bf->types[i].name, len); */
+ }
+ }
+
+ /* Read the STRC table */
+
+ MY_SEEK(file, strc_offs);
+ {
+ int i,j;
+ for (i=0; itypes[struc_type_index].is_struct = 1;
+ EXPANDO(bf->strc_indices, struc_type_index);
+ /*dprintf(stderr, "\n%s: ", bf->types[struc_type_index].name); */
+ for (j=0; jtypes[struc_type_index].fieldtypes, ftype);
+ EXPANDO(bf->types[struc_type_index].fieldnames, fname);
+ /*dprintf(stderr, "%s %s , ", bf->types[ftype].name, bf->names[fname]); */
+ }
+ }
+ }
+
+ blend_read_data(file, bf);
+
+ /* Return the new handle */
+
+ return bf;
+}
+
+
+static void free_btype_inner(BlendType *btype)
+{
+ if (btype->name) {
+ free(btype->name);
+ } else {
+ dprintf(stderr, "null typename.\n");
+ }
+
+ if (btype->fieldtypes) {
+ free(btype->fieldtypes);
+ }
+
+ if (btype->fieldnames) {
+ free(btype->fieldnames);
+ }
+}
+
+
+static void free_bfield_inner(BlendField *bfield)
+{
+ if (bfield->field_bytes) {
+ free(bfield->field_bytes);
+ }
+
+ if (bfield->field_offsets) {
+ free(bfield->field_offsets);
+ }
+}
+
+
+static void free_bblock_inner(BlendBlock *bblock)
+{
+ int i;
+
+ for (i=0; iarray_entries_count; ++i) {
+ free_bfield_inner(&bblock->array_entries[i]);
+ }
+ free(bblock->array_entries);
+}
+
+
+void
+blend_free(BlendFile* blend_file)
+{
+ int i;
+
+ for (i=0; itypes_count; ++i) {
+ free_btype_inner(&blend_file->types[i]);
+ }
+ if (blend_file->types) {
+ free(blend_file->types);
+ }
+
+ for (i=0; inames_count; ++i) {
+ if (blend_file->names[i]) {
+ free(blend_file->names[i]);
+ } else {
+ dprintf(stderr, "null name.\n");
+ }
+ }
+ if (blend_file->names) {
+ free(blend_file->names);
+ }
+
+ for (i=0; iblocks_count; ++i) {
+ free_bblock_inner(&blend_file->blocks[i]);
+ }
+ if (blend_file->blocks) {
+ free(blend_file->blocks);
+ }
+
+ if (blend_file->strc_indices) {
+ free(blend_file->strc_indices);
+ }
+
+ free(blend_file);
+}
+
+
+/********************************************************************
+ * done with the reading/parsing logic; now for the querying logic *
+ ********************************************************************/
+
+
+
+/********************************************************************
+ * LOW-LEVEL *
+ ********************************************************************/
+
+
+
+const char*
+blend_block_get_tagname(BlendFile* blend_file,
+ BlendBlockPointer block)
+{
+ const BlendBlock *const bb = block;
+ return bb->tag;
+}
+
+
+const char*
+blend_block_get_typename(BlendFile* blend_file,
+ BlendBlockPointer block)
+{
+ const BlendBlock *const bb = block;
+ return blend_file->types[bb->type_index].name;
+}
+
+
+int
+blend_block_get_entry_count(BlendFile* blend_file,
+ BlendBlockPointer block)
+{
+ const BlendBlock *const bb = block;
+ return bb->array_entries_count;;
+}
+
+
+void
+blend_foreach_block(BlendFile* blend_file,
+ BlendBlockCallback* func,
+ void* userdata)
+{
+ int i;
+ for (i=0; iblocks_count; ++i) {
+ if (!func(&blend_file->blocks[i], blend_file, userdata)) return;
+ }
+}
+
+
+static int
+blend_type_basename_compare(const char *fancy, const char *raw) {
+ const int flen = strlen(fancy);
+ const int rlen = strlen(raw);
+ int i, strcmp_result = 123;
+
+ i = 0;
+ while (i < flen && (fancy[i]=='*' || fancy[i]=='(')) {
+ ++i;
+ }
+
+ strcmp_result = strncmp(&fancy[i], raw, rlen);
+
+ if (strcmp_result == 0 && flen > rlen+i) {
+ i = rlen + i;
+ if (fancy[i] != ')' && fancy[i] != '(' && fancy[i] != '[') {
+ strcmp_result = -1;
+ }
+ }
+
+ return strcmp_result;
+}
+
+
+static BlendObjType
+typestring_to_blendobj_type(BlendFile* blend_file,
+ const char* type_name) {
+ if (blend_type_basename_compare(type_name, "char") == 0) {
+ return BLEND_OBJ_CHAR8;
+ } else if (blend_type_basename_compare(type_name, "uchar") == 0) {
+ return BLEND_OBJ_UCHAR8;
+ } else if (blend_type_basename_compare(type_name, "short") == 0) {
+ return BLEND_OBJ_SHORT16;
+ } else if (blend_type_basename_compare(type_name, "ushort") == 0) {
+ return BLEND_OBJ_USHORT16;
+ } else if (blend_type_basename_compare(type_name, "int") == 0) {
+ return BLEND_OBJ_LONG32;
+ } else if (blend_type_basename_compare(type_name, "long") == 0) {
+ return BLEND_OBJ_LONG32;
+ } else if (blend_type_basename_compare(type_name, "ulong") == 0) {
+ return BLEND_OBJ_ULONG32;
+ } else if (blend_type_basename_compare(type_name, "float") == 0) {
+ return BLEND_OBJ_FLOAT;
+ } else if (blend_type_basename_compare(type_name, "double") == 0) {
+ return BLEND_OBJ_DOUBLE;
+ } else if (blend_type_basename_compare(type_name, "void") == 0) {
+ return BLEND_OBJ_OPAQUE;
+ } else {
+ return BLEND_OBJ_STRUCT; /* structure */
+ }
+}
+
+
+static BlendObjType
+typelong_to_blendobj_type(BlendFile* blend_file,
+ long btype, long bname)
+{
+ if (name_is_pointer(blend_file->names[bname])) {
+ return BLEND_OBJ_POINTER;
+ } else if (blend_file->types[btype].is_struct) {
+ return BLEND_OBJ_STRUCT;
+ } else {
+ return typestring_to_blendobj_type(blend_file,
+ blend_file->types[btype].name);
+ }
+}
+
+
+BlendObjType
+blend_object_type(BlendFile* blend_file,
+ BlendObject obj) {
+ return typelong_to_blendobj_type(blend_file,
+ obj.type,
+ obj.name);
+}
+
+
+BlendBlockPointer
+blend_block_from_blendpointer(BlendFile *blend_file,
+ uint32_t blendpointer)
+{
+ int i;
+
+ /* fprintf(stderr, "%04x: ", blendpointer);*/
+
+ if (blendpointer != 0) {
+ for (i=0; iblocks_count; ++i) {
+ /*fprintf(stderr, "%04x? ", blend_file->blocks[i].blender_pointer); */
+ if (blend_file->blocks[i].blender_pointer == blendpointer) {
+ return &blend_file->blocks[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+static BlendBlockPointer
+blend_block_from_object(BlendFile *blend_file,
+ BlendObject *obj) {
+ return obj->block;
+}
+
+
+int
+blend_object_array_getdata(BlendFile* blend_file,
+ void* dest, BlendObject obj,
+ int dim_index_1, int dim_index_2)
+{
+ const char* type_name = blend_file->types[obj.type].name;
+ const BlendBlock *const bb = obj.block;
+ BlendField *bf = &bb->array_entries[obj.entry_index];
+ void* data;
+ int dim1, dim2;
+
+ name_is_array(blend_file->names[obj.name], &dim1, &dim2);
+ /*dprintf(stderr, "copying:'%s'[%d][%d] (of [%d][%d]) ", type_name, dim_index_1, dim_index_2, dim1, dim2);*/
+
+ if (dim_index_1 >= dim1 ||
+ dim_index_2 >= dim2) {
+ dprintf(stderr, "Array index (%d,%d) out of bounds for dimensionality [%d][%d]\n", dim_index_1, dim_index_2, dim1, dim2);
+ return 0;
+ }
+
+ data = &bf->field_bytes[bf->field_offsets[obj.field_index +
+ dim2*dim_index_1 + dim_index_2]];
+ /*dprintf(stderr, "fi[%d]byteo[%d]", obj.field_index,
+ bf->field_offsets[obj.field_index +
+ dim2*dim_index_1 + dim_index_2]);*/
+
+ if (name_is_pointer(blend_file->names[obj.name])) {
+ *(BlendBlockPointer*)dest =
+ blend_block_from_blendpointer(blend_file,
+ BGETLEUINT32(data));
+ return 1;
+ }
+
+ /* FIXME: might be a good idea to do less-crappy word-size conversions
+ here -- these might read beyond the end of malloc'd blocks if we
+ ever change our field-padding policy. There were endian problems
+ too; these are believed fixed now. */
+ /* The signed conversions look strange because they have to sign-expand
+ negative results without relying on right-shifts which have undefined
+ behaviour on negative data according to ANSI C. */
+ if (blend_type_basename_compare(type_name, "char") == 0) {
+ *(char*)dest = (*(char*)data) << (8*sizeof(char)-8) / (1<<(8*sizeof(char)-8));
+ } else if (blend_type_basename_compare(type_name, "uchar") == 0) {
+ *(unsigned char*)dest = *(unsigned char*)data;
+ } else if (blend_type_basename_compare(type_name, "short") == 0) {
+ *(int16_t*)dest = BGETLEINT16(data) << (8*sizeof(int16_t)-16) / (1<<(8*sizeof(int16_t)-16));
+ } else if (blend_type_basename_compare(type_name, "ushort") == 0) {
+ *(uint16_t*)dest = BGETLEUINT16(data);
+ } else if (blend_type_basename_compare(type_name, "int") == 0) {
+ *(int32_t*)dest = BGETLEINT32(data) << (8*sizeof(int32_t)-32) / (1<<(8*sizeof(int32_t)-32));
+ } else if (blend_type_basename_compare(type_name, "long") == 0) {
+ *(int32_t*)dest = BGETLEINT32(data) << (8*sizeof(int32_t)-32) / (1<<(8*sizeof(int32_t)-32));
+ } else if (blend_type_basename_compare(type_name, "ulong") == 0) {
+ *(uint32_t*)dest = BGETLEUINT32(data);
+ } else if (blend_type_basename_compare(type_name, "float") == 0) {
+ *(float*)dest = BGETLEFLOAT32(data);
+ /*fprintf(stderr, "GOT{%f'%f} ", *(float*)dest, *(float*)data);*/
+ } else if (blend_type_basename_compare(type_name, "double") == 0) {
+ *(double*)dest = BGETLEDOUBLE64(data);
+ } else if (blend_type_basename_compare(type_name, "void") == 0) {
+ dprintf(stderr, "Tried to fetch a void.\n");
+ return 0;
+ } else {
+ dprintf(stderr, "Tried to fetch a whole structure.\n");
+ return 0;
+ }
+ return 1; /* success */
+}
+
+
+int
+blend_object_getdata(BlendFile* blend_file,
+ void* dest, BlendObject obj)
+{
+ int dim1, dim2;
+
+ if (name_is_array(blend_file->names[obj.name], &dim1, &dim2)) {
+ if (dim1 != 1 || dim2 != 1) {
+ dprintf(stderr, "Tried to fetch a whole array.\n");
+ return 0;
+ }
+ }
+
+ return (blend_object_array_getdata(blend_file, dest, obj, 0, 0));
+}
+
+
+/* recursively count the number of fields and array items in this
+ structure, for the purposes of skipping in the field offset array */
+static long
+get_num_type_segments(BlendFile* blend_file,
+ BlendObject obj)
+{
+ int i;
+ long rtn = 0;
+ int dim1,dim2;
+
+ name_is_array(blend_file->names[obj.name],
+ &dim1, &dim2);
+
+ if (name_is_pointer(blend_file->names[obj.name]) ||
+ !blend_file->types[obj.type].is_struct) {
+ return (1 * dim1 * dim2);
+ }
+
+ /* fprintf(stderr, "STRUCTYAYYY ");*/
+
+ for (i=0; itypes[obj.type].fieldnames_count; ++i) {
+ BlendObject qo = obj;
+ qo.type = blend_file->types[obj.type].fieldtypes[i];
+ qo.name = blend_file->types[obj.type].fieldnames[i];
+ qo.field_index = i;
+ rtn += get_num_type_segments(blend_file, qo) * dim1 * dim2;
+ }
+
+ return (rtn);
+}
+
+
+int
+blend_object_structure_getfield(BlendFile* blend_file,
+ BlendObject *result,
+ BlendObject obj,
+ const char* field_name)
+{
+ if (blend_file->types[obj.type].is_struct) {
+ int i;
+ int field_index = 0;
+ for (i=0; itypes[obj.type].fieldnames_count; ++i) {
+ if (blend_type_basename_compare(
+ blend_file->names[blend_file->types[obj.type].fieldnames[i]],
+ field_name)
+ == 0) {
+ result->type = blend_file->types[obj.type].fieldtypes[i];
+ result->name = blend_file->types[obj.type].fieldnames[i];
+ result->block = obj.block;
+ result->entry_index = obj.entry_index;
+ result->field_index = field_index;
+ return 1;
+ }
+
+ {
+ BlendObject qo = obj;
+ int fos;
+ qo.type = blend_file->types[obj.type].fieldtypes[i];
+ qo.name = blend_file->types[obj.type].fieldnames[i];
+ qo.field_index = field_index;
+ fos = get_num_type_segments(blend_file, qo);
+ /*fprintf(stderr, ">>%s %s:%d ",
+ blend_file->types[qo.type].name,
+ blend_file->names[qo.name], fos);*/
+ field_index += fos;
+ }
+ }
+ return 0;
+ } else {
+ dprintf(stderr, "Indexed object isn't a structure!\n");
+ return 0;
+ }
+}
+
+
+void
+blend_object_array_getdims(BlendFile* blend_file,
+ BlendObject obj,
+ int* dim1, int* dim2)
+{
+ name_is_array(blend_file->names[obj.name],
+ dim1, dim2);
+}
+
+
+BlendObject
+blend_block_get_object(BlendFile* blend_file,
+ BlendBlockPointer block,
+ int entry_index)
+{
+ BlendObject bo;
+ const BlendBlock *const bb = block;
+ /*BlendField *bf = &bb->array_entries[entry_index]; */
+
+ bo.type = bb->type_index;
+ bo.name = blend_file->name_undef;
+ bo.block = block;
+ bo.entry_index = entry_index;
+ bo.field_index = 0;
+
+ return bo;
+}
+
+
+
+
+/********************************************************************
+ * MID-LEVEL *
+ ********************************************************************/
+
+
+/* general helpers */
+
+
+int
+blend_object_getstring(BlendFile* blend_file,
+ BlendObject obj,
+ char *dest, int max_chars)
+{
+ int strpos = 0;
+ int dim1, dim2;
+ int rtn = 1;
+ BlendObjType bo_type;
+
+ name_is_array(blend_file->names[obj.name],
+ &dim1, &dim2);
+
+ if (dim2 < max_chars) {
+ max_chars = dim2;
+ }
+
+ bo_type = blend_object_type(blend_file, obj);
+ if (! (bo_type==BLEND_OBJ_CHAR8 || bo_type==BLEND_OBJ_UCHAR8)) {
+ dprintf(stderr, "tried to get string from an object that's not of type uchar/char. (is type %d)\n", bo_type);
+ rtn = 0;
+ goto done;
+ }
+
+ for (strpos=0; strposIDname) { /* don't freak out, but hmm, do we do the right thing? */
+ const int strl = strlen(ifd->IDname);
+ char *obj_string = malloc(3+ strl);
+
+ for (i=0; iIDname) == 0) {
+ ifd->found = obj;
+ ifd->success = 1;
+ want_more = 0;
+ goto done;
+ } else {
+ /* next entry please. */
+ }
+ }
+
+ done:;
+ free(obj_string);
+ }
+
+ return want_more;
+}
+
+
+int
+blend_object_get_by_IDname(BlendFile* blend_file,
+ BlendObject *result,
+ const char* IDname)
+{
+ IDFinderData ifd;
+
+ ifd.success = 0;
+ ifd.IDname = IDname;
+ ifd.just_print_it = 0;
+
+ blend_foreach_block(blend_file, block_ID_finder, &ifd);
+
+ if (!ifd.success) {
+ return 0;
+ }
+
+ *result = ifd.found;
+ return 1;
+}
+
+
+void
+blend_dump_typedefs(BlendFile* bf)
+{
+ int i;
+
+ /* dump out display of types and their sizes */
+ for (i=0; itypes_count; ++i) {
+ /* if (!bf->types[i].is_struct)*/
+ {
+ printf("%3d: sizeof(%s%s)=%d",
+ i,
+ bf->types[i].is_struct ? "struct " : "atomic ",
+ bf->types[i].name, bf->types[i].size);
+ if (bf->types[i].is_struct) {
+ int j;
+ printf(", %d fields: { ", bf->types[i].fieldtypes_count);
+ for (j=0; jtypes[i].fieldtypes_count; ++j) {
+ printf("%s %s",
+ bf->types[bf->types[i].fieldtypes[j]].name,
+ bf->names[bf->types[i].fieldnames[j]]);
+ if (j == bf->types[i].fieldtypes_count-1) {
+ printf("; }\n");
+ } else {
+ printf("; ");
+ }
+ }
+ }
+
+ }
+ }
+}
+
+
+void
+blend_dump_blocks(BlendFile* bf)
+{
+ int i;
+ IDFinderData ifd;
+
+ ifd.success = 0;
+ ifd.IDname = NULL;
+ ifd.just_print_it = 1;
+
+ for (i=0; iblocks_count; ++i) {
+ BlendBlock* bb = &bf->blocks[i];
+ printf("tag='%s'\tptr=%p\ttype=%s\t[%4d]",
+ bb->tag, /*bb->blender_pointer,*/ bb,
+ bf->types[bb->type_index].name,
+ bb->array_entries_count);
+ block_ID_finder(bb, bf, &ifd);
+ printf("\n");
+ }
+}
+
+
+int
+blend_obj_is_rootobject(BlendFile *bf, BlendObject *objobj) {
+ BlendObject obj;
+ BlendBlockPointer block;
+ if ((blend_object_structure_getfield(bf, &obj, *objobj, "parent") &&
+ blend_object_getdata(bf, &block, obj))) {
+ return (block == NULL);
+ }
+ return 0;
+}
+
+
+BlendLayerMask
+blend_obj_get_layermask(BlendFile *bf, BlendObject *objobj) {
+ BlendObject obj;
+ int32_t ldata;
+ if ((blend_object_structure_getfield(bf, &obj, *objobj, "lay") &&
+ blend_object_getdata(bf, &ldata, obj))) {
+ return (BlendLayerMask) ldata;
+ }
+ return 0;
+}
+
+
+/* Bizarrely, blender doesn't seem to carry a mapping of parent
+ to children -- only of children to parent. So, to find the children
+ of an Object we have to scan all objects to see if their parent is
+ the Object in question. */
+typedef struct {
+ BlendBlockPointer wanted_parent;
+ BlendBlockPointer child;
+ int just_counting;
+ int num_so_far;
+ int wanted_index;
+} childfinderData;
+static BLENDBLOCKCALLBACK_RETURN
+block_childfinder(BLENDBLOCKCALLBACK_ARGS) {
+ childfinderData *cfd = (childfinderData *)userdata;
+ const char *tagname = blend_block_get_typename(blend_file, block);
+ int entry_count = blend_block_get_entry_count(blend_file, block);
+ int want_more = 1;
+ int i;
+
+ if (strcmp(tagname, "Object") == 0) {
+ /* Is Object */
+ for (i=0; iwanted_parent) {
+ if ((cfd->num_so_far == cfd->wanted_index) &&
+ !cfd->just_counting) {
+ cfd->child = block;
+ want_more = 0;
+ }
+ ++cfd->num_so_far;
+ } else {
+ cfd->child = NULL;
+ }
+ }
+ }
+ }
+
+ return want_more;
+}
+
+int
+blend_obj_get_childcount(BlendFile *bf, BlendObject *objobj) {
+ childfinderData cfd;
+ cfd.wanted_parent = blend_block_from_object(bf, objobj);
+ cfd.child = NULL;
+ cfd.just_counting = 1;
+ cfd.num_so_far = 0;
+ cfd.wanted_index = 0;
+ blend_foreach_block(bf, block_childfinder, &cfd);
+ return cfd.num_so_far;
+}
+
+BlendBlockPointer
+blend_obj_get_child(BlendFile *bf, BlendObject *objobj, int childnum) {
+ childfinderData cfd;
+ cfd.wanted_parent = blend_block_from_object(bf, objobj);
+ cfd.child = NULL;
+ cfd.just_counting = 0;
+ cfd.num_so_far = 0;
+ cfd.wanted_index = childnum;
+ blend_foreach_block(bf, block_childfinder, &cfd);
+ return cfd.child;
+}
+
+
+/********************************************************************
+ * HIGH-LEVEL *
+ ********************************************************************/
+
+#ifndef LEN3POW2
+/* length squared */
+#define LEN3POW2(xd,yd,zd) ((xd)*(xd) + (yd)*(yd) + (zd)*(zd))
+#endif
+
+#ifndef LEN3
+/* length (expensive) */
+#define LEN3(xd,yd,zd) (sqrt(LEN3POW2((xd),(yd),(zd))))
+#endif
+
+#ifndef NORMALIZE3
+/* vector normalization (expensive) */
+#define NORMALIZE3(xd,yd,zd) \
+do { \
+ const double norm3_macro_len3 = LEN3((xd),(yd),(zd)); \
+ if (norm3_macro_len3 != 0.0F) \
+ { \
+ (xd) = (xd) / norm3_macro_len3; \
+ (yd) = (yd) / norm3_macro_len3; \
+ (zd) = (zd) / norm3_macro_len3; \
+ } \
+} while (0)
+#endif
+
+
+static void bMatIdentity(bMatrix mat) {
+ int i,j;
+ for (i=0; i<4; ++i) {
+ for (j=0; j<4; ++j) {
+ mat[i][j] = 0.0f;
+ }
+ mat[i][i] = 1.0f;
+ }
+}
+
+static void bMatMultVec(float xyz[3], bMatrix mat) {
+ int i;
+ float r[3];
+ for (i=0; i<3; ++i) {
+ r[i] = 0.0f;
+ }
+ for (i=0; i<3; ++i) {
+ r[i] += xyz[0] * mat[0][i];
+ r[i] += xyz[1] * mat[1][i];
+ r[i] += xyz[2] * mat[2][i];
+ r[i] += 1.0f * mat[3][i];
+ }
+ for (i=0; i<3; ++i) {
+ xyz[i] = r[i];
+ }
+}
+
+bMesh *blend_alloc_mesh(void) {
+ return malloc(sizeof(bMesh));
+}
+
+void
+blend_init_mesh(bMesh *mesh)
+{
+ int i,j;
+ mesh->vert = NULL;
+ mesh->vert_count = 0;
+ mesh->face = NULL;
+ mesh->face_count = 0;
+ mesh->material = NULL;
+}
+
+void
+blend_free_mesh_inner(bMesh *mesh)
+{
+ if (mesh->vert) {
+ free(mesh->vert);
+ }
+ if (mesh->face) {
+ free(mesh->face);
+ }
+ if (mesh->material) {
+ blend_free_material(mesh->material);
+ }
+}
+
+void
+blend_free_mesh(bMesh *mesh)
+{
+ blend_free_mesh_inner(mesh);
+ free(mesh);
+}
+
+
+bObj *
+blend_alloc_obj(void) {
+ return malloc(sizeof(bObj));
+}
+
+void
+blend_init_obj(bObj *obj) {
+ obj->type = BOBJ_TYPE_UNKNOWN;
+ obj->name = NULL;
+ bMatIdentity(obj->transform);
+ bMatIdentity(obj->parentimat);
+ obj->location[0] =
+ obj->location[1] =
+ obj->location[2] = 0.0f;
+ obj->scaling[0] =
+ obj->scaling[1] =
+ obj->scaling[2] = 1.0f;
+ obj->rotphr[0] =
+ obj->rotphr[1] =
+ obj->rotphr[2] = 0.0f;
+ obj->data.dummy = NULL;
+ obj->transflags = 0;
+}
+
+void
+blend_free_obj_inner(bObj *obj) {
+ if (obj->name) {
+ free(obj->name);
+ }
+ switch (obj->type) {
+ case BOBJ_TYPE_MESH:
+ blend_free_mesh(obj->data.mesh);
+ case BOBJ_TYPE_UNKNOWN:
+ case BOBJ_TYPE_NULL:
+ default:
+ break;
+ }
+}
+
+void
+blend_free_obj(bObj *obj) {
+ blend_free_obj_inner(obj);
+ free(obj);
+}
+
+void
+blend_acquire_obj_from_obj(BlendFile *bf, BlendObject *objobj,
+ bObj *outobj, BlendObject **mallocdoutblendobject) {
+
+ BlendObject obj, dataobj;
+ BlendBlockPointer block;
+ short sdata = 12345;
+#define B_IDNAME_MAX_SIZE 80
+ char *idname = malloc(1 + B_IDNAME_MAX_SIZE);
+ float fdata1, fdata2, fdata3;
+
+ if (mallocdoutblendobject) *mallocdoutblendobject = NULL;
+
+ blend_init_obj(outobj);
+
+ if (!(blend_object_structure_getfield(bf, &obj, *objobj,
+ "type") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort(); /* couldn't get type */
+ } else {
+ switch (sdata) {
+ case 1: /* mesh */
+ outobj->type = BOBJ_TYPE_MESH; break;
+ case 10: /* lamp */
+ case 11: /* camera */
+ default:
+ outobj->type = BOBJ_TYPE_UNKNOWN; break;
+ }
+ }
+
+ if (blend_object_get_IDname(bf, *objobj, idname, B_IDNAME_MAX_SIZE)) {
+ outobj->name = idname;
+ } else {
+ free(idname);
+ abort(); /* couldn't get obj name */
+ }
+
+ /* now we override the mesh transform with the object's. should
+ we merge, instead??? - hm, dunno, don't think so. */
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "loc") &&
+ blend_object_array_getdata(bf, &fdata1, obj, 0,0) &&
+ blend_object_array_getdata(bf, &fdata2, obj, 0,1) &&
+ blend_object_array_getdata(bf, &fdata3, obj, 0,2)) {
+ outobj->location[0] = fdata1;
+ outobj->location[1] = fdata2;
+ outobj->location[2] = fdata3;
+ } else {
+ outobj->location[0] =
+ outobj->location[1] =
+ outobj->location[2] = 0.0f;
+ }
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "size") &&
+ blend_object_array_getdata(bf, &fdata1, obj, 0,0) &&
+ blend_object_array_getdata(bf, &fdata2, obj, 0,1) &&
+ blend_object_array_getdata(bf, &fdata3, obj, 0,2)) {
+ outobj->scaling[0] = fdata1;
+ outobj->scaling[1] = fdata2;
+ outobj->scaling[2] = fdata3;
+ } else {
+ outobj->scaling[0] =
+ outobj->scaling[1] =
+ outobj->scaling[2] = 1.0f;
+ }
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "rot") &&
+ blend_object_array_getdata(bf, &fdata1, obj, 0,0) &&
+ blend_object_array_getdata(bf, &fdata2, obj, 0,1) &&
+ blend_object_array_getdata(bf, &fdata3, obj, 0,2)) {
+ outobj->rotphr[0] = fdata1;
+ outobj->rotphr[1] = fdata2;
+ outobj->rotphr[2] = fdata3;
+ } else {
+ outobj->rotphr[0] =
+ outobj->rotphr[1] =
+ outobj->rotphr[2] = 0.0f;
+ }
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "parentinv")) {
+ int i,j;
+ for (i=0; i<4; ++i) {
+ for (j=0; j<4; ++j) {
+ blend_object_array_getdata(bf, &fdata1, obj, i,j);
+ outobj->parentimat[i][j] = fdata1;
+ }
+ }
+ }
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "transflag")) {
+ char cdata;
+ /* TODO: decode what these flags precisely mean. */
+ /* top bit is 'powertrack' */
+ if (blend_object_getdata(bf, &cdata, obj)) {
+ outobj->transflags = (unsigned char)cdata;
+ }
+ }
+
+ if (blend_object_structure_getfield(bf, &obj, *objobj, "obmat")) {
+ int i,j;
+ for (i=0; i<4; ++i) {
+ for (j=0; j<4; ++j) {
+ blend_object_array_getdata(bf, &fdata1, obj, i,j);
+ outobj->transform[i][j] = fdata1;
+ dprintf(stderr, "%0.3f ", fdata1);
+ }
+ dprintf(stderr, "\n");
+ }
+ }
+
+ /* get actual obj here */
+
+ if (! (blend_object_structure_getfield(bf, &obj, *objobj, "data") &&
+ blend_object_getdata(bf, &block, obj))) {
+ abort();
+ }
+
+ if (block == NULL) {
+ outobj->type = BOBJ_TYPE_NULL;
+ } else {
+ dataobj = blend_block_get_object(bf, block, 0);
+ if (mallocdoutblendobject) {
+ *mallocdoutblendobject = malloc(sizeof(BlendObject));
+ **mallocdoutblendobject = dataobj;
+ }
+ }
+
+
+ switch (outobj->type) {
+ case BOBJ_TYPE_MESH:
+ outobj->data.mesh = blend_alloc_mesh();
+ blend_acquire_mesh_from_obj(bf, &dataobj, outobj->data.mesh);
+ break;
+ case BOBJ_TYPE_UNKNOWN:
+ default:
+ case BOBJ_TYPE_NULL:
+ outobj->data.dummy = NULL;
+ break;
+ }
+}
+
+
+
+bTexLayer *
+blend_alloc_texlayer(void) {
+ return malloc(sizeof(bTexLayer));
+}
+
+void
+blend_init_texlayer(bTexLayer *tl) {
+ tl->filename = NULL;
+ tl->affects_mask = 0;
+ tl->blend_mode = BTEX_BLEND_NORMAL;
+ tl->coords_type = BTEX_COORDS_NONE;
+ tl->is_st_clamped = 0;
+ tl->flags = 0;
+ tl->Nflags = tl->Ntype = 0;
+ tl->xrepeat = tl->yrepeat = 1;
+}
+
+static void
+blend_free_texlayer_inner(bTexLayer *tl) {
+ if (tl->filename) {
+ free(tl->filename);
+ }
+}
+
+void
+blend_free_texlayer(bTexLayer *tl) {
+ blend_free_texlayer_inner(tl);
+ free(tl);
+}
+
+
+void /* MTex */
+blend_acquire_texlayer_from_obj(BlendFile *bf, BlendObject *tlobj,
+ bTexLayer *tl) {
+ BlendObject obj;
+ BlendBlockPointer tex_block;
+ short sdata = 12345;
+
+ blend_init_texlayer(tl);
+
+ if (!(blend_object_structure_getfield(bf, &obj, *tlobj,
+ "mapto") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ }
+ if (sdata & 0x01) {
+ tl->affects_mask |= BTEX_AFFECT_COLOUR;
+ }
+ if (sdata & 0x40) {
+ tl->affects_mask |= BTEX_AFFECT_EMIT;
+ }
+ if (sdata & 0x80) {
+ tl->affects_mask |= BTEX_AFFECT_ALPHA;
+ }
+ /* note: mapto not fully decoded. */
+
+ if (!(blend_object_structure_getfield(bf, &obj, *tlobj,
+ "texflag") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ }
+ if (sdata & 0x02) {
+ tl->affects_mask |= BTEX_AFFECT_STENCIL;
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *tlobj,
+ "texco") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ }
+ switch (sdata) {
+ case 1:
+ case 2:
+ tl->coords_type = BTEX_COORDS_REFLECT;
+ break;
+ case 16:
+ tl->coords_type = BTEX_COORDS_UV;
+ break;
+ default:
+ /* I haven't seen this happen, but it probably does... */
+ tl->coords_type = BTEX_COORDS_NONE;
+ break;
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *tlobj,
+ "blendtype") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ }
+ tl->blend_mode = sdata; /* not decoded yet :( */
+
+ if (blend_object_structure_getfield(bf, &obj, *tlobj, "tex") &&
+ blend_object_getdata(bf, &tex_block, obj) && tex_block) {
+ BlendObject tobj = blend_block_get_object(bf, tex_block, 0);
+ BlendBlockPointer im_block;
+ BlendObject obj;
+
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "extend") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ }
+ tl->is_st_clamped = !(sdata & 2 /*'repeat'*/);
+ /*fprintf(stderr, "CLAMP=%d (was %d)\n", tl->is_st_clamped, sdata);*/
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "xrepeat") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ tl->xrepeat = 1;
+ } else {
+ tl->xrepeat = sdata;
+ }
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "yrepeat") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ tl->yrepeat = 1;
+ } else {
+ tl->yrepeat = sdata;
+ }
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "flag") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ } else {
+ tl->flags = sdata;
+ }
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "imaflag") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ } else {
+ if (sdata & 0x0001) {
+ tl->flags |= BIMG_FLAG_INTERPOLATE;
+ }
+ if (sdata & 0x0004) {
+ tl->flags |= BIMG_FLAG_MIPMAP;
+ }
+ if (sdata & 0x0100) {
+ tl->flags |= BIMG_FLAG_ANTIALIAS;
+ }
+ }
+ if (!(blend_object_structure_getfield(bf, &obj, tobj, "type") &&
+ blend_object_getdata(bf, &sdata, obj))) {
+ abort();
+ } else {
+ tl->Ntype = sdata;
+ }
+ if (blend_object_structure_getfield(bf, &obj, tobj, "ima") &&
+ blend_object_getdata(bf, &im_block, obj) && im_block) {
+ BlendObject imobj = blend_block_get_object(bf, im_block, 0);
+#define BF_IMAGE_FILENAME_MAXSIZE 160
+ tl->filename = malloc(BF_IMAGE_FILENAME_MAXSIZE);
+ if (!(blend_object_structure_getfield(bf, &obj, imobj, "name") &&
+ blend_object_getstring(bf, obj,
+ tl->filename, BF_IMAGE_FILENAME_MAXSIZE))) {
+ abort();
+ }
+ }
+ } else {
+ abort();
+ }
+}
+
+
+bMaterial *
+blend_alloc_material(void) {
+ return malloc(sizeof(bMaterial));
+}
+
+void
+blend_init_material(bMaterial *mat) {
+ int i;
+ for (i=0; itex_layer[i] = NULL;
+ }
+ mat->feature_mask = 0;
+ for (i=0; i<4; ++i) {
+ mat->colour_rgba[i] = 1.0f;
+ }
+ mat->emit = 0.0f;
+}
+
+void
+blend_free_material(bMaterial *mat) {
+ int i;
+ for (i=0; itex_layer[i]) {
+ blend_free_texlayer(mat->tex_layer[i]);
+ }
+ }
+}
+
+void
+blend_acquire_material_from_obj(BlendFile *bf, BlendObject *matobj,
+ bMaterial *mat) {
+ BlendObject obj;
+ int i;
+ int32_t ldata = 123456;
+ float fdata = 123456.0;
+
+ blend_init_material(mat);
+
+ if ((blend_object_structure_getfield(bf, &obj, *matobj, "r") &&
+ blend_object_getdata(bf, &fdata, obj))) {
+ mat->colour_rgba[0] = fdata;
+ }
+ if ((blend_object_structure_getfield(bf, &obj, *matobj, "g") &&
+ blend_object_getdata(bf, &fdata, obj))) {
+ mat->colour_rgba[1] = fdata;
+ }
+ if ((blend_object_structure_getfield(bf, &obj, *matobj, "b") &&
+ blend_object_getdata(bf, &fdata, obj))) {
+ mat->colour_rgba[2] = fdata;
+ }
+ if ((blend_object_structure_getfield(bf, &obj, *matobj, "alpha") &&
+ blend_object_getdata(bf, &fdata, obj))) {
+ mat->colour_rgba[3] = fdata;
+ }
+ if ((blend_object_structure_getfield(bf, &obj, *matobj, "emit") &&
+ blend_object_getdata(bf, &fdata, obj))) {
+ mat->emit = fdata;
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *matobj, "mode") &&
+ blend_object_getdata(bf, &ldata, obj))) {
+ abort();
+ }
+ if (ldata & 0x04) {
+ mat->feature_mask |= BMAT_FEATURE_SHADELESS;
+ }
+ if (ldata & 0x08) {
+ mat->feature_mask |= BMAT_FEATURE_WIRE;
+ }
+ if (ldata & 0x10) {
+ mat->feature_mask |= BMAT_FEATURE_VCOLLIGHT;
+ }
+ if (ldata & 0x80) {
+ mat->feature_mask |= BMAT_FEATURE_VCOLPAINT;
+ }
+ if (ldata & (1024 | 512 | 256)) { /* not sure about this, it's strange. */
+ mat->feature_mask |= BMAT_FEATURE_TEXFACE;
+ }
+
+ for (i=0; itex_layer[i] = blend_alloc_texlayer();
+ /*fprintf(stderr, "GETTING LAYER AT POS %d\n", i);*/
+ blend_acquire_texlayer_from_obj(bf, &tlobj, mat->tex_layer[i]);
+ } else {
+ /*fprintf(stderr, "NOTHING FOUND AT POS %d\n", i);*/
+ }
+ }
+}
+
+void
+blend_acquire_mesh_from_obj(BlendFile *bf, BlendObject *meobj, bMesh *mesh)
+{
+ {
+ BlendObject obj;
+ BlendBlockPointer vblock, fblock, cblock, tblock,
+ dblock, matlink;
+ int i;
+ int32_t ldata = 123456;
+ float fdata1, fdata2, fdata3;
+
+ blend_init_mesh(mesh);
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "totvert") &&
+ blend_object_getdata(bf, &ldata, obj))) {
+ abort();
+ }
+ mesh->vert_count = ldata;
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "totface") &&
+ blend_object_getdata(bf, &ldata, obj))) {
+ abort();
+ }
+ mesh->face_count = ldata;
+
+ dprintf(stderr, "%d verts, %d faces...\n",
+ mesh->vert_count, mesh->face_count);
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "mface") &&
+ blend_object_getdata(bf, &fblock, obj))) {
+ abort();
+ }
+ /* null fblock is okay */
+
+ if (blend_object_structure_getfield(bf, &obj, *meobj,
+ "mat") &&
+ blend_object_getdata(bf, &matlink, obj) && matlink) {
+ /* found an indirect material link, follow it */
+ BlendObject matlinkobj = blend_block_get_object(bf, matlink, 0);
+ BlendBlockPointer matblock;
+ if (blend_object_structure_getfield(bf, &obj, matlinkobj,
+ "next") &&
+ blend_object_getdata(bf, &matblock, obj)) {
+ if (matblock) {
+ BlendObject matobj = blend_block_get_object(bf, matblock, 0);
+ mesh->material = blend_alloc_material();
+ blend_acquire_material_from_obj(bf, &matobj, mesh->material);
+ } else {
+ /* um, okay, link went nowhere, leave mesh->material NULL */
+ }
+ } else {
+ abort();
+ }
+ } else {
+ /* no material -- mesh->material will remain NULL */
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "mvert") &&
+ blend_object_getdata(bf, &vblock, obj))) {
+ abort();
+ }
+ /* null vblock is okay */
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "tface") &&
+ blend_object_getdata(bf, &tblock, obj))) {
+ abort();
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "mcol") &&
+ blend_object_getdata(bf, &cblock, obj))) {
+ abort();
+ }
+
+ if (!(blend_object_structure_getfield(bf, &obj, *meobj,
+ "dvert") &&
+ blend_object_getdata(bf, &dblock, obj))) {
+ /* sometimes there isn't a dvert block... */
+ dblock = NULL;
+ }
+
+ mesh->vert = malloc(sizeof(bVert) * mesh->vert_count);
+
+ for (i=0; ivert_count; ++i) {
+ BlendObject obj = blend_block_get_object(bf, vblock, i);
+ BlendObject aobj;
+ float fdata1, fdata2, fdata3;
+ int32_t sdata1, sdata2, sdata3;
+ char cdata;
+
+ mesh->vert[i].xyz[0] =
+ mesh->vert[i].xyz[1] =
+ mesh->vert[i].xyz[2] = -12345;
+ mesh->vert[i].cnormal[0] =
+ mesh->vert[i].cnormal[1] =
+ mesh->vert[i].cnormal[2] = -12345;
+ mesh->vert[i].mat = -1;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "co") &&
+ blend_object_array_getdata(bf, &fdata1, aobj, 0,0) &&
+ blend_object_array_getdata(bf, &fdata2, aobj, 0,1) &&
+ blend_object_array_getdata(bf, &fdata3, aobj, 0,2))) {
+ abort();
+ }
+ mesh->vert[i].xyz[0] = fdata1;
+ mesh->vert[i].xyz[1] = fdata2;
+ mesh->vert[i].xyz[2] = fdata3;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "no") &&
+ blend_object_array_getdata(bf, &sdata1, aobj, 0,0) &&
+ blend_object_array_getdata(bf, &sdata2, aobj, 0,1) &&
+ blend_object_array_getdata(bf, &sdata3, aobj, 0,2))) {
+ abort();
+ }
+ mesh->vert[i].cnormal[0] = sdata1;
+ mesh->vert[i].cnormal[1] = sdata2;
+ mesh->vert[i].cnormal[2] = sdata3;
+ /*fprintf(stderr, "%f ", LEN3(mesh->vert[i].normal[0],
+ mesh->vert[i].normal[1],
+ mesh->vert[i].normal[2]));*/
+ if (sdata1 != 0 || sdata2 != 0 || sdata3 != 0) {
+ NORMALIZE3(mesh->vert[i].cnormal[0],
+ mesh->vert[i].cnormal[1],
+ mesh->vert[i].cnormal[2]);
+ }
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "mat_nr") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->vert[i].mat = cdata;
+ }
+
+ mesh->face = malloc(sizeof(bFace) * mesh->face_count);
+
+ for (i=0; iface_count; ++i) {
+ int j,k;
+ BlendObject obj = blend_block_get_object(bf, fblock, i);
+ BlendObject aobj;
+ char cdata;
+
+ mesh->face[i].v[0] = mesh->face[i].v[1] =
+ mesh->face[i].v[2] = mesh->face[i].v[3] = -1;
+ mesh->face[i].mat = -1;
+ mesh->face[i].flags = 0;
+ for (j=0; j<4; ++j) {
+ for (k=0; k<3; ++k) {
+ mesh->face[i].rgba[j][k] = 1.0;
+ }
+ mesh->face[i].rgba[j][3] = 1.0f;
+ mesh->face[i].uv[j][0] = 0.0f;
+ mesh->face[i].uv[j][1] = 0.0f;
+ mesh->face[i].image_id = NULL;
+ }
+
+ if (blend_object_structure_getfield(bf, &aobj, obj, "v1")) {
+ if (0!=strcmp(bf->types[aobj.type].name,"int") &&
+ 0!=strcmp(bf->types[aobj.type].name,"ushort")) {
+ dprintf(stderr, "Expected vertex-index type to be 'ushort' or 'int', got '%s'\n", bf->types[aobj.type].name);
+ abort();
+ }
+
+ if (0==strcmp(bf->types[aobj.type].name,"int")) {
+ /* index type is a 32bit int, generated by newish Blenders */
+ int32_t idata;
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v1") &&
+ blend_object_getdata(bf, &idata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[0] = idata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v2") &&
+ blend_object_getdata(bf, &idata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[1] = idata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v3") &&
+ blend_object_getdata(bf, &idata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[2] = idata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v4") &&
+ blend_object_getdata(bf, &idata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[3] = idata;
+ } else {
+ /* index type is a 16bit ushort, generated by old Blenders */
+ uint16_t usdata;
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v1") &&
+ blend_object_getdata(bf, &usdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[0] = usdata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v2") &&
+ blend_object_getdata(bf, &usdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[1] = usdata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v3") &&
+ blend_object_getdata(bf, &usdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[2] = usdata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "v4") &&
+ blend_object_getdata(bf, &usdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].v[3] = usdata;
+ }
+ } else {
+ abort();
+ }
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "mat_nr") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].mat = cdata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "flag") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].flags = cdata;
+ }
+
+ if (cblock) {
+ /* we have vertex colours */
+ for (i=0; iface_count; ++i) {
+ int j;
+ unsigned char cdata;
+ BlendObject aobj;
+ for (j=0; j<4; ++j) {
+ BlendObject obj = blend_block_get_object(bf, cblock, i*4+j);
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "b") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].rgba[j][0] = cdata / 255.0f;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "g") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].rgba[j][1] = cdata / 255.0f;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "r") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].rgba[j][2] = cdata / 255.0f;
+
+ /* alpha seems to be nonsense :( */
+ /*
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "a") &&
+ blend_object_getdata(bf, &cdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].rgba[j][3] = cdata / 255.0f;
+ */
+ mesh->face[i].rgba[j][3] = 1.0f;
+ }
+ }
+ } else {
+ /* !cblock (no vertex colours) */
+ for (i=0; iface_count; ++i) {
+ int j;
+ for (j=0; j<4; ++j) {
+ mesh->face[i].rgba[j][0] = 1.0f;
+ mesh->face[i].rgba[j][1] = 1.0f;
+ mesh->face[i].rgba[j][2] = 1.0f;
+ mesh->face[i].rgba[j][3] = 1.0f;
+ }
+ }
+ }
+
+ if (tblock) {
+ /* we have tex co-ords */
+ for (i=0; iface_count; ++i) {
+ int j,k;
+ void *pdata;
+ BlendObject aobj;
+ BlendObject obj = blend_block_get_object(bf, tblock, i);
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "tpage") &&
+ blend_object_getdata(bf, &pdata, aobj))) {
+ abort();
+ }
+ mesh->face[i].image_id = pdata;
+
+ for (j=0; j<4; ++j) {
+ uint32_t uldata;
+ for (k=0; k<2; ++k) {
+ float fdata;
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "uv") &&
+ blend_object_array_getdata(bf, &fdata, aobj, j,k))) {
+ abort();
+ }
+ mesh->face[i].uv[j][k] = fdata;
+ }
+ mesh->face[i].uv[j][1] = 1.0f - mesh->face[i].uv[j][1];
+ /* texture face colour... not sure how this conceptually
+ differs from the face vertex colours, but it does. */
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "col") &&
+ blend_object_array_getdata(bf, &uldata, aobj, 0,j))) {
+ abort();
+ }
+ /* in its usual inconsistant style, blender packs this
+ RGBA value into the bytes of an unsigned long... */
+ mesh->face[i].rgba2[j][0] = ((uldata >> 24) & 0xFF) / 255.0f;
+ mesh->face[i].rgba2[j][1] = ((uldata >> 16) & 0xFF) / 255.0f;
+ mesh->face[i].rgba2[j][2] = ((uldata >> 8) & 0xFF) / 255.0f;
+ mesh->face[i].rgba2[j][3] = ((uldata >> 0) & 0xFF) / 255.0f;
+ }
+ /*mesh->face[i].uv[0][0]=0; mesh->face[i].uv[0][1]=0;
+ mesh->face[i].uv[1][0]=1; mesh->face[i].uv[1][1]=0;
+ mesh->face[i].uv[2][0]=1; mesh->face[i].uv[2][1]=1;
+ mesh->face[i].uv[3][0]=0; mesh->face[i].uv[3][1]=1;*/
+
+ }
+ } else {
+ /* !tblock (no texture co-ords, no face tex-colours) */
+ for (i=0; iface_count; ++i) {
+ int j;
+ for (j=0; j<4; ++j) {
+ mesh->face[i].rgba2[j][0] = 1.0f;
+ mesh->face[i].rgba2[j][1] = 1.0f;
+ mesh->face[i].rgba2[j][2] = 1.0f;
+ mesh->face[i].rgba2[j][3] = 1.0f;
+ }
+ }
+ }
+
+ if (dblock) {
+ /* we have vertex deformation weights */
+ for (i=0; ivert_count; ++i) {
+ int j;
+ int32_t ldata;
+ float fdata;
+ BlendBlockPointer pdata;
+ BlendObject aobj;
+ BlendObject obj = blend_block_get_object(bf, dblock, i);
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "totweight") &&
+ blend_object_getdata(bf, &ldata, aobj))) {
+ abort();
+ }
+ mesh->vert[i].deform_weights_count = ldata;
+ mesh->vert[i].deform_weights = malloc(ldata*sizeof(bDeformWeight));
+
+ if (!(blend_object_structure_getfield(bf, &aobj, obj, "dw") &&
+ blend_object_getdata(bf, &pdata, aobj))) {
+ abort();
+ }
+
+ for (j=0; jvert[i].deform_weights_count; ++j) {
+ BlendObject dwobj = blend_block_get_object(bf, pdata, j);
+
+ if (!(blend_object_structure_getfield(bf, &aobj, dwobj, "def_nr")
+ && blend_object_getdata(bf, &ldata, aobj))) {
+ abort();
+ }
+ mesh->vert[i].deform_weights[j].bone_id = ldata;
+
+ if (!(blend_object_structure_getfield(bf, &aobj, dwobj, "weight")
+ && blend_object_getdata(bf, &fdata, aobj))) {
+ abort();
+ }
+ mesh->vert[i].deform_weights[j].weight = fdata;
+ }
+ }
+ } else {
+ /* !dblock (no vertex deformation weights) */
+ for (i=0; ivert_count; ++i) {
+ mesh->vert[i].deform_weights = NULL;
+ mesh->vert[i].deform_weights_count = 0;
+ }
+ }
+ }
+}
+
+
+
+void
+blend_acquire_mesh(const char *fname, const char *want_name, bMesh *mesh)
+{
+ BlendFile* bf;
+ MY_FILETYPE *fp;
+
+ fp = MY_OPEN_FOR_READ(fname);
+
+ if (!fp) {
+ dprintf(stderr, "couldn't open file %s.\n", fname);
+ abort();
+ }
+
+ bf = blend_read(fp);
+ {
+ BlendObject meobj;
+ if (!blend_object_get_by_IDname(bf, &meobj, want_name)) {
+ dprintf(stderr, "couldn't find %s.\n", want_name);
+ abort();
+ }
+ blend_dump_blocks(bf);
+ blend_acquire_mesh_from_obj(bf, &meobj, mesh);
+ }
+ blend_free(bf);
+
+ MY_CLOSE(fp);
+}
+
+
+/* apply pitch, head, roll */
+static void bRotPHR(float xyz[3], const float rot[3]) {
+ float rx,ry,rz;
+ float ix,iy,iz;
+ float cosang, sinang;
+
+ ix = xyz[0]; iy = xyz[1]; iz = xyz[2];
+
+ cosang = cos(rot[0]);
+ sinang = sin(rot[0]);
+ /* pitch */
+ rx = ix;
+ ry = iy * cosang - iz * sinang;
+ rz = iy * sinang + iz * cosang;
+
+ ix = rx; iy = ry; iz = rz;
+
+ cosang = cos(rot[1]);
+ sinang = sin(rot[1]);
+ /* head */
+ rx = ix * cosang + iz * sinang;
+ ry = iy;
+ rz = -ix * sinang + iz * cosang;
+
+ ix = rx; iy = ry; iz = rz;
+
+ cosang = cos(rot[2]);
+ sinang = sin(rot[2]);
+ /* roll */
+ rx = ix * cosang - iy * sinang;
+ ry = ix * sinang + iy * cosang;
+ rz = iz;
+
+ xyz[0] = rx; xyz[1] = ry; xyz[2] = rz;
+}
+
+
+void
+blend_transform_mesh_from_obj(bMesh *mesh, bObj *obj) {
+ int i;
+ for (i=0; ivert_count; ++i) {
+ /* this one looks good. */
+ bMatMultVec(mesh->vert[i].xyz, obj->transform);
+ bRotPHR(mesh->vert[i].cnormal, obj->rotphr);
+ }
+}
+
diff --git a/Extras/readblend/readblend.h b/Extras/readblend/readblend.h
new file mode 100644
index 000000000..49390cbfe
--- /dev/null
+++ b/Extras/readblend/readblend.h
@@ -0,0 +1,459 @@
+/***************************************************************
+ * readblend.h --
+ * ReadBlend, a data extraction API for Blender's .blend files
+ *
+ * (c) 2003 Adam D. Moss
+ *
+ */
+
+/* VERSION HISTORY
+ * 2003-04-05 : v1.0
+ * 2008-10-05 : v1.1-beta
+ */
+
+/* Blender files are 'curiously' structured and this is mirrored in
+ * the low-level data access API (the section labelled 'Low-level
+ * querying functions').
+ *
+ * The mid-level data access API (the section labelled 'Mid-level
+ * querying functions') tries to provide some handy utilities to
+ * ease your data-extraction pain, and finally the (partially written)
+ * high-level data access API incorporates semantic knowledge of Blender's
+ * useful high-level types such as Meshes and Materials to shield you
+ * from the full horror.
+ */
+#ifndef _READBLEND_H
+#define _READBLEND_H
+
+#include
+//#include
+#include
+#include //size_t for MSVC 6.0
+
+#include
+#include
+
+#include "abs-file.h"
+
+/* TODO: Doxygen me. */
+
+
+/* don't worry yourself about this. */
+#ifndef BlendFile
+#define BlendFile void
+#endif
+
+
+
+
+/**************************************/
+/* .blend handle load/free functions */
+/*************************************/
+
+
+/* Pass in an already-opened file handle; this function will then
+ examine the file to check that it is a Blender file and scan the
+ entire file to extract all vital information (quite expensive)
+ before any query operations can be performed. The resulting
+ .blend data handle is returned.
+*/
+
+BlendFile* blend_read(MY_FILETYPE* file);
+
+
+/* Free all of the given BlendFile data (note: obviously, do not attempt
+ to extract data from a BlendFile which has been freed!) */
+
+void blend_free(BlendFile* blend_file);
+
+
+/********************************/
+/* Public data querying types */
+/********************************/
+
+
+/* heed this enum well. */
+typedef enum {
+ /* you're unlikely to encounter these in the wild. */
+ BLEND_OBJ_NULL,
+ BLEND_OBJ_OPAQUE,
+
+ /* these object types are fetchable as-is */
+ BLEND_OBJ_UCHAR8,
+ BLEND_OBJ_CHAR8,
+ BLEND_OBJ_USHORT16,
+ BLEND_OBJ_SHORT16,
+ BLEND_OBJ_ULONG32,
+ BLEND_OBJ_LONG32,
+ BLEND_OBJ_FLOAT,
+ BLEND_OBJ_DOUBLE,
+ BLEND_OBJ_POINTER,
+
+ /* you'll need to extract the fields from these individually */
+ BLEND_OBJ_STRUCT
+} BlendObjType;
+
+typedef void* BlendBlockPointer;
+
+/* note: treat this as an opaque type and you'll live longer. */
+typedef struct {
+ long type; long name; BlendBlockPointer block;
+ long entry_index; long field_index;
+} BlendObject;
+
+/* The callback type for passing to blend_foreach_block() (the callback
+ should return zero if it doesn't want to see any more blocks.) */
+#define BLENDBLOCKCALLBACK_RETURN int
+#define BLENDBLOCKCALLBACK_ARGS BlendBlockPointer block, \
+ BlendFile* blend_file, \
+ void* userdata
+typedef BLENDBLOCKCALLBACK_RETURN(BlendBlockCallback)
+ (BLENDBLOCKCALLBACK_ARGS);
+
+
+/********************************/
+/* File info dumping functions */
+/********************************/
+
+/* these functions simply print dumps of information about the given
+ BlendFile. These are vital for familiarising yourself with the
+ structure of Blender's various scene objects if you're using the
+ low/mid-level APIs to construct queries to extract information about
+ specific types of objects that haven't been conveniently abstracted by
+ the high-level API yet. (That is, most of them right now, particularly
+ the more obscure or deprecated ones.) */
+
+/* print out the size, field-types and field-names of all of the varieties
+ of types (atomic and struct) represented within this BlendFile. The
+ sizes are the in-file representations; the in-memory representations
+ are potentially subject to additional padding and conversions.
+*/
+void blend_dump_typedefs(BlendFile* bf);
+
+/* For every top-level block of data in the file, print the block's
+ tag, its in-memory address, its type, and the number of entries of that
+ type. (Every pointer in a structure should point to one of these
+ in-memory block addresses because we convert from in-file pointers to
+ in-memory pointers automagically; a few in-file pointers within structures
+ do actually point somewhere into the middle of a block or nowhere, which we
+ don't currently support and silently convert to NULL.) Also, any structs
+ at the top-level with an id->name have that name printed out. */
+void blend_dump_blocks(BlendFile* bf);
+
+
+/********************************/
+/* Low-level querying functions */
+/********************************/
+
+
+/* Calls the user-supplied callback function for every top-level
+ data block in the .blend file. You can use this to count blocks
+ (which is pretty pointless) or search for a particular block you're
+ interested in. */
+void blend_foreach_block(BlendFile* blend_file,
+ BlendBlockCallback* func,
+ void* userdata);
+
+/* Returns the tag-name ('IM', 'DATA', 'ME', etc) for the given
+ top-level data block. */
+const char* blend_block_get_tagname(BlendFile* blend_file,
+ BlendBlockPointer block);
+
+/* Returns the type of the given top-level data block in raw
+ string form ('uchar', 'float', 'MFace', 'Mesh', etc). */
+const char* blend_block_get_typename(BlendFile* blend_file,
+ BlendBlockPointer block);
+
+/* Translate a pointer from the file's raw data into a BlendBlockPointer
+ that you can query via the API. You will usually NOT need to ever
+ use this unless you're manually extracting pointers from opaque raw data
+ types. Returns NULL on failure or if the input pointer is really NULL. */
+BlendBlockPointer blend_block_from_blendpointer(BlendFile *blend_file,
+ uint32_t blendpointer);
+
+/* Returns the number of entries there are in the given top-level
+ data block (a top-level data block is like an array of entries of a specific
+ Blender type, this type usually being one of Blender's structs). */
+int blend_block_get_entry_count(BlendFile* blend_file,
+ BlendBlockPointer block);
+
+/* This gets an object handle on the Nth piece of data (in the range
+ 0..TOTAL-1, where TOTAL is the figure returned by
+ blend_block_get_entry_count() ) in the given top-level block. */
+BlendObject blend_block_get_object(BlendFile* blend_file,
+ BlendBlockPointer block,
+ int entry_index);
+
+/* Returns a BlendObjType enum handy for checking that the general type
+ of the object you have a handle on is what you're expecting. */
+BlendObjType blend_object_type(BlendFile* blend_file,
+ BlendObject obj);
+
+/* Given a BlendObject of type BLEND_OBJ_STRUCT in 'obj', fill in the
+ BlendObject pointed to by 'result' with a handle to the named
+ field of that structure (note that the resulting data object might
+ itself be a structure!). 0 is returned on failure (i.e. this structure
+ does not have a field of the requested name, or you supplied an
+ object which is not a structure). */
+int blend_object_structure_getfield(BlendFile* blend_file,
+ BlendObject *result,
+ BlendObject obj,
+ const char* field_name);
+
+/* Gets the size (in elements) of an object which is a 1D or 2D array.
+ A non-array is equivalent to an array of size 1x1. A 1D array will
+ always have dim1 == 1. */
+void blend_object_array_getdims(BlendFile* blend_file,
+ BlendObject obj,
+ int* dim1, int* dim2);
+
+/* This fetches a piece of data from the .blend file in a format
+ suitable for your architecture (i.e. ints will be of proper size
+ and endianness, pointers are transformed to valid BlendBlockPointers
+ or NULL, etc) into the address pointed to by dest. It's up to you to
+ check that the data you're asking for will fit into the type you're
+ trying to put it in (use blend_object_type() and see the BlendObjType
+ enum to check that the object's type is the one you're expecting.)
+
+ Composite structures (BLEND_OBJ_STRUCT) are not handled atomically;
+ use blend_object_structure_getfield() to extract each named field from
+ a structure individually.
+
+ 1 is returned on success. Failure occurs when you try to extract
+ data from a structure (see blend_object_structure_getfield()) or
+ an array (use blend_object_array_getdata()).
+*/
+int blend_object_getdata(BlendFile* blend_file,
+ void* dest, BlendObject obj);
+
+/* This operates like blend_object_getdata() except that it is happy
+ to copy an item of data out of an array. The array is always treated
+ as two-dimensional (data[dimension1][dimension2]); if you're accessing
+ a one-dimensional array then simply specify dim_index_1 as 0.
+ (Indices are in the range 0..DIMSIZE-1.)
+
+ Similarly, plain non-array data can be fetched by specifying
+ dim_index_1 == dim_index_2 == 0. This makes
+ blend_object_array_getdata(bf, dest, obj, 0, 0) equivalent to
+ blend_object_getdata(bf, dest, obj) except that it is happy to
+ be called with an array, from which it will extract the first
+ element.
+
+ Like blend_object_getdata(), it will not fetch a structure (from
+ an array of structures; fortunately I've not seen a .blend file
+ featuring an array of structures so we'll bridge that API gap
+ when we come to it).
+ */
+int blend_object_array_getdata(BlendFile* blend_file,
+ void* dest, BlendObject obj,
+ int dim_index_1, int dim_index_2);
+
+/********************************/
+/* Mid-level querying functions */
+/********************************/
+
+typedef unsigned long BlendLayerMask;
+
+/* extract a string from a char-array or uchar-array object, up to
+ max_chars in length including the \0 terminator. If the object is
+ a two-dimensional array then the first string is extracted. Returns
+ 0 on failure.
+*/
+int blend_object_getstring(BlendFile* blend_file,
+ BlendObject obj,
+ char *dest, int max_chars);
+
+/* Searches the file for a top-level object with the given ID name.
+ Typical ID names are 'OBCamera', 'MECircle.003', 'MAplastic', 'TEgrass' etc.
+ The first two characters of an ID name keep the namespaces separate,
+ so that a material (MA) with the name 'metal' is distinguishable from
+ a texture (TE) with the name 'metal'. That's a foible of Blender itself.
+
+ blend_object_get_by_IDname() returns 0 on failure. It only returns one
+ object of the given IDname; there should indeed be only one, as Blender
+ enforces this uniqueness.
+ */
+int blend_object_get_by_IDname(BlendFile* blend_file,
+ BlendObject *result,
+ const char* IDname);
+/* get string from [obj].ID.name -- caller allocs/frees */
+int blend_object_get_IDname(BlendFile* blend_file,
+ BlendObject obj,
+ char *dest, int max_chars);
+
+/* returns !0 if the blender Object whose handle is objobj is a top-level
+ object -- that is, it's not a child of another Object. */
+int blend_obj_is_rootobject(BlendFile *bf, BlendObject *objobj);
+/* return the layers (bitmask) that an Object lives on. */
+BlendLayerMask blend_obj_get_layermask(BlendFile *bf, BlendObject *objobj);
+
+/* These functions can be a bit slow -- each one requires a linear scan
+ of the file's blocks. But they're handy... */
+/* Return number of children that this Object has. Can be 0. */
+int blend_obj_get_childcount(BlendFile *bf, BlendObject *objobj);
+/* Gets child number childnum of the Object. childnum=0 returns
+ the first, childnum=1 returns the second, etc. */
+BlendBlockPointer blend_obj_get_child(BlendFile *bf, BlendObject *objobj,
+ int childnum);
+
+/*********************************/
+/* High-level querying functions */
+/*********************************/
+
+typedef struct {
+ int bone_id;
+ float weight;
+} bDeformWeight;
+
+typedef struct {
+ float xyz[3];
+ float cnormal[3]; /* cosmetic normal */
+ int mat;
+ bDeformWeight* deform_weights;
+ int deform_weights_count;
+} bVert;
+#define BVERT_HAS_CNORMAL(BV) ((BV)->cnormal[0] != 0.0f || \
+ (BV)->cnormal[1] != 0.0f || \
+ (BV)->cnormal[2] != 0.0f)
+
+#define BFACE_FLAG_SMOOTH 0x01
+typedef struct {
+ int v[4];
+ float rgba[4][4]; /* vertex colours */
+ float rgba2[4][4]; /* texture face colours (errrr...?) */
+ float uv[4][2];
+ BlendBlockPointer image_id;
+ int mat;
+ unsigned char flags;
+} bFace;
+#define BFACE_HAS_TEXTURE(BF) ((BF)->image_id != NULL)
+#define BFACE_IS_QUAD(BF) ((BF)->v[3] != 0)
+#define BFACE_IS_TRI(BF) ((BF)->v[2] != 0 && (BF)->v[3] == 0)
+#define BFACE_IS_LINE(BF) ((BF)->v[1] != 0 && (BF)->v[2] == 0)
+
+typedef enum {
+ BTEX_AFFECT_COLOUR = 0x01,
+ BTEX_AFFECT_ALPHA = 0x02,
+ BTEX_AFFECT_EMIT = 0x04,
+ BTEX_AFFECT_NORMAL = 0x08,
+ BTEX_AFFECT_NEGNORM = 0x10,
+ BTEX_AFFECT_STENCIL = 0x20 /* not really an 'affect' in blender, but it is */
+} bTexLayerAffects;
+
+typedef enum {
+ BTEX_BLEND_NORMAL, /* 'mix' */
+ BTEX_BLEND_MULTIPLY, /* 'mul' -- modulate. */
+ BTEX_BLEND_ADD,
+ BTEX_BLEND_SUB
+} bTexLayerBlendmode;
+
+typedef enum {
+ BTEX_COORDS_NONE,
+ BTEX_COORDS_UV,
+ BTEX_COORDS_REFLECT
+} bTexLayerCoordsType;
+
+typedef enum {
+ BIMG_FLAG_INTERPOLATE = 0x01, /* upsample */
+ BIMG_FLAG_ANTIALIAS = 0x02, /* downsample */
+ BIMG_FLAG_MIPMAP = 0x04 /* use mipmaps */
+} bTexImageFlags;
+
+typedef struct {
+ char *filename; /* image file name -- absolute path! -- NULL if not image. */
+ bTexLayerAffects affects_mask;
+ bTexLayerBlendmode blend_mode;
+ bTexLayerCoordsType coords_type;
+ /* Tex substruct */
+ int is_st_clamped; /* otherwise, repeat... (GL texwrap mode.) */
+ bTexImageFlags flags;
+ short Nflags, Ntype; /* not decoded yet */
+ short xrepeat, yrepeat; /* amounts to scale texcoords by, really */
+} bTexLayer;
+
+typedef enum {
+ BMAT_FEATURE_VCOLLIGHT = 0x01,
+ BMAT_FEATURE_VCOLPAINT = 0x02,
+ BMAT_FEATURE_TEXFACE = 0x04,
+ BMAT_FEATURE_SHADELESS = 0x08,
+ BMAT_FEATURE_WIRE = 0x10 /* wireframe rendering */
+} bMatFeatureMask;
+
+#define BLENDER_MAX_TEX_LAYERS 8
+typedef struct {
+ bTexLayer *tex_layer[BLENDER_MAX_TEX_LAYERS];
+ bMatFeatureMask feature_mask;
+ float colour_rgba[4]; /* main material colour */
+ float emit; /* emissive strength, 0..1 */
+} bMaterial;
+
+typedef float bMatrix[4][4];
+
+typedef struct {
+ bVert *vert;
+ int vert_count;
+ bFace *face;
+ int face_count;
+ bMaterial *material; /* or NULL if none */
+} bMesh;
+
+typedef enum {
+ BOBJ_TYPE_UNKNOWN,
+ BOBJ_TYPE_NULL, /* indicates object has no data associated with it! */
+ BOBJ_TYPE_MESH
+} bObjType;
+
+typedef enum {
+ BAQ_INCLUDE_CHILDREN = 0x0001
+} bAcquireFlags;
+
+typedef struct {
+ bObjType type;
+ char *name;
+
+ bMatrix transform; /* local transformation matrix */
+ bMatrix parentimat; /* parent's inverse transform matrix */
+ float scaling[3]; /* scaling component of transform */
+ float rotphr[3]; /* pitch/head/roll rotation component of transform (use for normals) */
+ float location[3]; /* location component of transform */
+ unsigned char transflags; /* NOT DECODED YET, RAW BYTE */
+
+ union {
+ void *dummy;
+ bMesh *mesh;
+ } data;
+} bObj;
+
+
+bObj *blend_alloc_obj(void);
+void blend_init_obj(bObj *obj);
+void blend_free_obj(bObj *obj);
+void blend_free_obj_inner(bObj *obj);
+void blend_acquire_obj_from_obj(BlendFile *bf, BlendObject *objobj,
+ bObj *outobj,
+ /* malloc'd BlendObject will be written here if pointer if non-NULL: */ BlendObject **outblendobject);
+
+bMesh *blend_alloc_mesh(void);
+void blend_init_mesh(bMesh *mesh);
+void blend_free_mesh_inner(bMesh *mesh);
+void blend_free_mesh(bMesh *mesh);
+void blend_acquire_mesh(const char *fname, const char *want_name, bMesh *mesh);
+void blend_acquire_mesh_from_obj(BlendFile *bf,
+ BlendObject *meobj,
+ bMesh *mesh);
+/* apply transformation found in 'obj' to 'mesh' */
+void blend_transform_mesh_from_obj(bMesh *mesh, bObj *obj);
+
+bMaterial *blend_alloc_material(void);
+void blend_init_material(bMaterial *mat);
+void blend_free_material(bMaterial *mat);
+void blend_acquire_material_from_obj(BlendFile *bf, BlendObject *meobj,
+ bMaterial *mat);
+
+bTexLayer *blend_alloc_texlayer(void);
+void blend_init_texlayer(bTexLayer *tl);
+void blend_free_texlayer(bTexLayer *tl);
+void blend_acquire_texlayer_from_obj(BlendFile *bf, BlendObject *tlobj,
+ bTexLayer *tl);
+
+#endif
diff --git a/Extras/readblend/testblend.c b/Extras/readblend/testblend.c
new file mode 100644
index 000000000..488294274
--- /dev/null
+++ b/Extras/readblend/testblend.c
@@ -0,0 +1,110 @@
+#include
+#include
+
+#include "abs-file.h"
+
+#include "readblend.h"
+
+void crawl(BlendFile* blend_file,
+ BlendObject obj)
+{
+ BlendObject data_obj;
+
+ if (blend_object_structure_getfield(blend_file, &data_obj, obj, "totvert")) {
+ if (blend_object_type(blend_file, data_obj) == BLEND_OBJ_LONG32) {
+ long data1 = 123456;
+ if (blend_object_getdata(blend_file, &data1, data_obj)) {
+ fprintf(stderr, "TOTVERT=(%ld) ", data1);
+ } else {
+ fprintf(stderr, "FETCH ERROR\n");
+ }
+ }
+ }
+
+ if (blend_object_structure_getfield(blend_file, &data_obj, obj, "co")) {
+ if (blend_object_type(blend_file, data_obj) == BLEND_OBJ_FLOAT) {
+ float data1 = 123456.7;
+ float data2 = 123456.8;
+ float data3 = 123456.9;
+ if (blend_object_array_getdata(blend_file, &data1, data_obj, 0,0) &&
+ blend_object_array_getdata(blend_file, &data2, data_obj, 0,1) &&
+ blend_object_array_getdata(blend_file, &data3, data_obj, 0,2)) {
+ fprintf(stderr, "CO=(%f,%f,%f) ", data1, data2, data3);
+ } else {
+ fprintf(stderr, "FETCH ERROR\n");
+ }
+ }
+ }
+
+}
+
+static BLENDBLOCKCALLBACK_RETURN
+blockiter(BLENDBLOCKCALLBACK_ARGS) {
+ int i;
+ int entry_count = blend_block_get_entry_count(blend_file, block);
+#if 0
+ const char* tagname = blend_block_get_tagname(blend_file, block);
+ const char* typename = blend_block_get_typename(blend_file, block);
+ fprintf(stderr, "block: tag=%s type=%s entries=%d ",
+ tagname, typename, entry_count);
+#endif
+ for (i=0; i