From 2998d2a8f3ac1a74ab021341cdf4fcc029ea48c0 Mon Sep 17 00:00:00 2001 From: "erwin.coumans" Date: Mon, 6 Oct 2008 03:53:51 +0000 Subject: [PATCH] Added ReadBlend, a data extraction API for Blender's .blend files This can come in handy, to author physics data for Bullet from Blender, and directly get the physics data straight from the .blend file Thanks a lot to Adam D. Moss, to digg up this code and make it available under the MIT license It was mentioned several years ago in this thread: http://archives.seul.org/linuxgames/Apr-2005/msg00002.html --- Extras/readblend/LICENSE.txt | 24 + Extras/readblend/Makefile | 36 + Extras/readblend/README.blendstruct | 26 + Extras/readblend/ReadBlend.sln | 20 + Extras/readblend/ReadBlend.vcproj | 213 +++ Extras/readblend/abs-file.h | 226 +++ Extras/readblend/readblend.c | 2397 +++++++++++++++++++++++++++ Extras/readblend/readblend.h | 459 +++++ Extras/readblend/testblend.c | 110 ++ 9 files changed, 3511 insertions(+) create mode 100644 Extras/readblend/LICENSE.txt create mode 100644 Extras/readblend/Makefile create mode 100644 Extras/readblend/README.blendstruct create mode 100644 Extras/readblend/ReadBlend.sln create mode 100644 Extras/readblend/ReadBlend.vcproj create mode 100644 Extras/readblend/abs-file.h create mode 100644 Extras/readblend/readblend.c create mode 100644 Extras/readblend/readblend.h create mode 100644 Extras/readblend/testblend.c 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