# HG changeset patch # User John Tsiombikas # Date 1391277499 -7200 # Node ID b2f14e5352531a891dbb5aabcc6f7e1fca686587 initial commit diff -r 000000000000 -r b2f14e535253 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,3 @@ +\.o$ +\.d$ +\.swp$ diff -r 000000000000 -r b2f14e535253 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,60 @@ +csrc = $(wildcard src/*.c) \ + $(wildcard libs/vmath/*.c) \ + $(wildcard libs/anim/*.c) \ + $(wildcard libs/imago/*.c) \ + $(wildcard libs/drawtext/*.c) \ + $(wildcard libs/ogg/*.c) \ + $(wildcard libs/vorbis/*.c) \ + $(wildcard libs/psys/*.c) \ + $(wildcard libs/kissfft/*.c) + +ccsrc = $(wildcard src/*.cc) \ + $(wildcard src/audio/*.cc) \ + $(wildcard libs/vmath/*.cc) + +obj = $(ccsrc:.cc=.o) $(csrc:.c=.o) +dep = $(obj:.o=.d) +bin = candyshoot + +USE_ASSIMP = true + +ifeq ($(USE_ASSIMP), true) + libs_cflags_ai = `pkg-config --cflags assimp` -DUSE_ASSIMP + libs_ldflags_ai = `pkg-config --libs assimp` +endif + +libs_cflags = -Isrc -Ilibs -Ilibs/imago -Ilibs/drawtext -Ilibs/vorbis -Ilibs/kissfft \ + `pkg-config --cflags freetype2` $(libs_cflags_ai) +libs_ldflags = -lz -lpng -ljpeg `pkg-config --libs freetype2` \ + $(libs_ldflags_ai) + + +CC = clang +CXX = clang++ +CFLAGS = -pedantic -Wall -g $(libs_cflags) +CXXFLAGS = $(CFLAGS) +LDFLAGS = $(libgl) $(libal) -lpthread -lm -ldl $(libs_ldflags) + +ifeq ($(shell uname -s), Darwin) + libgl = -framework OpenGL -framework GLUT -lGLEW + libal = -framework OpenAL +else + libgl = -lGL -lGLU -lglut -lGLEW + libal = -lopenal +endif + + +$(bin): $(obj) + $(CXX) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +%.d: %.cc + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff -r 000000000000 -r b2f14e535253 libs/anim/anim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/anim.c Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,532 @@ +#include +#include +#include +#include "anim.h" +#include "dynarr.h" + +#define ROT_USE_SLERP + +static void invalidate_cache(struct anm_node *node); + +int anm_init_node(struct anm_node *node) +{ + int i, j; + static const float defaults[] = { + 0.0f, 0.0f, 0.0f, /* default position */ + 0.0f, 0.0f, 0.0f, 1.0f, /* default rotation quat */ + 1.0f, 1.0f, 1.0f /* default scale factor */ + }; + + memset(node, 0, sizeof *node); + + /* initialize thread-local matrix cache */ + pthread_key_create(&node->cache_key, 0); + pthread_mutex_init(&node->cache_list_lock, 0); + + for(i=0; itracks + i) == -1) { + for(j=0; jtracks + i); + } + } + anm_set_track_default(node->tracks + i, defaults[i]); + } + return 0; +} + +void anm_destroy_node(struct anm_node *node) +{ + int i; + free(node->name); + + for(i=0; itracks + i); + } + + /* destroy thread-specific cache */ + pthread_key_delete(node->cache_key); + + while(node->cache_list) { + struct mat_cache *tmp = node->cache_list; + node->cache_list = tmp->next; + free(tmp); + } +} + +void anm_destroy_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_destroy_node_tree(tmp); + } + anm_destroy_node(tree); +} + +struct anm_node *anm_create_node(void) +{ + struct anm_node *n; + + if((n = malloc(sizeof *n))) { + if(anm_init_node(n) == -1) { + free(n); + return 0; + } + } + return n; +} + +void anm_free_node(struct anm_node *node) +{ + anm_destroy_node(node); + free(node); +} + +void anm_free_node_tree(struct anm_node *tree) +{ + struct anm_node *c, *tmp; + + if(!tree) return; + + c = tree->child; + while(c) { + tmp = c; + c = c->next; + + anm_free_node_tree(tmp); + } + + anm_free_node(tree); +} + +int anm_set_node_name(struct anm_node *node, const char *name) +{ + char *str; + + if(!(str = malloc(strlen(name) + 1))) { + return -1; + } + strcpy(str, name); + free(node->name); + node->name = str; + return 0; +} + +const char *anm_get_node_name(struct anm_node *node) +{ + return node->name ? node->name : ""; +} + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in) +{ + int i; + + for(i=0; itracks + i, in); + } + invalidate_cache(node); +} + +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex) +{ + int i; + + for(i=0; itracks + i, ex); + } + invalidate_cache(node); +} + +void anm_link_node(struct anm_node *p, struct anm_node *c) +{ + c->next = p->child; + p->child = c; + + c->parent = p; + invalidate_cache(c); +} + +int anm_unlink_node(struct anm_node *p, struct anm_node *c) +{ + struct anm_node *iter; + + if(p->child == c) { + p->child = c->next; + c->next = 0; + invalidate_cache(c); + return 0; + } + + iter = p->child; + while(iter->next) { + if(iter->next == c) { + iter->next = c->next; + c->next = 0; + invalidate_cache(c); + return 0; + } + } + return -1; +} + +void anm_clear(struct anm_node *node) +{ + int i; + + for(i=0; itracks[i]); + } + invalidate_cache(node); +} + +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_POS_X, tm, pos.x); + anm_set_value(node->tracks + ANM_TRACK_POS_Y, tm, pos.y); + anm_set_value(node->tracks + ANM_TRACK_POS_Z, tm, pos.z); + invalidate_cache(node); +} + +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_POS_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_POS_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_POS_Z, tm); + return v; +} + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_ROT_X, tm, rot.x); + anm_set_value(node->tracks + ANM_TRACK_ROT_Y, tm, rot.y); + anm_set_value(node->tracks + ANM_TRACK_ROT_Z, tm, rot.z); + anm_set_value(node->tracks + ANM_TRACK_ROT_W, tm, rot.w); + invalidate_cache(node); +} + +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm) +{ +#ifndef ROT_USE_SLERP + quat_t q; + q.x = anm_get_value(node->tracks + ANM_TRACK_ROT_X, tm); + q.y = anm_get_value(node->tracks + ANM_TRACK_ROT_Y, tm); + q.z = anm_get_value(node->tracks + ANM_TRACK_ROT_Z, tm); + q.w = anm_get_value(node->tracks + ANM_TRACK_ROT_W, tm); + return q; +#else + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + struct anm_track *track_x, *track_y, *track_z, *track_w; + quat_t q, q1, q2; + + track_x = node->tracks + ANM_TRACK_ROT_X; + track_y = node->tracks + ANM_TRACK_ROT_Y; + track_z = node->tracks + ANM_TRACK_ROT_Z; + track_w = node->tracks + ANM_TRACK_ROT_W; + + if(!track_x->count) { + q.x = track_x->def_val; + q.y = track_y->def_val; + q.z = track_z->def_val; + q.w = track_w->def_val; + return q; + } + + last_idx = track_x->count - 1; + + tstart = track_x->keys[0].time; + tend = track_x->keys[last_idx].time; + + if(tstart == tend) { + q.x = track_x->keys[0].val; + q.y = track_y->keys[0].val; + q.z = track_z->keys[0].val; + q.w = track_w->keys[0].val; + return q; + } + + tm = anm_remap_time(track_x, tm, tstart, tend); + + idx0 = anm_get_key_interval(track_x, tm); + assert(idx0 >= 0 && idx0 < track_x->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + q.x = track_x->keys[idx0].val; + q.y = track_y->keys[idx0].val; + q.z = track_z->keys[idx0].val; + q.w = track_w->keys[idx0].val; + return q; + } + + dt = (float)(track_x->keys[idx1].time - track_x->keys[idx0].time); + t = (float)(tm - track_x->keys[idx0].time) / dt; + + q1.x = track_x->keys[idx0].val; + q1.y = track_y->keys[idx0].val; + q1.z = track_z->keys[idx0].val; + q1.w = track_w->keys[idx0].val; + + q2.x = track_x->keys[idx1].val; + q2.y = track_y->keys[idx1].val; + q2.z = track_z->keys[idx1].val; + q2.w = track_w->keys[idx1].val; + + /*q1 = quat_normalize(q1); + q2 = quat_normalize(q2);*/ + + return quat_slerp(q1, q2, t); +#endif +} + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm) +{ + anm_set_value(node->tracks + ANM_TRACK_SCL_X, tm, scl.x); + anm_set_value(node->tracks + ANM_TRACK_SCL_Y, tm, scl.y); + anm_set_value(node->tracks + ANM_TRACK_SCL_Z, tm, scl.z); + invalidate_cache(node); +} + +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t v; + v.x = anm_get_value(node->tracks + ANM_TRACK_SCL_X, tm); + v.y = anm_get_value(node->tracks + ANM_TRACK_SCL_Y, tm); + v.z = anm_get_value(node->tracks + ANM_TRACK_SCL_Z, tm); + return v; +} + + +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm) +{ + mat4_t xform; + vec3_t pos = {0.0, 0.0, 0.0}; + + if(!node->parent) { + return anm_get_node_position(node, tm); + } + + anm_get_matrix(node, xform, tm); + return v3_transform(pos, xform); +} + +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm) +{ + quat_t rot, prot; + rot = anm_get_node_rotation(node, tm); + + if(!node->parent) { + return rot; + } + + prot = anm_get_rotation(node->parent, tm); + return quat_mul(prot, rot); +} + +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm) +{ + vec3_t s, ps; + s = anm_get_node_scaling(node, tm); + + if(!node->parent) { + return s; + } + + ps = anm_get_scaling(node->parent, tm); + return v3_mul(s, ps); +} + +void anm_set_pivot(struct anm_node *node, vec3_t piv) +{ + node->pivot = piv; +} + +vec3_t anm_get_pivot(struct anm_node *node) +{ + return node->pivot; +} + +void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + int i; + mat4_t rmat; + vec3_t pos, scale; + quat_t rot; + + pos = anm_get_node_position(node, tm); + rot = anm_get_node_rotation(node, tm); + scale = anm_get_node_scaling(node, tm); + + m4_set_translation(mat, node->pivot.x, node->pivot.y, node->pivot.z); + + quat_to_mat4(rmat, rot); + for(i=0; i<3; i++) { + mat[i][0] = rmat[i][0]; + mat[i][1] = rmat[i][1]; + mat[i][2] = rmat[i][2]; + } + /* this loop is equivalent to: m4_mult(mat, mat, rmat); */ + + mat[0][0] *= scale.x; mat[0][1] *= scale.y; mat[0][2] *= scale.z; mat[0][3] += pos.x; + mat[1][0] *= scale.x; mat[1][1] *= scale.y; mat[1][2] *= scale.z; mat[1][3] += pos.y; + mat[2][0] *= scale.x; mat[2][1] *= scale.y; mat[2][2] *= scale.z; mat[2][3] += pos.z; + + m4_translate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z); + + /* that's basically: pivot * rotation * translation * scaling * -pivot */ +} + +void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + mat4_t tmp; + anm_get_node_matrix(node, tmp, tm); + m4_inverse(mat, tmp); +} + +void anm_eval_node(struct anm_node *node, anm_time_t tm) +{ + anm_get_node_matrix(node, node->matrix, tm); +} + +void anm_eval(struct anm_node *node, anm_time_t tm) +{ + struct anm_node *c; + + anm_eval_node(node, tm); + + if(node->parent) { + /* due to post-order traversal, the parent matrix is already evaluated */ + m4_mult(node->matrix, node->parent->matrix, node->matrix); + } + + /* recersively evaluate all children */ + c = node->child; + while(c) { + anm_eval(c, tm); + c = c->next; + } +} + +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } + + if(cache->time != tm) { + anm_get_node_matrix(node, cache->matrix, tm); + + if(node->parent) { + mat4_t parent_mat; + + anm_get_matrix(node->parent, parent_mat, tm); + m4_mult(cache->matrix, parent_mat, cache->matrix); + } + cache->time = tm; + } + m4_copy(mat, cache->matrix); +} + +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm) +{ + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(!cache) { + cache = malloc(sizeof *cache); + assert(cache); + + pthread_mutex_lock(&node->cache_list_lock); + cache->next = node->cache_list; + node->cache_list = cache; + pthread_mutex_unlock(&node->cache_list_lock); + + cache->inv_time = ANM_TIME_INVAL; + cache->inv_time = ANM_TIME_INVAL; + pthread_setspecific(node->cache_key, cache); + } + + if(cache->inv_time != tm) { + anm_get_matrix(node, mat, tm); + m4_inverse(cache->inv_matrix, mat); + cache->inv_time = tm; + } + m4_copy(mat, cache->inv_matrix); +} + +anm_time_t anm_get_start_time(struct anm_node *node) +{ + int i; + struct anm_node *c; + anm_time_t res = LONG_MAX; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->tracks[i].keys[0].time; + if(tm < res) { + res = tm; + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_start_time(c); + if(tm < res) { + res = tm; + } + c = c->next; + } + return res; +} + +anm_time_t anm_get_end_time(struct anm_node *node) +{ + int i; + struct anm_node *c; + anm_time_t res = LONG_MIN; + + for(i=0; itracks[i].count) { + anm_time_t tm = node->tracks[i].keys[node->tracks[i].count - 1].time; + if(tm > res) { + res = tm; + } + } + } + + c = node->child; + while(c) { + anm_time_t tm = anm_get_end_time(c); + if(tm > res) { + res = tm; + } + c = c->next; + } + return res; +} + +static void invalidate_cache(struct anm_node *node) +{ + struct mat_cache *cache = pthread_getspecific(node->cache_key); + if(cache) { + cache->time = cache->inv_time = ANM_TIME_INVAL; + } +} diff -r 000000000000 -r b2f14e535253 libs/anim/anim.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/anim.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,132 @@ +#ifndef LIBANIM_H_ +#define LIBANIM_H_ + +#include "config.h" + +#include + +#include +#include +#include +#include "track.h" + +enum { + ANM_TRACK_POS_X, + ANM_TRACK_POS_Y, + ANM_TRACK_POS_Z, + + ANM_TRACK_ROT_X, + ANM_TRACK_ROT_Y, + ANM_TRACK_ROT_Z, + ANM_TRACK_ROT_W, + + ANM_TRACK_SCL_X, + ANM_TRACK_SCL_Y, + ANM_TRACK_SCL_Z, + + ANM_NUM_TRACKS +}; + +struct anm_node { + char *name; + + struct anm_track tracks[ANM_NUM_TRACKS]; + vec3_t pivot; + + /* matrix cache */ + struct mat_cache { + mat4_t matrix, inv_matrix; + anm_time_t time, inv_time; + struct mat_cache *next; + } *cache_list; + pthread_key_t cache_key; + pthread_mutex_t cache_list_lock; + + /* matrix calculated by anm_eval functions (no locking, meant as a pre-pass) */ + mat4_t matrix; + + struct anm_node *parent; + struct anm_node *child; + struct anm_node *next; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* node constructor and destructor */ +int anm_init_node(struct anm_node *node); +void anm_destroy_node(struct anm_node *node); + +/* recursively destroy an animation node tree */ +void anm_destroy_node_tree(struct anm_node *tree); + +/* helper functions to allocate/construct and destroy/free with + * a single call. They call anm_init_node and anm_destroy_node + * internally. + */ +struct anm_node *anm_create_node(void); +void anm_free_node(struct anm_node *node); + +/* recursively destroy and free the nodes of a node tree */ +void anm_free_node_tree(struct anm_node *tree); + +int anm_set_node_name(struct anm_node *node, const char *name); +const char *anm_get_node_name(struct anm_node *node); + +void anm_set_interpolator(struct anm_node *node, enum anm_interpolator in); +void anm_set_extrapolator(struct anm_node *node, enum anm_extrapolator ex); + +/* link and unlink nodes with parent/child relations */ +void anm_link_node(struct anm_node *parent, struct anm_node *child); +int anm_unlink_node(struct anm_node *parent, struct anm_node *child); + +void anm_clear(struct anm_node *node); + +void anm_set_position(struct anm_node *node, vec3_t pos, anm_time_t tm); +vec3_t anm_get_node_position(struct anm_node *node, anm_time_t tm); + +void anm_set_rotation(struct anm_node *node, quat_t rot, anm_time_t tm); +quat_t anm_get_node_rotation(struct anm_node *node, anm_time_t tm); + +void anm_set_scaling(struct anm_node *node, vec3_t scl, anm_time_t tm); +vec3_t anm_get_node_scaling(struct anm_node *node, anm_time_t tm); + +/* these three return the full p/r/s taking hierarchy into account */ +vec3_t anm_get_position(struct anm_node *node, anm_time_t tm); +quat_t anm_get_rotation(struct anm_node *node, anm_time_t tm); +vec3_t anm_get_scaling(struct anm_node *node, anm_time_t tm); + +void anm_set_pivot(struct anm_node *node, vec3_t pivot); +vec3_t anm_get_pivot(struct anm_node *node); + +/* those return the start and end times of the whole tree */ +anm_time_t anm_get_start_time(struct anm_node *node); +anm_time_t anm_get_end_time(struct anm_node *node); + +/* these calculate the matrix and inverse matrix of this node alone */ +void anm_get_node_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); +void anm_get_node_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); + +/* ---- top-down matrix calculation interface ---- */ + +/* calculate and set the matrix of this node */ +void anm_eval_node(struct anm_node *node, anm_time_t tm); +/* calculate and set the matrix of this node and all its children recursively */ +void anm_eval(struct anm_node *node, anm_time_t tm); + + +/* ---- bottom-up lazy matrix calculation interface ---- */ + +/* These calculate the matrix and inverse matrix of this node taking hierarchy + * into account. The results are cached in thread-specific storage and returned + * if there's no change in time or tracks from the last query... + */ +void anm_get_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); +void anm_get_inv_matrix(struct anm_node *node, mat4_t mat, anm_time_t tm); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBANIM_H_ */ diff -r 000000000000 -r b2f14e535253 libs/anim/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/config.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,6 @@ +#ifndef ANIM_CONFIG_H_ +#define ANIM_CONFIG_H_ + +#undef ANIM_THREAD_SAFE + +#endif /* ANIM_CONFIG_H_ */ diff -r 000000000000 -r b2f14e535253 libs/anim/dynarr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/dynarr.c Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,122 @@ +#include +#include +#include +#include "dynarr.h" + +/* The array descriptor keeps auxilliary information needed to manipulate + * the dynamic array. It's allocated adjacent to the array buffer. + */ +struct arrdesc { + int nelem, szelem; + int max_elem; + int bufsz; /* not including the descriptor */ +}; + +#define DESC(x) ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc))) + +void *dynarr_alloc(int elem, int szelem) +{ + struct arrdesc *desc; + + if(!(desc = malloc(elem * szelem + sizeof *desc))) { + return 0; + } + desc->nelem = desc->max_elem = elem; + desc->szelem = szelem; + desc->bufsz = elem * szelem; + return (char*)desc + sizeof *desc; +} + +void dynarr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *dynarr_resize(void *da, int elem) +{ + int newsz; + void *tmp; + struct arrdesc *desc; + + if(!da) return 0; + desc = DESC(da); + + newsz = desc->szelem * elem; + + if(!(tmp = realloc(desc, newsz + sizeof *desc))) { + return 0; + } + desc = tmp; + + desc->nelem = desc->max_elem = elem; + desc->bufsz = newsz; + return (char*)desc + sizeof *desc; +} + +int dynarr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int dynarr_size(void *da) +{ + return DESC(da)->nelem; +} + + +/* stack semantics */ +void *dynarr_push(void *da, void *item) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(nelem >= desc->max_elem) { + /* need to resize */ + struct arrdesc *tmp; + int newsz = desc->max_elem ? desc->max_elem * 2 : 1; + + if(!(tmp = dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + + memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem); + return da; +} + +void *dynarr_pop(void *da) +{ + struct arrdesc *desc; + int nelem; + + desc = DESC(da); + nelem = desc->nelem; + + if(!nelem) return da; + + if(nelem <= desc->max_elem / 3) { + /* reclaim space */ + struct arrdesc *tmp; + int newsz = desc->max_elem / 2; + + if(!(tmp = dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + desc->nelem--; + + return da; +} diff -r 000000000000 -r b2f14e535253 libs/anim/dynarr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/dynarr.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,16 @@ +#ifndef DYNARR_H_ +#define DYNARR_H_ + +void *dynarr_alloc(int elem, int szelem); +void dynarr_free(void *da); +void *dynarr_resize(void *da, int elem); + +int dynarr_empty(void *da); +int dynarr_size(void *da); + +/* stack semantics */ +void *dynarr_push(void *da, void *item); +void *dynarr_pop(void *da); + + +#endif /* DYNARR_H_ */ diff -r 000000000000 -r b2f14e535253 libs/anim/track.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/track.c Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,331 @@ +#include +#include +#include +#include +#include "track.h" +#include "dynarr.h" + +static int keycmp(const void *a, const void *b); +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm); + +static float interp_step(float v0, float v1, float v2, float v3, float t); +static float interp_linear(float v0, float v1, float v2, float v3, float t); +static float interp_cubic(float v0, float v1, float v2, float v3, float t); + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end); +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end); + +/* XXX keep this in sync with enum anm_interpolator at track.h */ +static float (*interp[])(float, float, float, float, float) = { + interp_step, + interp_linear, + interp_cubic, + 0 +}; + +/* XXX keep this in sync with enum anm_extrapolator at track.h */ +static anm_time_t (*remap_time[])(anm_time_t, anm_time_t, anm_time_t) = { + remap_extend, + remap_clamp, + remap_repeat, + remap_pingpong, + 0 +}; + +int anm_init_track(struct anm_track *track) +{ + memset(track, 0, sizeof *track); + + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { + return -1; + } + track->interp = ANM_INTERP_LINEAR; + track->extrap = ANM_EXTRAP_CLAMP; + return 0; +} + +void anm_destroy_track(struct anm_track *track) +{ + dynarr_free(track->keys); +} + +struct anm_track *anm_create_track(void) +{ + struct anm_track *track; + + if((track = malloc(sizeof *track))) { + if(anm_init_track(track) == -1) { + free(track); + return 0; + } + } + return track; +} + +void anm_free_track(struct anm_track *track) +{ + anm_destroy_track(track); + free(track); +} + +void anm_copy_track(struct anm_track *dest, const struct anm_track *src) +{ + free(dest->name); + if(dest->keys) { + dynarr_free(dest->keys); + } + + if(src->name) { + dest->name = malloc(strlen(src->name) + 1); + strcpy(dest->name, src->name); + } + + dest->count = src->count; + dest->keys = dynarr_alloc(src->count, sizeof *dest->keys); + memcpy(dest->keys, src->keys, src->count * sizeof *dest->keys); + + dest->def_val = src->def_val; + dest->interp = src->interp; + dest->extrap = src->extrap; +} + +void anm_clear_track(struct anm_track *track) +{ + if(track->keys) { + dynarr_free(track->keys); + } + track->keys = 0; + track->count = 0; + + if(!(track->keys = dynarr_alloc(0, sizeof *track->keys))) { + fprintf(stderr, "anm_clear_track failed to allocate zero-length keyframe track\n"); + } + assert(track->keys); +} + +int anm_set_track_name(struct anm_track *track, const char *name) +{ + char *tmp; + + if(!(tmp = malloc(strlen(name) + 1))) { + return -1; + } + free(track->name); + track->name = tmp; + return 0; +} + +const char *anm_get_track_name(struct anm_track *track) +{ + return track->name; +} + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in) +{ + track->interp = in; +} + +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex) +{ + track->extrap = ex; +} + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_time[track->extrap](tm, start, end); +} + +void anm_set_track_default(struct anm_track *track, float def) +{ + track->def_val = def; +} + +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key) +{ + int idx = anm_get_key_interval(track, key->time); + + /* if we got a valid keyframe index, compare them... */ + if(idx >= 0 && idx < track->count && keycmp(key, track->keys + idx) == 0) { + /* ... it's the same key, just update the value */ + track->keys[idx].val = key->val; + } else { + /* ... it's a new key, add it and re-sort them */ + void *tmp; + if(!(tmp = dynarr_push(track->keys, key))) { + return -1; + } + track->keys = tmp; + /* TODO lazy qsort */ + qsort(track->keys, ++track->count, sizeof *track->keys, keycmp); + } + return 0; +} + +static int keycmp(const void *a, const void *b) +{ + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; +} + +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx) +{ + if(idx < 0 || idx >= track->count) { + return 0; + } + return track->keys + idx; +} + +int anm_get_key_interval(struct anm_track *track, anm_time_t tm) +{ + int last; + + if(!track->count || tm < track->keys[0].time) { + return -1; + } + + last = track->count - 1; + if(tm > track->keys[last].time) { + return last; + } + + return find_prev_key(track->keys, 0, last, tm); +} + +static int find_prev_key(struct anm_keyframe *arr, int start, int end, anm_time_t tm) +{ + int mid; + + if(end - start <= 1) { + return start; + } + + mid = (start + end) / 2; + if(tm < arr[mid].time) { + return find_prev_key(arr, start, mid, tm); + } + if(tm > arr[mid].time) { + return find_prev_key(arr, mid, end, tm); + } + return mid; +} + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val) +{ + struct anm_keyframe key; + key.time = tm; + key.val = val; + + return anm_set_keyframe(track, &key); +} + +float anm_get_value(struct anm_track *track, anm_time_t tm) +{ + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + float v0, v1, v2, v3; + + if(!track->count) { + return track->def_val; + } + + last_idx = track->count - 1; + + tstart = track->keys[0].time; + tend = track->keys[last_idx].time; + + if(tstart == tend) { + return track->keys[0].val; + } + + tm = remap_time[track->extrap](tm, tstart, tend); + + idx0 = anm_get_key_interval(track, tm); + assert(idx0 >= 0 && idx0 < track->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + return track->keys[idx0].val; + } + + dt = (float)(track->keys[idx1].time - track->keys[idx0].time); + t = (float)(tm - track->keys[idx0].time) / dt; + + v1 = track->keys[idx0].val; + v2 = track->keys[idx1].val; + + /* get the neigboring values to allow for cubic interpolation */ + v0 = idx0 > 0 ? track->keys[idx0 - 1].val : v1; + v3 = idx1 < last_idx ? track->keys[idx1 + 1].val : v2; + + return interp[track->interp](v0, v1, v2, v3, t); +} + + +static float interp_step(float v0, float v1, float v2, float v3, float t) +{ + return v1; +} + +static float interp_linear(float v0, float v1, float v2, float v3, float t) +{ + return v1 + (v2 - v1) * t; +} + +static float interp_cubic(float a, float b, float c, float d, float t) +{ + float x, y, z, w; + float tsq = t * t; + + x = -a + 3.0 * b - 3.0 * c + d; + y = 2.0 * a - 5.0 * b + 4.0 * c - d; + z = c - a; + w = 2.0 * b; + + return 0.5 * (x * tsq * t + y * tsq + z * t + w); +} + +static anm_time_t remap_extend(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + return remap_repeat(tm, start, end); +} + +static anm_time_t remap_clamp(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + if(start == end) { + return start; + } + return tm < start ? start : (tm >= end ? end - 1 : tm); +} + +static anm_time_t remap_repeat(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + anm_time_t x, interv = end - start; + + if(interv == 0) { + return start; + } + + x = (tm - start) % interv; + if(x < 0) { + x += interv; + } + return x + start; + + /*if(tm < start) { + while(tm < start) { + tm += interv; + } + return tm; + } + return (tm - start) % interv + start;*/ +} + +static anm_time_t remap_pingpong(anm_time_t tm, anm_time_t start, anm_time_t end) +{ + anm_time_t interv = end - start; + anm_time_t x = remap_repeat(tm, start, end + interv); + + return x > end ? end + interv - x : x; +} diff -r 000000000000 -r b2f14e535253 libs/anim/track.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/anim/track.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,103 @@ +/* An animation track defines the values of a single scalar over time + * and supports various interpolation and extrapolation modes. + */ +#ifndef LIBANIM_TRACK_H_ +#define LIBANIM_TRACK_H_ + +#include +#include "config.h" + +enum anm_interpolator { + ANM_INTERP_STEP, + ANM_INTERP_LINEAR, + ANM_INTERP_CUBIC +}; + +enum anm_extrapolator { + ANM_EXTRAP_EXTEND, /* extend to infinity */ + ANM_EXTRAP_CLAMP, /* clamp to last value */ + ANM_EXTRAP_REPEAT, /* repeat motion */ + ANM_EXTRAP_PINGPONG /* repeat with mirroring */ +}; + +typedef long anm_time_t; +#define ANM_TIME_INVAL LONG_MIN + +#define ANM_SEC2TM(x) ((anm_time_t)((x) * 1000)) +#define ANM_MSEC2TM(x) ((anm_time_t)(x)) +#define ANM_TM2SEC(x) ((x) / 1000.0) +#define ANM_TM2MSEC(x) (x) + +struct anm_keyframe { + anm_time_t time; + float val; +}; + +struct anm_track { + char *name; + int count; + struct anm_keyframe *keys; + + float def_val; + + enum anm_interpolator interp; + enum anm_extrapolator extrap; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* track constructor and destructor */ +int anm_init_track(struct anm_track *track); +void anm_destroy_track(struct anm_track *track); + +/* helper functions that use anm_init_track and anm_destroy_track internally */ +struct anm_track *anm_create_track(void); +void anm_free_track(struct anm_track *track); + +/* copies track src to dest + * XXX: dest must have been initialized first + */ +void anm_copy_track(struct anm_track *dest, const struct anm_track *src); + +void anm_clear_track(struct anm_track *track); + +int anm_set_track_name(struct anm_track *track, const char *name); +const char *anm_get_track_name(struct anm_track *track); + +void anm_set_track_interpolator(struct anm_track *track, enum anm_interpolator in); +void anm_set_track_extrapolator(struct anm_track *track, enum anm_extrapolator ex); + +anm_time_t anm_remap_time(struct anm_track *track, anm_time_t tm, anm_time_t start, anm_time_t end); + +void anm_set_track_default(struct anm_track *track, float def); + +/* set or update a keyframe */ +int anm_set_keyframe(struct anm_track *track, struct anm_keyframe *key); + +/* get the idx-th keyframe, returns null if it doesn't exist */ +struct anm_keyframe *anm_get_keyframe(struct anm_track *track, int idx); + +/* Finds the 0-based index of the intra-keyframe interval which corresponds + * to the specified time. If the time falls exactly onto the N-th keyframe + * the function returns N. + * + * Special cases: + * - if the time is before the first keyframe -1 is returned. + * - if the time is after the last keyframe, the index of the last keyframe + * is returned. + */ +int anm_get_key_interval(struct anm_track *track, anm_time_t tm); + +int anm_set_value(struct anm_track *track, anm_time_t tm, float val); + +/* evaluates and returns the value of the track for a particular time */ +float anm_get_value(struct anm_track *track, anm_time_t tm); + +#ifdef __cplusplus +} +#endif + + +#endif /* LIBANIM_TRACK_H_ */ diff -r 000000000000 -r b2f14e535253 libs/assimp/3DSConverter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/3DSConverter.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,841 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the 3ds importer class */ + +#include "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +// internal headers +#include "3DSLoader.h" +#include "TargetAnimation.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Setup final material indices, generae a default material if necessary +void Discreet3DSImporter::ReplaceDefaultMaterial() +{ + + // Try to find an existing material that matches the + // typical default material setting: + // - no textures + // - diffuse color (in grey!) + // NOTE: This is here to workaround the fact that some + // exporters are writing a default material, too. + unsigned int idx = 0xcdcdcdcd; + for (unsigned int i = 0; i < mScene->mMaterials.size();++i) + { + std::string s = mScene->mMaterials[i].mName; + for (std::string::iterator it = s.begin(); it != s.end(); ++it) + *it = ::tolower(*it); + + if (std::string::npos == s.find("default"))continue; + + if (mScene->mMaterials[i].mDiffuse.r != + mScene->mMaterials[i].mDiffuse.g || + mScene->mMaterials[i].mDiffuse.r != + mScene->mMaterials[i].mDiffuse.b)continue; + + if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || + mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 ) + { + continue; + } + idx = i; + } + if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); + + // now iterate through all meshes and through all faces and + // find all faces that are using the default material + unsigned int cnt = 0; + for (std::vector::iterator + i = mScene->mMeshes.begin(); + i != mScene->mMeshes.end();++i) + { + for (std::vector::iterator + a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a) + { + // NOTE: The additional check seems to be necessary, + // some exporters seem to generate invalid data here + if (0xcdcdcdcd == (*a)) + { + (*a) = idx; + ++cnt; + } + else if ( (*a) >= mScene->mMaterials.size()) + { + (*a) = idx; + DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material"); + ++cnt; + } + } + } + if (cnt && idx == mScene->mMaterials.size()) + { + // We need to create our own default material + D3DS::Material sMat; + sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); + sMat.mName = "%%%DEFAULT"; + mScene->mMaterials.push_back(sMat); + + DefaultLogger::get()->info("3DS: Generating default material"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached +void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh) +{ + for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i) + { + // check whether all indices are in range + for (unsigned int a = 0; a < 3;++a) + { + if ((*i).mIndices[a] >= sMesh.mPositions.size()) + { + DefaultLogger::get()->warn("3DS: Vertex index overflow)"); + (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; + } + if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) + { + DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)"); + (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Generate out unique verbose format representation +void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh) +{ + // TODO: really necessary? I don't think. Just a waste of memory and time + // to do it now in a separate buffer. + + // Allocate output storage + std::vector vNew (sMesh.mFaces.size() * 3); + std::vector vNew2; + if (sMesh.mTexCoords.size()) + vNew2.resize(sMesh.mFaces.size() * 3); + + for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i) + { + D3DS::Face& face = sMesh.mFaces[i]; + + // Positions + for (unsigned int a = 0; a < 3;++a,++base) + { + vNew[base] = sMesh.mPositions[face.mIndices[a]]; + if (sMesh.mTexCoords.size()) + vNew2[base] = sMesh.mTexCoords[face.mIndices[a]]; + + face.mIndices[a] = base; + } + } + sMesh.mPositions = vNew; + sMesh.mTexCoords = vNew2; +} + +// ------------------------------------------------------------------------------------------------ +// Convert a 3DS texture to texture keys in an aiMaterial +void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type) +{ + // Setup the texture name + aiString tex; + tex.Set( texture.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); + + // Setup the texture blend factor + if (is_not_qnan(texture.mTextureBlend)) + mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); + + // Setup the texture mapping mode + mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0)); + mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0)); + + // Mirroring - double the scaling values + // FIXME: this is not really correct ... + if (texture.mMapMode == aiTextureMapMode_Mirror) + { + texture.mScaleU *= 2.f; + texture.mScaleV *= 2.f; + texture.mOffsetU /= 2.f; + texture.mOffsetV /= 2.f; + } + + // Setup texture UV transformations + mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a 3DS material to an aiMaterial +void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat, + aiMaterial& mat) +{ + // NOTE: Pass the background image to the viewer by bypassing the + // material system. This is an evil hack, never do it again! + if (0 != mBackgroundImage.length() && bHasBG) + { + aiString tex; + tex.Set( mBackgroundImage); + mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); + + // Be sure this is only done for the first material + mBackgroundImage = std::string(""); + } + + // At first add the base ambient color of the scene to the material + oldMat.mAmbient.r += mClrAmbient.r; + oldMat.mAmbient.g += mClrAmbient.g; + oldMat.mAmbient.b += mClrAmbient.b; + + aiString name; + name.Set( oldMat.mName); + mat.AddProperty( &name, AI_MATKEY_NAME); + + // Material colors + mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + + // Phong shininess and shininess strength + if (D3DS::Discreet3DS::Phong == oldMat.mShading || + D3DS::Discreet3DS::Metal == oldMat.mShading) + { + if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) + { + oldMat.mShading = D3DS::Discreet3DS::Gouraud; + } + else + { + mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); + } + } + + // Opacity + mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); + + // Bump height scaling + mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); + + // Two sided rendering? + if (oldMat.mTwoSided) + { + int i = 1; + mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); + } + + // Shading mode + aiShadingMode eShading = aiShadingMode_NoShading; + switch (oldMat.mShading) + { + case D3DS::Discreet3DS::Flat: + eShading = aiShadingMode_Flat; break; + + // I don't know what "Wire" shading should be, + // assume it is simple lambertian diffuse shading + case D3DS::Discreet3DS::Wire: + { + // Set the wireframe flag + unsigned int iWire = 1; + mat.AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); + } + + case D3DS::Discreet3DS::Gouraud: + eShading = aiShadingMode_Gouraud; break; + + // assume cook-torrance shading for metals. + case D3DS::Discreet3DS::Phong : + eShading = aiShadingMode_Phong; break; + + case D3DS::Discreet3DS::Metal : + eShading = aiShadingMode_CookTorrance; break; + + // FIX to workaround a warning with GCC 4 who complained + // about a missing case Blinn: here - Blinn isn't a valid + // value in the 3DS Loader, it is just needed for ASE + case D3DS::Discreet3DS::Blinn : + eShading = aiShadingMode_Blinn; break; + } + mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); + + // DIFFUSE texture + if( oldMat.sTexDiffuse.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE); + + // SPECULAR texture + if( oldMat.sTexSpecular.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR); + + // OPACITY texture + if( oldMat.sTexOpacity.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY); + + // EMISSIVE texture + if( oldMat.sTexEmissive.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE); + + // BUMP texture + if( oldMat.sTexBump.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT); + + // SHININESS texture + if( oldMat.sTexShininess.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS); + + // REFLECTION texture + if( oldMat.sTexReflective.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION); + + // Store the name of the material itself, too + if( oldMat.mName.length()) { + aiString tex; + tex.Set( oldMat.mName); + mat.AddProperty( &tex, AI_MATKEY_NAME); + } +} + +// ------------------------------------------------------------------------------------------------ +// Split meshes by their materials and generate output aiMesh'es +void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) +{ + std::vector avOutMeshes; + avOutMeshes.reserve(mScene->mMeshes.size() * 2); + + unsigned int iFaceCnt = 0,num = 0; + aiString name; + + // we need to split all meshes by their materials + for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { + boost::scoped_array< std::vector > aiSplit(new std::vector[mScene->mMaterials.size()]); + + name.length = ASSIMP_itoa10(name.data,num++); + + unsigned int iNum = 0; + for (std::vector::const_iterator a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a,++iNum) + { + aiSplit[*a].push_back(iNum); + } + // now generate submeshes + for (unsigned int p = 0; p < mScene->mMaterials.size();++p) + { + if (aiSplit[p].empty()) { + continue; + } + aiMesh* meshOut = new aiMesh(); + meshOut->mName = name; + meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // be sure to setup the correct material index + meshOut->mMaterialIndex = p; + + // use the color data as temporary storage + meshOut->mColors[0] = (aiColor4D*)(&*i); + avOutMeshes.push_back(meshOut); + + // convert vertices + meshOut->mNumFaces = (unsigned int)aiSplit[p].size(); + meshOut->mNumVertices = meshOut->mNumFaces*3; + + // allocate enough storage for faces + meshOut->mFaces = new aiFace[meshOut->mNumFaces]; + iFaceCnt += meshOut->mNumFaces; + + meshOut->mVertices = new aiVector3D[meshOut->mNumVertices]; + meshOut->mNormals = new aiVector3D[meshOut->mNumVertices]; + if ((*i).mTexCoords.size()) + { + meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices]; + } + for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) + { + register unsigned int index = aiSplit[p][q]; + aiFace& face = meshOut->mFaces[q]; + + face.mIndices = new unsigned int[3]; + face.mNumIndices = 3; + + for (unsigned int a = 0; a < 3;++a,++base) + { + unsigned int idx = (*i).mFaces[index].mIndices[a]; + meshOut->mVertices[base] = (*i).mPositions[idx]; + meshOut->mNormals [base] = (*i).mNormals[idx]; + + if ((*i).mTexCoords.size()) + meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx]; + + face.mIndices[a] = base; + } + } + } + } + + // Copy them to the output array + pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); + pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); + for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) { + pcOut->mMeshes[a] = avOutMeshes[a]; + } + + // We should have at least one face here + if (!iFaceCnt) { + throw DeadlyImportError("No faces loaded. The mesh is empty"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Add a node to the scenegraph and setup its final transformation +void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, + D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/) +{ + std::vector iArray; + iArray.reserve(3); + + aiMatrix4x4 abs; + + // Find all meshes with the same name as the node + for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) + { + const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0]; + ai_assert(NULL != pcMesh); + + if (pcIn->mName == pcMesh->mName) + iArray.push_back(a); + } + if (!iArray.empty()) + { + // The matrix should be identical for all meshes with the + // same name. It HAS to be identical for all meshes ..... + D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]); + + // Compute the inverse of the transformation matrix to move the + // vertices back to their relative and local space + aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat; + mInv.Inverse();mInvTransposed.Transpose(); + aiVector3D pivot = pcIn->vPivot; + + pcOut->mNumMeshes = (unsigned int)iArray.size(); + pcOut->mMeshes = new unsigned int[iArray.size()]; + for (unsigned int i = 0;i < iArray.size();++i) { + const unsigned int iIndex = iArray[i]; + aiMesh* const mesh = pcSOut->mMeshes[iIndex]; + + // Transform the vertices back into their local space + // fixme: consider computing normals after this, so we don't need to transform them + const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices; + aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals; + + for (;pvCurrent != pvEnd;++pvCurrent,++t2) { + *pvCurrent = mInv * (*pvCurrent); + *t2 = mInvTransposed * (*t2); + } + + // Handle negative transformation matrix determinant -> invert vertex x + if (imesh->mMat.Determinant() < 0.0f) + { + /* we *must* have normals */ + for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) { + pvCurrent->x *= -1.f; + t2->x *= -1.f; + } + DefaultLogger::get()->info("3DS: Flipping mesh X-Axis"); + } + + // Handle pivot point + if(pivot.x || pivot.y || pivot.z) + { + for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) { + *pvCurrent -= pivot; + } + } + + // Setup the mesh index + pcOut->mMeshes[i] = iIndex; + } + } + + // Setup the name of the node + pcOut->mName.Set(pcIn->mName); + + // Now build the transformation matrix of the node + // ROTATION + if (pcIn->aRotationKeys.size()){ + + // FIX to get to Assimp's quaternion conventions + for (std::vector::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) { + (*it).mValue.w *= -1.f; + } + + pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() ); + } + else if (pcIn->aCameraRollKeys.size()) + { + aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue), + pcOut->mTransformation); + } + + // SCALING + aiMatrix4x4& m = pcOut->mTransformation; + if (pcIn->aScalingKeys.size()) + { + const aiVector3D& v = pcIn->aScalingKeys[0].mValue; + m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x; + m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y; + m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z; + } + + // TRANSLATION + if (pcIn->aPositionKeys.size()) + { + const aiVector3D& v = pcIn->aPositionKeys[0].mValue; + m.a4 += v.x; + m.b4 += v.y; + m.c4 += v.z; + } + + // Generate animation channels for the node + if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 || + pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 || + pcIn->aTargetPositionKeys.size() > 1) + { + aiAnimation* anim = pcSOut->mAnimations[0]; + ai_assert(NULL != anim); + + if (pcIn->aCameraRollKeys.size() > 1) + { + DefaultLogger::get()->debug("3DS: Converting camera roll track ..."); + + // Camera roll keys - in fact they're just rotations + // around the camera's z axis. The angles are given + // in degrees (and they're clockwise). + pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size()); + for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i) + { + aiQuatKey& q = pcIn->aRotationKeys[i]; + aiFloatKey& f = pcIn->aCameraRollKeys[i]; + + q.mTime = f.mTime; + + // FIX to get to Assimp quaternion conventions + q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue)); + } + } +#if 0 + if (pcIn->aTargetPositionKeys.size() > 1) + { + DefaultLogger::get()->debug("3DS: Converting target track ..."); + + // Camera or spot light - need to convert the separate + // target position channel to our representation + TargetAnimationHelper helper; + + if (pcIn->aPositionKeys.empty()) + { + // We can just pass zero here ... + helper.SetFixedMainAnimationChannel(aiVector3D()); + } + else helper.SetMainAnimationChannel(&pcIn->aPositionKeys); + helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys); + + // Do the conversion + std::vector distanceTrack; + helper.Process(&distanceTrack); + + // Now add a new node as child, name it .Target + // and assign the distance track to it. This is that the + // information where the target is and how it moves is + // not lost + D3DS::Node* nd = new D3DS::Node(); + pcIn->push_back(nd); + + nd->mName = pcIn->mName + ".Target"; + + aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); + nda->mNodeName.Set(nd->mName); + + nda->mNumPositionKeys = (unsigned int)distanceTrack.size(); + nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; + ::memcpy(nda->mPositionKeys,&distanceTrack[0], + sizeof(aiVectorKey)*nda->mNumPositionKeys); + } +#endif + + // Cameras or lights define their transformation in their parent node and in the + // corresponding light or camera chunks. However, we read and process the latter + // to to be able to return valid cameras/lights even if no scenegraph is given. + for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) { + if (pcSOut->mCameras[n]->mName == pcOut->mName) { + pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f); + } + } + for (unsigned int n = 0; n < pcSOut->mNumLights;++n) { + if (pcSOut->mLights[n]->mName == pcOut->mName) { + pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f); + } + } + + // Allocate a new node anim and setup its name + aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); + nda->mNodeName.Set(pcIn->mName); + + // POSITION keys + if (pcIn->aPositionKeys.size() > 0) + { + nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size(); + nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; + ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0], + sizeof(aiVectorKey)*nda->mNumPositionKeys); + } + + // ROTATION keys + if (pcIn->aRotationKeys.size() > 0) + { + nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size(); + nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys]; + + // Rotations are quaternion offsets + aiQuaternion abs; + for (unsigned int n = 0; n < nda->mNumRotationKeys;++n) + { + const aiQuatKey& q = pcIn->aRotationKeys[n]; + + abs = (n ? abs * q.mValue : q.mValue); + nda->mRotationKeys[n].mTime = q.mTime; + nda->mRotationKeys[n].mValue = abs.Normalize(); + } + } + + // SCALING keys + if (pcIn->aScalingKeys.size() > 0) + { + nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size(); + nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys]; + ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0], + sizeof(aiVectorKey)*nda->mNumScalingKeys); + } + } + + // Allocate storage for children + pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size(); + pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; + + // Recursively process all children + const unsigned int size = pcIn->mChildren.size(); + for (unsigned int i = 0; i < size;++i) + { + pcOut->mChildren[i] = new aiNode(); + pcOut->mChildren[i]->mParent = pcOut; + AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs); + } +} + +// ------------------------------------------------------------------------------------------------ +// Find out how many node animation channels we'll have finally +void CountTracks(D3DS::Node* node, unsigned int& cnt) +{ + ////////////////////////////////////////////////////////////////////////////// + // We will never generate more than one channel for a node, so + // this is rather easy here. + + if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 || + node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 || + node->aTargetPositionKeys.size() > 1) + { + ++cnt; + + // account for the additional channel for the camera/spotlight target position + if (node->aTargetPositionKeys.size() > 1)++cnt; + } + + // Recursively process all children + for (unsigned int i = 0; i < node->mChildren.size();++i) + CountTracks(node->mChildren[i],cnt); +} + +// ------------------------------------------------------------------------------------------------ +// Generate the output node graph +void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut) +{ + pcOut->mRootNode = new aiNode(); + if (0 == mRootNode->mChildren.size()) + { + ////////////////////////////////////////////////////////////////////////////// + // It seems the file is so messed up that it has not even a hierarchy. + // generate a flat hiearachy which looks like this: + // + // ROOT_NODE + // | + // ---------------------------------------- + // | | | | | + // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 .... + // + DefaultLogger::get()->warn("No hierarchy information has been found in the file. "); + + pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes + + mScene->mCameras.size() + mScene->mLights.size(); + + pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ]; + pcOut->mRootNode->mName.Set("<3DSDummyRoot>"); + + // Build dummy nodes for all meshes + unsigned int a = 0; + for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + pcNode->mNumMeshes = 1; + + // Build a name for the node + pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i); + } + + // Build dummy nodes for all cameras + for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + + // Build a name for the node + pcNode->mName = mScene->mCameras[i]->mName; + } + + // Build dummy nodes for all lights + for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + + // Build a name for the node + pcNode->mName = mScene->mLights[i]->mName; + } + } + else + { + // First of all: find out how many scaling, rotation and translation + // animation tracks we'll have afterwards + unsigned int numChannel = 0; + CountTracks(mRootNode,numChannel); + + if (numChannel) + { + // Allocate a primary animation channel + pcOut->mNumAnimations = 1; + pcOut->mAnimations = new aiAnimation*[1]; + aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation(); + + anim->mName.Set("3DSMasterAnim"); + + // Allocate enough storage for all node animation channels, + // but don't set the mNumChannels member - we'll use it to + // index into the array + anim->mChannels = new aiNodeAnim*[numChannel]; + } + + aiMatrix4x4 m; + AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m); + } + + // We used the first vertex color set to store some emporary values so we need to cleanup here + for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) + pcOut->mMeshes[a]->mColors[0] = NULL; + + pcOut->mRootNode->mTransformation = aiMatrix4x4( + 1.f,0.f,0.f,0.f, + 0.f,0.f,1.f,0.f, + 0.f,-1.f,0.f,0.f, + 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation; + + // If the root node is unnamed name it "<3DSRoot>" + if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) || + (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') ) + { + pcOut->mRootNode->mName.Set("<3DSRoot>"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert all meshes in the scene and generate the final output scene. +void Discreet3DSImporter::ConvertScene(aiScene* pcOut) +{ + // Allocate enough storage for all output materials + pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size(); + pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; + + // ... and convert the 3DS materials to aiMaterial's + for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) + { + aiMaterial* pcNew = new aiMaterial(); + ConvertMaterial(mScene->mMaterials[i],*pcNew); + pcOut->mMaterials[i] = pcNew; + } + + // Generate the output mesh list + ConvertMeshes(pcOut); + + // Now copy all light sources to the output scene + pcOut->mNumLights = (unsigned int)mScene->mLights.size(); + if (pcOut->mNumLights) + { + pcOut->mLights = new aiLight*[pcOut->mNumLights]; + ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights); + } + + // Now copy all cameras to the output scene + pcOut->mNumCameras = (unsigned int)mScene->mCameras.size(); + if (pcOut->mNumCameras) + { + pcOut->mCameras = new aiCamera*[pcOut->mNumCameras]; + ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras); + } +} + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/3DSHelper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/3DSHelper.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,577 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines helper data structures for the import of 3DS files */ + +#ifndef AI_3DSFILEHELPER_H_INC +#define AI_3DSFILEHELPER_H_INC + + +#include "SpatialSort.h" +#include "SmoothingGroups.h" + +namespace Assimp { +namespace D3DS { + +#include "assimp/Compiler/pushpack1.h" + +// --------------------------------------------------------------------------- +/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks +* and data structures. +*/ +class Discreet3DS +{ +private: + inline Discreet3DS() {} + +public: + + //! data structure for a single chunk in a .3ds file + struct Chunk + { + uint16_t Flag; + uint32_t Size; + } PACK_STRUCT; + + + //! Used for shading field in material3ds structure + //! From AutoDesk 3ds SDK + typedef enum + { + // translated to gouraud shading with wireframe active + Wire = 0x0, + + // if this material is set, no vertex normals will + // be calculated for the model. Face normals + gouraud + Flat = 0x1, + + // standard gouraud shading + Gouraud = 0x2, + + // phong shading + Phong = 0x3, + + // cooktorrance or anistropic phong shading ... + // the exact meaning is unknown, if you know it + // feel free to tell me ;-) + Metal = 0x4, + + // required by the ASE loader + Blinn = 0x5 + } shadetype3ds; + + // Flags for animated keys + enum + { + KEY_USE_TENS = 0x1, + KEY_USE_CONT = 0x2, + KEY_USE_BIAS = 0x4, + KEY_USE_EASE_TO = 0x8, + KEY_USE_EASE_FROM = 0x10 + } ; + + enum + { + + // ******************************************************************** + // Basic chunks which can be found everywhere in the file + CHUNK_VERSION = 0x0002, + CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B + CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B + + // Linear color values (gamma = 2.2?) + CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B + CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B + + CHUNK_PERCENTW = 0x0030, // int2 percentage + CHUNK_PERCENTF = 0x0031, // float4 percentage + // ******************************************************************** + + // Prj master chunk + CHUNK_PRJ = 0xC23D, + + // MDLI master chunk + CHUNK_MLI = 0x3DAA, + + // Primary main chunk of the .3ds file + CHUNK_MAIN = 0x4D4D, + + // Mesh main chunk + CHUNK_OBJMESH = 0x3D3D, + + // Specifies the background color of the .3ds file + // This is passed through the material system for + // viewing purposes. + CHUNK_BKGCOLOR = 0x1200, + + // Specifies the ambient base color of the scene. + // This is added to all materials in the file + CHUNK_AMBCOLOR = 0x2100, + + // Specifies the background image for the whole scene + // This value is passed through the material system + // to the viewer + CHUNK_BIT_MAP = 0x1100, + CHUNK_BIT_MAP_EXISTS = 0x1101, + + // ******************************************************************** + // Viewport related stuff. Ignored + CHUNK_DEFAULT_VIEW = 0x3000, + CHUNK_VIEW_TOP = 0x3010, + CHUNK_VIEW_BOTTOM = 0x3020, + CHUNK_VIEW_LEFT = 0x3030, + CHUNK_VIEW_RIGHT = 0x3040, + CHUNK_VIEW_FRONT = 0x3050, + CHUNK_VIEW_BACK = 0x3060, + CHUNK_VIEW_USER = 0x3070, + CHUNK_VIEW_CAMERA = 0x3080, + // ******************************************************************** + + // Mesh chunks + CHUNK_OBJBLOCK = 0x4000, + CHUNK_TRIMESH = 0x4100, + CHUNK_VERTLIST = 0x4110, + CHUNK_VERTFLAGS = 0x4111, + CHUNK_FACELIST = 0x4120, + CHUNK_FACEMAT = 0x4130, + CHUNK_MAPLIST = 0x4140, + CHUNK_SMOOLIST = 0x4150, + CHUNK_TRMATRIX = 0x4160, + CHUNK_MESHCOLOR = 0x4165, + CHUNK_TXTINFO = 0x4170, + CHUNK_LIGHT = 0x4600, + CHUNK_CAMERA = 0x4700, + CHUNK_HIERARCHY = 0x4F00, + + // Specifies the global scaling factor. This is applied + // to the root node's transformation matrix + CHUNK_MASTER_SCALE = 0x0100, + + // ******************************************************************** + // Material chunks + CHUNK_MAT_MATERIAL = 0xAFFF, + + // asciiz containing the name of the material + CHUNK_MAT_MATNAME = 0xA000, + CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk + CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk + CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk + + // Specifies the shininess of the material + // followed by percentage chunk + CHUNK_MAT_SHININESS = 0xA040, + CHUNK_MAT_SHININESS_PERCENT = 0xA041 , + + // Specifies the shading mode to be used + // followed by a short + CHUNK_MAT_SHADING = 0xA100, + + // NOTE: Emissive color (self illumination) seems not + // to be a color but a single value, type is unknown. + // Make the parser accept both of them. + // followed by percentage chunk (?) + CHUNK_MAT_SELF_ILLUM = 0xA080, + + // Always followed by percentage chunk (?) + CHUNK_MAT_SELF_ILPCT = 0xA084, + + // Always followed by percentage chunk + CHUNK_MAT_TRANSPARENCY = 0xA050, + + // Diffuse texture channel 0 + CHUNK_MAT_TEXTURE = 0xA200, + + // Contains opacity information for each texel + CHUNK_MAT_OPACMAP = 0xA210, + + // Contains a reflection map to be used to reflect + // the environment. This is partially supported. + CHUNK_MAT_REFLMAP = 0xA220, + + // Self Illumination map (emissive colors) + CHUNK_MAT_SELFIMAP = 0xA33d, + + // Bumpmap. Not specified whether it is a heightmap + // or a normal map. Assme it is a heightmap since + // artist normally prefer this format. + CHUNK_MAT_BUMPMAP = 0xA230, + + // Specular map. Seems to influence the specular color + CHUNK_MAT_SPECMAP = 0xA204, + + // Holds shininess data. + CHUNK_MAT_MAT_SHINMAP = 0xA33C, + + // Scaling in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_USCALE = 0xA354, + CHUNK_MAT_MAP_VSCALE = 0xA356, + + // Translation in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_UOFFSET = 0xA358, + CHUNK_MAT_MAP_VOFFSET = 0xA35a, + + // UV-coordinates rotation around the z-axis + // Assumed to be in radians. + CHUNK_MAT_MAP_ANG = 0xA35C, + + // Tiling flags for 3DS files + CHUNK_MAT_MAP_TILING = 0xa351, + + // Specifies the file name of a texture + CHUNK_MAPFILE = 0xA300, + + // Specifies whether a materail requires two-sided rendering + CHUNK_MAT_TWO_SIDE = 0xA081, + // ******************************************************************** + + // Main keyframer chunk. Contains translation/rotation/scaling data + CHUNK_KEYFRAMER = 0xB000, + + // Supported sub chunks + CHUNK_TRACKINFO = 0xB002, + CHUNK_TRACKOBJNAME = 0xB010, + CHUNK_TRACKDUMMYOBJNAME = 0xB011, + CHUNK_TRACKPIVOT = 0xB013, + CHUNK_TRACKPOS = 0xB020, + CHUNK_TRACKROTATE = 0xB021, + CHUNK_TRACKSCALE = 0xB022, + + // ******************************************************************** + // Keyframes for various other stuff in the file + // Partially ignored + CHUNK_AMBIENTKEY = 0xB001, + CHUNK_TRACKMORPH = 0xB026, + CHUNK_TRACKHIDE = 0xB029, + CHUNK_OBJNUMBER = 0xB030, + CHUNK_TRACKCAMERA = 0xB003, + CHUNK_TRACKFOV = 0xB023, + CHUNK_TRACKROLL = 0xB024, + CHUNK_TRACKCAMTGT = 0xB004, + CHUNK_TRACKLIGHT = 0xB005, + CHUNK_TRACKLIGTGT = 0xB006, + CHUNK_TRACKSPOTL = 0xB007, + CHUNK_FRAMES = 0xB008, + // ******************************************************************** + + // light sub-chunks + CHUNK_DL_OFF = 0x4620, + CHUNK_DL_OUTER_RANGE = 0x465A, + CHUNK_DL_INNER_RANGE = 0x4659, + CHUNK_DL_MULTIPLIER = 0x465B, + CHUNK_DL_EXCLUDE = 0x4654, + CHUNK_DL_ATTENUATE = 0x4625, + CHUNK_DL_SPOTLIGHT = 0x4610, + + // camera sub-chunks + CHUNK_CAM_RANGES = 0x4720 + }; +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds mesh face */ +struct Face : public FaceWithSmoothingGroup +{ +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a texture */ +struct Texture +{ + //! Default constructor + Texture() + : mOffsetU (0.0f) + , mOffsetV (0.0f) + , mScaleU (1.0f) + , mScaleV (1.0f) + , mRotation (0.0f) + , mMapMode (aiTextureMapMode_Wrap) + , iUVSrc (0) + { + mTextureBlend = get_qnan(); + } + + //! Specifies the blend factor for the texture + float mTextureBlend; + + //! Specifies the filename of the texture + std::string mMapName; + + //! Specifies texture coordinate offsets/scaling/rotations + float mOffsetU; + float mOffsetV; + float mScaleU; + float mScaleV; + float mRotation; + + //! Specifies the mapping mode to be used for the texture + aiTextureMapMode mMapMode; + + //! Used internally + bool bPrivate; + int iUVSrc; +}; + +#include "assimp/Compiler/poppack1.h" + +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds material */ +struct Material +{ + //! Default constructor. Builds a default name for the material + Material() + : + mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black + mSpecularExponent (0.0f), + mShininessStrength (1.0f), + mShading(Discreet3DS::Gouraud), + mTransparency (1.0f), + mBumpHeight (1.0f), + mTwoSided (false) + { + static int iCnt = 0; + + char szTemp[128]; + sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Name of the material + std::string mName; + //! Diffuse color of the material + aiColor3D mDiffuse; + //! Specular exponent + float mSpecularExponent; + //! Shininess strength, in percent + float mShininessStrength; + //! Specular color of the material + aiColor3D mSpecular; + //! Ambient color of the material + aiColor3D mAmbient; + //! Shading type to be used + Discreet3DS::shadetype3ds mShading; + //! Opacity of the material + float mTransparency; + //! Diffuse texture channel + Texture sTexDiffuse; + //! Opacity texture channel + Texture sTexOpacity; + //! Specular texture channel + Texture sTexSpecular; + //! Reflective texture channel + Texture sTexReflective; + //! Bump texture channel + Texture sTexBump; + //! Emissive texture channel + Texture sTexEmissive; + //! Shininess texture channel + Texture sTexShininess; + //! Scaling factor for the bump values + float mBumpHeight; + //! Emissive color + aiColor3D mEmissive; + //! Ambient texture channel + //! (used by the ASE format) + Texture sTexAmbient; + //! True if the material must be rendered from two sides + bool mTwoSided; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file mesh */ +struct Mesh : public MeshWithSmoothingGroups +{ + //! Default constructor + Mesh() + { + static int iCnt = 0; + + // Generate a default name for the mesh + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Name of the mesh + std::string mName; + + //! Texture coordinates + std::vector mTexCoords; + + //! Face materials + std::vector mFaceMaterials; + + //! Local transformation matrix + aiMatrix4x4 mMat; +}; + +// --------------------------------------------------------------------------- +/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the + C-API, so it would be difficult to make them a template. */ +struct aiFloatKey +{ + double mTime; ///< The time of this key + float mValue; ///< The value of this key + +#ifdef __cplusplus + + // time is not compared + bool operator == (const aiFloatKey& o) const + {return o.mValue == this->mValue;} + + bool operator != (const aiFloatKey& o) const + {return o.mValue != this->mValue;} + + // Only time is compared. This operator is defined + // for use with std::sort + bool operator < (const aiFloatKey& o) const + {return mTime < o.mTime;} + + bool operator > (const aiFloatKey& o) const + {return mTime < o.mTime;} + +#endif +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file node */ +struct Node +{ + Node() + + : mHierarchyPos (0) + , mHierarchyIndex (0) + + { + static int iCnt = 0; + + // Generate a default name for the node + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + + aRotationKeys.reserve (20); + aPositionKeys.reserve (20); + aScalingKeys.reserve (20); + } + + ~Node() + { + for (unsigned int i = 0; i < mChildren.size();++i) + delete mChildren[i]; + } + + //! Pointer to the parent node + Node* mParent; + + //! Holds all child nodes + std::vector mChildren; + + //! Name of the node + std::string mName; + + //! Dummy nodes: real name to be combined with the $$$DUMMY + std::string mDummyName; + + //! Position of the node in the hierarchy (tree depth) + int16_t mHierarchyPos; + + //! Index of the node + int16_t mHierarchyIndex; + + //! Rotation keys loaded from the file + std::vector aRotationKeys; + + //! Position keys loaded from the file + std::vector aPositionKeys; + + //! Scaling keys loaded from the file + std::vector aScalingKeys; + + + // For target lights (spot lights and directional lights): + // The position of the target + std::vector< aiVectorKey > aTargetPositionKeys; + + // For cameras: the camera roll angle + std::vector< aiFloatKey > aCameraRollKeys; + + //! Pivot position loaded from the file + aiVector3D vPivot; + + //! Add a child node, setup the right parent node for it + //! \param pc Node to be 'adopted' + inline Node& push_back(Node* pc) + { + mChildren.push_back(pc); + pc->mParent = this; + return *this; + } +}; +// --------------------------------------------------------------------------- +/** Helper structure analogue to aiScene */ +struct Scene +{ + //! List of all materials loaded + //! NOTE: 3ds references materials globally + std::vector mMaterials; + + //! List of all meshes loaded + std::vector mMeshes; + + //! List of all cameras loaded + std::vector mCameras; + + //! List of all lights loaded + std::vector mLights; + + //! Pointer to the root node of the scene + // --- moved to main class + // Node* pcRootNode; +}; + + +} // end of namespace D3DS +} // end of namespace Assimp + +#endif // AI_XFILEHELPER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/3DSLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/3DSLoader.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,1392 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file 3DSLoader.cpp + * @brief Implementation of the 3ds importer class + * + * http://www.the-labs.com/Blender/3DS-details.html + */ + +#include "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +// internal headers +#include "3DSLoader.h" + +using namespace Assimp; + +static const aiImporterDesc desc = { + "Discreet 3DS Importer", + "", + "", + "Limited animation support", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "3ds prj" +}; + + +// ------------------------------------------------------------------------------------------------ +// Begins a new parsing block +// - Reads the current chunk and validates it +// - computes its length +#define ASSIMP_3DS_BEGIN_CHUNK() \ + while (true) { \ + if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \ + return; \ + } \ + Discreet3DS::Chunk chunk; \ + ReadChunk(&chunk); \ + int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \ + const int oldReadLimit = stream->GetReadLimit(); \ + stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \ + + +// ------------------------------------------------------------------------------------------------ +// End a parsing block +// Must follow at the end of each parsing block, reset chunk end marker to previous value +#define ASSIMP_3DS_END_CHUNK() \ + stream->SkipToReadLimit(); \ + stream->SetReadLimit(oldReadLimit); \ + if (stream->GetRemainingSizeToLimit() == 0) \ + return; \ + } + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +Discreet3DSImporter::Discreet3DSImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +Discreet3DSImporter::~Discreet3DSImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + std::string extension = GetExtension(pFile); + if(extension == "3ds" || extension == "prj" ) { + return true; + } + if (!extension.length() || checkSig) { + uint16_t token[3]; + token[0] = 0x4d4d; + token[1] = 0x3dc2; + //token[2] = 0x3daa; + return CheckMagicToken(pIOHandler,pFile,token,2,0,2); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader registry entry +const aiImporterDesc* Discreet3DSImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties +void Discreet3DSImporter::SetupProperties(const Importer* /*pImp*/) +{ + // nothing to be done for the moment +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void Discreet3DSImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); + this->stream = &stream; + + // We should have at least one chunk + if (stream.GetRemainingSize() < 16) { + throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); + } + + // Allocate our temporary 3DS representation + mScene = new D3DS::Scene(); + + // Initialize members + mLastNodeIndex = -1; + mCurrentNode = new D3DS::Node(); + mRootNode = mCurrentNode; + mRootNode->mHierarchyPos = -1; + mRootNode->mHierarchyIndex = -1; + mRootNode->mParent = NULL; + mMasterScale = 1.0f; + mBackgroundImage = ""; + bHasBG = false; + bIsPrj = false; + + // Parse the file + ParseMainChunk(); + + // Process all meshes in the file. First check whether all + // face indices haev valid values. The generate our + // internal verbose representation. Finally compute normal + // vectors from the smoothing groups we read from the + // file. + for (std::vector::iterator i = mScene->mMeshes.begin(), + end = mScene->mMeshes.end(); i != end;++i) { + CheckIndices(*i); + MakeUnique (*i); + ComputeNormalsWithSmoothingsGroups(*i); + } + + // Replace all occurences of the default material with a + // valid material. Generate it if no material containing + // DEFAULT in its name has been found in the file + ReplaceDefaultMaterial(); + + // Convert the scene from our internal representation to an + // aiScene object. This involves copying all meshes, lights + // and cameras to the scene + ConvertScene(pScene); + + // Generate the node graph for the scene. This is a little bit + // tricky since we'll need to split some meshes into submeshes + GenerateNodeGraph(pScene); + + // Now apply the master scaling factor to the scene + ApplyMasterScale(pScene); + + // Delete our internal scene representation and the root + // node, so the whole hierarchy will follow + delete mRootNode; + delete mScene; + + AI_DEBUG_INVALIDATE_PTR(mRootNode); + AI_DEBUG_INVALIDATE_PTR(mScene); + AI_DEBUG_INVALIDATE_PTR(this->stream); +} + +// ------------------------------------------------------------------------------------------------ +// Applies a master-scaling factor to the imported scene +void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene) +{ + // There are some 3DS files with a zero scaling factor + if (!mMasterScale)mMasterScale = 1.0f; + else mMasterScale = 1.0f / mMasterScale; + + // Construct an uniform scaling matrix and multiply with it + pScene->mRootNode->mTransformation *= aiMatrix4x4( + mMasterScale,0.0f, 0.0f, 0.0f, + 0.0f, mMasterScale,0.0f, 0.0f, + 0.0f, 0.0f, mMasterScale,0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + // Check whether a scaling track is assigned to the root node. +} + +// ------------------------------------------------------------------------------------------------ +// Reads a new chunk from the file +void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut) +{ + ai_assert(pcOut != NULL); + + pcOut->Flag = stream->GetI2(); + pcOut->Size = stream->GetI4(); + + if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) + throw DeadlyImportError("Chunk is too large"); + + if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) + DefaultLogger::get()->error("3DS: Chunk overflow"); +} + +// ------------------------------------------------------------------------------------------------ +// Skip a chunk +void Discreet3DSImporter::SkipChunk() +{ + Discreet3DS::Chunk psChunk; + ReadChunk(&psChunk); + + stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk)); + return; +} + +// ------------------------------------------------------------------------------------------------ +// Process the primary chunk of the file +void Discreet3DSImporter::ParseMainChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + + case Discreet3DS::CHUNK_PRJ: + bIsPrj = true; + case Discreet3DS::CHUNK_MAIN: + ParseEditorChunk(); + break; + }; + + ASSIMP_3DS_END_CHUNK(); + // recursively continue processing this hierarchy level + return ParseMainChunk(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseEditorChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_OBJMESH: + + ParseObjectChunk(); + break; + + // NOTE: In several documentations in the internet this + // chunk appears at different locations + case Discreet3DS::CHUNK_KEYFRAMER: + + ParseKeyframeChunk(); + break; + + case Discreet3DS::CHUNK_VERSION: + { + // print the version number + char buff[10]; + ASSIMP_itoa10(buff,stream->GetI2()); + DefaultLogger::get()->info(std::string("3DS file format version: ") + buff); + } + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseObjectChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_OBJBLOCK: + { + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + + // Get the name of the geometry object + while (stream->GetI1())++cnt; + ParseChunk(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_MAT_MATERIAL: + + // Add a new material to the list + mScene->mMaterials.push_back(D3DS::Material()); + ParseMaterialChunk(); + break; + + case Discreet3DS::CHUNK_AMBCOLOR: + + // This is the ambient base color of the scene. + // We add it to the ambient color of all materials + ParseColorChunk(&mClrAmbient,true); + if (is_qnan(mClrAmbient.r)) + { + // We failed to read the ambient base color. + DefaultLogger::get()->error("3DS: Failed to read ambient base color"); + mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f; + } + break; + + case Discreet3DS::CHUNK_BIT_MAP: + { + // Specifies the background image. The string should already be + // properly 0 terminated but we need to be sure + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + while (stream->GetI1())++cnt; + mBackgroundImage = std::string(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_BIT_MAP_EXISTS: + bHasBG = true; + break; + + case Discreet3DS::CHUNK_MASTER_SCALE: + // Scene master scaling factor + mMasterScale = stream->GetF4(); + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // IMPLEMENTATION NOTE; + // Cameras or lights define their transformation in their parent node and in the + // corresponding light or camera chunks. However, we read and process the latter + // to to be able to return valid cameras/lights even if no scenegraph is given. + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRIMESH: + { + // this starts a new triangle mesh + mScene->mMeshes.push_back(D3DS::Mesh()); + D3DS::Mesh& m = mScene->mMeshes.back(); + + // Setup the name of the mesh + m.mName = std::string(name, num); + + // Read mesh chunks + ParseMeshChunk(); + } + break; + + case Discreet3DS::CHUNK_LIGHT: + { + // This starts a new light + aiLight* light = new aiLight(); + mScene->mLights.push_back(light); + + light->mName.Set(std::string(name, num)); + + // First read the position of the light + light->mPosition.x = stream->GetF4(); + light->mPosition.y = stream->GetF4(); + light->mPosition.z = stream->GetF4(); + + light->mColorDiffuse = aiColor3D(1.f,1.f,1.f); + + // Now check for further subchunks + if (!bIsPrj) /* fixme */ + ParseLightChunk(); + + // The specular light color is identical the the diffuse light color. The ambient light color + // is equal to the ambient base color of the whole scene. + light->mColorSpecular = light->mColorDiffuse; + light->mColorAmbient = mClrAmbient; + + if (light->mType == aiLightSource_UNDEFINED) + { + // It must be a point light + light->mType = aiLightSource_POINT; + }} + break; + + case Discreet3DS::CHUNK_CAMERA: + { + // This starts a new camera + aiCamera* camera = new aiCamera(); + mScene->mCameras.push_back(camera); + camera->mName.Set(std::string(name, num)); + + // First read the position of the camera + camera->mPosition.x = stream->GetF4(); + camera->mPosition.y = stream->GetF4(); + camera->mPosition.z = stream->GetF4(); + + // Then the camera target + camera->mLookAt.x = stream->GetF4() - camera->mPosition.x; + camera->mLookAt.y = stream->GetF4() - camera->mPosition.y; + camera->mLookAt.z = stream->GetF4() - camera->mPosition.z; + float len = camera->mLookAt.Length(); + if (len < 1e-5f) { + + // There are some files with lookat == position. Don't know why or whether it's ok or not. + DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector"); + camera->mLookAt = aiVector3D(0.f,1.f,0.f); + + } + else camera->mLookAt /= len; + + // And finally - the camera rotation angle, in counter clockwise direction + const float angle = AI_DEG_TO_RAD( stream->GetF4() ); + aiQuaternion quat(camera->mLookAt,angle); + camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f); + + // Read the lense angle + camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() ); + if (camera->mHorizontalFOV < 0.001f) { + camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); + } + + // Now check for further subchunks + if (!bIsPrj) /* fixme */ { + ParseCameraChunk(); + }} + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseLightChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + aiLight* light = mScene->mLights.back(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_DL_SPOTLIGHT: + // Now we can be sure that the light is a spot light + light->mType = aiLightSource_SPOT; + + // We wouldn't need to normalize here, but we do it + light->mDirection.x = stream->GetF4() - light->mPosition.x; + light->mDirection.y = stream->GetF4() - light->mPosition.y; + light->mDirection.z = stream->GetF4() - light->mPosition.z; + light->mDirection.Normalize(); + + // Now the hotspot and falloff angles - in degrees + light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() ); + + // FIX: the falloff angle is just an offset + light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() ); + break; + + // intensity multiplier + case Discreet3DS::CHUNK_DL_MULTIPLIER: + light->mColorDiffuse = light->mColorDiffuse * stream->GetF4(); + break; + + // light color + case Discreet3DS::CHUNK_RGBF: + case Discreet3DS::CHUNK_LINRGBF: + light->mColorDiffuse.r *= stream->GetF4(); + light->mColorDiffuse.g *= stream->GetF4(); + light->mColorDiffuse.b *= stream->GetF4(); + break; + + // light attenuation + case Discreet3DS::CHUNK_DL_ATTENUATE: + light->mAttenuationLinear = stream->GetF4(); + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseCameraChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + aiCamera* camera = mScene->mCameras.back(); + + // get chunk type + switch (chunk.Flag) + { + // near and far clip plane + case Discreet3DS::CHUNK_CAM_RANGES: + camera->mClipPlaneNear = stream->GetF4(); + camera->mClipPlaneFar = stream->GetF4(); + break; + } + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseKeyframeChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRACKCAMTGT: + case Discreet3DS::CHUNK_TRACKSPOTL: + case Discreet3DS::CHUNK_TRACKCAMERA: + case Discreet3DS::CHUNK_TRACKINFO: + case Discreet3DS::CHUNK_TRACKLIGHT: + case Discreet3DS::CHUNK_TRACKLIGTGT: + + // this starts a new mesh hierarchy chunk + ParseHierarchyChunk(chunk.Flag); + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Little helper function for ParseHierarchyChunk +void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent) +{ + if (!pcCurrent) { + mRootNode->push_back(pcNode); + return; + } + + if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) { + if(pcCurrent->mParent) { + pcCurrent->mParent->push_back(pcNode); + } + else pcCurrent->push_back(pcNode); + return; + } + return InverseNodeSearch(pcNode,pcCurrent->mParent); +} + +// ------------------------------------------------------------------------------------------------ +// Find a node with a specific name in the import hierarchy +D3DS::Node* FindNode(D3DS::Node* root, const std::string& name) +{ + if (root->mName == name) + return root; + for (std::vector::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) { + D3DS::Node* nd; + if (( nd = FindNode(*it,name))) + return nd; + } + return NULL; +} + +// ------------------------------------------------------------------------------------------------ +// Binary predicate for std::unique() +template +bool KeyUniqueCompare(const T& first, const T& second) +{ + return first.mTime == second.mTime; +} + +// ------------------------------------------------------------------------------------------------ +// Skip some additional import data. +void Discreet3DSImporter::SkipTCBInfo() +{ + unsigned int flags = stream->GetI2(); + + if (!flags) { + // Currently we can't do anything with these values. They occur + // quite rare, so it wouldn't be worth the effort implementing + // them. 3DS ist not really suitable for complex animations, + // so full support is not required. + DefaultLogger::get()->warn("3DS: Skipping TCB animation info"); + } + + if (flags & Discreet3DS::KEY_USE_TENS) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_BIAS) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_CONT) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_EASE_FROM) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_EASE_TO) { + stream->IncPtr(4); + } +} + +// ------------------------------------------------------------------------------------------------ +// Read hierarchy and keyframe info +void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRACKOBJNAME: + + // This is the name of the object to which the track applies. The chunk also + // defines the position of this object in the hierarchy. + { + + // First of all: get the name of the object + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + + while (stream->GetI1())++cnt; + std::string name = std::string(sz,cnt); + + // Now find out whether we have this node already (target animation channels + // are stored with a separate object ID) + D3DS::Node* pcNode = FindNode(mRootNode,name); + if (pcNode) + { + // Make this node the current node + mCurrentNode = pcNode; + break; + } + pcNode = new D3DS::Node(); + pcNode->mName = name; + + // There are two unknown values which we can safely ignore + stream->IncPtr(4); + + // Now read the hierarchy position of the object + uint16_t hierarchy = stream->GetI2() + 1; + pcNode->mHierarchyPos = hierarchy; + pcNode->mHierarchyIndex = mLastNodeIndex; + + // And find a proper position in the graph for it + if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) { + + // add to the parent of the last touched node + mCurrentNode->mParent->push_back(pcNode); + mLastNodeIndex++; + } + else if(hierarchy >= mLastNodeIndex) { + + // place it at the current position in the hierarchy + mCurrentNode->push_back(pcNode); + mLastNodeIndex = hierarchy; + } + else { + // need to go back to the specified position in the hierarchy. + InverseNodeSearch(pcNode,mCurrentNode); + mLastNodeIndex++; + } + // Make this node the current node + mCurrentNode = pcNode; + } + break; + + case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME: + + // This is the "real" name of a $$$DUMMY object + { + const char* sz = (const char*) stream->GetPtr(); + while (stream->GetI1()); + + // If object name is DUMMY, take this one instead + if (mCurrentNode->mName == "$$$DUMMY") { + //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); + mCurrentNode->mName = std::string(sz); + break; + } + } + break; + + case Discreet3DS::CHUNK_TRACKPIVOT: + + if ( Discreet3DS::CHUNK_TRACKINFO != parent) + { + DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object"); + break; + } + + // Pivot = origin of rotation and scaling + mCurrentNode->vPivot.x = stream->GetF4(); + mCurrentNode->vPivot.y = stream->GetF4(); + mCurrentNode->vPivot.z = stream->GetF4(); + break; + + + // //////////////////////////////////////////////////////////////////// + // POSITION KEYFRAME + case Discreet3DS::CHUNK_TRACKPOS: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + bool sortKeys = false; + + // This could also be meant as the target position for + // (targeted) lights and cameras + std::vector* l; + if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) { + l = & mCurrentNode->aTargetPositionKeys; + } + else l = & mCurrentNode->aPositionKeys; + + l->reserve(numFrames); + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + + // Setup a new position key + aiVectorKey v; + v.mTime = (double)fidx; + + SkipTCBInfo(); + v.mValue.x = stream->GetF4(); + v.mValue.y = stream->GetF4(); + v.mValue.z = stream->GetF4(); + + // check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Add the new keyframe to the list + l->push_back(v); + } + + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + + break; + + // //////////////////////////////////////////////////////////////////// + // CAMERA ROLL KEYFRAME + case Discreet3DS::CHUNK_TRACKROLL: + { + // roll keys are accepted for cameras only + if (parent != Discreet3DS::CHUNK_TRACKCAMERA) { + DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object"); + break; + } + bool sortKeys = false; + std::vector* l = &mCurrentNode->aCameraRollKeys; + + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + l->reserve(numFrames); + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + + // Setup a new position key + aiFloatKey v; + v.mTime = (double)fidx; + + // This is just a single float + SkipTCBInfo(); + v.mValue = stream->GetF4(); + + // Check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Add the new keyframe to the list + l->push_back(v); + } + + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + + + // //////////////////////////////////////////////////////////////////// + // CAMERA FOV KEYFRAME + case Discreet3DS::CHUNK_TRACKFOV: + { + DefaultLogger::get()->error("3DS: Skipping FOV animation track. " + "This is not supported"); + } + break; + + + // //////////////////////////////////////////////////////////////////// + // ROTATION KEYFRAME + case Discreet3DS::CHUNK_TRACKROTATE: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + + bool sortKeys = false; + std::vector* l = &mCurrentNode->aRotationKeys; + l->reserve(numFrames); + + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + SkipTCBInfo(); + + aiQuatKey v; + v.mTime = (double)fidx; + + // The rotation keyframe is given as an axis-angle pair + const float rad = stream->GetF4(); + aiVector3D axis; + axis.x = stream->GetF4(); + axis.y = stream->GetF4(); + axis.z = stream->GetF4(); + + if (!axis.x && !axis.y && !axis.z) + axis.y = 1.f; + + // Construct a rotation quaternion from the axis-angle pair + v.mValue = aiQuaternion(axis,rad); + + // Check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // add the new keyframe to the list + l->push_back(v); + } + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + + // //////////////////////////////////////////////////////////////////// + // SCALING KEYFRAME + case Discreet3DS::CHUNK_TRACKSCALE: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI2(); + stream->IncPtr(2); + + bool sortKeys = false; + std::vector* l = &mCurrentNode->aScalingKeys; + l->reserve(numFrames); + + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + SkipTCBInfo(); + + // Setup a new key + aiVectorKey v; + v.mTime = (double)fidx; + + // ... and read its value + v.mValue.x = stream->GetF4(); + v.mValue.y = stream->GetF4(); + v.mValue.z = stream->GetF4(); + + // check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files + if (!v.mValue.x) v.mValue.x = 1.f; + if (!v.mValue.y) v.mValue.y = 1.f; + if (!v.mValue.z) v.mValue.z = 1.f; + + l->push_back(v); + } + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a face chunk - it contains smoothing groups and material assignments +void Discreet3DSImporter::ParseFaceChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // Get the mesh we're currently working on + D3DS::Mesh& mMesh = mScene->mMeshes.back(); + + // Get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_SMOOLIST: + { + // This is the list of smoothing groups - a bitfield for every face. + // Up to 32 smoothing groups assigned to a single face. + unsigned int num = chunkSize/4, m = 0; + for (std::vector::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) { + // nth bit is set for nth smoothing group + (*i).iSmoothGroup = stream->GetI4(); + }} + break; + + case Discreet3DS::CHUNK_FACEMAT: + { + // at fist an asciiz with the material name + const char* sz = (const char*)stream->GetPtr(); + while (stream->GetI1()); + + // find the index of the material + unsigned int idx = 0xcdcdcdcd, cnt = 0; + for (std::vector::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) { + // use case independent comparisons. hopefully it will work. + if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) { + idx = cnt; + break; + } + } + if (0xcdcdcdcd == idx) { + DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz); + } + + // Now continue and read all material indices + cnt = (uint16_t)stream->GetI2(); + for (unsigned int i = 0; i < cnt;++i) { + unsigned int fidx = (uint16_t)stream->GetI2(); + + // check range + if (fidx >= mMesh.mFaceMaterials.size()) { + DefaultLogger::get()->error("3DS: Invalid face index in face material list"); + } + else mMesh.mFaceMaterials[fidx] = idx; + }} + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a mesh chunk. Here's the actual mesh data +void Discreet3DSImporter::ParseMeshChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // Get the mesh we're currently working on + D3DS::Mesh& mMesh = mScene->mMeshes.back(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_VERTLIST: + { + // This is the list of all vertices in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mPositions.reserve(num); + while (num-- > 0) { + aiVector3D v; + v.x = stream->GetF4(); + v.y = stream->GetF4(); + v.z = stream->GetF4(); + mMesh.mPositions.push_back(v); + }} + break; + case Discreet3DS::CHUNK_TRMATRIX: + { + // This is the RLEATIVE transformation matrix of the current mesh. Vertices are + // pretransformed by this matrix wonder. + mMesh.mMat.a1 = stream->GetF4(); + mMesh.mMat.b1 = stream->GetF4(); + mMesh.mMat.c1 = stream->GetF4(); + mMesh.mMat.a2 = stream->GetF4(); + mMesh.mMat.b2 = stream->GetF4(); + mMesh.mMat.c2 = stream->GetF4(); + mMesh.mMat.a3 = stream->GetF4(); + mMesh.mMat.b3 = stream->GetF4(); + mMesh.mMat.c3 = stream->GetF4(); + mMesh.mMat.a4 = stream->GetF4(); + mMesh.mMat.b4 = stream->GetF4(); + mMesh.mMat.c4 = stream->GetF4(); + } + break; + + case Discreet3DS::CHUNK_MAPLIST: + { + // This is the list of all UV coords in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mTexCoords.reserve(num); + while (num-- > 0) { + aiVector3D v; + v.x = stream->GetF4(); + v.y = stream->GetF4(); + mMesh.mTexCoords.push_back(v); + }} + break; + + case Discreet3DS::CHUNK_FACELIST: + { + // This is the list of all faces in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mFaces.reserve(num); + while (num-- > 0) { + // 3DS faces are ALWAYS triangles + mMesh.mFaces.push_back(D3DS::Face()); + D3DS::Face& sFace = mMesh.mFaces.back(); + + sFace.mIndices[0] = (uint16_t)stream->GetI2(); + sFace.mIndices[1] = (uint16_t)stream->GetI2(); + sFace.mIndices[2] = (uint16_t)stream->GetI2(); + + stream->IncPtr(2); // skip edge visibility flag + } + + // Resize the material array (0xcdcdcdcd marks the default material; so if a face is + // not referenced by a material, $$DEFAULT will be assigned to it) + mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd); + + // Larger 3DS files could have multiple FACE chunks here + chunkSize = stream->GetRemainingSizeToLimit(); + if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) ) + ParseFaceChunk(); + } + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a 3DS material chunk +void Discreet3DSImporter::ParseMaterialChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_MAT_MATNAME: + + { + // The material name string is already zero-terminated, but we need to be sure ... + const char* sz = (const char*)stream->GetPtr(); + unsigned int cnt = 0; + while (stream->GetI1()) + ++cnt; + + if (!cnt) { + // This may not be, we use the default name instead + DefaultLogger::get()->error("3DS: Empty material name"); + } + else mScene->mMaterials.back().mName = std::string(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_MAT_DIFFUSE: + { + // This is the diffuse material color + aiColor3D* pc = &mScene->mMaterials.back().mDiffuse; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk"); + pc->r = pc->g = pc->b = 1.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_SPECULAR: + { + // This is the specular material color + aiColor3D* pc = &mScene->mMaterials.back().mSpecular; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk"); + pc->r = pc->g = pc->b = 1.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_AMBIENT: + { + // This is the ambient material color + aiColor3D* pc = &mScene->mMaterials.back().mAmbient; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk"); + pc->r = pc->g = pc->b = 0.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_SELF_ILLUM: + { + // This is the emissive material color + aiColor3D* pc = &mScene->mMaterials.back().mEmissive; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk"); + pc->r = pc->g = pc->b = 0.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_TRANSPARENCY: + { + // This is the material's transparency + float* pcf = &mScene->mMaterials.back().mTransparency; + *pcf = ParsePercentageChunk(); + + // NOTE: transparency, not opacity + if (is_qnan(*pcf)) + *pcf = 1.0f; + else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_SHADING: + // This is the material shading mode + mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2(); + break; + + case Discreet3DS::CHUNK_MAT_TWO_SIDE: + // This is the two-sided flag + mScene->mMaterials.back().mTwoSided = true; + break; + + case Discreet3DS::CHUNK_MAT_SHININESS: + { // This is the shininess of the material + float* pcf = &mScene->mMaterials.back().mSpecularExponent; + *pcf = ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 0.0f; + else *pcf *= (float)0xFFFF; + } + break; + + case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: + { // This is the shininess strength of the material + float* pcf = &mScene->mMaterials.back().mShininessStrength; + *pcf = ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 0.0f; + else *pcf *= (float)0xffff / 100.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_SELF_ILPCT: + { // This is the self illumination strength of the material + float f = ParsePercentageChunk(); + if (is_qnan(f)) + f = 0.0f; + else f *= (float)0xFFFF / 100.0f; + mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f); + } + break; + + // Parse texture chunks + case Discreet3DS::CHUNK_MAT_TEXTURE: + // Diffuse texture + ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse); + break; + case Discreet3DS::CHUNK_MAT_BUMPMAP: + // Height map + ParseTextureChunk(&mScene->mMaterials.back().sTexBump); + break; + case Discreet3DS::CHUNK_MAT_OPACMAP: + // Opacity texture + ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity); + break; + case Discreet3DS::CHUNK_MAT_MAT_SHINMAP: + // Shininess map + ParseTextureChunk(&mScene->mMaterials.back().sTexShininess); + break; + case Discreet3DS::CHUNK_MAT_SPECMAP: + // Specular map + ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular); + break; + case Discreet3DS::CHUNK_MAT_SELFIMAP: + // Self-illumination (emissive) map + ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive); + break; + case Discreet3DS::CHUNK_MAT_REFLMAP: + // Reflection map + ParseTextureChunk(&mScene->mMaterials.back().sTexReflective); + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_MAPFILE: + { + // The material name string is already zero-terminated, but we need to be sure ... + const char* sz = (const char*)stream->GetPtr(); + unsigned int cnt = 0; + while (stream->GetI1()) + ++cnt; + pcOut->mMapName = std::string(sz,cnt); + } + break; + + + case Discreet3DS::CHUNK_PERCENTF: + // Manually parse the blend factor + pcOut->mTextureBlend = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_PERCENTW: + // Manually parse the blend factor + pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f; + break; + + case Discreet3DS::CHUNK_MAT_MAP_USCALE: + // Texture coordinate scaling in the U direction + pcOut->mScaleU = stream->GetF4(); + if (0.0f == pcOut->mScaleU) + { + DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1."); + pcOut->mScaleU = 1.0f; + } + break; + case Discreet3DS::CHUNK_MAT_MAP_VSCALE: + // Texture coordinate scaling in the V direction + pcOut->mScaleV = stream->GetF4(); + if (0.0f == pcOut->mScaleV) + { + DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1."); + pcOut->mScaleV = 1.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_MAP_UOFFSET: + // Texture coordinate offset in the U direction + pcOut->mOffsetU = -stream->GetF4(); + break; + + case Discreet3DS::CHUNK_MAT_MAP_VOFFSET: + // Texture coordinate offset in the V direction + pcOut->mOffsetV = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_MAT_MAP_ANG: + // Texture coordinate rotation, CCW in DEGREES + pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() ); + break; + + case Discreet3DS::CHUNK_MAT_MAP_TILING: + { + const uint16_t iFlags = stream->GetI2(); + + // Get the mapping mode (for both axes) + if (iFlags & 0x2u) + pcOut->mMapMode = aiTextureMapMode_Mirror; + + else if (iFlags & 0x10u) + pcOut->mMapMode = aiTextureMapMode_Decal; + + // wrapping in all remaining cases + else pcOut->mMapMode = aiTextureMapMode_Wrap; + } + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a percentage chunk +float Discreet3DSImporter::ParsePercentageChunk() +{ + Discreet3DS::Chunk chunk; + ReadChunk(&chunk); + + if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) + return stream->GetF4(); + else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) + return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF; + return get_qnan(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color +void Discreet3DSImporter::ParseColorChunk(aiColor3D* out, + bool acceptPercent) +{ + ai_assert(out != NULL); + + // error return value + const float qnan = get_qnan(); + static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan); + + Discreet3DS::Chunk chunk; + ReadChunk(&chunk); + const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk); + + bool bGamma = false; + + // Get the type of the chunk + switch(chunk.Flag) + { + case Discreet3DS::CHUNK_LINRGBF: + bGamma = true; + + case Discreet3DS::CHUNK_RGBF: + if (sizeof(float) * 3 > diff) { + *out = clrError; + return; + } + out->r = stream->GetF4(); + out->g = stream->GetF4(); + out->b = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_LINRGBB: + bGamma = true; + case Discreet3DS::CHUNK_RGBB: + if (sizeof(char) * 3 > diff) { + *out = clrError; + return; + } + out->r = (float)(uint8_t)stream->GetI1() / 255.0f; + out->g = (float)(uint8_t)stream->GetI1() / 255.0f; + out->b = (float)(uint8_t)stream->GetI1() / 255.0f; + break; + + // Percentage chunks are accepted, too. + case Discreet3DS::CHUNK_PERCENTF: + if (acceptPercent && 4 <= diff) { + out->g = out->b = out->r = stream->GetF4(); + break; + } + *out = clrError; + return; + + case Discreet3DS::CHUNK_PERCENTW: + if (acceptPercent && 1 <= diff) { + out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f; + break; + } + *out = clrError; + return; + + default: + stream->IncPtr(diff); + // Skip unknown chunks, hope this won't cause any problems. + return ParseColorChunk(out,acceptPercent); + }; + (void)bGamma; +} + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/3DSLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/3DSLoader.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,280 @@ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file 3DSLoader.h + * @brief 3DS File format loader + */ +#ifndef AI_3DSIMPORTER_H_INC +#define AI_3DSIMPORTER_H_INC + +#include "BaseImporter.h" +#include "assimp/types.h" + +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +struct aiNode; +#include "3DSHelper.h" + +namespace Assimp { + + +using namespace D3DS; + +// --------------------------------------------------------------------------------- +/** Importer class for 3D Studio r3 and r4 3DS files + */ +class Discreet3DSImporter : public BaseImporter +{ +public: + + Discreet3DSImporter(); + ~Discreet3DSImporter(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details + */ + const aiImporterDesc* GetInfo () const; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Converts a temporary material to the outer representation + */ + void ConvertMaterial(D3DS::Material& p_cMat, + aiMaterial& p_pcOut); + + // ------------------------------------------------------------------- + /** Read a chunk + * + * @param pcOut Receives the current chunk + */ + void ReadChunk(Discreet3DS::Chunk* pcOut); + + // ------------------------------------------------------------------- + /** Parse a percentage chunk. mCurrent will point to the next + * chunk behind afterwards. If no percentage chunk is found + * QNAN is returned. + */ + float ParsePercentageChunk(); + + // ------------------------------------------------------------------- + /** Parse a color chunk. mCurrent will point to the next + * chunk behind afterwards. If no color chunk is found + * QNAN is returned in all members. + */ + void ParseColorChunk(aiColor3D* p_pcOut, + bool p_bAcceptPercent = true); + + + // ------------------------------------------------------------------- + /** Skip a chunk in the file + */ + void SkipChunk(); + + // ------------------------------------------------------------------- + /** Generate the nodegraph + */ + void GenerateNodeGraph(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Parse a main top-level chunk in the file + */ + void ParseMainChunk(); + + // ------------------------------------------------------------------- + /** Parse a top-level chunk in the file + */ + void ParseChunk(const char* name, unsigned int num); + + // ------------------------------------------------------------------- + /** Parse a top-level editor chunk in the file + */ + void ParseEditorChunk(); + + // ------------------------------------------------------------------- + /** Parse a top-level object chunk in the file + */ + void ParseObjectChunk(); + + // ------------------------------------------------------------------- + /** Parse a material chunk in the file + */ + void ParseMaterialChunk(); + + // ------------------------------------------------------------------- + /** Parse a mesh chunk in the file + */ + void ParseMeshChunk(); + + // ------------------------------------------------------------------- + /** Parse a light chunk in the file + */ + void ParseLightChunk(); + + // ------------------------------------------------------------------- + /** Parse a camera chunk in the file + */ + void ParseCameraChunk(); + + // ------------------------------------------------------------------- + /** Parse a face list chunk in the file + */ + void ParseFaceChunk(); + + // ------------------------------------------------------------------- + /** Parse a keyframe chunk in the file + */ + void ParseKeyframeChunk(); + + // ------------------------------------------------------------------- + /** Parse a hierarchy chunk in the file + */ + void ParseHierarchyChunk(uint16_t parent); + + // ------------------------------------------------------------------- + /** Parse a texture chunk in the file + */ + void ParseTextureChunk(D3DS::Texture* pcOut); + + // ------------------------------------------------------------------- + /** Convert the meshes in the file + */ + void ConvertMeshes(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Replace the default material in the scene + */ + void ReplaceDefaultMaterial(); + + // ------------------------------------------------------------------- + /** Convert the whole scene + */ + void ConvertScene(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** generate unique vertices for a mesh + */ + void MakeUnique(D3DS::Mesh& sMesh); + + // ------------------------------------------------------------------- + /** Add a node to the node graph + */ + void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn, + aiMatrix4x4& absTrafo); + + // ------------------------------------------------------------------- + /** Search for a node in the graph. + * Called recursively + */ + void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent); + + // ------------------------------------------------------------------- + /** Apply the master scaling factor to the mesh + */ + void ApplyMasterScale(aiScene* pScene); + + // ------------------------------------------------------------------- + /** Clamp all indices in the file to a valid range + */ + void CheckIndices(D3DS::Mesh& sMesh); + + // ------------------------------------------------------------------- + /** Skip the TCB info in a track key + */ + void SkipTCBInfo(); + +protected: + + /** Stream to read from */ + StreamReaderLE* stream; + + /** Last touched node index */ + short mLastNodeIndex; + + /** Current node, root node */ + D3DS::Node* mCurrentNode, *mRootNode; + + /** Scene under construction */ + D3DS::Scene* mScene; + + /** Ambient base color of the scene */ + aiColor3D mClrAmbient; + + /** Master scaling factor of the scene */ + float mMasterScale; + + /** Path to the background image of the scene */ + std::string mBackgroundImage; + bool bHasBG; + + /** true if PRJ file */ + bool bIsPrj; +}; + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/ACLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ACLoader.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,866 @@ + +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the AC3D importer class */ + +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER + +// internal headers +#include "ACLoader.h" +#include "ParsingUtils.h" +#include "fast_atof.h" +#include "Subdivision.h" + +using namespace Assimp; + +static const aiImporterDesc desc = { + "AC3D Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ac acc ac3d" +}; + +// ------------------------------------------------------------------------------------------------ +// skip to the next token +#define AI_AC_SKIP_TO_NEXT_TOKEN() \ + if (!SkipSpaces(&buffer)) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \ + continue; \ + } + +// ------------------------------------------------------------------------------------------------ +// read a string (may be enclosed in double quotation marks). buffer must point to " +#define AI_AC_GET_STRING(out) \ + ++buffer; \ + const char* sz = buffer; \ + while ('\"' != *buffer) \ + { \ + if (IsLineEnd( *buffer )) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \ + out = "ERROR"; \ + break; \ + } \ + ++buffer; \ + } \ + if (IsLineEnd( *buffer ))continue; \ + out = std::string(sz,(unsigned int)(buffer-sz)); \ + ++buffer; + + +// ------------------------------------------------------------------------------------------------ +// read 1 to n floats prefixed with an optional predefined identifier +#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \ + AI_AC_SKIP_TO_NEXT_TOKEN(); \ + if (name_length) \ + { \ + if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \ + continue; \ + } \ + buffer += name_length+1; \ + } \ + for (unsigned int i = 0; i < num;++i) \ + { \ + AI_AC_SKIP_TO_NEXT_TOKEN(); \ + buffer = fast_atoreal_move(buffer,((float*)out)[i]); \ + } + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +AC3DImporter::AC3DImporter() +{ + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +AC3DImporter::~AC3DImporter() +{ + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + std::string extension = GetExtension(pFile); + + // fixme: are acc and ac3d *really* used? Some sources say they are + if(extension == "ac" || extension == "ac3d" || extension == "acc") { + return true; + } + if (!extension.length() || checkSig) { + uint32_t token = AI_MAKE_MAGIC("AC3D"); + return CheckMagicToken(pIOHandler,pFile,&token,1,0); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* AC3DImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Get a pointer to the next line from the file +bool AC3DImporter::GetNextLine( ) +{ + SkipLine(&buffer); + return SkipSpaces(&buffer); +} + +// ------------------------------------------------------------------------------------------------ +// Parse an object section in an AC file +void AC3DImporter::LoadObjectSection(std::vector& objects) +{ + if (!TokenMatch(buffer,"OBJECT",6)) + return; + + SkipSpaces(&buffer); + + ++mNumMeshes; + + objects.push_back(Object()); + Object& obj = objects.back(); + + aiLight* light = NULL; + if (!ASSIMP_strincmp(buffer,"light",5)) + { + // This is a light source. Add it to the list + mLights->push_back(light = new aiLight()); + + // Return a point light with no attenuation + light->mType = aiLightSource_POINT; + light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f); + light->mAttenuationConstant = 1.f; + + // Generate a default name for both the light source and the node + // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. + light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast(mLights->size())-1); + obj.name = std::string( light->mName.data ); + + DefaultLogger::get()->debug("AC3D: Light source encountered"); + obj.type = Object::Light; + } + else if (!ASSIMP_strincmp(buffer,"group",5)) + { + obj.type = Object::Group; + } + else if (!ASSIMP_strincmp(buffer,"world",5)) + { + obj.type = Object::World; + } + else obj.type = Object::Poly; + while (GetNextLine()) + { + if (TokenMatch(buffer,"kids",4)) + { + SkipSpaces(&buffer); + unsigned int num = strtoul10(buffer,&buffer); + GetNextLine(); + if (num) + { + // load the children of this object recursively + obj.children.reserve(num); + for (unsigned int i = 0; i < num; ++i) + LoadObjectSection(obj.children); + } + return; + } + else if (TokenMatch(buffer,"name",4)) + { + SkipSpaces(&buffer); + AI_AC_GET_STRING(obj.name); + + // If this is a light source, we'll also need to store + // the name of the node in it. + if (light) + { + light->mName.Set(obj.name); + } + } + else if (TokenMatch(buffer,"texture",7)) + { + SkipSpaces(&buffer); + AI_AC_GET_STRING(obj.texture); + } + else if (TokenMatch(buffer,"texrep",6)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat); + if (!obj.texRepeat.x || !obj.texRepeat.y) + obj.texRepeat = aiVector2D (1.f,1.f); + } + else if (TokenMatch(buffer,"texoff",6)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset); + } + else if (TokenMatch(buffer,"rot",3)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation); + } + else if (TokenMatch(buffer,"loc",3)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation); + } + else if (TokenMatch(buffer,"subdiv",6)) + { + SkipSpaces(&buffer); + obj.subDiv = strtoul10(buffer,&buffer); + } + else if (TokenMatch(buffer,"crease",6)) + { + SkipSpaces(&buffer); + obj.crease = fast_atof(buffer); + } + else if (TokenMatch(buffer,"numvert",7)) + { + SkipSpaces(&buffer); + + unsigned int t = strtoul10(buffer,&buffer); + obj.vertices.reserve(t); + for (unsigned int i = 0; i < t;++i) + { + if (!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet"); + break; + } + else if (!IsNumeric(*buffer)) + { + DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet"); + --buffer; // make sure the line is processed a second time + break; + } + obj.vertices.push_back(aiVector3D()); + aiVector3D& v = obj.vertices.back(); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x); + } + } + else if (TokenMatch(buffer,"numsurf",7)) + { + SkipSpaces(&buffer); + + bool Q3DWorkAround = false; + + const unsigned int t = strtoul10(buffer,&buffer); + obj.surfaces.reserve(t); + for (unsigned int i = 0; i < t;++i) + { + GetNextLine(); + if (!TokenMatch(buffer,"SURF",4)) + { + // FIX: this can occur for some files - Quick 3D for + // example writes no surf chunks + if (!Q3DWorkAround) + { + DefaultLogger::get()->warn("AC3D: SURF token was expected"); + DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled"); + } + --buffer; // make sure the line is processed a second time + // break; --- see fix notes above + + Q3DWorkAround = true; + } + SkipSpaces(&buffer); + obj.surfaces.push_back(Surface()); + Surface& surf = obj.surfaces.back(); + surf.flags = strtoul_cppstyle(buffer); + + while (1) + { + if(!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete"); + break; + } + if (TokenMatch(buffer,"mat",3)) + { + SkipSpaces(&buffer); + surf.mat = strtoul10(buffer); + } + else if (TokenMatch(buffer,"refs",4)) + { + // --- see fix notes above + if (Q3DWorkAround) + { + if (!surf.entries.empty()) + { + buffer -= 6; + break; + } + } + + SkipSpaces(&buffer); + const unsigned int m = strtoul10(buffer); + surf.entries.reserve(m); + + obj.numRefs += m; + + for (unsigned int k = 0; k < m; ++k) + { + if(!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete"); + break; + } + surf.entries.push_back(Surface::SurfaceEntry()); + Surface::SurfaceEntry& entry = surf.entries.back(); + + entry.first = strtoul10(buffer,&buffer); + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second); + } + } + else + { + + --buffer; // make sure the line is processed a second time + break; + } + } + } + } + } + DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected"); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a material from AC3DImporter::Material to aiMaterial +void AC3DImporter::ConvertMaterial(const Object& object, + const Material& matSrc, + aiMaterial& matDest) +{ + aiString s; + + if (matSrc.name.length()) + { + s.Set(matSrc.name); + matDest.AddProperty(&s,AI_MATKEY_NAME); + } + if (object.texture.length()) + { + s.Set(object.texture); + matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + // UV transformation + if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || + object.texOffset.x || object.texOffset.y) + { + aiUVTransform transform; + transform.mScaling = object.texRepeat; + transform.mTranslation = object.texOffset; + matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } + } + + matDest.AddProperty(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE); + matDest.AddProperty(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT); + matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); + matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); + + int n; + if (matSrc.shin) + { + n = aiShadingMode_Phong; + matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); + } + else n = aiShadingMode_Gouraud; + matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); + + float f = 1.f - matSrc.trans; + matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); +} + +// ------------------------------------------------------------------------------------------------ +// Converts the loaded data to the internal verbose representation +aiNode* AC3DImporter::ConvertObjectSection(Object& object, + std::vector& meshes, + std::vector& outMaterials, + const std::vector& materials, + aiNode* parent) +{ + aiNode* node = new aiNode(); + node->mParent = parent; + if (object.vertices.size()) + { + if (!object.surfaces.size() || !object.numRefs) + { + /* " An object with 7 vertices (no surfaces, no materials defined). + This is a good way of getting point data into AC3D. + The Vertex->create convex-surface/object can be used on these + vertices to 'wrap' a 3d shape around them " + (http://www.opencity.info/html/ac3dfileformat.html) + + therefore: if no surfaces are defined return point data only + */ + + DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, " + "a point list is returned"); + + meshes.push_back(new aiMesh()); + aiMesh* mesh = meshes.back(); + + mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); + aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + + for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts) + { + *verts = object.vertices[i]; + faces->mNumIndices = 1; + faces->mIndices = new unsigned int[1]; + faces->mIndices[0] = i; + } + + // use the primary material in this case. this should be the + // default material if all objects of the file contain points + // and no faces. + mesh->mMaterialIndex = 0; + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[0], *outMaterials.back()); + } + else + { + // need to generate one or more meshes for this object. + // find out how many different materials we have + typedef std::pair< unsigned int, unsigned int > IntPair; + typedef std::vector< IntPair > MatTable; + MatTable needMat(materials.size(),IntPair(0,0)); + + std::vector::iterator it,end = object.surfaces.end(); + std::vector::iterator it2,end2; + + for (it = object.surfaces.begin(); it != end; ++it) + { + register unsigned int idx = (*it).mat; + if (idx >= needMat.size()) + { + DefaultLogger::get()->error("AC3D: material index is out of range"); + idx = 0; + } + if ((*it).entries.empty()) + { + DefaultLogger::get()->warn("AC3D: surface her zero vertex references"); + } + + // validate all vertex indices to make sure we won't crash here + for (it2 = (*it).entries.begin(), + end2 = (*it).entries.end(); it2 != end2; ++it2) + { + if ((*it2).first >= object.vertices.size()) + { + DefaultLogger::get()->warn("AC3D: Invalid vertex reference"); + (*it2).first = 0; + } + } + + if (!needMat[idx].first)++node->mNumMeshes; + + switch ((*it).flags & 0xf) + { + // closed line + case 0x1: + + needMat[idx].first += (unsigned int)(*it).entries.size(); + needMat[idx].second += (unsigned int)(*it).entries.size()<<1u; + break; + + // unclosed line + case 0x2: + + needMat[idx].first += (unsigned int)(*it).entries.size()-1; + needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u; + break; + + // 0 == polygon, else unknown + default: + + if ((*it).flags & 0xf) + { + DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown"); + (*it).flags &= ~(0xf); + } + + // the number of faces increments by one, the number + // of vertices by surface.numref. + needMat[idx].first++; + needMat[idx].second += (unsigned int)(*it).entries.size(); + }; + } + unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes]; + unsigned int mat = 0; + const size_t oldm = meshes.size(); + for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); + cit != cend; ++cit, ++mat) + { + if (!(*cit).first)continue; + + // allocate a new aiMesh object + *pip++ = (unsigned int)meshes.size(); + aiMesh* mesh = new aiMesh(); + meshes.push_back(mesh); + + mesh->mMaterialIndex = (unsigned int)outMaterials.size(); + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[mat], *outMaterials.back()); + + // allocate storage for vertices and normals + mesh->mNumFaces = (*cit).first; + aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + + mesh->mNumVertices = (*cit).second; + aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + unsigned int cur = 0; + + // allocate UV coordinates, but only if the texture name for the + // surface is not empty + aiVector3D* uv = NULL; + if(object.texture.length()) + { + uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + mesh->mNumUVComponents[0] = 2; + } + + for (it = object.surfaces.begin(); it != end; ++it) + { + if (mat == (*it).mat) + { + const Surface& src = *it; + + // closed polygon + unsigned int type = (*it).flags & 0xf; + if (!type) + { + aiFace& face = *faces++; + if((face.mNumIndices = (unsigned int)src.entries.size())) + { + face.mIndices = new unsigned int[face.mNumIndices]; + for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices) + { + const Surface::SurfaceEntry& entry = src.entries[i]; + face.mIndices[i] = cur++; + + // copy vertex positions + *vertices = object.vertices[entry.first] + object.translation; + + + // copy texture coordinates + if (uv) + { + uv->x = entry.second.x; + uv->y = entry.second.y; + ++uv; + } + } + } + } + else + { + + it2 = (*it).entries.begin(); + + // either a closed or an unclosed line + register unsigned int tmp = (unsigned int)(*it).entries.size(); + if (0x2 == type)--tmp; + for (unsigned int m = 0; m < tmp;++m) + { + aiFace& face = *faces++; + + face.mNumIndices = 2; + face.mIndices = new unsigned int[2]; + face.mIndices[0] = cur++; + face.mIndices[1] = cur++; + + // copy vertex positions + *vertices++ = object.vertices[(*it2).first]; + + // copy texture coordinates + if (uv) + { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + + + if (0x1 == type && tmp-1 == m) + { + // if this is a closed line repeat its beginning now + it2 = (*it).entries.begin(); + } + else ++it2; + + // second point + *vertices++ = object.vertices[(*it2).first]; + + if (uv) + { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + } + } + } + } + } + + // Now apply catmull clark subdivision if necessary. We split meshes into + // materials which is not done by AC3D during smoothing, so we need to + // collect all meshes using the same material group. + if (object.subDiv) { + if (configEvalSubdivision) { + boost::scoped_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); + DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name); + + std::vector cpy(meshes.size()-oldm,NULL); + div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true); + std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm); + + // previous meshes are deleted vy Subdivide(). + } + else { + DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: " + +object.name); + } + } + } + } + + if (object.name.length()) + node->mName.Set(object.name); + else + { + // generate a name depending on the type of the node + switch (object.type) + { + case Object::Group: + node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++); + break; + case Object::Poly: + node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++); + break; + case Object::Light: + node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++); + break; + + // there shouldn't be more than one world, but we don't care + case Object::World: + node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++); + break; + } + } + + + // setup the local transformation matrix of the object + // compute the transformation offset to the parent node + node->mTransformation = aiMatrix4x4 ( object.rotation ); + + if (object.type == Object::Group || !object.numRefs) + { + node->mTransformation.a4 = object.translation.x; + node->mTransformation.b4 = object.translation.y; + node->mTransformation.c4 = object.translation.z; + } + + // add children to the object + if (object.children.size()) + { + node->mNumChildren = (unsigned int)object.children.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node); + } + } + + return node; +} + +// ------------------------------------------------------------------------------------------------ +void AC3DImporter::SetupProperties(const Importer* pImp) +{ + configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false; + configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void AC3DImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open AC3D file " + pFile + "."); + + // allocate storage and copy the contents of the file to a memory buffer + std::vector mBuffer2; + TextFileToBuffer(file.get(),mBuffer2); + + buffer = &mBuffer2[0]; + mNumMeshes = 0; + + lights = polys = worlds = groups = 0; + + if (::strncmp(buffer,"AC3D",4)) { + throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); + } + + // print the file format version to the console + unsigned int version = HexDigitToDecimal( buffer[4] ); + char msg[3]; + ASSIMP_itoa10(msg,3,version); + DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg); + + std::vector materials; + materials.reserve(5); + + std::vector rootObjects; + rootObjects.reserve(5); + + std::vector lights; + mLights = & lights; + + while (GetNextLine()) + { + if (TokenMatch(buffer,"MATERIAL",8)) + { + materials.push_back(Material()); + Material& mat = materials.back(); + + // manually parse the material ... sscanf would use the buldin atof ... + // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f + + AI_AC_SKIP_TO_NEXT_TOKEN(); + if ('\"' == *buffer) + { + AI_AC_GET_STRING(mat.name); + AI_AC_SKIP_TO_NEXT_TOKEN(); + } + + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans); + } + LoadObjectSection(rootObjects); + } + + if (rootObjects.empty() || !mNumMeshes) + { + throw DeadlyImportError("AC3D: No meshes have been loaded"); + } + if (materials.empty()) + { + DefaultLogger::get()->warn("AC3D: No material has been found"); + materials.push_back(Material()); + } + + mNumMeshes += (mNumMeshes>>2u) + 1; + std::vector meshes; + meshes.reserve(mNumMeshes); + + std::vector omaterials; + materials.reserve(mNumMeshes); + + // generate a dummy root if there are multiple objects on the top layer + Object* root; + if (1 == rootObjects.size()) + root = &rootObjects[0]; + else + { + root = new Object(); + } + + // now convert the imported stuff to our output data structure + pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials); + if (1 != rootObjects.size())delete root; + + if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4)) + pScene->mRootNode->mName.Set(""); + + // copy meshes + if (meshes.empty()) + { + throw DeadlyImportError("An unknown error occured during converting"); + } + pScene->mNumMeshes = (unsigned int)meshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); + + // copy materials + pScene->mNumMaterials = (unsigned int)omaterials.size(); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*)); + + // copy lights + pScene->mNumLights = (unsigned int)lights.size(); + if (lights.size()) + { + pScene->mLights = new aiLight*[lights.size()]; + ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*)); + } +} + +#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/ACLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ACLoader.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,267 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ACLoader.h + * @brief Declaration of the .ac importer class. + */ +#ifndef AI_AC3DLOADER_H_INCLUDED +#define AI_AC3DLOADER_H_INCLUDED + +#include + +#include "BaseImporter.h" +#include "assimp/types.h" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** AC3D (*.ac) importer class +*/ +class AC3DImporter : public BaseImporter +{ +public: + AC3DImporter(); + ~AC3DImporter(); + + + + // Represents an AC3D material + struct Material + { + Material() + : rgb (0.6f,0.6f,0.6f) + , spec (1.f,1.f,1.f) + , shin (0.f) + , trans (0.f) + {} + + // base color of the material + aiColor3D rgb; + + // ambient color of the material + aiColor3D amb; + + // emissive color of the material + aiColor3D emis; + + // specular color of the material + aiColor3D spec; + + // shininess exponent + float shin; + + // transparency. 0 == opaque + float trans; + + // name of the material. optional. + std::string name; + }; + + // Represents an AC3D surface + struct Surface + { + Surface() + : mat (0) + , flags (0) + {} + + unsigned int mat,flags; + + typedef std::pair SurfaceEntry; + std::vector< SurfaceEntry > entries; + }; + + // Represents an AC3D object + struct Object + { + Object() + : type (World) + , name( "" ) + , children() + , texture( "" ) + , texRepeat( 1.f, 1.f ) + , texOffset( 0.0f, 0.0f ) + , rotation() + , translation() + , vertices() + , surfaces() + , numRefs (0) + , subDiv (0) + {} + + // Type description + enum Type + { + World = 0x0, + Poly = 0x1, + Group = 0x2, + Light = 0x4 + } type; + + // name of the object + std::string name; + + // object children + std::vector children; + + // texture to be assigned to all surfaces of the object + std::string texture; + + // texture repat factors (scaling for all coordinates) + aiVector2D texRepeat, texOffset; + + // rotation matrix + aiMatrix3x3 rotation; + + // translation vector + aiVector3D translation; + + // vertices + std::vector vertices; + + // surfaces + std::vector surfaces; + + // number of indices (= num verts in verbose format) + unsigned int numRefs; + + // number of subdivisions to be performed on the + // imported data + unsigned int subDiv; + + // max angle limit for smoothing + float crease; + }; + + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details */ + const aiImporterDesc* GetInfo () const; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details*/ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list.*/ + void SetupProperties(const Importer* pImp); + +private: + + // ------------------------------------------------------------------- + /** Get the next line from the file. + * @return false if the end of the file was reached*/ + bool GetNextLine(); + + // ------------------------------------------------------------------- + /** Load the object section. This method is called recursively to + * load subobjects, the method returns after a 'kids 0' was + * encountered. + * @objects List of output objects*/ + void LoadObjectSection(std::vector& objects); + + // ------------------------------------------------------------------- + /** Convert all objects into meshes and nodes. + * @param object Current object to work on + * @param meshes Pointer to the list of output meshes + * @param outMaterials List of output materials + * @param materials Material list + * @param Scenegraph node for the object */ + aiNode* ConvertObjectSection(Object& object, + std::vector& meshes, + std::vector& outMaterials, + const std::vector& materials, + aiNode* parent = NULL); + + // ------------------------------------------------------------------- + /** Convert a material + * @param object Current object + * @param matSrc Source material description + * @param matDest Destination material to be filled */ + void ConvertMaterial(const Object& object, + const Material& matSrc, + aiMaterial& matDest); + +private: + + + // points to the next data line + const char* buffer; + + // Configuration option: if enabled, up to two meshes + // are generated per material: those faces who have + // their bf cull flags set are separated. + bool configSplitBFCull; + + // Configuration switch: subdivision surfaces are only + // evaluated if the value is true. + bool configEvalSubdivision; + + // counts how many objects we have in the tree. + // basing on this information we can find a + // good estimate how many meshes we'll have in the final scene. + unsigned int mNumMeshes; + + // current list of light sources + std::vector* mLights; + + // name counters + unsigned int lights, groups, polys, worlds; +}; + +} // end of namespace Assimp + +#endif // AI_AC3DIMPORTER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/ASELoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ASELoader.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,1317 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ASELoader.cpp + * @brief Implementation of the ASE importer class + */ + +#include "AssimpPCH.h" +#include "assimp/config.h" +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER + +// internal headers +#include "ASELoader.h" +#include "StringComparison.h" +#include "SkeletonMeshBuilder.h" +#include "TargetAnimation.h" + +// utilities +#include "fast_atof.h" + +using namespace Assimp; +using namespace Assimp::ASE; + +static const aiImporterDesc desc = { + "ASE Importer", + "", + "", + "Similar to 3DS but text-encoded", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ase ask" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ASEImporter::ASEImporter() +: noSkeletonMesh() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ASEImporter::~ASEImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const +{ + // check file extension + const std::string extension = GetExtension(pFile); + + if( extension == "ase" || extension == "ask") + return true; + + if ((!extension.length() || cs) && pIOHandler) { + const char* tokens[] = {"*3dsmax_asciiexport"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* ASEImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration options +void ASEImporter::SetupProperties(const Importer* pImp) +{ + configRecomputeNormals = (pImp->GetPropertyInteger( + AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); + + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void ASEImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) { + throw DeadlyImportError( "Failed to open ASE file " + pFile + "."); + } + + // Allocate storage and copy the contents of the file to a memory buffer + std::vector mBuffer2; + TextFileToBuffer(file.get(),mBuffer2); + + this->mBuffer = &mBuffer2[0]; + this->pcScene = pScene; + + // ------------------------------------------------------------------ + // Guess the file format by looking at the extension + // ASC is considered to be the older format 110, + // ASE is the actual version 200 (that is currently written by max) + // ------------------------------------------------------------------ + unsigned int defaultFormat; + std::string::size_type s = pFile.length()-1; + switch (pFile.c_str()[s]) { + + case 'C': + case 'c': + defaultFormat = AI_ASE_OLD_FILE_FORMAT; + break; + default: + defaultFormat = AI_ASE_NEW_FILE_FORMAT; + }; + + // Construct an ASE parser and parse the file + ASE::Parser parser(mBuffer,defaultFormat); + mParser = &parser; + mParser->Parse(); + + //------------------------------------------------------------------ + // Check whether we god at least one mesh. If we did - generate + // materials and copy meshes. + // ------------------------------------------------------------------ + if ( !mParser->m_vMeshes.empty()) { + + // If absolutely no material has been loaded from the file + // we need to generate a default material + GenerateDefaultMaterial(); + + // process all meshes + bool tookNormals = false; + std::vector avOutMeshes; + avOutMeshes.reserve(mParser->m_vMeshes.size()*2); + for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { + if ((*i).bSkip) { + continue; + } + BuildUniqueRepresentation(*i); + + // Need to generate proper vertex normals if necessary + if(GenerateNormals(*i)) { + tookNormals = true; + } + + // Convert all meshes to aiMesh objects + ConvertMeshes(*i,avOutMeshes); + } + if (tookNormals) { + DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " + "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " + "experience problems"); + } + + // Now build the output mesh list. Remove dummies + pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); + aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + for (std::vector::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { + if (!(*i)->mNumFaces) { + continue; + } + *pp++ = *i; + } + pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); + + // Build final material indices (remove submaterials and setup + // the final list) + BuildMaterialIndices(); + } + + // ------------------------------------------------------------------ + // Copy all scene graph nodes - lights, cameras, dummies and meshes + // into one huge list. + //------------------------------------------------------------------ + std::vector nodes; + nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() + + mParser->m_vCameras.size() + mParser->m_vDummies.size()); + + // Lights + for (std::vector::iterator it = mParser->m_vLights.begin(), + end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it)); + // Cameras + for (std::vector::iterator it = mParser->m_vCameras.begin(), + end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it)); + // Meshes + for (std::vector::iterator it = mParser->m_vMeshes.begin(), + end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it)); + // Dummies + for (std::vector::iterator it = mParser->m_vDummies.begin(), + end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it)); + + // build the final node graph + BuildNodes(nodes); + + // build output animations + BuildAnimations(nodes); + + // build output cameras + BuildCameras(); + + // build output lights + BuildLights(); + + // ------------------------------------------------------------------ + // If we have no meshes use the SkeletonMeshBuilder helper class + // to build a mesh for the animation skeleton + // FIXME: very strange results + // ------------------------------------------------------------------ + if (!pScene->mNumMeshes) { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + if (!noSkeletonMesh) { + SkeletonMeshBuilder skeleton(pScene); + } + } +} +// ------------------------------------------------------------------------------------------------ +void ASEImporter::GenerateDefaultMaterial() +{ + ai_assert(NULL != mParser); + + bool bHas = false; + for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { + if ((*i).bSkip)continue; + if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { + (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); + bHas = true; + } + } + if (bHas || mParser->m_vMaterials.empty()) { + // add a simple material without submaterials to the parser's list + mParser->m_vMaterials.push_back ( ASE::Material() ); + ASE::Material& mat = mParser->m_vMaterials.back(); + + mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); + mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); + mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); + mat.mShading = Discreet3DS::Gouraud; + mat.mName = AI_DEFAULT_MATERIAL_NAME; + } +} + +// ------------------------------------------------------------------------------------------------ +void ASEImporter::BuildAnimations(const std::vector& nodes) +{ + // check whether we have at least one mesh which has animations + std::vector::const_iterator i = nodes.begin(); + unsigned int iNum = 0; + for (;i != nodes.end();++i) { + + // TODO: Implement Bezier & TCB support + if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " + "This is not supported."); + } + if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " + "This is not supported."); + } + if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " + "This is not supported."); + } + + // We compare against 1 here - firstly one key is not + // really an animation and secondly MAX writes dummies + // that represent the node transformation. + if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ + ++iNum; + } + if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { + ++iNum; + } + } + if (iNum) { + // Generate a new animation channel and setup everything for it + pcScene->mNumAnimations = 1; + pcScene->mAnimations = new aiAnimation*[1]; + aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); + pcAnim->mNumChannels = iNum; + pcAnim->mChannels = new aiNodeAnim*[iNum]; + pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; + + iNum = 0; + + // Now iterate through all meshes and collect all data we can find + for (i = nodes.begin();i != nodes.end();++i) { + + ASE::BaseNode* me = *i; + if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { + // Generate an extra channel for the camera/light target. + // BuildNodes() does also generate an extra node, named + // .Target. + aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); + nd->mNodeName.Set(me->mName + ".Target"); + + // If there is no input position channel we will need + // to supply the default position from the node's + // local transformation matrix. + /*TargetAnimationHelper helper; + if (me->mAnim.akeyPositions.empty()) + { + aiMatrix4x4& mat = (*i)->mTransform; + helper.SetFixedMainAnimationChannel(aiVector3D( + mat.a4, mat.b4, mat.c4)); + } + else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); + helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); + + helper.Process(&me->mTargetAnim.akeyPositions);*/ + + // Allocate the key array and fill it + nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + + ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], + nd->mNumPositionKeys * sizeof(aiVectorKey)); + } + + if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { + // Begin a new node animation channel for this node + aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); + nd->mNodeName.Set(me->mName); + + // copy position keys + if (me->mAnim.akeyPositions.size() > 1 ) + { + // Allocate the key array and fill it + nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + + ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], + nd->mNumPositionKeys * sizeof(aiVectorKey)); + } + // copy rotation keys + if (me->mAnim.akeyRotations.size() > 1 ) { + // Allocate the key array and fill it + nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); + nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; + + // -------------------------------------------------------------------- + // Rotation keys are offsets to the previous keys. + // We have the quaternion representations of all + // of them, so we just need to concatenate all + // (unit-length) quaternions to get the absolute + // rotations. + // Rotation keys are ABSOLUTE for older files + // -------------------------------------------------------------------- + + aiQuaternion cur; + for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { + aiQuatKey q = me->mAnim.akeyRotations[a]; + + if (mParser->iFileFormat > 110) { + cur = (a ? cur*q.mValue : q.mValue); + q.mValue = cur.Normalize(); + } + nd->mRotationKeys[a] = q; + + // need this to get to Assimp quaternion conventions + nd->mRotationKeys[a].mValue.w *= -1.f; + } + } + // copy scaling keys + if (me->mAnim.akeyScaling.size() > 1 ) { + // Allocate the key array and fill it + nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); + nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; + + ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], + nd->mNumScalingKeys * sizeof(aiVectorKey)); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build output cameras +void ASEImporter::BuildCameras() +{ + if (!mParser->m_vCameras.empty()) { + pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); + pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; + + for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { + aiCamera* out = pcScene->mCameras[i] = new aiCamera(); + ASE::Camera& in = mParser->m_vCameras[i]; + + // copy members + out->mClipPlaneFar = in.mFar; + out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); + out->mHorizontalFOV = in.mFOV; + + out->mName.Set(in.mName); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build output lights +void ASEImporter::BuildLights() +{ + if (!mParser->m_vLights.empty()) { + pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); + pcScene->mLights = new aiLight*[pcScene->mNumLights]; + + for (unsigned int i = 0; i < pcScene->mNumLights;++i) { + aiLight* out = pcScene->mLights[i] = new aiLight(); + ASE::Light& in = mParser->m_vLights[i]; + + // The direction is encoded in the transformation matrix of the node. + // In 3DS MAX the light source points into negative Z direction if + // the node transformation is the identity. + out->mDirection = aiVector3D(0.f,0.f,-1.f); + + out->mName.Set(in.mName); + switch (in.mLightType) + { + case ASE::Light::TARGET: + out->mType = aiLightSource_SPOT; + out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); + out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); + break; + + case ASE::Light::DIRECTIONAL: + out->mType = aiLightSource_DIRECTIONAL; + break; + + default: + //case ASE::Light::OMNI: + out->mType = aiLightSource_POINT; + break; + }; + out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ASEImporter::AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName) +{ + aiMatrix4x4 m; + AddNodes(nodes,pcParent,szName,m); +} + +// ------------------------------------------------------------------------------------------------ +// Add meshes to a given node +void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) +{ + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { + // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) + const aiMesh* pcMesh = pcScene->mMeshes[i]; + const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; + + if (mesh == snode) { + ++node->mNumMeshes; + } + } + + if(node->mNumMeshes) { + node->mMeshes = new unsigned int[node->mNumMeshes]; + for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { + + const aiMesh* pcMesh = pcScene->mMeshes[i]; + const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; + if (mesh == snode) { + node->mMeshes[p++] = i; + + // Transform all vertices of the mesh back into their local space -> + // at the moment they are pretransformed + aiMatrix4x4 m = mesh->mTransform; + m.Inverse(); + + aiVector3D* pvCurPtr = pcMesh->mVertices; + const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; + while (pvCurPtr != pvEndPtr) { + *pvCurPtr = m * (*pvCurPtr); + pvCurPtr++; + } + + // Do the same for the normal vectors, if we have them. + // As always, inverse transpose. + if (pcMesh->mNormals) { + aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); + m3.Transpose(); + + pvCurPtr = pcMesh->mNormals; + pvEndPtr = pvCurPtr + pcMesh->mNumVertices; + while (pvCurPtr != pvEndPtr) { + *pvCurPtr = m3 * (*pvCurPtr); + pvCurPtr++; + } + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Add child nodes to a given parent node +void ASEImporter::AddNodes (const std::vector& nodes, + aiNode* pcParent, const char* szName, + const aiMatrix4x4& mat) +{ + const size_t len = szName ? ::strlen(szName) : 0; + ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); + + // Receives child nodes for the pcParent node + std::vector apcNodes; + + // Now iterate through all nodes in the scene and search for one + // which has *us* as parent. + for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { + const BaseNode* snode = *it; + if (szName) { + if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) + continue; + } + else if (snode->mParent.length()) + continue; + + (*it)->mProcessed = true; + + // Allocate a new node and add it to the output data structure + apcNodes.push_back(new aiNode()); + aiNode* node = apcNodes.back(); + + node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); + node->mParent = pcParent; + + // Setup the transformation matrix of the node + aiMatrix4x4 mParentAdjust = mat; + mParentAdjust.Inverse(); + node->mTransformation = mParentAdjust*snode->mTransform; + + // Add sub nodes - prevent stack overflow due to recursive parenting + if (node->mName != node->mParent->mName) { + AddNodes(nodes,node,node->mName.data,snode->mTransform); + } + + // Further processing depends on the type of the node + if (snode->mType == ASE::BaseNode::Mesh) { + // If the type of this node is "Mesh" we need to search + // the list of output meshes in the data structure for + // all those that belonged to this node once. This is + // slightly inconvinient here and a better solution should + // be used when this code is refactored next. + AddMeshes(snode,node); + } + else if (is_not_qnan( snode->mTargetPosition.x )) { + // If this is a target camera or light we generate a small + // child node which marks the position of the camera + // target (the direction information is contained in *this* + // node's animation track but the exact target position + // would be lost otherwise) + if (!node->mNumChildren) { + node->mChildren = new aiNode*[1]; + } + + aiNode* nd = new aiNode(); + + nd->mName.Set ( snode->mName + ".Target" ); + + nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; + nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; + nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; + + nd->mParent = node; + + // The .Target node is always the first child node + for (unsigned int m = 0; m < node->mNumChildren;++m) + node->mChildren[m+1] = node->mChildren[m]; + + node->mChildren[0] = nd; + node->mNumChildren++; + + // What we did is so great, it is at least worth a debug message + DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")"); + } + } + + // Allocate enough space for the child nodes + // We allocate one slot more in case this is a target camera/light + pcParent->mNumChildren = (unsigned int)apcNodes.size(); + if (pcParent->mNumChildren) { + pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; + + // now build all nodes for our nice new children + for (unsigned int p = 0; p < apcNodes.size();++p) + pcParent->mChildren[p] = apcNodes[p]; + } + return; +} + +// ------------------------------------------------------------------------------------------------ +// Build the output node graph +void ASEImporter::BuildNodes(std::vector& nodes) { + ai_assert(NULL != pcScene); + + // allocate the one and only root node + aiNode* root = pcScene->mRootNode = new aiNode(); + root->mName.Set(""); + + // Setup the coordinate system transformation + pcScene->mRootNode->mNumChildren = 1; + pcScene->mRootNode->mChildren = new aiNode*[1]; + aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); + ch->mParent = root; + + // Change the transformation matrix of all nodes + for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { + aiMatrix4x4& m = (*it)->mTransform; + m.Transpose(); // row-order vs column-order + } + + // add all nodes + AddNodes(nodes,ch,NULL); + + // now iterate through al nodes and find those that have not yet + // been added to the nodegraph (= their parent could not be recognized) + std::vector aiList; + for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { + if ((*it)->mProcessed) { + continue; + } + + // check whether our parent is known + bool bKnowParent = false; + + // search the list another time, starting *here* and try to find out whether + // there is a node that references *us* as a parent + for (std::vector::const_iterator it2 = nodes.begin();it2 != end; ++it2) { + if (it2 == it) { + continue; + } + + if ((*it2)->mName == (*it)->mParent) { + bKnowParent = true; + break; + } + } + if (!bKnowParent) { + aiList.push_back(*it); + } + } + + // Are there ane orphaned nodes? + if (!aiList.empty()) { + std::vector apcNodes; + apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); + + for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) + apcNodes.push_back(pcScene->mRootNode->mChildren[i]); + + delete[] pcScene->mRootNode->mChildren; + for (std::vector::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { + const ASE::BaseNode* src = *i; + + // The parent is not known, so we can assume that we must add + // this node to the root node of the whole scene + aiNode* pcNode = new aiNode(); + pcNode->mParent = pcScene->mRootNode; + pcNode->mName.Set(src->mName); + AddMeshes(src,pcNode); + AddNodes(nodes,pcNode,pcNode->mName.data); + apcNodes.push_back(pcNode); + } + + // Regenerate our output array + pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; + for (unsigned int i = 0; i < apcNodes.size();++i) + pcScene->mRootNode->mChildren[i] = apcNodes[i]; + + pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); + } + + // Reset the third color set to NULL - we used this field to store a temporary pointer + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) + pcScene->mMeshes[i]->mColors[2] = NULL; + + // The root node should not have at least one child or the file is valid + if (!pcScene->mRootNode->mNumChildren) { + throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt"); + } + + // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system + pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, + 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); +} + +// ------------------------------------------------------------------------------------------------ +// Convert the imported data to the internal verbose representation +void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { + // allocate output storage + std::vector mPositions; + std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + std::vector mVertexColors; + std::vector mNormals; + std::vector mBoneVertices; + + unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; + mPositions.resize(iSize); + + // optional texture coordinates + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { + if (!mesh.amTexCoords[i].empty()) { + amTexCoords[i].resize(iSize); + } + } + // optional vertex colors + if (!mesh.mVertexColors.empty()) { + mVertexColors.resize(iSize); + } + + // optional vertex normals (vertex normals can simply be copied) + if (!mesh.mNormals.empty()) { + mNormals.resize(iSize); + } + // bone vertices. There is no need to change the bone list + if (!mesh.mBoneVertices.empty()) { + mBoneVertices.resize(iSize); + } + + // iterate through all faces in the mesh + unsigned int iCurrent = 0, fi = 0; + for (std::vector::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { + for (unsigned int n = 0; n < 3;++n,++iCurrent) + { + mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; + + // add texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (mesh.amTexCoords[c].empty())break; + amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; + } + // add vertex colors + if (!mesh.mVertexColors.empty()) { + mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; + } + // add normal vectors + if (!mesh.mNormals.empty()) { + mNormals[iCurrent] = mesh.mNormals[fi*3+n]; + mNormals[iCurrent].Normalize(); + } + + // handle bone vertices + if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { + // (sometimes this will cause bone verts to be duplicated + // however, I' quite sure Schrompf' JoinVerticesStep + // will fix that again ...) + mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; + } + (*i).mIndices[n] = iCurrent; + } + } + + // replace the old arrays + mesh.mNormals = mNormals; + mesh.mPositions = mPositions; + mesh.mVertexColors = mVertexColors; + + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + mesh.amTexCoords[c] = amTexCoords[c]; +} + +// ------------------------------------------------------------------------------------------------ +// Copy a texture from the ASE structs to the output material +void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type) +{ + // Setup the texture name + aiString tex; + tex.Set( texture.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); + + // Setup the texture blend factor + if (is_not_qnan(texture.mTextureBlend)) + mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); + + // Setup texture UV transformations + mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Convert from ASE material to output material +void ASEImporter::ConvertMaterial(ASE::Material& mat) +{ + // LARGE TODO: Much code her is copied from 3DS ... join them maybe? + + // Allocate the output material + mat.pcInstance = new aiMaterial(); + + // At first add the base ambient color of the + // scene to the material + mat.mAmbient.r += mParser->m_clrAmbient.r; + mat.mAmbient.g += mParser->m_clrAmbient.g; + mat.mAmbient.b += mParser->m_clrAmbient.b; + + aiString name; + name.Set( mat.mName); + mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); + + // material colors + mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + + // shininess + if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) + { + mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); + } + // If there is no shininess, we can disable phong lighting + else if (D3DS::Discreet3DS::Metal == mat.mShading || + D3DS::Discreet3DS::Phong == mat.mShading || + D3DS::Discreet3DS::Blinn == mat.mShading) + { + mat.mShading = D3DS::Discreet3DS::Gouraud; + } + + // opacity + mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); + + // Two sided rendering? + if (mat.mTwoSided) + { + int i = 1; + mat.pcInstance->AddProperty(&i,1,AI_MATKEY_TWOSIDED); + } + + // shading mode + aiShadingMode eShading = aiShadingMode_NoShading; + switch (mat.mShading) + { + case D3DS::Discreet3DS::Flat: + eShading = aiShadingMode_Flat; break; + case D3DS::Discreet3DS::Phong : + eShading = aiShadingMode_Phong; break; + case D3DS::Discreet3DS::Blinn : + eShading = aiShadingMode_Blinn; break; + + // I don't know what "Wire" shading should be, + // assume it is simple lambertian diffuse (L dot N) shading + case D3DS::Discreet3DS::Wire: + { + // set the wireframe flag + unsigned int iWire = 1; + mat.pcInstance->AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); + } + case D3DS::Discreet3DS::Gouraud: + eShading = aiShadingMode_Gouraud; break; + case D3DS::Discreet3DS::Metal : + eShading = aiShadingMode_CookTorrance; break; + } + mat.pcInstance->AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); + + // DIFFUSE texture + if( mat.sTexDiffuse.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); + + // SPECULAR texture + if( mat.sTexSpecular.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); + + // AMBIENT texture + if( mat.sTexAmbient.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); + + // OPACITY texture + if( mat.sTexOpacity.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); + + // EMISSIVE texture + if( mat.sTexEmissive.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); + + // BUMP texture + if( mat.sTexBump.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); + + // SHININESS texture + if( mat.sTexShininess.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); + + // store the name of the material itself, too + if( mat.mName.length() > 0) { + aiString tex;tex.Set( mat.mName); + mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +// Build output meshes +void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMeshes) +{ + // validate the material index of the mesh + if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { + mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; + DefaultLogger::get()->warn("Material index is out of range"); + } + + // If the material the mesh is assigned to is consisting of submeshes, split it + if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { + std::vector vSubMaterials = mParser-> + m_vMaterials[mesh.iMaterialIndex].avSubMaterials; + + std::vector* aiSplit = new std::vector[vSubMaterials.size()]; + + // build a list of all faces per submaterial + for (unsigned int i = 0; i < mesh.mFaces.size();++i) { + // check range + if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { + DefaultLogger::get()->warn("Submaterial index is out of range"); + + // use the last material instead + aiSplit[vSubMaterials.size()-1].push_back(i); + } + else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); + } + + // now generate submeshes + for (unsigned int p = 0; p < vSubMaterials.size();++p) { + if (!aiSplit[p].empty()) { + + aiMesh* p_pcOut = new aiMesh(); + p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // let the sub material index + p_pcOut->mMaterialIndex = p; + + // we will need this material + mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; + + // store the real index here ... color channel 3 + p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; + + // store a pointer to the mesh in color channel 2 + p_pcOut->mColors[2] = (aiColor4D*) &mesh; + avOutMeshes.push_back(p_pcOut); + + // convert vertices + p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; + p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); + + // receive output vertex weights + std::vector > *avOutputBones = NULL; + if (!mesh.mBones.empty()) { + avOutputBones = new std::vector >[mesh.mBones.size()]; + } + + // allocate enough storage for faces + p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; + + unsigned int iBase = 0,iIndex; + if (p_pcOut->mNumVertices) { + p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; + p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + + iIndex = aiSplit[p][q]; + + p_pcOut->mFaces[q].mIndices = new unsigned int[3]; + p_pcOut->mFaces[q].mNumIndices = 3; + + for (unsigned int t = 0; t < 3;++t, ++iBase) { + const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; + + p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; + p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; + + // convert bones, if existing + if (!mesh.mBones.empty()) { + // check whether there is a vertex weight for this vertex index + if (iIndex2 < mesh.mBoneVertices.size()) { + + for (std::vector >::const_iterator + blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); + blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { + + // NOTE: illegal cases have already been filtered out + avOutputBones[(*blubb).first].push_back(std::pair( + iBase,(*blubb).second)); + } + } + } + p_pcOut->mFaces[q].mIndices[t] = iBase; + } + } + } + // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (!mesh.amTexCoords[c].empty()) + { + p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; + iBase = 0; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + iIndex = aiSplit[p][q]; + for (unsigned int t = 0; t < 3;++t) { + p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; + } + } + // Setup the number of valid vertex components + p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; + } + } + + // Convert vertex colors (only one set supported) + if (!mesh.mVertexColors.empty()){ + p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; + iBase = 0; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + iIndex = aiSplit[p][q]; + for (unsigned int t = 0; t < 3;++t) { + p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; + } + } + } + // Copy bones + if (!mesh.mBones.empty()) { + p_pcOut->mNumBones = 0; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; + aiBone** pcBone = p_pcOut->mBones; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + { + if (!avOutputBones[mrspock].empty()) { + // we will need this bone. add it to the output mesh and + // add all per-vertex weights + aiBone* pc = *pcBone = new aiBone(); + pc->mName.Set(mesh.mBones[mrspock].mName); + + pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) + { + const std::pair& ref = avOutputBones[mrspock][captainkirk]; + pc->mWeights[captainkirk].mVertexId = ref.first; + pc->mWeights[captainkirk].mWeight = ref.second; + } + ++pcBone; + } + } + // delete allocated storage + delete[] avOutputBones; + } + } + } + // delete storage + delete[] aiSplit; + } + else + { + // Otherwise we can simply copy the data to one output mesh + // This codepath needs less memory and uses fast memcpy()s + // to do the actual copying. So I think it is worth the + // effort here. + + aiMesh* p_pcOut = new aiMesh(); + p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // set an empty sub material index + p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; + mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; + + // store the real index here ... in color channel 3 + p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; + + // store a pointer to the mesh in color channel 2 + p_pcOut->mColors[2] = (aiColor4D*) &mesh; + avOutMeshes.push_back(p_pcOut); + + // If the mesh hasn't faces or vertices, there are two cases + // possible: 1. the model is invalid. 2. This is a dummy + // helper object which we are going to remove later ... + if (mesh.mFaces.empty() || mesh.mPositions.empty()) { + return; + } + + // convert vertices + p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); + p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); + + // allocate enough storage for faces + p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; + + // copy vertices + p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; + memcpy(p_pcOut->mVertices,&mesh.mPositions[0], + mesh.mPositions.size() * sizeof(aiVector3D)); + + // copy normals + p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; + memcpy(p_pcOut->mNormals,&mesh.mNormals[0], + mesh.mNormals.size() * sizeof(aiVector3D)); + + // copy texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (!mesh.amTexCoords[c].empty()) { + p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; + memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], + mesh.amTexCoords[c].size() * sizeof(aiVector3D)); + + // setup the number of valid vertex components + p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; + } + } + + // copy vertex colors + if (!mesh.mVertexColors.empty()) { + p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; + memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], + mesh.mVertexColors.size() * sizeof(aiColor4D)); + } + + // copy faces + for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { + p_pcOut->mFaces[iFace].mNumIndices = 3; + p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; + + // copy indices + p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; + p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; + p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; + } + + // copy vertex bones + if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { + std::vector > avBonesOut( mesh.mBones.size() ); + + // find all vertex weights for this bone + unsigned int quak = 0; + for (std::vector::const_iterator harrypotter = mesh.mBoneVertices.begin(); + harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { + + for (std::vector >::const_iterator + ronaldweasley = (*harrypotter).mBoneWeights.begin(); + ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) + { + aiVertexWeight weight; + weight.mVertexId = quak; + weight.mWeight = (*ronaldweasley).second; + avBonesOut[(*ronaldweasley).first].push_back(weight); + } + } + + // now build a final bone list + p_pcOut->mNumBones = 0; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) + if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; + aiBone** pcBone = p_pcOut->mBones; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { + if (!avBonesOut[jfkennedy].empty()) { + aiBone* pc = *pcBone = new aiBone(); + pc->mName.Set(mesh.mBones[jfkennedy].mName); + pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], + sizeof(aiVertexWeight) * pc->mNumWeights); + ++pcBone; + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup proper material indices and build output materials +void ASEImporter::BuildMaterialIndices() +{ + ai_assert(NULL != pcScene); + + // iterate through all materials and check whether we need them + for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) + { + ASE::Material& mat = mParser->m_vMaterials[iMat]; + if (mat.bNeed) { + // Convert it to the aiMaterial layout + ConvertMaterial(mat); + ++pcScene->mNumMaterials; + } + for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) + { + ASE::Material& submat = mat.avSubMaterials[iSubMat]; + if (submat.bNeed) { + // Convert it to the aiMaterial layout + ConvertMaterial(submat); + ++pcScene->mNumMaterials; + } + } + } + + // allocate the output material array + pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; + D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; + + unsigned int iNum = 0; + for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { + ASE::Material& mat = mParser->m_vMaterials[iMat]; + if (mat.bNeed) + { + ai_assert(NULL != mat.pcInstance); + pcScene->mMaterials[iNum] = mat.pcInstance; + + // Store the internal material, too + pcIntMaterials[iNum] = &mat; + + // Iterate through all meshes and search for one which is using + // this top-level material index + for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) + { + aiMesh* mesh = pcScene->mMeshes[iMesh]; + if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && + iMat == (uintptr_t)mesh->mColors[3]) + { + mesh->mMaterialIndex = iNum; + mesh->mColors[3] = NULL; + } + } + iNum++; + } + for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { + ASE::Material& submat = mat.avSubMaterials[iSubMat]; + if (submat.bNeed) { + ai_assert(NULL != submat.pcInstance); + pcScene->mMaterials[iNum] = submat.pcInstance; + + // Store the internal material, too + pcIntMaterials[iNum] = &submat; + + // Iterate through all meshes and search for one which is using + // this sub-level material index + for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { + aiMesh* mesh = pcScene->mMeshes[iMesh]; + + if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { + mesh->mMaterialIndex = iNum; + mesh->mColors[3] = NULL; + } + } + iNum++; + } + } + } + + // Dekete our temporary array + delete[] pcIntMaterials; +} + +// ------------------------------------------------------------------------------------------------ +// Generate normal vectors basing on smoothing groups +bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { + + if (!mesh.mNormals.empty() && !configRecomputeNormals) + { + // Check whether there are only uninitialized normals. If there are + // some, skip all normals from the file and compute them on our own + for (std::vector::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { + if ((*qq).x || (*qq).y || (*qq).z) + { + return true; + } + } + } + // The array is reused. + ComputeNormalsWithSmoothingsGroups(mesh); + return false; +} + +#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/ASELoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ASELoader.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,205 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ASELoader.h + * @brief Definition of the .ASE importer class. + */ +#ifndef AI_ASELOADER_H_INCLUDED +#define AI_ASELOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "assimp/types.h" + +struct aiNode; +#include "ASEParser.h" + +namespace Assimp { + + +// -------------------------------------------------------------------------------- +/** Importer class for the 3DS ASE ASCII format. + * + */ +class ASEImporter : public BaseImporter { +public: + ASEImporter(); + ~ASEImporter(); + + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details + */ + const aiImporterDesc* GetInfo () const; + + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + +private: + + // ------------------------------------------------------------------- + /** Generate normal vectors basing on smoothing groups + * (in some cases the normal are already contained in the file) + * \param mesh Mesh to work on + * \return false if the normals have been recomputed + */ + bool GenerateNormals(ASE::Mesh& mesh); + + + // ------------------------------------------------------------------- + /** Create valid vertex/normal/UV/color/face lists. + * All elements are unique, faces have only one set of indices + * after this step occurs. + * \param mesh Mesh to work on + */ + void BuildUniqueRepresentation(ASE::Mesh& mesh); + + + /** Create one-material-per-mesh meshes ;-) + * \param mesh Mesh to work with + * \param Receives the list of all created meshes + */ + void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); + + + // ------------------------------------------------------------------- + /** Convert a material to a aiMaterial object + * \param mat Input material + */ + void ConvertMaterial(ASE::Material& mat); + + + // ------------------------------------------------------------------- + /** Setup the final material indices for each mesh + */ + void BuildMaterialIndices(); + + + // ------------------------------------------------------------------- + /** Build the node graph + */ + void BuildNodes(std::vector& nodes); + + + // ------------------------------------------------------------------- + /** Build output cameras + */ + void BuildCameras(); + + + // ------------------------------------------------------------------- + /** Build output lights + */ + void BuildLights(); + + + // ------------------------------------------------------------------- + /** Build output animations + */ + void BuildAnimations(const std::vector& nodes); + + + // ------------------------------------------------------------------- + /** Add sub nodes to a node + * \param pcParent parent node to be filled + * \param szName Name of the parent node + * \param matrix Current transform + */ + void AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName); + + void AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName, + const aiMatrix4x4& matrix); + + void AddMeshes(const ASE::BaseNode* snode,aiNode* node); + + // ------------------------------------------------------------------- + /** Generate a default material and add it to the parser's list + * Called if no material has been found in the file (rare for ASE, + * but not impossible) + */ + void GenerateDefaultMaterial(); + +protected: + + /** Parser instance */ + ASE::Parser* mParser; + + /** Buffer to hold the loaded file */ + char* mBuffer; + + /** Scene to be filled */ + aiScene* pcScene; + + /** Config options: Recompute the normals in every case - WA + for 3DS Max broken ASE normal export */ + bool configRecomputeNormals; + bool noSkeletonMesh; +}; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/ASEParser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ASEParser.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,2153 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ASEParser.cpp + * @brief Implementation of the ASE parser class + */ + +#include "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER + +// internal headers +#include "TextureTransform.h" +#include "ASELoader.h" +#include "MaterialSystem.h" +#include "fast_atof.h" + +using namespace Assimp; +using namespace Assimp::ASE; + + +// ------------------------------------------------------------------------------------------------ +// Begin an ASE parsing function + +#define AI_ASE_PARSER_INIT() \ + int iDepth = 0; + +// ------------------------------------------------------------------------------------------------ +// Handle a "top-level" section in the file. EOF is no error in this case. + +#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ + else if ('{' == *filePtr)iDepth++; \ + else if ('}' == *filePtr) \ + { \ + if (0 == --iDepth) \ + { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } \ + else if ('\0' == *filePtr) \ + { \ + return; \ + } \ + if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ + { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +// Handle a nested section in the file. EOF is an error in this case +// @param level "Depth" of the section +// @param msg Full name of the section (including the asterisk) + +#define AI_ASE_HANDLE_SECTION(level, msg) \ + if ('{' == *filePtr)iDepth++; \ + else if ('}' == *filePtr) \ + { \ + if (0 == --iDepth) \ + { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } \ + else if ('\0' == *filePtr) \ + { \ + LogError("Encountered unexpected EOL while parsing a " msg \ + " chunk (Level " level ")"); \ + } \ + if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ + { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +Parser::Parser (const char* szFile, unsigned int fileFormatDefault) +{ + ai_assert(NULL != szFile); + filePtr = szFile; + iFileFormat = fileFormatDefault; + + // make sure that the color values are invalid + m_clrBackground.r = get_qnan(); + m_clrAmbient.r = get_qnan(); + + // setup some default values + iLineNumber = 0; + iFirstFrame = 0; + iLastFrame = 0; + iFrameSpeed = 30; // use 30 as default value for this property + iTicksPerFrame = 1; // use 1 as default value for this property + bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogWarning(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); +#endif + + // output the warning to the logger ... + DefaultLogger::get()->warn(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogInfo(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); +#endif + + // output the information to the logger ... + DefaultLogger::get()->info(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogError(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn); +#endif + + // throw an exception + throw DeadlyImportError(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipToNextToken() +{ + while (true) + { + char me = *filePtr; + + // increase the line number counter if necessary + if (IsLineEnd(me) && !bLastWasEndLine) + { + ++iLineNumber; + bLastWasEndLine = true; + } + else bLastWasEndLine = false; + if ('*' == me || '}' == me || '{' == me)return true; + if ('\0' == me)return false; + + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipSection() +{ + // must handle subsections ... + int iCnt = 0; + while (true) + { + if ('}' == *filePtr) + { + --iCnt; + if (0 == iCnt) + { + // go to the next valid token ... + ++filePtr; + SkipToNextToken(); + return true; + } + } + else if ('{' == *filePtr) + { + ++iCnt; + } + else if ('\0' == *filePtr) + { + LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); + return false; + } + else if(IsLineEnd(*filePtr))++iLineNumber; + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::Parse() +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Version should be 200. Validate this ... + if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18)) + { + unsigned int fmt; + ParseLV4MeshLong(fmt); + + if (fmt > 200) + { + LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ + be <= 200"); + } + // ************************************************************* + // - fmt will be 0 if we're unable to read the version number + // there are some faulty files without a version number ... + // in this case we'll guess the exact file format by looking + // at the file extension (ASE, ASK, ASC) + // ************************************************************* + + if (fmt)iFileFormat = fmt; + continue; + } + // main scene information + if (TokenMatch(filePtr,"SCENE",5)) + { + ParseLV1SceneBlock(); + continue; + } + // "group" - no implementation yet, in facte + // we're just ignoring them for the moment + if (TokenMatch(filePtr,"GROUP",5)) + { + Parse(); + continue; + } + // material list + if (TokenMatch(filePtr,"MATERIAL_LIST",13)) + { + ParseLV1MaterialListBlock(); + continue; + } + // geometric object (mesh) + if (TokenMatch(filePtr,"GEOMOBJECT",10)) + + { + m_vMeshes.push_back(Mesh()); + ParseLV1ObjectBlock(m_vMeshes.back()); + continue; + } + // helper object = dummy in the hierarchy + if (TokenMatch(filePtr,"HELPEROBJECT",12)) + + { + m_vDummies.push_back(Dummy()); + ParseLV1ObjectBlock(m_vDummies.back()); + continue; + } + // light object + if (TokenMatch(filePtr,"LIGHTOBJECT",11)) + + { + m_vLights.push_back(Light()); + ParseLV1ObjectBlock(m_vLights.back()); + continue; + } + // camera object + if (TokenMatch(filePtr,"CAMERAOBJECT",12)) + { + m_vCameras.push_back(Camera()); + ParseLV1ObjectBlock(m_vCameras.back()); + continue; + } + // comment - print it on the console + if (TokenMatch(filePtr,"COMMENT",7)) + { + std::string out = ""; + ParseString(out,"*COMMENT"); + LogInfo(("Comment: " + out).c_str()); + continue; + } + // ASC bone weights + if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18)) + { + ParseLV1SoftSkinBlock(); + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SoftSkinBlock() +{ + // TODO: fix line counting here + + // ************************************************************** + // The soft skin block is formatted differently. There are no + // nested sections supported and the single elements aren't + // marked by keywords starting with an asterisk. + + /** + FORMAT BEGIN + + *MESH_SOFTSKINVERTS { + + + + [for times:] + [for times:] + } + + FORMAT END + */ + // ************************************************************** + while (true) + { + if (*filePtr == '}' ) {++filePtr;return;} + else if (*filePtr == '\0') return; + else if (*filePtr == '{' ) ++filePtr; + + else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) + { + ASE::Mesh* curMesh = NULL; + unsigned int numVerts = 0; + + const char* sz = filePtr; + while (!IsSpaceOrNewLine(*filePtr))++filePtr; + + const unsigned int diff = (unsigned int)(filePtr-sz); + if (diff) + { + std::string name = std::string(sz,diff); + for (std::vector::iterator it = m_vMeshes.begin(); + it != m_vMeshes.end(); ++it) + { + if ((*it).mName == name) + { + curMesh = & (*it); + break; + } + } + if (!curMesh) + { + LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); + + // Skip the mesh data - until we find a new mesh + // or the end of the *MESH_SOFTSKINVERTS section + while (true) + { + SkipSpacesAndLineEnd(&filePtr); + if (*filePtr == '}') + {++filePtr;return;} + else if (!IsNumeric(*filePtr)) + break; + + SkipLine(&filePtr); + } + } + else + { + SkipSpacesAndLineEnd(&filePtr); + ParseLV4MeshLong(numVerts); + + // Reserve enough storage + curMesh->mBoneVertices.reserve(numVerts); + + for (unsigned int i = 0; i < numVerts;++i) + { + SkipSpacesAndLineEnd(&filePtr); + unsigned int numWeights; + ParseLV4MeshLong(numWeights); + + curMesh->mBoneVertices.push_back(ASE::BoneVertex()); + ASE::BoneVertex& vert = curMesh->mBoneVertices.back(); + + // Reserve enough storage + vert.mBoneWeights.reserve(numWeights); + + for (unsigned int w = 0; w < numWeights;++w) + { + std::string bone; + ParseString(bone,"*MESH_SOFTSKINVERTS.Bone"); + + // Find the bone in the mesh's list + std::pair me; + me.first = -1; + + for (unsigned int n = 0; n < curMesh->mBones.size();++n) + { + if (curMesh->mBones[n].mName == bone) + { + me.first = n; + break; + } + } + if (-1 == me.first) + { + // We don't have this bone yet, so add it to the list + me.first = (int)curMesh->mBones.size(); + curMesh->mBones.push_back(ASE::Bone(bone)); + } + ParseLV4MeshFloat( me.second ); + + // Add the new bone weight to list + vert.mBoneWeights.push_back(me); + } + } + } + } + } + ++filePtr; + SkipSpacesAndLineEnd(&filePtr); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SceneBlock() +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple( &m_clrBackground.r ); + continue; + } + if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple( &m_clrAmbient.r ); + continue; + } + if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16)) + { + ParseLV4MeshLong(iFirstFrame); + continue; + } + if (TokenMatch(filePtr,"SCENE_LASTFRAME",15)) + { + ParseLV4MeshLong(iLastFrame); + continue; + } + if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16)) + { + ParseLV4MeshLong(iFrameSpeed); + continue; + } + if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19)) + { + ParseLV4MeshLong(iTicksPerFrame); + continue; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1MaterialListBlock() +{ + AI_ASE_PARSER_INIT(); + + unsigned int iMaterialCount = 0; + unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"MATERIAL_COUNT",14)) + { + ParseLV4MeshLong(iMaterialCount); + + // now allocate enough storage to hold all materials + m_vMaterials.resize(iOldMaterialCount+iMaterialCount); + continue; + } + if (TokenMatch(filePtr,"MATERIAL",8)) + { + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iMaterialCount) + { + LogWarning("Out of range: material index is too large"); + iIndex = iMaterialCount-1; + } + + // get a reference to the material + Material& sMat = m_vMaterials[iIndex+iOldMaterialCount]; + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MaterialBlock(ASE::Material& mat) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumSubMaterials = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"MATERIAL_NAME",13)) + { + if (!ParseString(mat.mName,"*MATERIAL_NAME")) + SkipToNextToken(); + continue; + } + // ambient material color + if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16)) + { + ParseLV4MeshFloatTriple(&mat.mAmbient.r); + continue; + } + // diffuse material color + if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) ) + { + ParseLV4MeshFloatTriple(&mat.mDiffuse.r); + continue; + } + // specular material color + if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17)) + { + ParseLV4MeshFloatTriple(&mat.mSpecular.r); + continue; + } + // material shading type + if (TokenMatch(filePtr,"MATERIAL_SHADING",16)) + { + if (TokenMatch(filePtr,"Blinn",5)) + { + mat.mShading = Discreet3DS::Blinn; + } + else if (TokenMatch(filePtr,"Phong",5)) + { + mat.mShading = Discreet3DS::Phong; + } + else if (TokenMatch(filePtr,"Flat",4)) + { + mat.mShading = Discreet3DS::Flat; + } + else if (TokenMatch(filePtr,"Wire",4)) + { + mat.mShading = Discreet3DS::Wire; + } + else + { + // assume gouraud shading + mat.mShading = Discreet3DS::Gouraud; + SkipToNextToken(); + } + continue; + } + // material transparency + if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21)) + { + ParseLV4MeshFloat(mat.mTransparency); + mat.mTransparency = 1.0f - mat.mTransparency;continue; + } + // material self illumination + if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18)) + { + float f = 0.0f; + ParseLV4MeshFloat(f); + + mat.mEmissive.r = f; + mat.mEmissive.g = f; + mat.mEmissive.b = f; + continue; + } + // material shininess + if (TokenMatch(filePtr,"MATERIAL_SHINE",14) ) + { + ParseLV4MeshFloat(mat.mSpecularExponent); + mat.mSpecularExponent *= 15; + continue; + } + // two-sided material + if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) ) + { + mat.mTwoSided = true; + continue; + } + // material shininess strength + if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22)) + { + ParseLV4MeshFloat(mat.mShininessStrength); + continue; + } + // diffuse color map + if (TokenMatch(filePtr,"MAP_DIFFUSE",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexDiffuse); + continue; + } + // ambient color map + if (TokenMatch(filePtr,"MAP_AMBIENT",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexAmbient); + continue; + } + // specular color map + if (TokenMatch(filePtr,"MAP_SPECULAR",12)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexSpecular); + continue; + } + // opacity map + if (TokenMatch(filePtr,"MAP_OPACITY",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexOpacity); + continue; + } + // emissive map + if (TokenMatch(filePtr,"MAP_SELFILLUM",13)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexEmissive); + continue; + } + // bump map + if (TokenMatch(filePtr,"MAP_BUMP",8)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexBump); + } + // specular/shininess map + if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexShininess); + continue; + } + // number of submaterials + if (TokenMatch(filePtr,"NUMSUBMTLS",10)) + { + ParseLV4MeshLong(iNumSubMaterials); + + // allocate enough storage + mat.avSubMaterials.resize(iNumSubMaterials); + } + // submaterial chunks + if (TokenMatch(filePtr,"SUBMATERIAL",11)) + { + + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iNumSubMaterials) + { + LogWarning("Out of range: submaterial index is too large"); + iIndex = iNumSubMaterials-1; + } + + // get a reference to the material + Material& sMat = mat.avSubMaterials[iIndex]; + + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","*MATERIAL"); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MapBlock(Texture& map) +{ + AI_ASE_PARSER_INIT(); + + // *********************************************************** + // *BITMAP should not be there if *MAP_CLASS is not BITMAP, + // but we need to expect that case ... if the path is + // empty the texture won't be used later. + // *********************************************************** + bool parsePath = true; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // type of map + if (TokenMatch(filePtr,"MAP_CLASS" ,9)) + { + std::string temp; + if(!ParseString(temp,"*MAP_CLASS")) + SkipToNextToken(); + if (temp != "Bitmap" && temp != "Normal Bump") + { + DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp); + parsePath = false; + } + continue; + } + // path to the texture + if (parsePath && TokenMatch(filePtr,"BITMAP" ,6)) + { + if(!ParseString(map.mMapName,"*BITMAP")) + SkipToNextToken(); + + if (map.mMapName == "None") + { + // Files with 'None' as map name are produced by + // an Maja to ASE exporter which name I forgot .. + DefaultLogger::get()->warn("ASE: Skipping invalid map entry"); + map.mMapName = ""; + } + + continue; + } + // offset on the u axis + if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12)) + { + ParseLV4MeshFloat(map.mOffsetU); + continue; + } + // offset on the v axis + if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12)) + { + ParseLV4MeshFloat(map.mOffsetV); + continue; + } + // tiling on the u axis + if (TokenMatch(filePtr,"UVW_U_TILING" ,12)) + { + ParseLV4MeshFloat(map.mScaleU); + continue; + } + // tiling on the v axis + if (TokenMatch(filePtr,"UVW_V_TILING" ,12)) + { + ParseLV4MeshFloat(map.mScaleV); + continue; + } + // rotation around the z-axis + if (TokenMatch(filePtr,"UVW_ANGLE" ,9)) + { + ParseLV4MeshFloat(map.mRotation); + continue; + } + // map blending factor + if (TokenMatch(filePtr,"MAP_AMOUNT" ,10)) + { + ParseLV4MeshFloat(map.mTextureBlend); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::ParseString(std::string& out,const char* szName) +{ + char szBuffer[1024]; + if (!SkipSpaces(&filePtr)) + { + + sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName); + LogWarning(szBuffer); + return false; + } + // there must be '"' + if ('\"' != *filePtr) + { + + sprintf(szBuffer,"Unable to parse %s block: Strings are expected " + "to be enclosed in double quotation marks",szName); + LogWarning(szBuffer); + return false; + } + ++filePtr; + const char* sz = filePtr; + while (true) + { + if ('\"' == *sz)break; + else if ('\0' == *sz) + { + sprintf(szBuffer,"Unable to parse %s block: Strings are expected to " + "be enclosed in double quotation marks but EOF was reached before " + "a closing quotation mark was encountered",szName); + LogWarning(szBuffer); + return false; + } + sz++; + } + out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr); + filePtr = sz+1; + return true; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // first process common tokens such as node name and transform + // name of the mesh/node + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + if(!ParseString(node.mName,"*NODE_NAME")) + SkipToNextToken(); + continue; + } + // name of the parent of the node + if (TokenMatch(filePtr,"NODE_PARENT" ,11) ) + { + if(!ParseString(node.mParent,"*NODE_PARENT")) + SkipToNextToken(); + continue; + } + // transformation matrix of the node + if (TokenMatch(filePtr,"NODE_TM" ,7)) + { + ParseLV2NodeTransformBlock(node); + continue; + } + // animation data of the node + if (TokenMatch(filePtr,"TM_ANIMATION" ,12)) + { + ParseLV2AnimationBlock(node); + continue; + } + + if (node.mType == BaseNode::Light) + { + // light settings + if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14)) + { + ParseLV2LightSettingsBlock((ASE::Light&)node); + continue; + } + // type of the light source + if (TokenMatch(filePtr,"LIGHT_TYPE" ,10)) + { + if (!ASSIMP_strincmp("omni",filePtr,4)) + { + ((ASE::Light&)node).mLightType = ASE::Light::OMNI; + } + else if (!ASSIMP_strincmp("target",filePtr,6)) + { + ((ASE::Light&)node).mLightType = ASE::Light::TARGET; + } + else if (!ASSIMP_strincmp("free",filePtr,4)) + { + ((ASE::Light&)node).mLightType = ASE::Light::FREE; + } + else if (!ASSIMP_strincmp("directional",filePtr,11)) + { + ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL; + } + else + { + LogWarning("Unknown kind of light source"); + } + continue; + } + } + else if (node.mType == BaseNode::Camera) + { + // Camera settings + if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15)) + { + ParseLV2CameraSettingsBlock((ASE::Camera&)node); + continue; + } + else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11)) + { + if (!ASSIMP_strincmp("target",filePtr,6)) + { + ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET; + } + else if (!ASSIMP_strincmp("free",filePtr,4)) + { + ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE; + } + else + { + LogWarning("Unknown kind of camera"); + } + continue; + } + } + else if (node.mType == BaseNode::Mesh) + { + // mesh data + // FIX: Older files use MESH_SOFTSKIN + if (TokenMatch(filePtr,"MESH" ,4) || + TokenMatch(filePtr,"MESH_SOFTSKIN",13)) + { + ParseLV2MeshBlock((ASE::Mesh&)node); + continue; + } + // mesh material index + if (TokenMatch(filePtr,"MATERIAL_REF" ,12)) + { + ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex); + continue; + } + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"CAMERA_NEAR" ,11)) + { + ParseLV4MeshFloat(camera.mNear); + continue; + } + if (TokenMatch(filePtr,"CAMERA_FAR" ,10)) + { + ParseLV4MeshFloat(camera.mFar); + continue; + } + if (TokenMatch(filePtr,"CAMERA_FOV" ,10)) + { + ParseLV4MeshFloat(camera.mFOV); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2LightSettingsBlock(ASE::Light& light) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"LIGHT_COLOR" ,11)) + { + ParseLV4MeshFloatTriple(&light.mColor.r); + continue; + } + if (TokenMatch(filePtr,"LIGHT_INTENS" ,12)) + { + ParseLV4MeshFloat(light.mIntensity); + continue; + } + if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13)) + { + ParseLV4MeshFloat(light.mAngle); + continue; + } + if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13)) + { + ParseLV4MeshFloat(light.mFalloff); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh) +{ + AI_ASE_PARSER_INIT(); + + ASE::Animation* anim = &mesh.mAnim; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + std::string temp; + if(!ParseString(temp,"*NODE_NAME")) + SkipToNextToken(); + + // If the name of the node contains .target it + // represents an animated camera or spot light + // target. + if (std::string::npos != temp.find(".Target")) + { + if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) && + ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET)) + { + + DefaultLogger::get()->error("ASE: Found target animation channel " + "but the node is neither a camera nor a spot light"); + anim = NULL; + } + else anim = &mesh.mTargetAnim; + } + continue; + } + + // position keyframes + if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) || + TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) || + TokenMatch(filePtr,"CONTROL_POS_TCB" ,15)) + { + if (!anim)SkipSection(); + else ParseLV3PosAnimationBlock(*anim); + continue; + } + // scaling keyframes + if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) || + TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) || + TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17)) + { + if (!anim || anim == &mesh.mTargetAnim) + { + // Target animation channels may have no rotation channels + DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation"); + SkipSection(); + } + else ParseLV3ScaleAnimationBlock(*anim); + continue; + } + // rotation keyframes + if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) || + TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) || + TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15)) + { + if (!anim || anim == &mesh.mTargetAnim) + { + // Target animation channels may have no rotation channels + DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation"); + SkipSection(); + } + else ParseLV3RotAnimationBlock(*anim); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","TM_ANIMATION"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20)) + { + b = true; + anim.mScalingType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24)) + { + b = true; + anim.mScalingType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21)) + { + b = true; + anim.mScalingType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyScaling.push_back(aiVectorKey()); + aiVectorKey& key = anim.akeyScaling.back(); + ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18)) + { + b = true; + anim.mPositionType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22)) + { + b = true; + anim.mPositionType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19)) + { + b = true; + anim.mPositionType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyPositions.push_back(aiVectorKey()); + aiVectorKey& key = anim.akeyPositions.back(); + ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18)) + { + b = true; + anim.mRotationType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22)) + { + b = true; + anim.mRotationType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19)) + { + b = true; + anim.mRotationType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyRotations.push_back(aiQuatKey()); + aiQuatKey& key = anim.akeyRotations.back(); + aiVector3D v;float f; + ParseLV4MeshFloatTriple(&v.x,iIndex); + ParseLV4MeshFloat(f); + key.mTime = (double)iIndex; + key.mValue = aiQuaternion(v,f); + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh) +{ + AI_ASE_PARSER_INIT(); + int mode = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // name of the node + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + std::string temp; + if(!ParseString(temp,"*NODE_NAME")) + SkipToNextToken(); + + std::string::size_type s; + if (temp == mesh.mName) + { + mode = 1; + } + else if (std::string::npos != (s = temp.find(".Target")) && + mesh.mName == temp.substr(0,s)) + { + // This should be either a target light or a target camera + if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) || + (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET)) + { + mode = 2; + } + else DefaultLogger::get()->error("ASE: Ignoring target transform, " + "this is no spot light or target camera"); + } + else + { + DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp); + // mode = 0 + } + continue; + } + if (mode) + { + // fourth row of the transformation matrix - and also the + // only information here that is interesting for targets + if (TokenMatch(filePtr,"TM_ROW3" ,7)) + { + ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); + continue; + } + if (mode == 1) + { + // first row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW0" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[0]); + continue; + } + // second row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW1" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[1]); + continue; + } + // third row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW2" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[2]); + continue; + } + // inherited position axes + if (TokenMatch(filePtr,"INHERIT_POS" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; + continue; + } + // inherited rotation axes + if (TokenMatch(filePtr,"INHERIT_ROT" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; + continue; + } + // inherited scaling axes + if (TokenMatch(filePtr,"INHERIT_SCL" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; + continue; + } + } + } + } + AI_ASE_HANDLE_SECTION("2","*NODE_TM"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0; + unsigned int iNumFaces = 0; + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + unsigned int iNumCVertices = 0; + unsigned int iNumCFaces = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // Number of vertices in the mesh + if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) + { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) + { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of vertex colors in the mesh + if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15)) + { + ParseLV4MeshLong(iNumCVertices); + continue; + } + // Number of regular faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMFACES" ,13)) + { + ParseLV4MeshLong(iNumFaces); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) + { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // Number of colored faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15)) + { + ParseLV4MeshLong(iNumCFaces); + continue; + } + // mesh vertex list block + if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16)) + { + ParseLV3MeshVertexListBlock(iNumVertices,mesh); + continue; + } + // mesh face list block + if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14)) + { + ParseLV3MeshFaceListBlock(iNumFaces,mesh); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) + { + ParseLV3MeshTListBlock(iNumTVertices,mesh); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) + { + ParseLV3MeshTFaceListBlock(iNumTFaces,mesh); + continue; + } + // mesh color vertex list block + if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14)) + { + ParseLV3MeshCListBlock(iNumCVertices,mesh); + continue; + } + // mesh color face block + if (TokenMatch(filePtr,"MESH_CFACELIST" ,14)) + { + ParseLV3MeshCFaceListBlock(iNumCFaces,mesh); + continue; + } + // mesh normals + if (TokenMatch(filePtr,"MESH_NORMALS" ,12)) + { + ParseLV3MeshNormalListBlock(mesh); + continue; + } + // another mesh UV channel ... + if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19)) + { + + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex < 2) + { + LogWarning("Mapping channel has an invalid index. Skipping UV channel"); + // skip it ... + SkipSection(); + } + if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) + { + LogWarning("Too many UV channels specified. Skipping channel .."); + // skip it ... + SkipSection(); + } + else + { + // parse the mapping channel + ParseLV3MappingChannel(iIndex-1,mesh); + } + continue; + } + // mesh animation keyframe. Not supported + if (TokenMatch(filePtr,"MESH_ANIMATION" ,14)) + { + + LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " + "Keyframe animation is not supported by Assimp, this element " + "will be ignored"); + //SkipSection(); + continue; + } + if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12)) + { + ParseLV3MeshWeightsBlock(mesh);continue; + } + } + AI_ASE_HANDLE_SECTION("2","*MESH"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0, iNumBones = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Number of bone vertices ... + if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) + { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of bones + if (TokenMatch(filePtr,"MESH_NUMBONE" ,11)) + { + ParseLV4MeshLong(iNumBones); + continue; + } + // parse the list of bones + if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14)) + { + ParseLV4MeshBones(iNumBones,mesh); + continue; + } + // parse the list of bones vertices + if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) ) + { + ParseLV4MeshBonesVertices(iNumVertices,mesh); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + mesh.mBones.resize(iNumBones); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Mesh bone with name ... + if (TokenMatch(filePtr,"MESH_BONE_NAME" ,16)) + { + // parse an index ... + if(SkipSpaces(&filePtr)) + { + unsigned int iIndex = strtoul10(filePtr,&filePtr); + if (iIndex >= iNumBones) + { + LogWarning("Bone index is out of bounds"); + continue; + } + if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME")) + SkipToNextToken(); + continue; + } + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + mesh.mBoneVertices.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Mesh bone vertex + if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16)) + { + // read the vertex index + unsigned int iIndex = strtoul10(filePtr,&filePtr); + if (iIndex >= mesh.mPositions.size()) + { + iIndex = (unsigned int)mesh.mPositions.size()-1; + LogWarning("Bone vertex index is out of bounds. Using the largest valid " + "bone vertex index instead"); + } + + // --- ignored + float afVert[3]; + ParseLV4MeshFloatTriple(afVert); + + std::pair pairOut; + while (true) + { + // first parse the bone index ... + if (!SkipSpaces(&filePtr))break; + pairOut.first = strtoul10(filePtr,&filePtr); + + // then parse the vertex weight + if (!SkipSpaces(&filePtr))break; + filePtr = fast_atoreal_move(filePtr,pairOut.second); + + // -1 marks unused entries + if (-1 != pairOut.first) + { + mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); + } + } + continue; + } + } + AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshVertexListBlock( + unsigned int iNumVertices, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mPositions.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_VERTEX" ,11)) + { + + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Invalid vertex index. It will be ignored"); + } + else mesh.mPositions[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the face array + mesh.mFaces.resize(iNumFaces); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_FACE" ,9)) + { + + ASE::Face mFace; + ParseLV4MeshFace(mFace); + + if (mFace.iFace >= iNumFaces) + { + LogWarning("Face has an invalid index. It will be ignored"); + } + else mesh.mFaces[mFace.iFace] = mFace; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, + ASE::Mesh& mesh, unsigned int iChannel) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.amTexCoords[iChannel].resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_TVERT" ,10)) + { + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Tvertex has an invalid index. It will be ignored"); + } + else mesh.amTexCoords[iChannel][iIndex] = vTemp; + + if (0.0f != vTemp.z) + { + // we need 3 coordinate channels + mesh.mNumUVComponents[iChannel] = 3; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, + ASE::Mesh& mesh, unsigned int iChannel) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_TFACE" ,10)) + { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues,iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) + { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } + else + { + // copy UV indices + mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; + mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; + mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) + { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) + { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) + { + ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) + { + ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mVertexColors.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_VERTCOL" ,12)) + { + aiColor4D vTemp; + vTemp.a = 1.0f; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.r,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Vertex color has an invalid index. It will be ignored"); + } + else mesh.mVertexColors[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_CFACE" ,11)) + { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues,iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) + { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } + else + { + // copy color indices + mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; + mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; + mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh) +{ + AI_ASE_PARSER_INIT(); + + // Allocate enough storage for the normals + sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f )); + unsigned int index, faceIdx = UINT_MAX; + + // FIXME: rewrite this and find out how to interpret the normals + // correctly. This is crap. + + // Smooth the vertex and face normals together. The result + // will be edgy then, but otherwise everything would be soft ... + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (faceIdx != UINT_MAX && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x,index); + if (faceIdx >= sMesh.mFaces.size()) + continue; + + // Make sure we assign it to the correct face + const ASE::Face& face = sMesh.mFaces[faceIdx]; + if (index == face.mIndices[0]) + index = 0; + else if (index == face.mIndices[1]) + index = 1; + else if (index == face.mIndices[2]) + index = 2; + else { + DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); + continue; + } + // We'll renormalize later + sMesh.mNormals[faceIdx*3+index] += vNormal; + continue; + } + if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x,faceIdx); + + if (faceIdx >= sMesh.mFaces.size()) { + DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section"); + continue; + } + + // We'll renormalize later + sMesh.mNormals[faceIdx*3] += vNormal; + sMesh.mNormals[faceIdx*3+1] += vNormal; + sMesh.mNormals[faceIdx*3+2] += vNormal; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFace(ASE::Face& out) +{ + // skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); + SkipToNextToken(); + return; + } + + // parse the face index + out.iFace = strtoul10(filePtr,&filePtr); + + // next character should be ':' + if(!SkipSpaces(&filePtr)) + { + // FIX: there are some ASE files which haven't got : here .... + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + // FIX: There are some ASE files which haven't got ':' here + if(':' == *filePtr)++filePtr; + + // Parse all mesh indices + for (unsigned int i = 0; i < 3;++i) + { + unsigned int iIndex = 0; + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); + SkipToNextToken(); + return; + } + switch (*filePtr) + { + case 'A': + case 'a': + break; + case 'B': + case 'b': + iIndex = 1; + break; + case 'C': + case 'c': + iIndex = 2; + break; + default: + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "A,B or C expected [#3]"); + SkipToNextToken(); + return; + }; + ++filePtr; + + // next character should be ':' + if(!SkipSpaces(&filePtr) || ':' != *filePtr) + { + LogWarning("Unable to parse *MESH_FACE Element: " + "Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + + ++filePtr; + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "Vertex index ecpected [#4]"); + SkipToNextToken(); + return; + } + out.mIndices[iIndex] = strtoul10(filePtr,&filePtr); + } + + // now we need to skip the AB, BC, CA blocks. + while (true) + { + if ('*' == *filePtr)break; + if (IsLineEnd(*filePtr)) + { + //iLineNumber++; + return; + } + filePtr++; + } + + // parse the smoothing group of the face + if (TokenMatch(filePtr,"*MESH_SMOOTHING",15)) + { + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_SMOOTHING Element: " + "Unexpected EOL. Smoothing group(s) expected [#5]"); + SkipToNextToken(); + return; + } + + // Parse smoothing groups until we don't anymore see commas + // FIX: There needn't always be a value, sad but true + while (true) + { + if (*filePtr < '9' && *filePtr >= '0') + { + out.iSmoothGroup |= (1 << strtoul10(filePtr,&filePtr)); + } + SkipSpaces(&filePtr); + if (',' != *filePtr) + { + break; + } + ++filePtr; + SkipSpaces(&filePtr); + } + } + + // *MESH_MTLID is optional, too + while (true) + { + if ('*' == *filePtr)break; + if (IsLineEnd(*filePtr)) + { + return; + } + filePtr++; + } + + if (TokenMatch(filePtr,"*MESH_MTLID",11)) + { + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " + "Material index expected [#6]"); + SkipToNextToken(); + return; + } + out.iMaterial = strtoul10(filePtr,&filePtr); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) +{ + ai_assert(NULL != apOut); + + for (unsigned int i = 0; i < 3;++i) + ParseLV4MeshLong(apOut[i]); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) +{ + ai_assert(NULL != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshLongTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) +{ + ai_assert(NULL != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshFloatTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut) +{ + ai_assert(NULL != apOut); + + for (unsigned int i = 0; i < 3;++i) + ParseLV4MeshFloat(apOut[i]); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloat(float& fOut) +{ + // skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + // LOG + LogWarning("Unable to parse float: unexpected EOL [#1]"); + fOut = 0.0f; + ++iLineNumber; + return; + } + // parse the first float + filePtr = fast_atoreal_move(filePtr,fOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLong(unsigned int& iOut) +{ + // Skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + // LOG + LogWarning("Unable to parse long: unexpected EOL [#1]"); + iOut = 0; + ++iLineNumber; + return; + } + // parse the value + iOut = strtoul10(filePtr,&filePtr); +} + +#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/ASEParser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/ASEParser.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,669 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines the helper data structures for importing ASE files */ +#ifndef AI_ASEFILEHELPER_H_INC +#define AI_ASEFILEHELPER_H_INC + +// STL/CRT headers +#include +#include +#include + +// public ASSIMP headers +#include "assimp/types.h" +#include "assimp/mesh.h" +#include "assimp/anim.h" + +// for some helper routines like IsSpace() +#include "ParsingUtils.h" +#include "qnan.h" + +// ASE is quite similar to 3ds. We can reuse some structures +#include "3DSLoader.h" + +namespace Assimp { +namespace ASE { + +using namespace D3DS; + +// --------------------------------------------------------------------------- +/** Helper structure representing an ASE material */ +struct Material : public D3DS::Material +{ + //! Default constructor + Material() : pcInstance(NULL), bNeed (false) + {} + + //! Contains all sub materials of this material + std::vector avSubMaterials; + + //! aiMaterial object + aiMaterial* pcInstance; + + //! Can we remove this material? + bool bNeed; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file face */ +struct Face : public FaceWithSmoothingGroup +{ + //! Default constructor. Initializes everything with 0 + Face() + { + mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + { + amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0; + } + + iMaterial = DEFAULT_MATINDEX; + iFace = 0; + } + + //! special value to indicate that no material index has + //! been assigned to a face. The default material index + //! will replace this value later. + static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF; + + + + //! Indices into each list of texture coordinates + unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3]; + + //! Index into the list of vertex colors + unsigned int mColorIndices[3]; + + //! (Sub)Material index to be assigned to this face + unsigned int iMaterial; + + //! Index of the face. It is not specified whether it is + //! a requirement of the file format that all faces are + //! written in sequential order, so we have to expect this case + unsigned int iFace; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone */ +struct Bone +{ + //! Constructor + Bone() + { + static int iCnt = 0; + + // Generate a default name for the bone + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Construction from an existing name + Bone( const std::string& name) + : mName (name) + {} + + //! Name of the bone + std::string mName; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone vertex */ +struct BoneVertex +{ + //! Bone and corresponding vertex weight. + //! -1 for unrequired bones .... + std::vector > mBoneWeights; + + //! Position of the bone vertex. + //! MUST be identical to the vertex position + //aiVector3D mPosition; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file animation */ +struct Animation +{ + enum Type + { + TRACK = 0x0, + BEZIER = 0x1, + TCB = 0x2 + } mRotationType, mScalingType, mPositionType; + + Animation() + : mRotationType (TRACK) + , mScalingType (TRACK) + , mPositionType (TRACK) + {} + + //! List of track rotation keyframes + std::vector< aiQuatKey > akeyRotations; + + //! List of track position keyframes + std::vector< aiVectorKey > akeyPositions; + + //! List of track scaling keyframes + std::vector< aiVectorKey > akeyScaling; + +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent the inheritance information of an ASE node */ +struct InheritanceInfo +{ + //! Default constructor + InheritanceInfo() + { + // set the inheritance flag for all axes by default to true + for (unsigned int i = 0; i < 3;++i) + abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true; + } + + //! Inherit the parent's position?, axis order is x,y,z + bool abInheritPosition[3]; + + //! Inherit the parent's rotation?, axis order is x,y,z + bool abInheritRotation[3]; + + //! Inherit the parent's scaling?, axis order is x,y,z + bool abInheritScaling[3]; +}; + +// --------------------------------------------------------------------------- +/** Represents an ASE file node. Base class for mesh, light and cameras */ +struct BaseNode +{ + enum Type {Light, Camera, Mesh, Dummy} mType; + + //! Constructor. Creates a default name for the node + BaseNode(Type _mType) + : mType (_mType) + , mProcessed (false) + { + // generate a default name for the node + static int iCnt = 0; + char szTemp[128]; // should be sufficiently large + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + + // Set mTargetPosition to qnan + const float qnan = get_qnan(); + mTargetPosition.x = qnan; + } + + //! Name of the mesh + std::string mName; + + //! Name of the parent of the node + //! "" if there is no parent ... + std::string mParent; + + //! Transformation matrix of the node + aiMatrix4x4 mTransform; + + //! Target position (target lights and cameras) + aiVector3D mTargetPosition; + + //! Specifies which axes transformations a node inherits + //! from its parent ... + InheritanceInfo inherit; + + //! Animation channels for the node + Animation mAnim; + + //! Needed for lights and cameras: target animation channel + //! Should contain position keys only. + Animation mTargetAnim; + + bool mProcessed; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file mesh */ +struct Mesh : public MeshWithSmoothingGroups, public BaseNode +{ + //! Constructor. + Mesh() + : BaseNode (BaseNode::Mesh) + , bSkip (false) + { + // use 2 texture vertex components by default + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + this->mNumUVComponents[c] = 2; + + // setup the default material index by default + iMaterialIndex = Face::DEFAULT_MATINDEX; + } + + //! List of all texture coordinate sets + std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + //! List of all vertex color sets. + std::vector mVertexColors; + + //! List of all bone vertices + std::vector mBoneVertices; + + //! List of all bones + std::vector mBones; + + //! Material index of the mesh + unsigned int iMaterialIndex; + + //! Number of vertex components for each UVW set + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + //! used internally + bool bSkip; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE light source */ +struct Light : public BaseNode +{ + enum LightType + { + OMNI, + TARGET, + FREE, + DIRECTIONAL + }; + + //! Constructor. + Light() + : BaseNode (BaseNode::Light) + , mLightType (OMNI) + , mColor (1.f,1.f,1.f) + , mIntensity (1.f) // light is white by default + , mAngle (45.f) + , mFalloff (0.f) + { + } + + LightType mLightType; + aiColor3D mColor; + float mIntensity; + float mAngle; // in degrees + float mFalloff; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE camera */ +struct Camera : public BaseNode +{ + enum CameraType + { + FREE, + TARGET + }; + + //! Constructor + Camera() + : BaseNode (BaseNode::Camera) + , mFOV (0.75f) // in radians + , mNear (0.1f) + , mFar (1000.f) // could be zero + , mCameraType (FREE) + { + } + + float mFOV, mNear, mFar; + CameraType mCameraType; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE helper object (dummy) */ +struct Dummy : public BaseNode +{ + //! Constructor + Dummy() + : BaseNode (BaseNode::Dummy) + { + } +}; + +// Parameters to Parser::Parse() +#define AI_ASE_NEW_FILE_FORMAT 200 +#define AI_ASE_OLD_FILE_FORMAT 110 + +// Internally we're a little bit more tolerant +#define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200) +#define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200) + +// ------------------------------------------------------------------------------- +/** \brief Class to parse ASE files + */ +class Parser +{ + +private: + + Parser() {} + +public: + + // ------------------------------------------------------------------- + //! Construct a parser from a given input file which is + //! guaranted to be terminated with zero. + //! @param szFile Input file + //! @param fileFormatDefault Assumed file format version. If the + //! file format is specified in the file the new value replaces + //! the default value. + Parser (const char* szFile, unsigned int fileFormatDefault); + + // ------------------------------------------------------------------- + //! Parses the file into the parsers internal representation + void Parse(); + + +private: + + // ------------------------------------------------------------------- + //! Parse the *SCENE block in a file + void ParseLV1SceneBlock(); + + // ------------------------------------------------------------------- + //! Parse the *MESH_SOFTSKINVERTS block in a file + void ParseLV1SoftSkinBlock(); + + // ------------------------------------------------------------------- + //! Parse the *MATERIAL_LIST block in a file + void ParseLV1MaterialListBlock(); + + // ------------------------------------------------------------------- + //! Parse a *OBJECT block in a file + //! \param mesh Node to be filled + void ParseLV1ObjectBlock(BaseNode& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MATERIAL blocks in a material list + //! \param mat Material structure to be filled + void ParseLV2MaterialBlock(Material& mat); + + // ------------------------------------------------------------------- + //! Parse a *NODE_TM block in a file + //! \param mesh Node (!) object to be filled + void ParseLV2NodeTransformBlock(BaseNode& mesh); + + // ------------------------------------------------------------------- + //! Parse a *TM_ANIMATION block in a file + //! \param mesh Mesh object to be filled + void ParseLV2AnimationBlock(BaseNode& mesh); + void ParseLV3PosAnimationBlock(ASE::Animation& anim); + void ParseLV3ScaleAnimationBlock(ASE::Animation& anim); + void ParseLV3RotAnimationBlock(ASE::Animation& anim); + + // ------------------------------------------------------------------- + //! Parse a *MESH block in a file + //! \param mesh Mesh object to be filled + void ParseLV2MeshBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *LIGHT_SETTINGS block in a file + //! \param light Light object to be filled + void ParseLV2LightSettingsBlock(Light& light); + + // ------------------------------------------------------------------- + //! Parse a *CAMERA_SETTINGS block in a file + //! \param cam Camera object to be filled + void ParseLV2CameraSettingsBlock(Camera& cam); + + // ------------------------------------------------------------------- + //! Parse the *MAP_XXXXXX blocks in a material + //! \param map Texture structure to be filled + void ParseLV3MapBlock(Texture& map); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERTEX_LIST block in a file + //! \param iNumVertices Value of *MESH_NUMVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshVertexListBlock( + unsigned int iNumVertices,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_FACE_LIST block in a file + //! \param iNumFaces Value of *MESH_NUMFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshFaceListBlock( + unsigned int iNumFaces,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TVERT_LIST block in a file + //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + //! \param iChannel Output UVW channel + void ParseLV3MeshTListBlock( + unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACELIST block in a file + //! \param iNumFaces Value of *MESH_NUMTVFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + //! \param iChannel Output UVW channel + void ParseLV3MeshTFaceListBlock( + unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0); + + // ------------------------------------------------------------------- + //! Parse an additional mapping channel + //! (specified via *MESH_MAPPINGCHANNEL) + //! \param iChannel Channel index to be filled + //! \param mesh Mesh object to be filled + void ParseLV3MappingChannel( + unsigned int iChannel, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_CVERTLIST block in a file + //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshCListBlock( + unsigned int iNumVertices, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_CFACELIST block in a file + //! \param iNumFaces Value of *MESH_NUMCVFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshCFaceListBlock( + unsigned int iNumFaces, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_NORMALS block in a file + //! \param mesh Mesh object to be filled + void ParseLV3MeshNormalListBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_WEIGHTSblock in a file + //! \param mesh Mesh object to be filled + void ParseLV3MeshWeightsBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone list of a file + //! \param mesh Mesh object to be filled + //! \param iNumBones Number of bones in the mesh + void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone vertices list of a file + //! \param mesh Mesh object to be filled + //! \param iNumVertices Number of vertices to be parsed + void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_FACE block in a file + //! \param out receive the face data + void ParseLV4MeshFace(ASE::Face& out); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERT block in a file + //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) + //! \param apOut Output buffer (3 floats) + //! \param rIndexOut Output index + void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERT block in a file + //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) + //! \param apOut Output buffer (3 floats) + void ParseLV4MeshFloatTriple(float* apOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACE block in a file + //! (also works for MESH_CFACE) + //! \param apOut Output buffer (3 ints) + //! \param rIndexOut Output index + void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACE block in a file + //! (also works for MESH_CFACE) + //! \param apOut Output buffer (3 ints) + void ParseLV4MeshLongTriple(unsigned int* apOut); + + // ------------------------------------------------------------------- + //! Parse a single float element + //! \param fOut Output float + void ParseLV4MeshFloat(float& fOut); + + // ------------------------------------------------------------------- + //! Parse a single int element + //! \param iOut Output integer + void ParseLV4MeshLong(unsigned int& iOut); + + // ------------------------------------------------------------------- + //! Skip everything to the next: '*' or '\0' + bool SkipToNextToken(); + + // ------------------------------------------------------------------- + //! Skip the current section until the token after the closing }. + //! This function handles embedded subsections correctly + bool SkipSection(); + + // ------------------------------------------------------------------- + //! Output a warning to the logger + //! \param szWarn Warn message + void LogWarning(const char* szWarn); + + // ------------------------------------------------------------------- + //! Output a message to the logger + //! \param szWarn Message + void LogInfo(const char* szWarn); + + // ------------------------------------------------------------------- + //! Output an error to the logger + //! \param szWarn Error message + void LogError(const char* szWarn); + + // ------------------------------------------------------------------- + //! Parse a string, enclosed in double quotation marks + //! \param out Output string + //! \param szName Name of the enclosing element -> used in error + //! messages. + //! \return false if an error occured + bool ParseString(std::string& out,const char* szName); + +public: + + //! Pointer to current data + const char* filePtr; + + //! background color to be passed to the viewer + //! QNAN if none was found + aiColor3D m_clrBackground; + + //! Base ambient color to be passed to all materials + //! QNAN if none was found + aiColor3D m_clrAmbient; + + //! List of all materials found in the file + std::vector m_vMaterials; + + //! List of all meshes found in the file + std::vector m_vMeshes; + + //! List of all dummies found in the file + std::vector m_vDummies; + + //! List of all lights found in the file + std::vector m_vLights; + + //! List of all cameras found in the file + std::vector m_vCameras; + + //! Current line in the file + unsigned int iLineNumber; + + //! First frame + unsigned int iFirstFrame; + + //! Last frame + unsigned int iLastFrame; + + //! Frame speed - frames per second + unsigned int iFrameSpeed; + + //! Ticks per frame + unsigned int iTicksPerFrame; + + //! true if the last character read was an end-line character + bool bLastWasEndLine; + + //! File format version + unsigned int iFileFormat; +}; + + +} // Namespace ASE +} // Namespace ASSIMP + +#endif // !! include guard diff -r 000000000000 -r b2f14e535253 libs/assimp/Assimp.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/Assimp.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,593 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Assimp.cpp + * @brief Implementation of the Plain-C API + */ + +#include "AssimpPCH.h" +#include "assimp/cimport.h" + +#include "GenericProperty.h" +#include "CInterfaceIOWrapper.h" +#include "Importer.h" + +// ------------------------------------------------------------------------------------------------ +#ifdef AI_C_THREADSAFE +# include +# include +#endif +// ------------------------------------------------------------------------------------------------ +using namespace Assimp; + +namespace Assimp +{ + // underlying structure for aiPropertyStore + typedef BatchLoader::PropertyMap PropertyMap; + + /** Stores the LogStream objects for all active C log streams */ + struct mpred { + bool operator () (const aiLogStream& s0, const aiLogStream& s1) const { + return s0.callback LogStreamMap; + + /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ + typedef std::list PredefLogStreamMap; + + /** Local storage of all active log streams */ + static LogStreamMap gActiveLogStreams; + + /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ + static PredefLogStreamMap gPredefinedStreams; + + /** Error message of the last failed import process */ + static std::string gLastErrorString; + + /** Verbose logging active or not? */ + static aiBool gVerboseLogging = false; +} + + +#ifdef AI_C_THREADSAFE +/** Global mutex to manage the access to the logstream map */ +static boost::mutex gLogStreamMutex; +#endif + + +// ------------------------------------------------------------------------------------------------ +// Custom LogStream implementation for the C-API +class LogToCallbackRedirector : public LogStream +{ +public: + LogToCallbackRedirector(const aiLogStream& s) + : stream (s) { + ai_assert(NULL != s.callback); + } + + ~LogToCallbackRedirector() { +#ifdef AI_C_THREADSAFE + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + // (HACK) Check whether the 'stream.user' pointer points to a + // custom LogStream allocated by #aiGetPredefinedLogStream. + // In this case, we need to delete it, too. Of course, this + // might cause strange problems, but the chance is quite low. + + PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), + gPredefinedStreams.end(), (Assimp::LogStream*)stream.user); + + if (it != gPredefinedStreams.end()) { + delete *it; + gPredefinedStreams.erase(it); + } + } + + /** @copydoc LogStream::write */ + void write(const char* message) { + stream.callback(message,stream.user); + } + +private: + aiLogStream stream; +}; + +// ------------------------------------------------------------------------------------------------ +void ReportSceneNotFoundError() +{ + DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. " + "The C-API does not accept scenes produced by the C++ API and vice versa"); + + assert(false); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its content. +const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) +{ + return aiImportFileEx(pFile,pFlags,NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) +{ + return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, + aiFileIO* pFS, + const aiPropertyStore* props) +{ + ai_assert(NULL != pFile); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + } + // setup a custom IO system if necessary + if (pFS) { + imp->SetIOHandler( new CIOSystemWrapper (pFS) ); + } + + // and have it read the file + scene = imp->ReadFile( pFile, pFlags); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } + else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemory( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint) +{ + return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemoryWithProperties( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint, + const aiPropertyStore* props) +{ + ai_assert(NULL != pBuffer && 0 != pLength); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + } + + // and have it read the file from the memory buffer + scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } + else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +// Releases all resources associated with the given import process. +void aiReleaseImport( const aiScene* pScene) +{ + if (!pScene) { + return; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + delete pScene; + } + else { + // deleting the Importer also deletes the scene + // Note: the reason that this is not written as 'delete priv->mOrigImporter' + // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339) + Importer* importer = priv->mOrigImporter; + delete importer; + } + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene, + unsigned int pFlags) +{ + const aiScene* sc = NULL; + + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return NULL; + } + + sc = priv->mOrigImporter->ApplyPostProcessing(pFlags); + + if (!sc) { + aiReleaseImport(pScene); + return NULL; + } + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void CallbackToLogRedirector (const char* msg, char* dt) +{ + ai_assert(NULL != msg && NULL != dt); + LogStream* s = (LogStream*)dt; + + s->write(msg); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file) +{ + aiLogStream sout; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + LogStream* stream = LogStream::createDefaultStream(pStream,file); + if (!stream) { + sout.callback = NULL; + sout.user = NULL; + } + else { + sout.callback = &CallbackToLogRedirector; + sout.user = (char*)stream; + } + gPredefinedStreams.push_back(stream); + ASSIMP_END_EXCEPTION_REGION(aiLogStream); + return sout; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiAttachLogStream( const aiLogStream* stream ) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifdef AI_C_THREADSAFE + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + + LogStream* lg = new LogToCallbackRedirector(*stream); + gActiveLogStreams[*stream] = lg; + + if (DefaultLogger::isNullLogger()) { + DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + DefaultLogger::get()->attachStream(lg); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifdef AI_C_THREADSAFE + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + // find the logstream associated with this data + LogStreamMap::iterator it = gActiveLogStreams.find( *stream); + // it should be there... else the user is playing fools with us + if( it == gActiveLogStreams.end()) { + return AI_FAILURE; + } + DefaultLogger::get()->detatchStream( it->second ); + delete it->second; + + gActiveLogStreams.erase( it); + + if (gActiveLogStreams.empty()) { + DefaultLogger::kill(); + } + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiDetachAllLogStreams(void) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); +#ifdef AI_C_THREADSAFE + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { + DefaultLogger::get()->detatchStream( it->second ); + delete it->second; + } + gActiveLogStreams.clear(); + DefaultLogger::kill(); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiEnableVerboseLogging(aiBool d) +{ + if (!DefaultLogger::isNullLogger()) { + DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + gVerboseLogging = d; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +const char* aiGetErrorString() +{ + return gLastErrorString.c_str(); +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +aiBool aiIsExtensionSupported(const char* szExtension) +{ + ai_assert(NULL != szExtension); + aiBool candoit=AI_FALSE; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; + + ASSIMP_END_EXCEPTION_REGION(aiBool); + return candoit; +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all file extensions supported by ASSIMP +void aiGetExtensionList(aiString* szOut) +{ + ai_assert(NULL != szOut); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + tmp.GetExtensionList(*szOut); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements for a particular import. +void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, + C_STRUCT aiMemoryInfo* in) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pIn); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return; + } + + return priv->mOrigImporter->GetMemoryRequirements(*in); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void) +{ + return reinterpret_cast( new PropertyMap() ); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p) +{ + delete reinterpret_cast(p); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyInteger +ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->ints,szName,value,NULL); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyFloat +ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, float value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->floats,szName,value,NULL); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyString +ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName, + const C_STRUCT aiString* st) +{ + if (!st) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->strings,szName,std::string(st->C_Str()),NULL); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Rotation matrix to quaternion +ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat) +{ + ai_assert(NULL != quat && NULL != mat); + *quat = aiQuaternion(*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix decomposition +ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling, + aiQuaternion* rotation, + aiVector3D* position) +{ + ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat); + mat->Decompose(*scaling,*rotation,*position); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix transpose +ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +// Vector transformation +ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec, + const aiMatrix3x3* mat) +{ + ai_assert(NULL != mat && NULL != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec, + const aiMatrix4x4* mat) +{ + ai_assert(NULL != mat && NULL != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix multiplication +ASSIMP_API void aiMultiplyMatrix4( + aiMatrix4x4* dst, + const aiMatrix4x4* src) +{ + ai_assert(NULL != dst && NULL != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMultiplyMatrix3( + aiMatrix3x3* dst, + const aiMatrix3x3* src) +{ + ai_assert(NULL != dst && NULL != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix identity +ASSIMP_API void aiIdentityMatrix3( + aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix3x3(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiIdentityMatrix4( + aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix4x4(); +} + + diff -r 000000000000 -r b2f14e535253 libs/assimp/AssimpCExport.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/AssimpCExport.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,115 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file AssimpCExport.cpp +Assimp C export interface. See Exporter.cpp for some notes. +*/ + +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_EXPORT +#include "CInterfaceIOWrapper.h" +#include "SceneCombiner.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API size_t aiGetExportFormatCount(void) +{ + return Exporter().GetExportFormatCount(); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex) +{ + return Exporter().GetExportFormatDescription(pIndex); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut) +{ + if (!pOut || !pIn) { + return; + } + + SceneCombiner::CopyScene(pOut,pIn,true); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing ) +{ + return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing ) +{ + Exporter exp; + + if (pIO) { + exp.SetIOHandler(new CIOSystemWrapper(pIO)); + } + return exp.Export(pScene,pFormatId,pFileName,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ) +{ + Exporter exp; + if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) { + return NULL; + } + const aiExportDataBlob* blob = exp.GetOrphanedBlob(); + ai_assert(blob); + + return blob; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData ) +{ + delete pData; +} + +#endif // !ASSIMP_BUILD_NO_EXPORT diff -r 000000000000 -r b2f14e535253 libs/assimp/AssimpPCH.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/AssimpPCH.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,132 @@ + +// Actually just a dummy, used by the compiler to build the precompiled header. + +#include "AssimpPCH.h" +#include "assimp/version.h" + +// -------------------------------------------------------------------------------- +// Legal information string - dont't remove this. +static const char* LEGAL_INFORMATION = + +"Open Asset Import Library (Assimp).\n" +"A free C/C++ library to import various 3D file formats into applications\n\n" + +"(c) 2008-2010, assimp team\n" +"License under the terms and conditions of the 3-clause BSD license\n" +"http://assimp.sourceforge.net\n" +; + +// ------------------------------------------------------------------------------------------------ +// Get legal string +ASSIMP_API const char* aiGetLegalString () { + return LEGAL_INFORMATION; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp minor version +ASSIMP_API unsigned int aiGetVersionMinor () { + return 0; +} + +// ------------------------------------------------------------------------------------------------ +// Get Assimp major version +ASSIMP_API unsigned int aiGetVersionMajor () { + return 3; +} + +// ------------------------------------------------------------------------------------------------ +// Get flags used for compilation +ASSIMP_API unsigned int aiGetCompileFlags () { + + unsigned int flags = 0; + +#ifdef ASSIMP_BUILD_BOOST_WORKAROUND + flags |= ASSIMP_CFLAGS_NOBOOST; +#endif +#ifdef ASSIMP_BUILD_SINGLETHREADED + flags |= ASSIMP_CFLAGS_SINGLETHREADED; +#endif +#ifdef ASSIMP_BUILD_DEBUG + flags |= ASSIMP_CFLAGS_DEBUG; +#endif +#ifdef ASSIMP_BUILD_DLL_EXPORT + flags |= ASSIMP_CFLAGS_SHARED; +#endif +#ifdef _STLPORT_VERSION + flags |= ASSIMP_CFLAGS_STLPORT; +#endif + + return flags; +} + +// include current build revision, which is even updated from time to time -- :-) +#include "revision.h" + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API unsigned int aiGetVersionRevision () +{ + return SVNRevision; +} + +// ------------------------------------------------------------------------------------------------ +aiScene::aiScene() + : mFlags() + , mRootNode() + , mNumMeshes() + , mMeshes() + , mNumMaterials() + , mMaterials() + , mNumAnimations() + , mAnimations() + , mNumTextures() + , mTextures() + , mNumLights() + , mLights() + , mNumCameras() + , mCameras() + , mPrivate(new Assimp::ScenePrivateData()) + { + } + +// ------------------------------------------------------------------------------------------------ +aiScene::~aiScene() +{ + // delete all sub-objects recursively + delete mRootNode; + + // To make sure we won't crash if the data is invalid it's + // much better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) + for( unsigned int a = 0; a < mNumMeshes; a++) + delete mMeshes[a]; + delete [] mMeshes; + + if (mNumMaterials && mMaterials) + for( unsigned int a = 0; a < mNumMaterials; a++) + delete mMaterials[a]; + delete [] mMaterials; + + if (mNumAnimations && mAnimations) + for( unsigned int a = 0; a < mNumAnimations; a++) + delete mAnimations[a]; + delete [] mAnimations; + + if (mNumTextures && mTextures) + for( unsigned int a = 0; a < mNumTextures; a++) + delete mTextures[a]; + delete [] mTextures; + + if (mNumLights && mLights) + for( unsigned int a = 0; a < mNumLights; a++) + delete mLights[a]; + delete [] mLights; + + if (mNumCameras && mCameras) + for( unsigned int a = 0; a < mNumCameras; a++) + delete mCameras[a]; + delete [] mCameras; + + delete static_cast( mPrivate ); +} + diff -r 000000000000 -r b2f14e535253 libs/assimp/AssimpPCH.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/AssimpPCH.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,162 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file AssimpPCH.h + * PCH master include. Every unit in Assimp has to include it. + */ + +#ifndef ASSIMP_PCH_INCLUDED +#define ASSIMP_PCH_INCLUDED +#define ASSIMP_INTERNAL_BUILD + +// ---------------------------------------------------------------------------------------- +/* General compile config taken from defs.h. It is important that the user compiles + * using exactly the same settings in defs.h. Settings in AssimpPCH.h may differ, + * they won't affect the public API. + */ +#include "assimp/defs.h" + +// Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw +#if defined( _MSC_VER) && (_MSC_VER < 1600) +# include "pstdint.h" +#else +# include +#endif + +/* Undefine the min/max macros defined by some platform headers (namely Windows.h) to + * avoid obvious conflicts with std::min() and std::max(). + */ +#undef min +#undef max + +/* Concatenate two tokens after evaluating them + */ +#define _AI_CONCAT(a,b) a ## b +#define AI_CONCAT(a,b) _AI_CONCAT(a,b) + +/* Helper macro to set a pointer to NULL in debug builds + */ +#if (defined _DEBUG) +# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL; +#else +# define AI_DEBUG_INVALIDATE_PTR(x) +#endif + +/* Beginning with MSVC8 some C string manipulation functions are mapped to their _safe_ + * counterparts (e.g. _itoa_s). This avoids a lot of trouble with deprecation warnings. + */ +#if _MSC_VER >= 1400 && !(defined _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +#endif + +/* size_t to unsigned int, possible loss of data. The compiler is right with his warning + * but this loss of data won't be a problem for us. So shut up, little boy. + */ +#ifdef _MSC_VER +# pragma warning (disable : 4267) +#endif + +// ---------------------------------------------------------------------------------------- +/* Actually that's not required for MSVC. It is included somewhere in the deeper parts of + * the MSVC STL but it's necessary for proper build with STLport. + */ +#include + +// Runtime/STL headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Boost headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Public ASSIMP headers +#include "assimp/DefaultLogger.hpp" +#include "assimp/IOStream.hpp" +#include "assimp/IOSystem.hpp" +#include "assimp/scene.h" +#include "assimp/importerdesc.h" +#include "assimp/postprocess.h" +#include "assimp/Importer.hpp" +#include "assimp/Exporter.hpp" + +// Internal utility headers +#include "BaseImporter.h" +#include "StringComparison.h" +#include "StreamReader.h" +#include "qnan.h" +#include "ScenePrivate.h" + + +// We need those constants, workaround for any platforms where nobody defined them yet +#if (!defined SIZE_MAX) +# define SIZE_MAX (~((size_t)0)) +#endif + +#if (!defined UINT_MAX) +# define UINT_MAX (~((unsigned int)0)) +#endif + + +#endif // !! ASSIMP_PCH_INCLUDED diff -r 000000000000 -r b2f14e535253 libs/assimp/B3DImporter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/B3DImporter.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,687 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file B3DImporter.cpp + * @brief Implementation of the b3d importer class + */ + +#include "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER + +// internal headers +#include "B3DImporter.h" +#include "TextureTransform.h" +#include "ConvertToLHProcess.h" + +using namespace Assimp; +using namespace std; + +static const aiImporterDesc desc = { + "BlitzBasic 3D Importer", + "", + "", + "http://www.blitzbasic.com/", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "b3d" +}; + +// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings +#ifdef _MSC_VER +# pragma warning (disable: 4018) +#endif + +//#define DEBUG_B3D + +// ------------------------------------------------------------------------------------------------ +bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ + + size_t pos=pFile.find_last_of( '.' ); + if( pos==string::npos ) return false; + + string ext=pFile.substr( pos+1 ); + if( ext.size()!=3 ) return false; + + return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* B3DImporter::GetInfo () const +{ + return &desc; +} + +#ifdef DEBUG_B3D + extern "C"{ void _stdcall AllocConsole(); } +#endif +// ------------------------------------------------------------------------------------------------ +void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ + +#ifdef DEBUG_B3D + AllocConsole(); + freopen( "conin$","r",stdin ); + freopen( "conout$","w",stdout ); + freopen( "conout$","w",stderr ); + cout<<"Hello world from the B3DImporter!"< file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); + + // check whether the .b3d file is large enough to contain + // at least one chunk. + size_t fileSize = file->FileSize(); + if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); + + _pos=0; + _buf.resize( fileSize ); + file->Read( &_buf[0],1,fileSize ); + _stack.clear(); + + ReadBB3D( pScene ); +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::Oops(){ + throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" ); +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::Fail( string str ){ +#ifdef DEBUG_B3D + cout<<"Error in B3D file data: "< +T *B3DImporter::to_array( const vector &v ){ + if( !v.size() ) return 0; + T *p=new T[v.size()]; + for( size_t i=0;i8 ){ + Fail( "Bad texture count" ); + } + while( ChunkSize() ){ + string name=ReadString(); + aiVector3D color=ReadVec3(); + float alpha=ReadFloat(); + float shiny=ReadFloat(); + /*int blend=**/ReadInt(); + int fx=ReadInt(); + + aiMaterial *mat=new aiMaterial; + _materials.push_back( mat ); + + // Name + aiString ainame( name ); + mat->AddProperty( &ainame,AI_MATKEY_NAME ); + + // Diffuse color + mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE ); + + // Opacity + mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY ); + + // Specular color + aiColor3D speccolor( shiny,shiny,shiny ); + mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR ); + + // Specular power + float specpow=shiny*128; + mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS ); + + // Double sided + if( fx & 0x10 ){ + int i=1; + mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED ); + } + + //Textures + for( int i=0;i=0 && texid>=static_cast(_textures.size())) ){ + Fail( "Bad texture id" ); + } + if( i==0 && texid>=0 ){ + aiString texname( _textures[texid] ); + mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadVRTS(){ + _vflags=ReadInt(); + _tcsets=ReadInt(); + _tcsize=ReadInt(); + if( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){ + Fail( "Bad texcoord data" ); + } + + int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4); + int n_verts=ChunkSize()/sz; + + int v0=_vertices.size(); + _vertices.resize( v0+n_verts ); + + for( int i=0;i=(int)_materials.size() ){ +#ifdef DEBUG_B3D + cout<<"material id="<mMaterialIndex=matid; + mesh->mNumFaces=0; + mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; + + int n_tris=ChunkSize()/12; + aiFace *face=mesh->mFaces=new aiFace[n_tris]; + + for( int i=0;i=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){ +#ifdef DEBUG_B3D + cout<<"Bad triangle index: i0="<mNumIndices=3; + face->mIndices=new unsigned[3]; + face->mIndices[0]=i0; + face->mIndices[1]=i1; + face->mIndices[2]=i2; + ++mesh->mNumFaces; + ++face; + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadMESH(){ + /*int matid=*/ReadInt(); + + int v0=_vertices.size(); + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="VRTS" ){ + ReadVRTS(); + }else if( t=="TRIS" ){ + ReadTRIS( v0 ); + } + ExitChunk(); + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadBONE( int id ){ + while( ChunkSize() ){ + int vertex=ReadInt(); + float weight=ReadFloat(); + if( vertex<0 || vertex>=(int)_vertices.size() ){ + Fail( "Bad vertex index" ); + } + + Vertex &v=_vertices[vertex]; + int i; + for( i=0;i<4;++i ){ + if( !v.weights[i] ){ + v.bones[i]=id; + v.weights[i]=weight; + break; + } + } +#ifdef DEBUG_B3D + if( i==4 ){ + cout<<"Too many bone weights"< trans,scale; + vector rot; + int flags=ReadInt(); + while( ChunkSize() ){ + int frame=ReadInt(); + if( flags & 1 ){ + trans.push_back( aiVectorKey( frame,ReadVec3() ) ); + } + if( flags & 2 ){ + scale.push_back( aiVectorKey( frame,ReadVec3() ) ); + } + if( flags & 4 ){ + rot.push_back( aiQuatKey( frame,ReadQuat() ) ); + } + } + + if( flags & 1 ){ + nodeAnim->mNumPositionKeys=trans.size(); + nodeAnim->mPositionKeys=to_array( trans ); + } + + if( flags & 2 ){ + nodeAnim->mNumScalingKeys=scale.size(); + nodeAnim->mScalingKeys=to_array( scale ); + } + + if( flags & 4 ){ + nodeAnim->mNumRotationKeys=rot.size(); + nodeAnim->mRotationKeys=to_array( rot ); + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadANIM(){ + /*int flags=*/ReadInt(); + int frames=ReadInt(); + float fps=ReadFloat(); + + aiAnimation *anim=new aiAnimation; + _animations.push_back( anim ); + + anim->mDuration=frames; + anim->mTicksPerSecond=fps; +} + +// ------------------------------------------------------------------------------------------------ +aiNode *B3DImporter::ReadNODE( aiNode *parent ){ + + string name=ReadString(); + aiVector3D t=ReadVec3(); + aiVector3D s=ReadVec3(); + aiQuaternion r=ReadQuat(); + + aiMatrix4x4 trans,scale,rot; + + aiMatrix4x4::Translation( t,trans ); + aiMatrix4x4::Scaling( s,scale ); + rot=aiMatrix4x4( r.GetMatrix() ); + + aiMatrix4x4 tform=trans * rot * scale; + + int nodeid=_nodes.size(); + + aiNode *node=new aiNode( name ); + _nodes.push_back( node ); + + node->mParent=parent; + node->mTransformation=tform; + + aiNodeAnim *nodeAnim=0; + vector meshes; + vector children; + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="MESH" ){ + int n=_meshes.size(); + ReadMESH(); + for( int i=n;i<(int)_meshes.size();++i ){ + meshes.push_back( i ); + } + }else if( t=="BONE" ){ + ReadBONE( nodeid ); + }else if( t=="ANIM" ){ + ReadANIM(); + }else if( t=="KEYS" ){ + if( !nodeAnim ){ + nodeAnim=new aiNodeAnim; + _nodeAnims.push_back( nodeAnim ); + nodeAnim->mNodeName=node->mName; + } + ReadKEYS( nodeAnim ); + }else if( t=="NODE" ){ + aiNode *child=ReadNODE( node ); + children.push_back( child ); + } + ExitChunk(); + } + + node->mNumMeshes=meshes.size(); + node->mMeshes=to_array( meshes ); + + node->mNumChildren=children.size(); + node->mChildren=to_array( children ); + + return node; +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadBB3D( aiScene *scene ){ + + _textures.clear(); + _materials.size(); + + _vertices.clear(); + _meshes.clear(); + + _nodes.clear(); + _nodeAnims.clear(); + _animations.clear(); + + string t=ReadChunk(); + if( t=="BB3D" ){ + int version=ReadInt(); + + if (!DefaultLogger::isNullLogger()) { + char dmp[128]; + sprintf(dmp,"B3D file format version: %i",version); + DefaultLogger::get()->info(dmp); + } + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="TEXS" ){ + ReadTEXS(); + }else if( t=="BRUS" ){ + ReadBRUS(); + }else if( t=="NODE" ){ + ReadNODE( 0 ); + } + ExitChunk(); + } + } + ExitChunk(); + + if( !_nodes.size() ) Fail( "No nodes" ); + + if( !_meshes.size() ) Fail( "No meshes" ); + + //Fix nodes/meshes/bones + for(size_t i=0;i<_nodes.size();++i ){ + aiNode *node=_nodes[i]; + + for( size_t j=0;jmNumMeshes;++j ){ + aiMesh *mesh=_meshes[node->mMeshes[j]]; + + int n_tris=mesh->mNumFaces; + int n_verts=mesh->mNumVertices=n_tris * 3; + + aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; + if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; + if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; + + aiFace *face=mesh->mFaces; + + vector< vector > vweights( _nodes.size() ); + + for( int i=0;imIndices[j]]; + + *mv++=v.vertex; + if( mn ) *mn++=v.normal; + if( mc ) *mc++=v.texcoords; + + face->mIndices[j]=i+j; + + for( int k=0;k<4;++k ){ + if( !v.weights[k] ) break; + + int bone=v.bones[k]; + float weight=v.weights[k]; + + vweights[bone].push_back( aiVertexWeight(i+j,weight) ); + } + } + ++face; + } + + vector bones; + for(size_t i=0;i &weights=vweights[i]; + if( !weights.size() ) continue; + + aiBone *bone=new aiBone; + bones.push_back( bone ); + + aiNode *bnode=_nodes[i]; + + bone->mName=bnode->mName; + bone->mNumWeights=weights.size(); + bone->mWeights=to_array( weights ); + + aiMatrix4x4 mat=bnode->mTransformation; + while( bnode->mParent ){ + bnode=bnode->mParent; + mat=bnode->mTransformation * mat; + } + bone->mOffsetMatrix=mat.Inverse(); + } + mesh->mNumBones=bones.size(); + mesh->mBones=to_array( bones ); + } + } + + //nodes + scene->mRootNode=_nodes[0]; + + //material + if( !_materials.size() ){ + _materials.push_back( new aiMaterial ); + } + scene->mNumMaterials=_materials.size(); + scene->mMaterials=to_array( _materials ); + + //meshes + scene->mNumMeshes=_meshes.size(); + scene->mMeshes=to_array( _meshes ); + + //animations + if( _animations.size()==1 && _nodeAnims.size() ){ + + aiAnimation *anim=_animations.back(); + anim->mNumChannels=_nodeAnims.size(); + anim->mChannels=to_array( _nodeAnims ); + + scene->mNumAnimations=_animations.size(); + scene->mAnimations=to_array( _animations ); + } + + // convert to RH + MakeLeftHandedProcess makeleft; + makeleft.Execute( scene ); + + FlipWindingOrderProcess flip; + flip.Execute( scene ); +} + +#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/B3DImporter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/B3DImporter.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,126 @@ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the .b3d importer class. */ + +#ifndef AI_B3DIMPORTER_H_INC +#define AI_B3DIMPORTER_H_INC + +#include "assimp/types.h" +#include "assimp/mesh.h" +#include "assimp/material.h" + +#include +#include + +namespace Assimp{ + +class B3DImporter : public BaseImporter{ +public: + + virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + +protected: + + virtual const aiImporterDesc* GetInfo () const; + virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + +private: + + int ReadByte(); + int ReadInt(); + float ReadFloat(); + aiVector2D ReadVec2(); + aiVector3D ReadVec3(); + aiQuaternion ReadQuat(); + std::string ReadString(); + std::string ReadChunk(); + void ExitChunk(); + unsigned ChunkSize(); + + template + T *to_array( const std::vector &v ); + + struct Vertex{ + aiVector3D vertex; + aiVector3D normal; + aiVector3D texcoords; + unsigned char bones[4]; + float weights[4]; + }; + + void Oops(); + void Fail( std::string str ); + + void ReadTEXS(); + void ReadBRUS(); + + void ReadVRTS(); + void ReadTRIS( int v0 ); + void ReadMESH(); + void ReadBONE( int id ); + void ReadKEYS( aiNodeAnim *nodeAnim ); + void ReadANIM(); + + aiNode *ReadNODE( aiNode *parent ); + + void ReadBB3D( aiScene *scene ); + + unsigned _pos; +// unsigned _size; + std::vector _buf; + std::vector _stack; + + std::vector _textures; + std::vector _materials; + + int _vflags,_tcsets,_tcsize; + std::vector _vertices; + + std::vector _nodes; + std::vector _meshes; + std::vector _nodeAnims; + std::vector _animations; +}; + +} + +#endif diff -r 000000000000 -r b2f14e535253 libs/assimp/BVHLoader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BVHLoader.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,534 @@ +/** Implementation of the BVH loader */ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER + +#include "BVHLoader.h" +#include "fast_atof.h" +#include "SkeletonMeshBuilder.h" + +using namespace Assimp; + +static const aiImporterDesc desc = { + "BVH Importer (MoCap)", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "bvh" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BVHLoader::BVHLoader() +: noSkeletonMesh() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BVHLoader::~BVHLoader() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const +{ + // check file extension + const std::string extension = GetExtension(pFile); + + if( extension == "bvh") + return true; + + if ((!extension.length() || cs) && pIOHandler) { + const char* tokens[] = {"HIERARCHY"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +void BVHLoader::SetupProperties(const Importer* pImp) +{ + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* BVHLoader::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + mFileName = pFile; + + // read file into memory + boost::scoped_ptr file( pIOHandler->Open( pFile)); + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open file " + pFile + "."); + + size_t fileSize = file->FileSize(); + if( fileSize == 0) + throw DeadlyImportError( "File is too small."); + + mBuffer.resize( fileSize); + file->Read( &mBuffer.front(), 1, fileSize); + + // start reading + mReader = mBuffer.begin(); + mLine = 1; + ReadStructure( pScene); + + if (!noSkeletonMesh) { + // build a dummy mesh for the skeleton so that we see something at least + SkeletonMeshBuilder meshBuilder( pScene); + } + + // construct an animation from all the motion data we read + CreateAnimation( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the file +void BVHLoader::ReadStructure( aiScene* pScene) +{ + // first comes hierarchy + std::string header = GetNextToken(); + if( header != "HIERARCHY") + ThrowException( "Expected header string \"HIERARCHY\"."); + ReadHierarchy( pScene); + + // then comes the motion data + std::string motion = GetNextToken(); + if( motion != "MOTION") + ThrowException( "Expected beginning of motion data \"MOTION\"."); + ReadMotion( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the hierarchy +void BVHLoader::ReadHierarchy( aiScene* pScene) +{ + std::string root = GetNextToken(); + if( root != "ROOT") + ThrowException( "Expected root node \"ROOT\"."); + + // Go read the hierarchy from here + pScene->mRootNode = ReadNode(); +} + +// ------------------------------------------------------------------------------------------------ +// Reads a node and recursively its childs and returns the created node; +aiNode* BVHLoader::ReadNode() +{ + // first token is name + std::string nodeName = GetNextToken(); + if( nodeName.empty() || nodeName == "{") + ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); + + // then an opening brace should follow + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); + + // Create a node + aiNode* node = new aiNode( nodeName); + std::vector childNodes; + + // and create an bone entry for it + mNodes.push_back( Node( node)); + Node& internNode = mNodes.back(); + + // now read the node's contents + while( 1) + { + std::string token = GetNextToken(); + + // node offset to parent node + if( token == "OFFSET") + ReadNodeOffset( node); + else if( token == "CHANNELS") + ReadNodeChannels( internNode); + else if( token == "JOINT") + { + // child node follows + aiNode* child = ReadNode(); + child->mParent = node; + childNodes.push_back( child); + } + else if( token == "End") + { + // The real symbol is "End Site". Second part comes in a separate token + std::string siteToken = GetNextToken(); + if( siteToken != "Site") + ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken)); + + aiNode* child = ReadEndSite( nodeName); + child->mParent = node; + childNodes.push_back( child); + } + else if( token == "}") + { + // we're done with that part of the hierarchy + break; + } else + { + // everything else is a parse error + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); + } + } + + // add the child nodes if there are any + if( childNodes.size() > 0) + { + node->mNumChildren = childNodes.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + std::copy( childNodes.begin(), childNodes.end(), node->mChildren); + } + + // and return the sub-hierarchy we built here + return node; +} + +// ------------------------------------------------------------------------------------------------ +// Reads an end node and returns the created node. +aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) +{ + // check opening brace + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); + + // Create a node + aiNode* node = new aiNode( "EndSite_" + pParentName); + + // now read the node's contents. Only possible entry is "OFFSET" + while( 1) + { + std::string token = GetNextToken(); + + // end node's offset + if( token == "OFFSET") + { + ReadNodeOffset( node); + } + else if( token == "}") + { + // we're done with the end node + break; + } else + { + // everything else is a parse error + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); + } + } + + // and return the sub-hierarchy we built here + return node; +} +// ------------------------------------------------------------------------------------------------ +// Reads a node offset for the given node +void BVHLoader::ReadNodeOffset( aiNode* pNode) +{ + // Offset consists of three floats to read + aiVector3D offset; + offset.x = GetNextTokenAsFloat(); + offset.y = GetNextTokenAsFloat(); + offset.z = GetNextTokenAsFloat(); + + // build a transformation matrix from it + pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, + 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the animation channels for the given node +void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode) +{ + // number of channels. Use the float reader because we're lazy + float numChannelsFloat = GetNextTokenAsFloat(); + unsigned int numChannels = (unsigned int) numChannelsFloat; + + for( unsigned int a = 0; a < numChannels; a++) + { + std::string channelToken = GetNextToken(); + + if( channelToken == "Xposition") + pNode.mChannels.push_back( Channel_PositionX); + else if( channelToken == "Yposition") + pNode.mChannels.push_back( Channel_PositionY); + else if( channelToken == "Zposition") + pNode.mChannels.push_back( Channel_PositionZ); + else if( channelToken == "Xrotation") + pNode.mChannels.push_back( Channel_RotationX); + else if( channelToken == "Yrotation") + pNode.mChannels.push_back( Channel_RotationY); + else if( channelToken == "Zrotation") + pNode.mChannels.push_back( Channel_RotationZ); + else + ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken)); + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the motion data +void BVHLoader::ReadMotion( aiScene* /*pScene*/) +{ + // Read number of frames + std::string tokenFrames = GetNextToken(); + if( tokenFrames != "Frames:") + ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); + + float numFramesFloat = GetNextTokenAsFloat(); + mAnimNumFrames = (unsigned int) numFramesFloat; + + // Read frame duration + std::string tokenDuration1 = GetNextToken(); + std::string tokenDuration2 = GetNextToken(); + if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") + ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); + + mAnimTickDuration = GetNextTokenAsFloat(); + + // resize value vectors for each node + for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); + + // now read all the data and store it in the corresponding node's value vector + for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) + { + // on each line read the values for all nodes + for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + { + // get as many values as the node has channels + for( unsigned int c = 0; c < it->mChannels.size(); ++c) + it->mChannelValues.push_back( GetNextTokenAsFloat()); + } + + // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it + } +} + +// ------------------------------------------------------------------------------------------------ +// Retrieves the next token +std::string BVHLoader::GetNextToken() +{ + // skip any preceeding whitespace + while( mReader != mBuffer.end()) + { + if( !isspace( *mReader)) + break; + + // count lines + if( *mReader == '\n') + mLine++; + + ++mReader; + } + + // collect all chars till the next whitespace. BVH is easy in respect to that. + std::string token; + while( mReader != mBuffer.end()) + { + if( isspace( *mReader)) + break; + + token.push_back( *mReader); + ++mReader; + + // little extra logic to make sure braces are counted correctly + if( token == "{" || token == "}") + break; + } + + // empty token means end of file, which is just fine + return token; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the next token as a float +float BVHLoader::GetNextTokenAsFloat() +{ + std::string token = GetNextToken(); + if( token.empty()) + ThrowException( "Unexpected end of file while trying to read a float"); + + // check if the float is valid by testing if the atof() function consumed every char of the token + const char* ctoken = token.c_str(); + float result = 0.0f; + ctoken = fast_atoreal_move( ctoken, result); + + if( ctoken != token.c_str() + token.length()) + ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +// Aborts the file reading with an exception +void BVHLoader::ThrowException( const std::string& pError) +{ + throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); +} + +// ------------------------------------------------------------------------------------------------ +// Constructs an animation for the motion data and stores it in the given scene +void BVHLoader::CreateAnimation( aiScene* pScene) +{ + // create the animation + pScene->mNumAnimations = 1; + pScene->mAnimations = new aiAnimation*[1]; + aiAnimation* anim = new aiAnimation; + pScene->mAnimations[0] = anim; + + // put down the basic parameters + anim->mName.Set( "Motion"); + anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); + anim->mDuration = double( mAnimNumFrames - 1); + + // now generate the tracks for all nodes + anim->mNumChannels = mNodes.size(); + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + + // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown + for (unsigned int i = 0; i < anim->mNumChannels;++i) + anim->mChannels[i] = NULL; + + for( unsigned int a = 0; a < anim->mNumChannels; a++) + { + const Node& node = mNodes[a]; + const std::string nodeName = std::string( node.mNode->mName.data ); + aiNodeAnim* nodeAnim = new aiNodeAnim; + anim->mChannels[a] = nodeAnim; + nodeAnim->mNodeName.Set( nodeName); + + // translational part, if given + if( node.mChannels.size() == 6) + { + nodeAnim->mNumPositionKeys = mAnimNumFrames; + nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; + aiVectorKey* poskey = nodeAnim->mPositionKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + poskey->mTime = double( fr); + + // Now compute all translations in the right order + for( unsigned int channel = 0; channel < 3; ++channel) + { + switch( node.mChannels[channel]) + { + case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + ++poskey; + } + } else + { + // if no translation part is given, put a default sequence + aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); + nodeAnim->mNumPositionKeys = 1; + nodeAnim->mPositionKeys = new aiVectorKey[1]; + nodeAnim->mPositionKeys[0].mTime = 0.0; + nodeAnim->mPositionKeys[0].mValue = nodePos; + } + + // rotation part. Always present. First find value offsets + { + unsigned int rotOffset = 0; + if( node.mChannels.size() == 6) + { + // Offset all further calculations + rotOffset = 3; + } + + // Then create the number of rotation keys + nodeAnim->mNumRotationKeys = mAnimNumFrames; + nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; + aiQuatKey* rotkey = nodeAnim->mRotationKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + aiMatrix4x4 temp; + aiMatrix3x3 rotMatrix; + + for( unsigned int channel = 0; channel < 3; ++channel) + { + // translate ZXY euler angels into a quaternion + const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; + + // Compute rotation transformations in the right order + switch (node.mChannels[rotOffset+channel]) + { + case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + + rotkey->mTime = double( fr); + rotkey->mValue = aiQuaternion( rotMatrix); + ++rotkey; + } + } + + // scaling part. Always just a default track + { + nodeAnim->mNumScalingKeys = 1; + nodeAnim->mScalingKeys = new aiVectorKey[1]; + nodeAnim->mScalingKeys[0].mTime = 0.0; + nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); + } + } +} + +#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER diff -r 000000000000 -r b2f14e535253 libs/assimp/BVHLoader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BVHLoader.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,169 @@ +/** Defines the BHV motion capturing loader class */ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BVHLoader.h + * @brief Biovision BVH import + */ + +#ifndef AI_BVHLOADER_H_INC +#define AI_BVHLOADER_H_INC + +#include "BaseImporter.h" + +namespace Assimp +{ + +// -------------------------------------------------------------------------------- +/** Loader class to read Motion Capturing data from a .bvh file. + * + * This format only contains a hierarchy of joints and a series of keyframes for + * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh + * inside the loader just to be able to see something. +*/ +class BVHLoader : public BaseImporter +{ + + /** Possible animation channels for which the motion data holds the values */ + enum ChannelType + { + Channel_PositionX, + Channel_PositionY, + Channel_PositionZ, + Channel_RotationX, + Channel_RotationY, + Channel_RotationZ + }; + + /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */ + struct Node + { + const aiNode* mNode; + std::vector mChannels; + std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames + + Node() { } + Node( const aiNode* pNode) : mNode( pNode) { } + }; + +public: + + BVHLoader(); + ~BVHLoader(); + +public: + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const; + + void SetupProperties(const Importer* pImp); + const aiImporterDesc* GetInfo () const; + +protected: + + + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + +protected: + /** Reads the file */ + void ReadStructure( aiScene* pScene); + + /** Reads the hierarchy */ + void ReadHierarchy( aiScene* pScene); + + /** Reads a node and recursively its childs and returns the created node. */ + aiNode* ReadNode(); + + /** Reads an end node and returns the created node. */ + aiNode* ReadEndSite( const std::string& pParentName); + + /** Reads a node offset for the given node */ + void ReadNodeOffset( aiNode* pNode); + + /** Reads the animation channels into the given node */ + void ReadNodeChannels( BVHLoader::Node& pNode); + + /** Reads the motion data */ + void ReadMotion( aiScene* pScene); + + /** Retrieves the next token */ + std::string GetNextToken(); + + /** Reads the next token as a float */ + float GetNextTokenAsFloat(); + + /** Aborts the file reading with an exception */ + void ThrowException( const std::string& pError); + + /** Constructs an animation for the motion data and stores it in the given scene */ + void CreateAnimation( aiScene* pScene); + +protected: + /** Filename, for a verbose error message */ + std::string mFileName; + + /** Buffer to hold the loaded file */ + std::vector mBuffer; + + /** Next char to read from the buffer */ + std::vector::const_iterator mReader; + + /** Current line, for error messages */ + unsigned int mLine; + + /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index. + * Also contain the motion data for the node's channels + */ + std::vector mNodes; + + /** basic Animation parameters */ + float mAnimTickDuration; + unsigned int mAnimNumFrames; + + bool noSkeletonMesh; +}; + +} // end of namespace Assimp + +#endif // AI_BVHLOADER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/BaseImporter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BaseImporter.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,560 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file BaseImporter.cpp + * @brief Implementation of BaseImporter + */ + +#include "AssimpPCH.h" +#include "BaseImporter.h" +#include "FileSystemFilter.h" + +#include "Importer.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() +: progress() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) +{ + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + // Gather configuration properties for this run + SetupProperties( pImp ); + + // Construct a file system filter to improve our success ratio at reading external files + FileSystemFilter filter(pFile,pIOHandler); + + // create a scene object to hold the data + ScopeGuard sc(new aiScene()); + + // dispatch importing + try + { + InternReadFile( pFile, sc, &filter); + + } catch( const std::exception& err ) { + // extract error description + mErrorText = err.what(); + DefaultLogger::get()->error(mErrorText); + return NULL; + } + + // return what we gathered from the import. + sc.dismiss(); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::GetExtensionList(std::set& extensions) +{ + const aiImporterDesc* desc = GetInfo(); + ai_assert(desc != NULL); + + const char* ext = desc->mFileExtensions; + ai_assert(ext != NULL); + + const char* last = ext; + do { + if (!*ext || *ext == ' ') { + extensions.insert(std::string(last,ext-last)); + ai_assert(ext-last > 0); + last = ext; + while(*last == ' ') { + ++last; + } + } + } + while(*ext++); +} + +// ------------------------------------------------------------------------------------------------ +/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler, + const std::string& pFile, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes /* = 200 */, + bool tokensSol /* false */) +{ + ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes); + if (!pIOHandler) + return false; + + boost::scoped_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + // read 200 characters from the file + boost::scoped_array _buffer (new char[searchBytes+1 /* for the '\0' */]); + char* buffer = _buffer.get(); + + const unsigned int read = pStream->Read(buffer,1,searchBytes); + if (!read) + return false; + + for (unsigned int i = 0; i < read;++i) + buffer[i] = ::tolower(buffer[i]); + + // It is not a proper handling of unicode files here ... + // ehm ... but it works in most cases. + char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; + while (cur != end) { + if (*cur) + *cur2++ = *cur; + ++cur; + } + *cur2 = '\0'; + + for (unsigned int i = 0; i < numTokens;++i) { + ai_assert(NULL != tokens[i]); + + + const char* r = strstr(buffer,tokens[i]); + if (!r) + continue; + // We got a match, either we don't care where it is, or it happens to + // be in the beginning of the file / line + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { + DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]); + return true; + } + } + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Simple check for file extension +/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, + const char* ext0, + const char* ext1, + const char* ext2) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension - can't read + if( pos == std::string::npos) + return false; + + const char* ext_real = & pFile[ pos+1 ]; + if( !ASSIMP_stricmp(ext_real,ext0) ) + return true; + + // check for other, optional, file extensions + if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) + return true; + + if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension from path +/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension at all + if( pos == std::string::npos) + return ""; + + std::string ret = pFile.substr(pos+1); + std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint + return ret; +} + +// ------------------------------------------------------------------------------------------------ +// Check for magic bytes at the beginning of the file. +/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, + const void* _magic, unsigned int num, unsigned int offset, unsigned int size) +{ + ai_assert(size <= 16 && _magic); + + if (!pIOHandler) { + return false; + } + union { + const char* magic; + const uint16_t* magic_u16; + const uint32_t* magic_u32; + }; + magic = reinterpret_cast(_magic); + boost::scoped_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + + // skip to offset + pStream->Seek(offset,aiOrigin_SET); + + // read 'size' characters from the file + union { + char data[16]; + uint16_t data_u16[8]; + uint32_t data_u32[4]; + }; + if(size != pStream->Read(data,1,size)) { + return false; + } + + for (unsigned int i = 0; i < num; ++i) { + // also check against big endian versions of tokens with size 2,4 + // that's just for convinience, the chance that we cause conflicts + // is quite low and it can save some lines and prevent nasty bugs + if (2 == size) { + uint16_t rev = *magic_u16; + ByteSwap::Swap(&rev); + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { + return true; + } + } + else if (4 == size) { + uint32_t rev = *magic_u32; + ByteSwap::Swap(&rev); + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { + return true; + } + } + else { + // any length ... just compare + if(!memcmp(magic,data,size)) { + return true; + } + } + magic += size; + } + } + return false; +} + +#include "ConvertUTF/ConvertUTF.h" + +// ------------------------------------------------------------------------------------------------ +void ReportResult(ConversionResult res) +{ + if(res == sourceExhausted) { + DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails"); + } + else if(res == sourceIllegal) { + DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data +void BaseImporter::ConvertToUTF8(std::vector& data) +{ + ConversionResult result; + if(data.size() < 8) { + throw DeadlyImportError("File is too small"); + } + + // UTF 8 with BOM + if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { + DefaultLogger::get()->debug("Found UTF-8 BOM ..."); + + std::copy(data.begin()+3,data.end(),data.begin()); + data.resize(data.size()-3); + return; + } + + // UTF 32 BE with BOM + if(*((uint32_t*)&data.front()) == 0xFFFE0000) { + + // swap the endianess .. + for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { + AI_SWAP4P(p); + } + } + + // UTF 32 LE with BOM + if(*((uint32_t*)&data.front()) == 0x0000FFFE) { + DefaultLogger::get()->debug("Found UTF-32 BOM ..."); + + const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1; + char* dstart,*dend; + std::vector output; + do { + output.resize(output.size()?output.size()*3/2:data.size()/2); + dstart = &output.front(),dend = &output.back()+1; + + result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); + } while(result == targetExhausted); + + ReportResult(result); + + // copy to output buffer. + const size_t outlen = (size_t)(dstart-&output.front()); + data.assign(output.begin(),output.begin()+outlen); + return; + } + + // UTF 16 BE with BOM + if(*((uint16_t*)&data.front()) == 0xFFFE) { + + // swap the endianess .. + for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { + ByteSwap::Swap2(p); + } + } + + // UTF 16 LE with BOM + if(*((uint16_t*)&data.front()) == 0xFEFF) { + DefaultLogger::get()->debug("Found UTF-16 BOM ..."); + + const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)(&data.back()+1); + char* dstart,*dend; + std::vector output; + do { + output.resize(output.size()?output.size()*3/2:data.size()*3/4); + dstart = &output.front(),dend = &output.back()+1; + + result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); + } while(result == targetExhausted); + + ReportResult(result); + + // copy to output buffer. + const size_t outlen = (size_t)(dstart-&output.front()); + data.assign(output.begin(),output.begin()+outlen); + return; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::TextFileToBuffer(IOStream* stream, + std::vector& data) +{ + ai_assert(NULL != stream); + + const size_t fileSize = stream->FileSize(); + if(!fileSize) { + throw DeadlyImportError("File is empty"); + } + + data.reserve(fileSize+1); + data.resize(fileSize); + if(fileSize != stream->Read( &data[0], 1, fileSize)) { + throw DeadlyImportError("File read error"); + } + + ConvertToUTF8(data); + + // append a binary zero to simplify string parsing + data.push_back(0); +} + +// ------------------------------------------------------------------------------------------------ +namespace Assimp +{ + // Represents an import request + struct LoadRequest + { + LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) + : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id) + { + if (_map) + map = *_map; + } + + const std::string file; + unsigned int flags; + unsigned int refCnt; + aiScene* scene; + bool loaded; + BatchLoader::PropertyMap map; + unsigned int id; + + bool operator== (const std::string& f) { + return file == f; + } + }; +} + +// ------------------------------------------------------------------------------------------------ +// BatchLoader::pimpl data structure +struct Assimp::BatchData +{ + BatchData() + : next_id(0xffff) + {} + + // IO system to be used for all imports + IOSystem* pIOSystem; + + // Importer used to load all meshes + Importer* pImporter; + + // List of all imports + std::list requests; + + // Base path + std::string pathBase; + + // Id for next item + unsigned int next_id; +}; + +// ------------------------------------------------------------------------------------------------ +BatchLoader::BatchLoader(IOSystem* pIO) +{ + ai_assert(NULL != pIO); + + data = new BatchData(); + data->pIOSystem = pIO; + + data->pImporter = new Importer(); + data->pImporter->SetIOHandler(data->pIOSystem); +} + +// ------------------------------------------------------------------------------------------------ +BatchLoader::~BatchLoader() +{ + // delete all scenes wthat have not been polled by the user + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + + delete (*it).scene; + } + data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ + delete data->pImporter; + delete data; +} + + +// ------------------------------------------------------------------------------------------------ +unsigned int BatchLoader::AddLoadRequest (const std::string& file, + unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) +{ + ai_assert(!file.empty()); + + // check whether we have this loading request already + std::list::iterator it; + for (it = data->requests.begin();it != data->requests.end(); ++it) { + + // Call IOSystem's path comparison function here + if (data->pIOSystem->ComparePaths((*it).file,file)) { + + if (map) { + if (!((*it).map == *map)) + continue; + } + else if (!(*it).map.empty()) + continue; + + (*it).refCnt++; + return (*it).id; + } + } + + // no, we don't have it. So add it to the queue ... + data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); + return data->next_id++; +} + +// ------------------------------------------------------------------------------------------------ +aiScene* BatchLoader::GetImport (unsigned int which) +{ + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + + if ((*it).id == which && (*it).loaded) { + + aiScene* sc = (*it).scene; + if (!(--(*it).refCnt)) { + data->requests.erase(it); + } + return sc; + } + } + return NULL; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::LoadAll() +{ + // no threaded implementation for the moment + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + // force validation in debug builds + unsigned int pp = (*it).flags; +#ifdef _DEBUG + pp |= aiProcess_ValidateDataStructure; +#endif + // setup config properties if necessary + ImporterPimpl* pimpl = data->pImporter->Pimpl(); + pimpl->mFloatProperties = (*it).map.floats; + pimpl->mIntProperties = (*it).map.ints; + pimpl->mStringProperties = (*it).map.strings; + + if (!DefaultLogger::isNullLogger()) + { + DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%"); + DefaultLogger::get()->info("File: " + (*it).file); + } + data->pImporter->ReadFile((*it).file,pp); + (*it).scene = data->pImporter->GetOrphanedScene(); + (*it).loaded = true; + + DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%"); + } +} + + + + diff -r 000000000000 -r b2f14e535253 libs/assimp/BaseImporter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BaseImporter.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,359 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the base class for all importer worker classes. */ +#ifndef INCLUDED_AI_BASEIMPORTER_H +#define INCLUDED_AI_BASEIMPORTER_H + +#include "Exceptional.h" + +#include +#include +#include +#include "assimp/types.h" + +struct aiScene; + +namespace Assimp { + +class IOSystem; +class Importer; +class BaseImporter; +class BaseProcess; +class SharedPostProcessInfo; +class IOStream; + +// utility to do char4 to uint32 in a portable manner +#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ + (string[1] << 16) + (string[2] << 8) + string[3])) + +// --------------------------------------------------------------------------- +template +struct ScopeGuard +{ + ScopeGuard(T* obj) : obj(obj), mdismiss() {} + ~ScopeGuard () throw() { + if (!mdismiss) { + delete obj; + } + obj = NULL; + } + + T* dismiss() { + mdismiss=true; + return obj; + } + + operator T*() { + return obj; + } + + T* operator -> () { + return obj; + } + +private: + T* obj; + bool mdismiss; +}; + + + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface + * for all importer worker classes. + * + * The interface defines two functions: CanRead() is used to check if the + * importer can handle the format of the given file. If an implementation of + * this function returns true, the importer then calls ReadFile() which + * imports the given file. ReadFile is not overridable, it just calls + * InternReadFile() and catches any ImportErrorException that might occur. + */ +class BaseImporter +{ + friend class Importer; + +public: + + /** Constructor to be privately used by #Importer */ + BaseImporter(); + + /** Destructor, private as well */ + virtual ~BaseImporter(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * + * The implementation should be as quick as possible. A check for + * the file extension is enough. If no suitable loader is found with + * this strategy, CanRead() is called again, the 'checkSig' parameter + * set to true this time. Now the implementation is expected to + * perform a full check of the file structure, possibly searching the + * first bytes of the file for magic identifiers or keywords. + * + * @param pFile Path and file name of the file to be examined. + * @param pIOHandler The IO handler to use for accessing any file. + * @param checkSig Set to true if this method is called a second time. + * This time, the implementation may take more time to examine the + * contents of the file to be loaded for magic bytes, keywords, etc + * to be able to load files with unknown/not existent file extensions. + * @return true if the class can read this file, false if not. + */ + virtual bool CanRead( + const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const = 0; + + // ------------------------------------------------------------------- + /** Imports the given file and returns the imported data. + * If the import succeeds, ownership of the data is transferred to + * the caller. If the import fails, NULL is returned. The function + * takes care that any partially constructed data is destroyed + * beforehand. + * + * @param pImp #Importer object hosting this loader. + * @param pFile Path of the file to be imported. + * @param pIOHandler IO-Handler used to open this and possible other files. + * @return The imported data or NULL if failed. If it failed a + * human-readable error description can be retrieved by calling + * GetErrorText() + * + * @note This function is not intended to be overridden. Implement + * InternReadFile() to do the import. If an exception is thrown somewhere + * in InternReadFile(), this function will catch it and transform it into + * a suitable response to the caller. + */ + aiScene* ReadFile( + const Importer* pImp, + const std::string& pFile, + IOSystem* pIOHandler + ); + + // ------------------------------------------------------------------- + /** Returns the error description of the last error that occured. + * @return A description of the last error that occured. An empty + * string if there was no error. + */ + const std::string& GetErrorText() const { + return mErrorText; + } + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + * @param pImp Importer instance + */ + virtual void SetupProperties( + const Importer* pImp + ); + + + // ------------------------------------------------------------------- + /** Called by #Importer::GetImporterInfo to get a description of + * some loader features. Importers must provide this information. */ + virtual const aiImporterDesc* GetInfo() const = 0; + + + + // ------------------------------------------------------------------- + /** Called by #Importer::GetExtensionList for each loaded importer. + * Take the extension list contained in the structure returned by + * #GetInfo and insert all file extensions into the given set. + * @param extension set to collect file extensions in*/ + void GetExtensionList(std::set& extensions); + +protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. The + * function is expected to throw an ImportErrorException if there is + * an error. If it terminates normally, the data in aiScene is + * expected to be correct. Override this function to implement the + * actual importing. + *
+ * The output scene must meet the following requirements:
+ *
    + *
  • At least a root node must be there, even if its only purpose + * is to reference one mesh.
  • + *
  • aiMesh::mPrimitiveTypes may be 0. The types of primitives + * in the mesh are determined automatically in this case.
  • + *
  • the vertex data is stored in a pseudo-indexed "verbose" format. + * In fact this means that every vertex that is referenced by + * a face is unique. Or the other way round: a vertex index may + * not occur twice in a single aiMesh.
  • + *
  • aiAnimation::mDuration may be -1. Assimp determines the length + * of the animation automatically in this case as the length of + * the longest animation channel.
  • + *
  • aiMesh::mBitangents may be NULL if tangents and normals are + * given. In this case bitangents are computed as the cross product + * between normal and tangent.
  • + *
  • There needn't be a material. If none is there a default material + * is generated. However, it is recommended practice for loaders + * to generate a default material for yourself that matches the + * default material setting for the file format better than Assimp's + * generic default material. Note that default materials *should* + * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded + * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) + * texture.
  • + *
+ * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
    + *
  • at least one mesh must be there
  • + *
  • there may be no meshes with 0 vertices or faces
  • + *
+ * This won't be checked (except by the validation step): Assimp will + * crash if one of the conditions is not met! + * + * @param pFile Path of the file to be imported. + * @param pScene The scene object to hold the imported data. + * NULL is not a valid parameter. + * @param pIOHandler The IO handler to use for any file access. + * NULL is not a valid parameter. */ + virtual void InternReadFile( + const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ) = 0; + +public: // static utilities + + // ------------------------------------------------------------------- + /** A utility for CanRead(). + * + * The function searches the header of a file for a specific token + * and returns true if this token is found. This works for text + * files only. There is a rudimentary handling of UNICODE files. + * The comparison is case independent. + * + * @param pIOSystem IO System to work with + * @param file File name of the file + * @param tokens List of tokens to search for + * @param numTokens Size of the token array + * @param searchBytes Number of bytes to be searched for the tokens. + */ + static bool SearchFileHeaderForToken( + IOSystem* pIOSystem, + const std::string& file, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes = 200, + bool tokensSol = false); + + // ------------------------------------------------------------------- + /** @brief Check whether a file has a specific file extension + * @param pFile Input file + * @param ext0 Extension to check for. Lowercase characters only, no dot! + * @param ext1 Optional second extension + * @param ext2 Optional third extension + * @note Case-insensitive + */ + static bool SimpleExtensionCheck ( + const std::string& pFile, + const char* ext0, + const char* ext1 = NULL, + const char* ext2 = NULL); + + // ------------------------------------------------------------------- + /** @brief Extract file extension from a string + * @param pFile Input file + * @return Extension without trailing dot, all lowercase + */ + static std::string GetExtension ( + const std::string& pFile); + + // ------------------------------------------------------------------- + /** @brief Check whether a file starts with one or more magic tokens + * @param pFile Input file + * @param pIOHandler IO system to be used + * @param magic n magic tokens + * @params num Size of magic + * @param offset Offset from file start where tokens are located + * @param Size of one token, in bytes. Maximally 16 bytes. + * @return true if one of the given tokens was found + * + * @note For convinence, the check is also performed for the + * byte-swapped variant of all tokens (big endian). Only for + * tokens of size 2,4. + */ + static bool CheckMagicToken( + IOSystem* pIOHandler, + const std::string& pFile, + const void* magic, + unsigned int num, + unsigned int offset = 0, + unsigned int size = 4); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file to our + * UTF8 character set. Errors are reported, but ignored. + * + * @param data File buffer to be converted to UTF8 data. The buffer + * is resized as appropriate. */ + static void ConvertToUTF8( + std::vector& data); + + // ------------------------------------------------------------------- + /** Utility for text file loaders which copies the contents of the + * file into a memory buffer and converts it to our UTF8 + * representation. + * @param stream Stream to read from. + * @param data Output buffer to be resized and filled with the + * converted text file data. The buffer is terminated with + * a binary 0. */ + static void TextFileToBuffer( + IOStream* stream, + std::vector& data); + +protected: + + /** Error description in case there was one. */ + std::string mErrorText; + + /** Currently set progress handler */ + ProgressHandler* progress; +}; + + + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/BaseProcess.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BaseProcess.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,105 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of BaseProcess */ + +#include "AssimpPCH.h" +#include "BaseImporter.h" +#include "BaseProcess.h" + +#include "Importer.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseProcess::BaseProcess() +: shared() +, progress() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseProcess::~BaseProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::ExecuteOnScene( Importer* pImp) +{ + ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); + + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + SetupProperties( pImp ); + + // catch exceptions thrown inside the PostProcess-Step + try + { + Execute(pImp->Pimpl()->mScene); + + } catch( const std::exception& err ) { + + // extract error description + pImp->Pimpl()->mErrorString = err.what(); + DefaultLogger::get()->error(pImp->Pimpl()->mErrorString); + + // and kill the partially imported data + delete pImp->Pimpl()->mScene; + pImp->Pimpl()->mScene = NULL; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +bool BaseProcess::RequireVerboseFormat() const +{ + return true; +} + diff -r 000000000000 -r b2f14e535253 libs/assimp/BaseProcess.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BaseProcess.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,294 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Base class of all import post processing steps */ +#ifndef INCLUDED_AI_BASEPROCESS_H +#define INCLUDED_AI_BASEPROCESS_H + +#include + +#include "assimp/types.h" +#include "GenericProperty.h" + +struct aiScene; + +namespace Assimp { + +class Importer; + +// --------------------------------------------------------------------------- +/** Helper class to allow post-processing steps to interact with each other. + * + * The class maintains a simple property list that can be used by pp-steps + * to provide additional information to other steps. This is primarily + * intended for cross-step optimizations. + */ +class SharedPostProcessInfo +{ +public: + + struct Base + { + virtual ~Base() + {} + }; + + //! Represents data that is allocated on the heap, thus needs to be deleted + template + struct THeapData : public Base + { + THeapData(T* in) + : data (in) + {} + + ~THeapData() + { + delete data; + } + T* data; + }; + + //! Represents static, by-value data not allocated on the heap + template + struct TStaticData : public Base + { + TStaticData(T in) + : data (in) + {} + + ~TStaticData() + {} + + T data; + }; + + // some typedefs for cleaner code + typedef unsigned int KeyType; + typedef std::map PropertyMap; + +public: + + //! Destructor + ~SharedPostProcessInfo() + { + Clean(); + } + + //! Remove all stored properties from the table + void Clean() + { + // invoke the virtual destructor for all stored properties + for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); + it != end; ++it) + { + delete (*it).second; + } + pmap.clear(); + } + + //! Add a heap property to the list + template + void AddProperty( const char* name, T* in ){ + AddProperty(name,(Base*)new THeapData(in)); + } + + //! Add a static by-value property to the list + template + void AddProperty( const char* name, T in ){ + AddProperty(name,(Base*)new TStaticData(in)); + } + + + //! Get a heap property + template + bool GetProperty( const char* name, T*& out ) const + { + THeapData* t = (THeapData*)GetPropertyInternal(name); + if(!t) + { + out = NULL; + return false; + } + out = t->data; + return true; + } + + //! Get a static, by-value property + template + bool GetProperty( const char* name, T& out ) const + { + TStaticData* t = (TStaticData*)GetPropertyInternal(name); + if(!t)return false; + out = t->data; + return true; + } + + //! Remove a property of a specific type + void RemoveProperty( const char* name) { + SetGenericPropertyPtr(pmap,name,NULL); + } + +private: + + void AddProperty( const char* name, Base* data) { + SetGenericPropertyPtr(pmap,name,data); + } + + Base* GetPropertyInternal( const char* name) const { + return GetGenericProperty(pmap,name,NULL); + } + +private: + + //! Map of all stored properties + PropertyMap pmap; +}; + +#if 0 + +// --------------------------------------------------------------------------- +/** @brief Represents a dependency table for a postprocessing steps. + * + * For future use. + */ + struct PPDependencyTable + { + unsigned int execute_me_before_these; + unsigned int execute_me_after_these; + unsigned int only_if_these_are_not_specified; + unsigned int mutually_exclusive_with; + }; + +#endif + + +#define AI_SPP_SPATIAL_SORT "$Spat" + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). + * Enum #aiPostProcessSteps defines which flags are available. + * After a successful import the Importer iterates over its internal array + * of processes and calls IsActive() on each process to evaluate if the step + * should be executed. If the function returns true, the class' Execute() + * function is called subsequently. + */ +class ASSIMP_API_WINONLY BaseProcess +{ + friend class Importer; + +public: + + /** Constructor to be privately used by Importer */ + BaseProcess(); + + /** Destructor, private as well */ + virtual ~BaseProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + virtual bool IsActive( unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Check whether this step expects its input vertex data to be + * in verbose format. */ + virtual bool RequireVerboseFormat() const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * The function deletes the scene if the postprocess step fails ( + * the object pointer will be set to NULL). + * @param pImp Importer instance (pImp->mScene must be valid) + */ + void ExecuteOnScene( Importer* pImp); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * A process should throw an ImportErrorException* if it fails. + * This method must be implemented by deriving classes. + * @param pScene The imported data to work at. + */ + virtual void Execute( aiScene* pScene) = 0; + + + // ------------------------------------------------------------------- + /** Assign a new SharedPostProcessInfo to the step. This object + * allows multiple postprocess steps to share data. + * @param sh May be NULL + */ + inline void SetSharedData(SharedPostProcessInfo* sh) { + shared = sh; + } + + // ------------------------------------------------------------------- + /** Get the shared data that is assigned to the step. + */ + inline SharedPostProcessInfo* GetSharedData() { + return shared; + } + +protected: + + /** See the doc of #SharedPostProcessInfo for more details */ + SharedPostProcessInfo* shared; + + /** Currently active progress handler */ + ProgressHandler* progress; +}; + + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC diff -r 000000000000 -r b2f14e535253 libs/assimp/BlenderDNA.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BlenderDNA.cpp Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,372 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BlenderDNA.cpp + * @brief Implementation of the Blender `DNA`, that is its own + * serialized set of data structures. + */ +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER +#include "BlenderDNA.h" +#include "StreamReader.h" +#include "fast_atof.h" + +using namespace Assimp; +using namespace Assimp::Blender; +using namespace Assimp::Formatter; + +#define for_each BOOST_FOREACH +bool match4(StreamReaderAny& stream, const char* string) { + char tmp[] = { + (stream).GetI1(), + (stream).GetI1(), + (stream).GetI1(), + (stream).GetI1() + }; + return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); +} + +struct Type { + size_t size; + std::string name; +}; + +// ------------------------------------------------------------------------------------------------ +void DNAParser :: Parse () +{ + StreamReaderAny& stream = *db.reader.get(); + DNA& dna = db.dna; + + if(!match4(stream,"SDNA")) { + throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); + } + + // name dictionary + if(!match4(stream,"NAME")) { + throw DeadlyImportError("BlenderDNA: Expected NAME field"); + } + + std::vector names (stream.GetI4()); + for_each(std::string& s, names) { + while (char c = stream.GetI1()) { + s += c; + } + } + + // type dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"TYPE")) { + throw DeadlyImportError("BlenderDNA: Expected TYPE field"); + } + + std::vector types (stream.GetI4()); + for_each(Type& s, types) { + while (char c = stream.GetI1()) { + s.name += c; + } + } + + // type length dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"TLEN")) { + throw DeadlyImportError("BlenderDNA: Expected TLEN field"); + } + + for_each(Type& s, types) { + s.size = stream.GetI2(); + } + + // structures dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"STRC")) { + throw DeadlyImportError("BlenderDNA: Expected STRC field"); + } + + size_t end = stream.GetI4(), fields = 0; + + dna.structures.reserve(end); + for(size_t i = 0; i != end; ++i) { + + uint16_t n = stream.GetI2(); + if (n >= types.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid type index in structure name" ,n, + " (there are only ", types.size(), " entries)" + )); + } + + // maintain separate indexes + dna.indices[types[n].name] = dna.structures.size(); + + dna.structures.push_back(Structure()); + Structure& s = dna.structures.back(); + s.name = types[n].name; + //s.index = dna.structures.size()-1; + + n = stream.GetI2(); + s.fields.reserve(n); + + size_t offset = 0; + for (size_t m = 0; m < n; ++m, ++fields) { + + uint16_t j = stream.GetI2(); + if (j >= types.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid type index in structure field ", j, + " (there are only ", types.size(), " entries)" + )); + } + s.fields.push_back(Field()); + Field& f = s.fields.back(); + f.offset = offset; + + f.type = types[j].name; + f.size = types[j].size; + + j = stream.GetI2(); + if (j >= names.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid name index in structure field ", j, + " (there are only ", names.size(), " entries)" + )); + } + + f.name = names[j]; + f.flags = 0u; + + // pointers always specify the size of the pointee instead of their own. + // The pointer asterisk remains a property of the lookup name. + if (f.name[0] == '*') { + f.size = db.i64bit ? 8 : 4; + f.flags |= FieldFlag_Pointer; + } + + // arrays, however, specify the size of a single element so we + // need to parse the (possibly multi-dimensional) array declaration + // in order to obtain the actual size of the array in the file. + // Also we need to alter the lookup name to include no array + // brackets anymore or size fixup won't work (if our size does + // not match the size read from the DNA). + if (*f.name.rbegin() == ']') { + const std::string::size_type rb = f.name.find('['); + if (rb == std::string::npos) { + throw DeadlyImportError((format(), + "BlenderDNA: Encountered invalid array declaration ", + f.name + )); + } + + f.flags |= FieldFlag_Array; + DNA::ExtractArraySize(f.name,f.array_sizes); + f.name = f.name.substr(0,rb); + + f.size *= f.array_sizes[0] * f.array_sizes[1]; + } + + // maintain separate indexes + s.indices[f.name] = s.fields.size()-1; + offset += f.size; + } + s.size = offset; + } + + DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), + " structures with totally ",fields," fields")); + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + dna.DumpToFile(); +#endif + + dna.AddPrimitiveStructures(); + dna.RegisterConverters(); +} + + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + +#include +// ------------------------------------------------------------------------------------------------ +void DNA :: DumpToFile() +{ + // we dont't bother using the VFS here for this is only for debugging. + // (and all your bases are belong to us). + + std::ofstream f("dna.txt"); + if (f.fail()) { + DefaultLogger::get()->error("Could not dump dna to dna.txt"); + return; + } + f << "Field format: type name offset size" << "\n"; + f << "Structure format: name size" << "\n"; + + for_each(const Structure& s, structures) { + f << s.name << " " << s.size << "\n\n"; + for_each(const Field& ff, s.fields) { + f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl; + } + f << std::endl; + } + DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); +} +#endif + +// ------------------------------------------------------------------------------------------------ +/*static*/ void DNA :: ExtractArraySize( + const std::string& out, + size_t array_sizes[2] +) +{ + array_sizes[0] = array_sizes[1] = 1; + std::string::size_type pos = out.find('['); + if (pos++ == std::string::npos) { + return; + } + array_sizes[0] = strtoul10(&out[pos]); + + pos = out.find('[',pos); + if (pos++ == std::string::npos) { + return; + } + array_sizes[1] = strtoul10(&out[pos]); +} + +// ------------------------------------------------------------------------------------------------ +boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( + const Structure& structure, + const FileDatabase& db +) const +{ + std::map::const_iterator it = converters.find(structure.name); + if (it == converters.end()) { + return boost::shared_ptr< ElemBase >(); + } + + boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); + (structure.*((*it).second.second))(ret,db); + + return ret; +} + +// ------------------------------------------------------------------------------------------------ +DNA::FactoryPair DNA :: GetBlobToStructureConverter( + const Structure& structure, + const FileDatabase& /*db*/ +) const +{ + std::map::const_iterator it = converters.find(structure.name); + return it == converters.end() ? FactoryPair() : (*it).second; +} + +// basing on http://www.blender.org/development/architecture/notes-on-sdna/ +// ------------------------------------------------------------------------------------------------ +void DNA :: AddPrimitiveStructures() +{ + // NOTE: these are just dummies. Their presence enforces + // Structure::Convert to be called on these + // empty structures. These converters are special + // overloads which scan the name of the structure and + // perform the required data type conversion if one + // of these special names is found in the structure + // in question. + + indices["int"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "int"; + structures.back().size = 4; + + indices["short"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "short"; + structures.back().size = 2; + + + indices["char"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "char"; + structures.back().size = 1; + + + indices["float"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "float"; + structures.back().size = 4; + + + indices["double"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "double"; + structures.back().size = 8; + + // no long, seemingly. +} + +// ------------------------------------------------------------------------------------------------ +void SectionParser :: Next() +{ + stream.SetCurrentPos(current.start + current.size); + + const char tmp[] = { + stream.GetI1(), + stream.GetI1(), + stream.GetI1(), + stream.GetI1() + }; + current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); + + current.size = stream.GetI4(); + current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); + + current.dna_index = stream.GetI4(); + current.num = stream.GetI4(); + + current.start = stream.GetCurrentPos(); + if (stream.GetRemainingSizeToLimit() < current.size) { + throw DeadlyImportError("BLEND: invalid size of file block"); + } + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + DefaultLogger::get()->debug(current.id); +#endif +} + + + +#endif diff -r 000000000000 -r b2f14e535253 libs/assimp/BlenderDNA.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/assimp/BlenderDNA.h Sat Feb 01 19:58:19 2014 +0200 @@ -0,0 +1,798 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BlenderDNA.h + * @brief Blender `DNA` (file format specification embedded in + * blend file itself) loader. + */ +#ifndef INCLUDED_AI_BLEND_DNA_H +#define INCLUDED_AI_BLEND_DNA_H + +#include "BaseImporter.h" +#include "TinyFormatter.h" + +// enable verbose log output. really verbose, so be careful. +#ifdef _DEBUG +# define ASSIMP_BUILD_BLENDER_DEBUG +#endif + +// #define ASSIMP_BUILD_BLENDER_NO_STATS + +namespace Assimp { + template class StreamReader; + typedef StreamReader StreamReaderAny; + + namespace Blender { + class FileDatabase; + struct FileBlockHead; + + template