From: John Tsiombikas Date: Thu, 30 Mar 2023 21:57:34 +0000 (+0200) Subject: adding goat3d to the project X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=commitdiff_plain;h=487d801f62b3f7cea009b5e560cfd06e57170970;hp=103b67f9f1e041dc5f6ffbb59004ebe846e3f3f9;p=deeprace adding goat3d to the project --- diff --git a/Makefile.sgi b/Makefile.sgi index d924436..fe4c6e9 100644 --- a/Makefile.sgi +++ b/Makefile.sgi @@ -1,27 +1,33 @@ -obj = src/main.o src/miniglut.o src/game.o src/scr_menu.o src/scr_game.o src/util.o +obj = src/main.o src/miniglut.o src/game.o src/scr_menu.o src/scr_game.o \ + src/util.o bin = game dbg = -g opt = -O3 def = -DMINIGLUT_USE_LIBC +inc = -Ilibs -Ilibs/imago -Ilibs/treestor/include -Ilibs/goat3d/include +libs = libs/unix/imago.a libs/unix/goat3d.a libs/unix/treestor.a CFLAGS = $(warn) $(dbg) $(opt) $(inc) $(def) -LDFLAGS = -lGL -lGLU -lX11 -lm +LDFLAGS = $(libs) -lGL -lGLU -lX11 -lm $(bin): $(obj) libs $(CC) -o $@ $(obj) $(LDFLAGS) .c.o: - $(CC) -c $< $(CFLAGS) -o $@ + $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean clean: rm -f $(obj) $(bin) .PHONY: libs -libs: - $(MAKE) -C libs +libs: make-libs + +.PHONY: make-libs +make-libs: + cd libs; $(MAKE) .PHONY: clean-libs clean-libs: - $(MAKE) -C libs clean + cd libs; $(MAKE) clean diff --git a/libs/Makefile b/libs/Makefile index e197eb2..757c5d0 100644 --- a/libs/Makefile +++ b/libs/Makefile @@ -1,14 +1,38 @@ .PHONY: all -all: imago +all: make-imago make-treestor make-goat3d .PHONY: clean -clean: clean-imago - +clean: clean-imago clean-treestor clean-goat3d .PHONY: imago -imago: - $(MAKE) -C imago +imago: make-imago + +.PHONY: make-imago +make-imago: + cd imago; $(MAKE) .PHONY: clean-imago clean-imago: - $(MAKE) -C imago clean + cd imago; $(MAKE) clean + +.PHONY: treestor +treestor: make-treestor + +.PHONY: make-treestor +make-treestor: + cd treestor; $(MAKE) + +.PHONY: clean-treestor +clean-treestor: + cd treestor; $(MAKE) clean + +.PHONY: goat3d +goat3d: make-goat3d + +.PHONY: make-goat3d +make-goat3d: + cd goat3d; $(MAKE) + +.PHONY: clean-goat3d +clean-goat3d: + cd goat3d; $(MAKE) clean diff --git a/libs/cgmath/cgmath.h b/libs/cgmath/cgmath.h index c41608d..d1ead57 100644 --- a/libs/cgmath/cgmath.h +++ b/libs/cgmath/cgmath.h @@ -59,196 +59,204 @@ typedef enum cgm_euler_mode { #ifdef __cplusplus extern "C" { +#else + +#if (__STDC_VERSION__ >= 199901) || defined(__GNUC__) +#define CGM_INLINE inline +#else +#define CGM_INLINE __inline +#endif + #endif /* --- operations on cgm_vec3 --- */ -static inline void cgm_vcons(cgm_vec3 *v, float x, float y, float z); - -static inline void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b); -static inline void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a+b*s */ -static inline void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b); -static inline void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a-b*s */ -static inline void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b); -static inline void cgm_vscale(cgm_vec3 *v, float s); -static inline void cgm_vmul_m4v3(cgm_vec3 *v, const float *m); /* m4x4 * v */ -static inline void cgm_vmul_v3m4(cgm_vec3 *v, const float *m); /* v * m4x4 */ -static inline void cgm_vmul_m3v3(cgm_vec3 *v, const float *m); /* m3x3 * v (m still 16 floats) */ -static inline void cgm_vmul_v3m3(cgm_vec3 *v, const float *m); /* v * m3x3 (m still 16 floats) */ - -static inline float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b); -static inline void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b); -static inline float cgm_vlength(const cgm_vec3 *v); -static inline float cgm_vlength_sq(const cgm_vec3 *v); -static inline float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b); -static inline float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b); -static inline void cgm_vnormalize(cgm_vec3 *v); - -static inline void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n); -static inline void cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior); - -static inline void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q); -static inline void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle); -static inline void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z); -static inline void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode); - -static inline void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t); +static CGM_INLINE void cgm_vcons(cgm_vec3 *v, float x, float y, float z); + +static CGM_INLINE void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a+b*s */ +static CGM_INLINE void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s); /* a-b*s */ +static CGM_INLINE void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE void cgm_vscale(cgm_vec3 *v, float s); +static CGM_INLINE void cgm_vmul_m4v3(cgm_vec3 *v, const float *m); /* m4x4 * v */ +static CGM_INLINE void cgm_vmul_v3m4(cgm_vec3 *v, const float *m); /* v * m4x4 */ +static CGM_INLINE void cgm_vmul_m3v3(cgm_vec3 *v, const float *m); /* m3x3 * v (m still 16 floats) */ +static CGM_INLINE void cgm_vmul_v3m3(cgm_vec3 *v, const float *m); /* v * m3x3 (m still 16 floats) */ + +static CGM_INLINE float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE float cgm_vlength(const cgm_vec3 *v); +static CGM_INLINE float cgm_vlength_sq(const cgm_vec3 *v); +static CGM_INLINE float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b); +static CGM_INLINE void cgm_vnormalize(cgm_vec3 *v); + +static CGM_INLINE void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n); +static CGM_INLINE void cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior); + +static CGM_INLINE void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q); +static CGM_INLINE void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle); +static CGM_INLINE void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z); +static CGM_INLINE void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode); + +static CGM_INLINE void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t); #define cgm_velem(vptr, idx) ((&(vptr)->x)[idx]) /* --- operations on cgm_vec4 --- */ -static inline void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w); +static CGM_INLINE void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w); -static inline void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b); -static inline void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b); -static inline void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b); -static inline void cgm_wscale(cgm_vec4 *v, float s); +static CGM_INLINE void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE void cgm_wscale(cgm_vec4 *v, float s); -static inline void cgm_wmul_m4v4(cgm_vec4 *v, const float *m); -static inline void cgm_wmul_v4m4(cgm_vec4 *v, const float *m); -static inline void cgm_wmul_m34v4(cgm_vec4 *v, const float *m); /* doesn't affect w */ -static inline void cgm_wmul_v4m43(cgm_vec4 *v, const float *m); /* doesn't affect w */ -static inline void cgm_wmul_m3v4(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ -static inline void cgm_wmul_v4m3(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ +static CGM_INLINE void cgm_wmul_m4v4(cgm_vec4 *v, const float *m); +static CGM_INLINE void cgm_wmul_v4m4(cgm_vec4 *v, const float *m); +static CGM_INLINE void cgm_wmul_m34v4(cgm_vec4 *v, const float *m); /* doesn't affect w */ +static CGM_INLINE void cgm_wmul_v4m43(cgm_vec4 *v, const float *m); /* doesn't affect w */ +static CGM_INLINE void cgm_wmul_m3v4(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ +static CGM_INLINE void cgm_wmul_v4m3(cgm_vec4 *v, const float *m); /* (m still 16 floats) */ -static inline float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b); -static inline float cgm_wlength(const cgm_vec4 *v); -static inline float cgm_wlength_sq(const cgm_vec4 *v); -static inline float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b); -static inline float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b); -static inline void cgm_wnormalize(cgm_vec4 *v); +static CGM_INLINE float cgm_wlength(const cgm_vec4 *v); +static CGM_INLINE float cgm_wlength_sq(const cgm_vec4 *v); +static CGM_INLINE float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b); +static CGM_INLINE void cgm_wnormalize(cgm_vec4 *v); -static inline void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t); +static CGM_INLINE void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t); #define cgm_welem(vptr, idx) ((&(vptr)->x)[idx]) /* --- operations on quaternions --- */ -static inline void cgm_qcons(cgm_quat *q, float x, float y, float z, float w); +static CGM_INLINE void cgm_qcons(cgm_quat *q, float x, float y, float z, float w); -static inline void cgm_qneg(cgm_quat *q); -static inline void cgm_qadd(cgm_quat *a, const cgm_quat *b); -static inline void cgm_qsub(cgm_quat *a, const cgm_quat *b); -static inline void cgm_qmul(cgm_quat *a, const cgm_quat *b); +static CGM_INLINE void cgm_qneg(cgm_quat *q); +static CGM_INLINE void cgm_qadd(cgm_quat *a, const cgm_quat *b); +static CGM_INLINE void cgm_qsub(cgm_quat *a, const cgm_quat *b); +static CGM_INLINE void cgm_qmul(cgm_quat *a, const cgm_quat *b); -static inline float cgm_qlength(const cgm_quat *q); -static inline float cgm_qlength_sq(const cgm_quat *q); -static inline void cgm_qnormalize(cgm_quat *q); -static inline void cgm_qconjugate(cgm_quat *q); -static inline void cgm_qinvert(cgm_quat *q); +static CGM_INLINE float cgm_qlength(const cgm_quat *q); +static CGM_INLINE float cgm_qlength_sq(const cgm_quat *q); +static CGM_INLINE void cgm_qnormalize(cgm_quat *q); +static CGM_INLINE void cgm_qconjugate(cgm_quat *q); +static CGM_INLINE void cgm_qinvert(cgm_quat *q); -static inline void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z); -static inline void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z); +static CGM_INLINE void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z); +static CGM_INLINE void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z); -static inline void cgm_qslerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); -static inline void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); +static CGM_INLINE void cgm_qslerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); +static CGM_INLINE void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t); #define cgm_qelem(qptr, idx) ((&(qptr)->x)[idx]) /* --- operations on matrices --- */ -static inline void cgm_mcopy(float *dest, const float *src); -static inline void cgm_mzero(float *m); -static inline void cgm_midentity(float *m); - -static inline void cgm_mmul(float *a, const float *b); -static inline void cgm_mpremul(float *a, const float *b); - -static inline void cgm_msubmatrix(float *m, int row, int col); -static inline void cgm_mupper3(float *m); -static inline float cgm_msubdet(const float *m, int row, int col); -static inline float cgm_mcofactor(const float *m, int row, int col); -static inline float cgm_mdet(const float *m); -static inline void cgm_mtranspose(float *m); -static inline void cgm_mcofmatrix(float *m); -static inline int cgm_minverse(float *m); /* returns 0 on success, -1 for singular */ - -static inline void cgm_mtranslation(float *m, float x, float y, float z); -static inline void cgm_mscaling(float *m, float sx, float sy, float sz); -static inline void cgm_mrotation_x(float *m, float angle); -static inline void cgm_mrotation_y(float *m, float angle); -static inline void cgm_mrotation_z(float *m, float angle); -static inline void cgm_mrotation_axis(float *m, int idx, float angle); -static inline void cgm_mrotation(float *m, float angle, float x, float y, float z); -static inline void cgm_mrotation_euler(float *m, float a, float b, float c, int mode); -static inline void cgm_mrotation_quat(float *m, const cgm_quat *q); - -static inline void cgm_mtranslate(float *m, float x, float y, float z); -static inline void cgm_mscale(float *m, float sx, float sy, float sz); -static inline void cgm_mrotate_x(float *m, float angle); -static inline void cgm_mrotate_y(float *m, float angle); -static inline void cgm_mrotate_z(float *m, float angle); -static inline void cgm_mrotate_axis(float *m, int idx, float angle); -static inline void cgm_mrotate(float *m, float angle, float x, float y, float z); -static inline void cgm_mrotate_euler(float *m, float a, float b, float c, int mode); -static inline void cgm_mrotate_quat(float *m, const cgm_quat *q); - -static inline void cgm_mpretranslate(float *m, float x, float y, float z); -static inline void cgm_mprescale(float *m, float sx, float sy, float sz); -static inline void cgm_mprerotate_x(float *m, float angle); -static inline void cgm_mprerotate_y(float *m, float angle); -static inline void cgm_mprerotate_z(float *m, float angle); -static inline void cgm_mprerotate_axis(float *m, int idx, float angle); -static inline void cgm_mprerotate(float *m, float angle, float x, float y, float z); -static inline void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode); -static inline void cgm_mprerotate_quat(float *m, const cgm_quat *q); - -static inline void cgm_mget_translation(const float *m, cgm_vec3 *res); -static inline void cgm_mget_rotation(const float *m, cgm_quat *res); -static inline void cgm_mget_scaling(const float *m, cgm_vec3 *res); -static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res); - -static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, +static CGM_INLINE void cgm_mcopy(float *dest, const float *src); +static CGM_INLINE void cgm_mzero(float *m); +static CGM_INLINE void cgm_midentity(float *m); + +static CGM_INLINE void cgm_mmul(float *a, const float *b); +static CGM_INLINE void cgm_mpremul(float *a, const float *b); + +static CGM_INLINE void cgm_msubmatrix(float *m, int row, int col); +static CGM_INLINE void cgm_mupper3(float *m); +static CGM_INLINE float cgm_msubdet(const float *m, int row, int col); +static CGM_INLINE float cgm_mcofactor(const float *m, int row, int col); +static CGM_INLINE float cgm_mdet(const float *m); +static CGM_INLINE void cgm_mtranspose(float *m); +static CGM_INLINE void cgm_mcofmatrix(float *m); +static CGM_INLINE int cgm_minverse(float *m); /* returns 0 on success, -1 for singular */ + +static CGM_INLINE void cgm_mtranslation(float *m, float x, float y, float z); +static CGM_INLINE void cgm_mscaling(float *m, float sx, float sy, float sz); +static CGM_INLINE void cgm_mrotation_x(float *m, float angle); +static CGM_INLINE void cgm_mrotation_y(float *m, float angle); +static CGM_INLINE void cgm_mrotation_z(float *m, float angle); +static CGM_INLINE void cgm_mrotation_axis(float *m, int idx, float angle); +static CGM_INLINE void cgm_mrotation(float *m, float angle, float x, float y, float z); +static CGM_INLINE void cgm_mrotation_euler(float *m, float a, float b, float c, int mode); +static CGM_INLINE void cgm_mrotation_quat(float *m, const cgm_quat *q); + +static CGM_INLINE void cgm_mtranslate(float *m, float x, float y, float z); +static CGM_INLINE void cgm_mscale(float *m, float sx, float sy, float sz); +static CGM_INLINE void cgm_mrotate_x(float *m, float angle); +static CGM_INLINE void cgm_mrotate_y(float *m, float angle); +static CGM_INLINE void cgm_mrotate_z(float *m, float angle); +static CGM_INLINE void cgm_mrotate_axis(float *m, int idx, float angle); +static CGM_INLINE void cgm_mrotate(float *m, float angle, float x, float y, float z); +static CGM_INLINE void cgm_mrotate_euler(float *m, float a, float b, float c, int mode); +static CGM_INLINE void cgm_mrotate_quat(float *m, const cgm_quat *q); + +static CGM_INLINE void cgm_mpretranslate(float *m, float x, float y, float z); +static CGM_INLINE void cgm_mprescale(float *m, float sx, float sy, float sz); +static CGM_INLINE void cgm_mprerotate_x(float *m, float angle); +static CGM_INLINE void cgm_mprerotate_y(float *m, float angle); +static CGM_INLINE void cgm_mprerotate_z(float *m, float angle); +static CGM_INLINE void cgm_mprerotate_axis(float *m, int idx, float angle); +static CGM_INLINE void cgm_mprerotate(float *m, float angle, float x, float y, float z); +static CGM_INLINE void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode); +static CGM_INLINE void cgm_mprerotate_quat(float *m, const cgm_quat *q); + +static CGM_INLINE void cgm_mget_translation(const float *m, cgm_vec3 *res); +static CGM_INLINE void cgm_mget_rotation(const float *m, cgm_quat *res); +static CGM_INLINE void cgm_mget_scaling(const float *m, cgm_vec3 *res); +static CGM_INLINE void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res); + +static CGM_INLINE void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, const cgm_vec3 *up); -static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, +static CGM_INLINE void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, const cgm_vec3 *up); -static inline void cgm_mortho(float *m, float left, float right, float bot, float top, +static CGM_INLINE void cgm_mortho(float *m, float left, float right, float bot, float top, float znear, float zfar); -static inline void cgm_mfrustum(float *m, float left, float right, float bot, float top, +static CGM_INLINE void cgm_mfrustum(float *m, float left, float right, float bot, float top, float znear, float zfar); -static inline void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar); +static CGM_INLINE void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar); -static inline void cgm_mmirror(float *m, float a, float b, float c, float d); +static CGM_INLINE void cgm_mmirror(float *m, float a, float b, float c, float d); /* --- operations on rays --- */ -static inline void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz); +static CGM_INLINE void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz); -static inline void cgm_rmul_mr(cgm_ray *ray, const float *m); /* m4x4 * ray */ -static inline void cgm_rmul_rm(cgm_ray *ray, const float *m); /* ray * m4x4 */ +static CGM_INLINE void cgm_rmul_mr(cgm_ray *ray, const float *m); /* m4x4 * ray */ +static CGM_INLINE void cgm_rmul_rm(cgm_ray *ray, const float *m); /* ray * m4x4 */ -static inline void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n); -static inline void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior); +static CGM_INLINE void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n); +static CGM_INLINE void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior); /* --- miscellaneous utility functions --- */ -static inline float cgm_deg_to_rad(float deg); -static inline float cgm_rad_to_deg(float rad); +static CGM_INLINE float cgm_deg_to_rad(float deg); +static CGM_INLINE float cgm_rad_to_deg(float rad); -static inline float cgm_smoothstep(float a, float b, float x); -static inline float cgm_lerp(float a, float b, float t); -static inline float cgm_logerp(float a, float b, float t); -static inline float cgm_bezier(float a, float b, float c, float d, float t); -static inline float cgm_bspline(float a, float b, float c, float d, float t); -static inline float cgm_spline(float a, float b, float c, float d, float t); +static CGM_INLINE float cgm_smoothstep(float a, float b, float x); +static CGM_INLINE float cgm_lerp(float a, float b, float t); +static CGM_INLINE float cgm_logerp(float a, float b, float t); +static CGM_INLINE float cgm_bezier(float a, float b, float c, float d, float t); +static CGM_INLINE float cgm_bspline(float a, float b, float c, float d, float t); +static CGM_INLINE float cgm_spline(float a, float b, float c, float d, float t); -static inline void cgm_discrand(cgm_vec3 *v, float rad); -static inline void cgm_sphrand(cgm_vec3 *v, float rad); +static CGM_INLINE void cgm_discrand(cgm_vec3 *v, float rad); +static CGM_INLINE void cgm_sphrand(cgm_vec3 *v, float rad); -static inline void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, +static CGM_INLINE void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, const float *inv_viewproj); -static inline void cgm_glu_unproject(float winx, float winy, float winz, +static CGM_INLINE void cgm_glu_unproject(float winx, float winy, float winz, const float *view, const float *proj, const int *vp, float *objx, float *objy, float *objz); -static inline void cgm_pick_ray(cgm_ray *ray, float nx, float ny, +static CGM_INLINE void cgm_pick_ray(cgm_ray *ray, float nx, float ny, const float *viewmat, const float *projmat); -static inline void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t); +static CGM_INLINE void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t); /* calculate barycentric coordinates of point pt in triangle (a, b, c) */ -static inline void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, +static CGM_INLINE void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, const cgm_vec3 *b, const cgm_vec3 *c, const cgm_vec3 *pt); /* convert between unit vectors and spherical coordinates */ -static inline void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v); -static inline void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi); +static CGM_INLINE void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v); +static CGM_INLINE void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi); #include "cgmvec3.inl" #include "cgmvec4.inl" diff --git a/libs/cgmath/cgmmat.inl b/libs/cgmath/cgmmat.inl index 2eb4519..2fb54fb 100644 --- a/libs/cgmath/cgmmat.inl +++ b/libs/cgmath/cgmmat.inl @@ -6,24 +6,24 @@ * If you intend to redistribute parts of the code without the LICENSE file * replace this paragraph with the full contents of the LICENSE file. */ -static inline void cgm_mcopy(float *dest, const float *src) +static CGM_INLINE void cgm_mcopy(float *dest, const float *src) { memcpy(dest, src, 16 * sizeof(float)); } -static inline void cgm_mzero(float *m) +static CGM_INLINE void cgm_mzero(float *m) { static float z[16]; cgm_mcopy(m, z); } -static inline void cgm_midentity(float *m) +static CGM_INLINE void cgm_midentity(float *m) { static float id[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; cgm_mcopy(m, id); } -static inline void cgm_mmul(float *a, const float *b) +static CGM_INLINE void cgm_mmul(float *a, const float *b) { int i, j; float res[16]; @@ -40,7 +40,7 @@ static inline void cgm_mmul(float *a, const float *b) cgm_mcopy(a, res); } -static inline void cgm_mpremul(float *a, const float *b) +static CGM_INLINE void cgm_mpremul(float *a, const float *b) { int i, j; float res[16]; @@ -57,7 +57,7 @@ static inline void cgm_mpremul(float *a, const float *b) cgm_mcopy(a, res); } -static inline void cgm_msubmatrix(float *m, int row, int col) +static CGM_INLINE void cgm_msubmatrix(float *m, int row, int col) { float orig[16]; int i, j, subi, subj; @@ -80,13 +80,13 @@ static inline void cgm_msubmatrix(float *m, int row, int col) cgm_mupper3(m); } -static inline void cgm_mupper3(float *m) +static CGM_INLINE void cgm_mupper3(float *m) { m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f; m[15] = 1.0f; } -static inline float cgm_msubdet(const float *m, int row, int col) +static CGM_INLINE float cgm_msubdet(const float *m, int row, int col) { float tmp[16]; float subdet00, subdet01, subdet02; @@ -101,19 +101,19 @@ static inline float cgm_msubdet(const float *m, int row, int col) return tmp[0] * subdet00 - tmp[1] * subdet01 + tmp[2] * subdet02; } -static inline float cgm_mcofactor(const float *m, int row, int col) +static CGM_INLINE float cgm_mcofactor(const float *m, int row, int col) { float min = cgm_msubdet(m, row, col); return (row + col) & 1 ? -min : min; } -static inline float cgm_mdet(const float *m) +static CGM_INLINE float cgm_mdet(const float *m) { return m[0] * cgm_msubdet(m, 0, 0) - m[1] * cgm_msubdet(m, 0, 1) + m[2] * cgm_msubdet(m, 0, 2) - m[3] * cgm_msubdet(m, 0, 3); } -static inline void cgm_mtranspose(float *m) +static CGM_INLINE void cgm_mtranspose(float *m) { int i, j; for(i=0; i<4; i++) { @@ -127,7 +127,7 @@ static inline void cgm_mtranspose(float *m) } } -static inline void cgm_mcofmatrix(float *m) +static CGM_INLINE void cgm_mcofmatrix(float *m) { float tmp[16]; int i, j; @@ -141,7 +141,7 @@ static inline void cgm_mcofmatrix(float *m) } } -static inline int cgm_minverse(float *m) +static CGM_INLINE int cgm_minverse(float *m) { int i, j; float tmp[16]; @@ -160,7 +160,7 @@ static inline int cgm_minverse(float *m) return 0; } -static inline void cgm_mtranslation(float *m, float x, float y, float z) +static CGM_INLINE void cgm_mtranslation(float *m, float x, float y, float z) { cgm_midentity(m); m[12] = x; @@ -168,7 +168,7 @@ static inline void cgm_mtranslation(float *m, float x, float y, float z) m[14] = z; } -static inline void cgm_mscaling(float *m, float sx, float sy, float sz) +static CGM_INLINE void cgm_mscaling(float *m, float sx, float sy, float sz) { cgm_mzero(m); m[0] = sx; @@ -177,7 +177,7 @@ static inline void cgm_mscaling(float *m, float sx, float sy, float sz) m[15] = 1.0f; } -static inline void cgm_mrotation_x(float *m, float angle) +static CGM_INLINE void cgm_mrotation_x(float *m, float angle) { float sa = sin(angle); float ca = cos(angle); @@ -189,7 +189,7 @@ static inline void cgm_mrotation_x(float *m, float angle) m[10] = ca; } -static inline void cgm_mrotation_y(float *m, float angle) +static CGM_INLINE void cgm_mrotation_y(float *m, float angle) { float sa = sin(angle); float ca = cos(angle); @@ -201,7 +201,7 @@ static inline void cgm_mrotation_y(float *m, float angle) m[10] = ca; } -static inline void cgm_mrotation_z(float *m, float angle) +static CGM_INLINE void cgm_mrotation_z(float *m, float angle) { float sa = sin(angle); float ca = cos(angle); @@ -213,7 +213,7 @@ static inline void cgm_mrotation_z(float *m, float angle) m[5] = ca; } -static inline void cgm_mrotation_axis(float *m, int idx, float angle) +static CGM_INLINE void cgm_mrotation_axis(float *m, int idx, float angle) { switch(idx) { case 0: @@ -228,7 +228,7 @@ static inline void cgm_mrotation_axis(float *m, int idx, float angle) } } -static inline void cgm_mrotation(float *m, float angle, float x, float y, float z) +static CGM_INLINE void cgm_mrotation(float *m, float angle, float x, float y, float z) { float sa = sin(angle); float ca = cos(angle); @@ -253,7 +253,7 @@ static inline void cgm_mrotation(float *m, float angle, float x, float y, float m[10] = zsq + (1.0f - zsq) * ca; } -static inline void cgm_mrotation_euler(float *m, float a, float b, float c, int mode) +static CGM_INLINE void cgm_mrotation_euler(float *m, float a, float b, float c, int mode) { /* this array must match the EulerMode enum */ static const int axis[][3] = { @@ -273,7 +273,7 @@ static inline void cgm_mrotation_euler(float *m, float a, float b, float c, int cgm_mmul(m, ma); } -static inline void cgm_mrotation_quat(float *m, const cgm_quat *q) +static CGM_INLINE void cgm_mrotation_quat(float *m, const cgm_quat *q) { float xsq2 = 2.0f * q->x * q->x; float ysq2 = 2.0f * q->y * q->y; @@ -296,63 +296,63 @@ static inline void cgm_mrotation_quat(float *m, const cgm_quat *q) m[10] = sz; } -static inline void cgm_mtranslate(float *m, float x, float y, float z) +static CGM_INLINE void cgm_mtranslate(float *m, float x, float y, float z) { float tm[16]; cgm_mtranslation(tm, x, y, z); cgm_mmul(m, tm); } -static inline void cgm_mscale(float *m, float sx, float sy, float sz) +static CGM_INLINE void cgm_mscale(float *m, float sx, float sy, float sz) { float sm[16]; cgm_mscaling(sm, sx, sy, sz); cgm_mmul(m, sm); } -static inline void cgm_mrotate_x(float *m, float angle) +static CGM_INLINE void cgm_mrotate_x(float *m, float angle) { float rm[16]; cgm_mrotation_x(rm, angle); cgm_mmul(m, rm); } -static inline void cgm_mrotate_y(float *m, float angle) +static CGM_INLINE void cgm_mrotate_y(float *m, float angle) { float rm[16]; cgm_mrotation_y(rm, angle); cgm_mmul(m, rm); } -static inline void cgm_mrotate_z(float *m, float angle) +static CGM_INLINE void cgm_mrotate_z(float *m, float angle) { float rm[16]; cgm_mrotation_z(rm, angle); cgm_mmul(m, rm); } -static inline void cgm_mrotate_axis(float *m, int idx, float angle) +static CGM_INLINE void cgm_mrotate_axis(float *m, int idx, float angle) { float rm[16]; cgm_mrotation_axis(rm, idx, angle); cgm_mmul(m, rm); } -static inline void cgm_mrotate(float *m, float angle, float x, float y, float z) +static CGM_INLINE void cgm_mrotate(float *m, float angle, float x, float y, float z) { float rm[16]; cgm_mrotation(rm, angle, x, y, z); cgm_mmul(m, rm); } -static inline void cgm_mrotate_euler(float *m, float a, float b, float c, int mode) +static CGM_INLINE void cgm_mrotate_euler(float *m, float a, float b, float c, int mode) { float rm[16]; cgm_mrotation_euler(rm, a, b, c, mode); cgm_mmul(m, rm); } -static inline void cgm_mrotate_quat(float *m, const cgm_quat *q) +static CGM_INLINE void cgm_mrotate_quat(float *m, const cgm_quat *q) { float rm[16]; cgm_mrotation_quat(rm, q); @@ -360,63 +360,63 @@ static inline void cgm_mrotate_quat(float *m, const cgm_quat *q) } -static inline void cgm_mpretranslate(float *m, float x, float y, float z) +static CGM_INLINE void cgm_mpretranslate(float *m, float x, float y, float z) { float tm[16]; cgm_mtranslation(tm, x, y, z); cgm_mpremul(m, tm); } -static inline void cgm_mprescale(float *m, float sx, float sy, float sz) +static CGM_INLINE void cgm_mprescale(float *m, float sx, float sy, float sz) { float sm[16]; cgm_mscaling(sm, sx, sy, sz); cgm_mpremul(m, sm); } -static inline void cgm_mprerotate_x(float *m, float angle) +static CGM_INLINE void cgm_mprerotate_x(float *m, float angle) { float rm[16]; cgm_mrotation_x(rm, angle); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate_y(float *m, float angle) +static CGM_INLINE void cgm_mprerotate_y(float *m, float angle) { float rm[16]; cgm_mrotation_y(rm, angle); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate_z(float *m, float angle) +static CGM_INLINE void cgm_mprerotate_z(float *m, float angle) { float rm[16]; cgm_mrotation_z(rm, angle); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate_axis(float *m, int idx, float angle) +static CGM_INLINE void cgm_mprerotate_axis(float *m, int idx, float angle) { float rm[16]; cgm_mrotation_axis(rm, idx, angle); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate(float *m, float angle, float x, float y, float z) +static CGM_INLINE void cgm_mprerotate(float *m, float angle, float x, float y, float z) { float rm[16]; cgm_mrotation(rm, angle, x, y, z); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode) +static CGM_INLINE void cgm_mprerotate_euler(float *m, float a, float b, float c, int mode) { float rm[16]; cgm_mrotation_euler(rm, a, b, c, mode); cgm_mpremul(m, rm); } -static inline void cgm_mprerotate_quat(float *m, const cgm_quat *q) +static CGM_INLINE void cgm_mprerotate_quat(float *m, const cgm_quat *q) { float rm[16]; cgm_mrotation_quat(rm, q); @@ -424,7 +424,7 @@ static inline void cgm_mprerotate_quat(float *m, const cgm_quat *q) } -static inline void cgm_mget_translation(const float *m, cgm_vec3 *res) +static CGM_INLINE void cgm_mget_translation(const float *m, cgm_vec3 *res) { res->x = m[12]; res->y = m[13]; @@ -435,7 +435,7 @@ static inline void cgm_mget_translation(const float *m, cgm_vec3 *res) * article "Quaternion Calculus and Fast Animation". * adapted from: http://www.geometrictools.com/LibMathematics/Algebra/Wm5Quaternion.inl */ -static inline void cgm_mget_rotation(const float *m, cgm_quat *res) +static CGM_INLINE void cgm_mget_rotation(const float *m, cgm_quat *res) { static const int next[3] = {1, 2, 0}; float quat[4]; @@ -477,14 +477,14 @@ static inline void cgm_mget_rotation(const float *m, cgm_quat *res) } } -static inline void cgm_mget_scaling(const float *m, cgm_vec3 *res) +static CGM_INLINE void cgm_mget_scaling(const float *m, cgm_vec3 *res) { res->x = sqrt(m[0] * m[0] + m[4] * m[4] + m[8] * m[8]); res->y = sqrt(m[1] * m[1] + m[5] * m[5] + m[9] * m[9]); res->z = sqrt(m[2] * m[2] + m[6] * m[6] + m[10] * m[10]); } -static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res) +static CGM_INLINE void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res) { int row = p >> 1; const float *rowptr = m + row * 4; @@ -502,7 +502,7 @@ static inline void cgm_mget_frustum_plane(const float *m, int p, cgm_vec4 *res) } } -static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, +static CGM_INLINE void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, const cgm_vec3 *up) { float trans[16]; @@ -530,7 +530,7 @@ static inline void cgm_mlookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *ta cgm_mmul(m, trans); } -static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, +static CGM_INLINE void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 *targ, const cgm_vec3 *up) { float rot[16]; @@ -558,7 +558,7 @@ static inline void cgm_minv_lookat(float *m, const cgm_vec3 *pos, const cgm_vec3 cgm_mmul(m, rot); } -static inline void cgm_mortho(float *m, float left, float right, float bot, float top, +static CGM_INLINE void cgm_mortho(float *m, float left, float right, float bot, float top, float znear, float zfar) { float dx = right - left; @@ -574,7 +574,7 @@ static inline void cgm_mortho(float *m, float left, float right, float bot, floa m[14] = -(zfar + znear) / dz; } -static inline void cgm_mfrustum(float *m, float left, float right, float bot, float top, +static CGM_INLINE void cgm_mfrustum(float *m, float left, float right, float bot, float top, float znear, float zfar) { float dx = right - left; @@ -591,7 +591,7 @@ static inline void cgm_mfrustum(float *m, float left, float right, float bot, fl m[11] = -1.0f; } -static inline void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar) +static CGM_INLINE void cgm_mperspective(float *m, float vfov, float aspect, float znear, float zfar) { float s = 1.0f / (float)tan(vfov / 2.0f); float range = znear - zfar; @@ -604,7 +604,7 @@ static inline void cgm_mperspective(float *m, float vfov, float aspect, float zn m[11] = -1.0f; } -static inline void cgm_mmirror(float *m, float a, float b, float c, float d) +static CGM_INLINE void cgm_mmirror(float *m, float a, float b, float c, float d) { m[0] = 1.0f - 2.0f * a * a; m[5] = 1.0f - 2.0f * b * b; diff --git a/libs/cgmath/cgmmisc.inl b/libs/cgmath/cgmmisc.inl index fafaf4a..2ef8a11 100644 --- a/libs/cgmath/cgmmisc.inl +++ b/libs/cgmath/cgmmisc.inl @@ -8,17 +8,17 @@ */ #include -static inline float cgm_deg_to_rad(float deg) +static CGM_INLINE float cgm_deg_to_rad(float deg) { return M_PI * deg / 180.0f; } -static inline float cgm_rad_to_deg(float rad) +static CGM_INLINE float cgm_rad_to_deg(float rad) { return 180.0f * rad / M_PI; } -static inline float cgm_smoothstep(float a, float b, float x) +static CGM_INLINE float cgm_smoothstep(float a, float b, float x) { if(x < a) return 0.0f; if(x >= b) return 1.0f; @@ -27,18 +27,18 @@ static inline float cgm_smoothstep(float a, float b, float x) return x * x * (3.0f - 2.0f * x); } -static inline float cgm_lerp(float a, float b, float t) +static CGM_INLINE float cgm_lerp(float a, float b, float t) { return a + (b - a) * t; } -static inline float cgm_logerp(float a, float b, float t) +static CGM_INLINE float cgm_logerp(float a, float b, float t) { if(a == 0.0f) return 0.0f; return a * pow(b / a, t); } -static inline float cgm_bezier(float a, float b, float c, float d, float t) +static CGM_INLINE float cgm_bezier(float a, float b, float c, float d, float t) { float omt, omt3, t3, f; t3 = t * t * t; @@ -49,7 +49,7 @@ static inline float cgm_bezier(float a, float b, float c, float d, float t) return (a * omt3) + (b * f * omt) + (c * f * t) + (d * t3); } -static inline float cgm_bspline(float a, float b, float c, float d, float t) +static CGM_INLINE float cgm_bspline(float a, float b, float c, float d, float t) { static const float mat[] = { -1, 3, -3, 1, @@ -67,7 +67,7 @@ static inline float cgm_bspline(float a, float b, float c, float d, float t) return cgm_wdot(&tmp, &qfact); } -static inline float cgm_spline(float a, float b, float c, float d, float t) +static CGM_INLINE float cgm_spline(float a, float b, float c, float d, float t) { static const float mat[] = { -1, 2, -1, 0, @@ -85,7 +85,7 @@ static inline float cgm_spline(float a, float b, float c, float d, float t) return cgm_wdot(&tmp, &qfact); } -static inline void cgm_discrand(cgm_vec3 *pt, float rad) +static CGM_INLINE void cgm_discrand(cgm_vec3 *pt, float rad) { float theta = 2.0f * M_PI * (float)rand() / RAND_MAX; float r = sqrt((float)rand() / RAND_MAX) * rad; @@ -94,7 +94,7 @@ static inline void cgm_discrand(cgm_vec3 *pt, float rad) pt->z = 0.0f; } -static inline void cgm_sphrand(cgm_vec3 *pt, float rad) +static CGM_INLINE void cgm_sphrand(cgm_vec3 *pt, float rad) { float u, v, theta, phi; @@ -109,7 +109,7 @@ static inline void cgm_sphrand(cgm_vec3 *pt, float rad) pt->z = cos(phi) * rad; } -static inline void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, +static CGM_INLINE void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, const float *inv_viewproj) { cgm_vec4 pos; @@ -126,7 +126,7 @@ static inline void cgm_unproject(cgm_vec3 *res, const cgm_vec3 *norm_scrpos, res->z = pos.z / pos.w; } -static inline void cgm_glu_unproject(float winx, float winy, float winz, +static CGM_INLINE void cgm_glu_unproject(float winx, float winy, float winz, const float *view, const float *proj, const int *vp, float *objx, float *objy, float *objz) { @@ -147,7 +147,7 @@ static inline void cgm_glu_unproject(float winx, float winy, float winz, *objz = res.z; } -static inline void cgm_pick_ray(cgm_ray *ray, float nx, float ny, +static CGM_INLINE void cgm_pick_ray(cgm_ray *ray, float nx, float ny, const float *viewmat, const float *projmat) { cgm_vec3 npos, farpt; @@ -166,14 +166,14 @@ static inline void cgm_pick_ray(cgm_ray *ray, float nx, float ny, ray->dir.z = farpt.z - ray->origin.z; } -static inline void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t) +static CGM_INLINE void cgm_raypos(cgm_vec3 *p, const cgm_ray *ray, float t) { p->x = ray->origin.x + ray->dir.x * t; p->y = ray->origin.y + ray->dir.y * t; p->z = ray->origin.z + ray->dir.z * t; } -static inline void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, +static CGM_INLINE void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, const cgm_vec3 *b, const cgm_vec3 *c, const cgm_vec3 *pt) { float d00, d01, d11, d20, d21, denom; @@ -195,13 +195,13 @@ static inline void cgm_bary(cgm_vec3 *bary, const cgm_vec3 *a, bary->x = 1.0f - bary->y - bary->z; } -static inline void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v) +static CGM_INLINE void cgm_uvec_to_sph(float *theta, float *phi, const cgm_vec3 *v) { *theta = atan2(v->z, v->x); *phi = acos(v->y); } -static inline void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi) +static CGM_INLINE void cgm_sph_to_uvec(cgm_vec3 *v, float theta, float phi) { v->x = sin(theta) * cos(phi); v->y = sin(phi); diff --git a/libs/cgmath/cgmquat.inl b/libs/cgmath/cgmquat.inl index 743d818..4351a1f 100644 --- a/libs/cgmath/cgmquat.inl +++ b/libs/cgmath/cgmquat.inl @@ -6,7 +6,7 @@ * If you intend to redistribute parts of the code without the LICENSE file * replace this paragraph with the full contents of the LICENSE file. */ -static inline void cgm_qcons(cgm_quat *q, float x, float y, float z, float w) +static CGM_INLINE void cgm_qcons(cgm_quat *q, float x, float y, float z, float w) { q->x = x; q->y = y; @@ -15,7 +15,7 @@ static inline void cgm_qcons(cgm_quat *q, float x, float y, float z, float w) } -static inline void cgm_qneg(cgm_quat *q) +static CGM_INLINE void cgm_qneg(cgm_quat *q) { q->x = -q->x; q->y = -q->y; @@ -23,7 +23,7 @@ static inline void cgm_qneg(cgm_quat *q) q->w = -q->w; } -static inline void cgm_qadd(cgm_quat *a, const cgm_quat *b) +static CGM_INLINE void cgm_qadd(cgm_quat *a, const cgm_quat *b) { a->x += b->x; a->y += b->y; @@ -31,7 +31,7 @@ static inline void cgm_qadd(cgm_quat *a, const cgm_quat *b) a->w += b->w; } -static inline void cgm_qsub(cgm_quat *a, const cgm_quat *b) +static CGM_INLINE void cgm_qsub(cgm_quat *a, const cgm_quat *b) { a->x -= b->x; a->y -= b->y; @@ -39,7 +39,7 @@ static inline void cgm_qsub(cgm_quat *a, const cgm_quat *b) a->w -= b->w; } -static inline void cgm_qmul(cgm_quat *a, const cgm_quat *b) +static CGM_INLINE void cgm_qmul(cgm_quat *a, const cgm_quat *b) { float x, y, z, dot; cgm_vec3 cross; @@ -56,17 +56,17 @@ static inline void cgm_qmul(cgm_quat *a, const cgm_quat *b) a->z = z; } -static inline float cgm_qlength(const cgm_quat *q) +static CGM_INLINE float cgm_qlength(const cgm_quat *q) { return sqrt(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w); } -static inline float cgm_qlength_sq(const cgm_quat *q) +static CGM_INLINE float cgm_qlength_sq(const cgm_quat *q) { return q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w; } -static inline void cgm_qnormalize(cgm_quat *q) +static CGM_INLINE void cgm_qnormalize(cgm_quat *q) { float len = cgm_qlength(q); if(len != 0.0f) { @@ -78,14 +78,14 @@ static inline void cgm_qnormalize(cgm_quat *q) } } -static inline void cgm_qconjugate(cgm_quat *q) +static CGM_INLINE void cgm_qconjugate(cgm_quat *q) { q->x = -q->x; q->y = -q->y; q->z = -q->z; } -static inline void cgm_qinvert(cgm_quat *q) +static CGM_INLINE void cgm_qinvert(cgm_quat *q) { float len_sq = cgm_qlength_sq(q); cgm_qconjugate(q); @@ -98,7 +98,7 @@ static inline void cgm_qinvert(cgm_quat *q) } } -static inline void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z) +static CGM_INLINE void cgm_qrotation(cgm_quat *q, float angle, float x, float y, float z) { float hangle = angle * 0.5f; float sin_ha = sin(hangle); @@ -108,14 +108,14 @@ static inline void cgm_qrotation(cgm_quat *q, float angle, float x, float y, flo q->z = z * sin_ha; } -static inline void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z) +static CGM_INLINE void cgm_qrotate(cgm_quat *q, float angle, float x, float y, float z) { cgm_quat qrot; cgm_qrotation(&qrot, angle, x, y, z); cgm_qmul(q, &qrot); } -static inline void cgm_qslerp(cgm_quat *res, const cgm_quat *quat1, const cgm_quat *q2, float t) +static CGM_INLINE void cgm_qslerp(cgm_quat *res, const cgm_quat *quat1, const cgm_quat *q2, float t) { float angle, dot, a, b, sin_angle; cgm_quat q1 = *quat1; @@ -150,7 +150,7 @@ static inline void cgm_qslerp(cgm_quat *res, const cgm_quat *quat1, const cgm_qu res->w = q1.w * a + q2->w * b; } -static inline void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t) +static CGM_INLINE void cgm_qlerp(cgm_quat *res, const cgm_quat *a, const cgm_quat *b, float t) { res->x = a->x + (b->x - a->x) * t; res->y = a->y + (b->y - a->y) * t; diff --git a/libs/cgmath/cgmray.inl b/libs/cgmath/cgmray.inl index 063a7e0..c396a55 100644 --- a/libs/cgmath/cgmray.inl +++ b/libs/cgmath/cgmray.inl @@ -6,7 +6,7 @@ * If you intend to redistribute parts of the code without the LICENSE file * replace this paragraph with the full contents of the LICENSE file. */ -static inline void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz) +static CGM_INLINE void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, float dy, float dz) { r->origin.x = x; r->origin.y = y; @@ -16,24 +16,24 @@ static inline void cgm_rcons(cgm_ray *r, float x, float y, float z, float dx, fl r->dir.z = dz; } -static inline void cgm_rmul_mr(cgm_ray *ray, const float *m) +static CGM_INLINE void cgm_rmul_mr(cgm_ray *ray, const float *m) { cgm_vmul_m4v3(&ray->origin, m); cgm_vmul_m3v3(&ray->dir, m); } -static inline void cgm_rmul_rm(cgm_ray *ray, const float *m) +static CGM_INLINE void cgm_rmul_rm(cgm_ray *ray, const float *m) { cgm_vmul_v3m4(&ray->origin, m); cgm_vmul_v3m3(&ray->dir, m); } -static inline void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n) +static CGM_INLINE void cgm_rreflect(cgm_ray *ray, const cgm_vec3 *n) { cgm_vreflect(&ray->dir, n); } -static inline void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior) +static CGM_INLINE void cgm_rrefract(cgm_ray *ray, const cgm_vec3 *n, float ior) { cgm_vrefract(&ray->dir, n, ior); } diff --git a/libs/cgmath/cgmvec3.inl b/libs/cgmath/cgmvec3.inl index e712b5b..0774706 100644 --- a/libs/cgmath/cgmvec3.inl +++ b/libs/cgmath/cgmvec3.inl @@ -6,56 +6,56 @@ * If you intend to redistribute parts of the code without the LICENSE file * replace this paragraph with the full contents of the LICENSE file. */ -static inline void cgm_vcons(cgm_vec3 *v, float x, float y, float z) +static CGM_INLINE void cgm_vcons(cgm_vec3 *v, float x, float y, float z) { v->x = x; v->y = y; v->z = z; } -static inline void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE void cgm_vadd(cgm_vec3 *a, const cgm_vec3 *b) { a->x += b->x; a->y += b->y; a->z += b->z; } -static inline void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) +static CGM_INLINE void cgm_vadd_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) { a->x += b->x * s; a->y += b->y * s; a->z += b->z * s; } -static inline void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE void cgm_vsub(cgm_vec3 *a, const cgm_vec3 *b) { a->x -= b->x; a->y -= b->y; a->z -= b->z; } -static inline void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) +static CGM_INLINE void cgm_vsub_scaled(cgm_vec3 *a, const cgm_vec3 *b, float s) { a->x -= b->x * s; a->y -= b->y * s; a->z -= b->z * s; } -static inline void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE void cgm_vmul(cgm_vec3 *a, const cgm_vec3 *b) { a->x *= b->x; a->y *= b->y; a->z *= b->z; } -static inline void cgm_vscale(cgm_vec3 *v, float s) +static CGM_INLINE void cgm_vscale(cgm_vec3 *v, float s) { v->x *= s; v->y *= s; v->z *= s; } -static inline void cgm_vmul_m4v3(cgm_vec3 *v, const float *m) +static CGM_INLINE void cgm_vmul_m4v3(cgm_vec3 *v, const float *m) { float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + m[12]; float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + m[13]; @@ -64,7 +64,7 @@ static inline void cgm_vmul_m4v3(cgm_vec3 *v, const float *m) v->y = y; } -static inline void cgm_vmul_v3m4(cgm_vec3 *v, const float *m) +static CGM_INLINE void cgm_vmul_v3m4(cgm_vec3 *v, const float *m) { float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + m[3]; float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + m[7]; @@ -73,7 +73,7 @@ static inline void cgm_vmul_v3m4(cgm_vec3 *v, const float *m) v->y = y; } -static inline void cgm_vmul_m3v3(cgm_vec3 *v, const float *m) +static CGM_INLINE void cgm_vmul_m3v3(cgm_vec3 *v, const float *m) { float x = v->x * m[0] + v->y * m[4] + v->z * m[8]; float y = v->x * m[1] + v->y * m[5] + v->z * m[9]; @@ -82,7 +82,7 @@ static inline void cgm_vmul_m3v3(cgm_vec3 *v, const float *m) v->y = y; } -static inline void cgm_vmul_v3m3(cgm_vec3 *v, const float *m) +static CGM_INLINE void cgm_vmul_v3m3(cgm_vec3 *v, const float *m) { float x = v->x * m[0] + v->y * m[1] + v->z * m[2]; float y = v->x * m[4] + v->y * m[5] + v->z * m[6]; @@ -91,29 +91,29 @@ static inline void cgm_vmul_v3m3(cgm_vec3 *v, const float *m) v->y = y; } -static inline float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE float cgm_vdot(const cgm_vec3 *a, const cgm_vec3 *b) { return a->x * b->x + a->y * b->y + a->z * b->z; } -static inline void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE void cgm_vcross(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b) { res->x = a->y * b->z - a->z * b->y; res->y = a->z * b->x - a->x * b->z; res->z = a->x * b->y - a->y * b->x; } -static inline float cgm_vlength(const cgm_vec3 *v) +static CGM_INLINE float cgm_vlength(const cgm_vec3 *v) { return sqrt(v->x * v->x + v->y * v->y + v->z * v->z); } -static inline float cgm_vlength_sq(const cgm_vec3 *v) +static CGM_INLINE float cgm_vlength_sq(const cgm_vec3 *v) { return v->x * v->x + v->y * v->y + v->z * v->z; } -static inline float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b) { float dx = a->x - b->x; float dy = a->y - b->y; @@ -121,7 +121,7 @@ static inline float cgm_vdist(const cgm_vec3 *a, const cgm_vec3 *b) return sqrt(dx * dx + dy * dy + dz * dz); } -static inline float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b) +static CGM_INLINE float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b) { float dx = a->x - b->x; float dy = a->y - b->y; @@ -129,7 +129,7 @@ static inline float cgm_vdist_sq(const cgm_vec3 *a, const cgm_vec3 *b) return dx * dx + dy * dy + dz * dz; } -static inline void cgm_vnormalize(cgm_vec3 *v) +static CGM_INLINE void cgm_vnormalize(cgm_vec3 *v) { float len = cgm_vlength(v); if(len != 0.0f) { @@ -140,7 +140,7 @@ static inline void cgm_vnormalize(cgm_vec3 *v) } } -static inline void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n) +static CGM_INLINE void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n) { float ndotv2 = cgm_vdot(v, n) * 2.0f; v->x -= n->x * ndotv2; @@ -148,7 +148,7 @@ static inline void cgm_vreflect(cgm_vec3 *v, const cgm_vec3 *n) v->z -= n->z * ndotv2; } -static inline void cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior) +static CGM_INLINE void cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior) { float ndotv = cgm_vdot(v, n); float k = 1.0f - ior * ior * (1.0f - ndotv * ndotv); @@ -162,7 +162,7 @@ static inline void cgm_vrefract(cgm_vec3 *v, const cgm_vec3 *n, float ior) } } -static inline void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q) +static CGM_INLINE void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q) { cgm_quat vq, inv_q = *q, tmp_q = *q; @@ -173,28 +173,28 @@ static inline void cgm_vrotate_quat(cgm_vec3 *v, const cgm_quat *q) cgm_vcons(v, tmp_q.x, tmp_q.y, tmp_q.z); } -static inline void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle) +static CGM_INLINE void cgm_vrotate_axis(cgm_vec3 *v, int axis, float angle) { float m[16]; cgm_mrotation_axis(m, axis, angle); cgm_vmul_m3v3(v, m); } -static inline void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z) +static CGM_INLINE void cgm_vrotate(cgm_vec3 *v, float angle, float x, float y, float z) { float m[16]; cgm_mrotation(m, angle, x, y, z); cgm_vmul_m3v3(v, m); } -static inline void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode) +static CGM_INLINE void cgm_vrotate_euler(cgm_vec3 *v, float a, float b, float c, enum cgm_euler_mode mode) { float m[16]; cgm_mrotation_euler(m, a, b, c, mode); cgm_vmul_m3v3(v, m); } -static inline void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t) +static CGM_INLINE void cgm_vlerp(cgm_vec3 *res, const cgm_vec3 *a, const cgm_vec3 *b, float t) { res->x = a->x + (b->x - a->x) * t; res->y = a->y + (b->y - a->y) * t; diff --git a/libs/cgmath/cgmvec4.inl b/libs/cgmath/cgmvec4.inl index b68856c..47f9ab0 100644 --- a/libs/cgmath/cgmvec4.inl +++ b/libs/cgmath/cgmvec4.inl @@ -6,7 +6,7 @@ * If you intend to redistribute parts of the code without the LICENSE file * replace this paragraph with the full contents of the LICENSE file. */ -static inline void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w) +static CGM_INLINE void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w) { v->x = x; v->y = y; @@ -14,7 +14,7 @@ static inline void cgm_wcons(cgm_vec4 *v, float x, float y, float z, float w) v->w = w; } -static inline void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b) { a->x += b->x; a->y += b->y; @@ -22,7 +22,7 @@ static inline void cgm_wadd(cgm_vec4 *a, const cgm_vec4 *b) a->w += b->w; } -static inline void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b) { a->x -= b->x; a->y -= b->y; @@ -30,7 +30,7 @@ static inline void cgm_wsub(cgm_vec4 *a, const cgm_vec4 *b) a->w -= b->w; } -static inline void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b) { a->x *= b->x; a->y *= b->y; @@ -38,7 +38,7 @@ static inline void cgm_wmul(cgm_vec4 *a, const cgm_vec4 *b) a->w *= b->w; } -static inline void cgm_wscale(cgm_vec4 *v, float s) +static CGM_INLINE void cgm_wscale(cgm_vec4 *v, float s) { v->x *= s; v->y *= s; @@ -46,7 +46,7 @@ static inline void cgm_wscale(cgm_vec4 *v, float s) v->w *= s; } -static inline void cgm_wmul_m4v4(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_m4v4(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + v->w * m[12]; float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + v->w * m[13]; @@ -57,7 +57,7 @@ static inline void cgm_wmul_m4v4(cgm_vec4 *v, const float *m) v->z = z; } -static inline void cgm_wmul_v4m4(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_v4m4(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + v->w * m[3]; float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + v->w * m[7]; @@ -68,7 +68,7 @@ static inline void cgm_wmul_v4m4(cgm_vec4 *v, const float *m) v->z = z; } -static inline void cgm_wmul_m34v4(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_m34v4(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[4] + v->z * m[8] + v->w * m[12]; float y = v->x * m[1] + v->y * m[5] + v->z * m[9] + v->w * m[13]; @@ -77,7 +77,7 @@ static inline void cgm_wmul_m34v4(cgm_vec4 *v, const float *m) v->y = y; } -static inline void cgm_wmul_v4m43(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_v4m43(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[1] + v->z * m[2] + v->w * m[3]; float y = v->x * m[4] + v->y * m[5] + v->z * m[6] + v->w * m[7]; @@ -86,7 +86,7 @@ static inline void cgm_wmul_v4m43(cgm_vec4 *v, const float *m) v->y = y; } -static inline void cgm_wmul_m3v4(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_m3v4(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[4] + v->z * m[8]; float y = v->x * m[1] + v->y * m[5] + v->z * m[9]; @@ -95,7 +95,7 @@ static inline void cgm_wmul_m3v4(cgm_vec4 *v, const float *m) v->y = y; } -static inline void cgm_wmul_v4m3(cgm_vec4 *v, const float *m) +static CGM_INLINE void cgm_wmul_v4m3(cgm_vec4 *v, const float *m) { float x = v->x * m[0] + v->y * m[1] + v->z * m[2]; float y = v->x * m[4] + v->y * m[5] + v->z * m[6]; @@ -104,22 +104,22 @@ static inline void cgm_wmul_v4m3(cgm_vec4 *v, const float *m) v->y = y; } -static inline float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE float cgm_wdot(const cgm_vec4 *a, const cgm_vec4 *b) { return a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; } -static inline float cgm_wlength(const cgm_vec4 *v) +static CGM_INLINE float cgm_wlength(const cgm_vec4 *v) { return sqrt(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w); } -static inline float cgm_wlength_sq(const cgm_vec4 *v) +static CGM_INLINE float cgm_wlength_sq(const cgm_vec4 *v) { return v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w; } -static inline float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b) { float dx = a->x - b->x; float dy = a->y - b->y; @@ -128,7 +128,7 @@ static inline float cgm_wdist(const cgm_vec4 *a, const cgm_vec4 *b) return sqrt(dx * dx + dy * dy + dz * dz + dw * dw); } -static inline float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b) +static CGM_INLINE float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b) { float dx = a->x - b->x; float dy = a->y - b->y; @@ -137,7 +137,7 @@ static inline float cgm_wdist_sq(const cgm_vec4 *a, const cgm_vec4 *b) return dx * dx + dy * dy + dz * dz + dw * dw; } -static inline void cgm_wnormalize(cgm_vec4 *v) +static CGM_INLINE void cgm_wnormalize(cgm_vec4 *v) { float len = cgm_wlength(v); if(len != 0.0f) { @@ -149,7 +149,7 @@ static inline void cgm_wnormalize(cgm_vec4 *v) } } -static inline void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t) +static CGM_INLINE void cgm_wlerp(cgm_vec4 *res, const cgm_vec4 *a, const cgm_vec4 *b, float t) { res->x = a->x + (b->x - a->x) * t; res->y = a->y + (b->y - a->y) * t; diff --git a/libs/goat3d/COPYING b/libs/goat3d/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/libs/goat3d/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/libs/goat3d/COPYING.LESSER b/libs/goat3d/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/libs/goat3d/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libs/goat3d/Makefile b/libs/goat3d/Makefile new file mode 100644 index 0000000..6c02b17 --- /dev/null +++ b/libs/goat3d/Makefile @@ -0,0 +1,15 @@ +obj = src/aabox.o src/chunk.o src/dynarr.o src/extmesh.o src/g3danm.o \ + src/g3dscn.o src/goat3d.o src/log.o src/read.o src/track.o src/write.o +alib = ../unix/goat3d.a + +CFLAGS = -O3 -Iinclude -I../treestor/include -I.. + +$(alib): $(obj) + $(AR) rcs $@ $(obj) + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.PHONY: clean +clean: + rm -f $(obj) $(alib) diff --git a/libs/goat3d/README.md b/libs/goat3d/README.md new file mode 100644 index 0000000..951207d --- /dev/null +++ b/libs/goat3d/README.md @@ -0,0 +1,55 @@ +goat3d +====== + +About +----- +Goat3D is a hierarchical 3D scene, character, and animation file format, and +acompanying read/write library, targeting mostly real-time applications. + +The specification defines a hierarchical structure (see `doc/goatfmt`, and +`doc/goatanimfmt` for details), which can be stored in either text or binary +form. An application using the provided library to read/write goat3d files, +should be able to handle either variant, with no extra effort (NOTE: currently +the binary format is not implemented). The animations can be part of the scene +file, or in separate files. + +This project provides the specification of the file format, a simple library +with a clean C API for reading and writing files in the goat3d scene and +animation files, as well as a number of tools dealing with such files. + +Specifically, at the moment, the goat3d project provides the following: + - *libgoat3d*, a library for reading and writing goat3d scene and animation files. + - *ass2goat*, a universal 3D asset conversion utility based on the excellent + assimp library, from a huge number of 3D file formats to the goat3d file + format. + - *goatinfo*, a command-line tool for inspecting the contents of goat3d files. + - *goatview*, a 3D scene and animation preview tool, based on OpenGL and Qt. + - *goatprim*, a procedural 3D model (primitive) generator for quick testing. + +License +------- +Copyright (C) 2014-2023 John Tsiombikas + +Goat3D is free software, you may use, modify and/or redistribute it under the +terms of the GNU Lesser General Public License v3, or at your option any later +version published by the Free Software Foundation. See COPYING and +COPYING.LESSER for details. + +Build +----- +To build and install libgoat3d on UNIX, run the usual: + + ./configure + make + make install + +See `./configure --help` for a complete list of build-time options. + +To cross-compile for windows with mingw-w64, try the following incantation: + + ./configure --prefix=/usr/i686-w64-mingw32 + make CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar sys=mingw + make install sys=mingw + +The rest of the tools can be built and installed in the exact same way from +their respective subdirectories. diff --git a/libs/goat3d/include/goat3d.h b/libs/goat3d/include/goat3d.h new file mode 100644 index 0000000..8b9b38d --- /dev/null +++ b/libs/goat3d/include/goat3d.h @@ -0,0 +1,383 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOAT3D_H_ +#define GOAT3D_H_ + +#include +#include + +#ifdef WIN32 +#define GOAT3DAPI __declspec(dllexport) +#else +#ifdef __GNUC__ +#define GOAT3DAPI __attribute__((visibility("default"))) +#else +#define GOAT3DAPI +#endif +#endif + +#define GOAT3D_MAT_ATTR_DIFFUSE "diffuse" +#define GOAT3D_MAT_ATTR_SPECULAR "specular" +#define GOAT3D_MAT_ATTR_SHININESS "shininess" +#define GOAT3D_MAT_ATTR_NORMAL "normal" +#define GOAT3D_MAT_ATTR_BUMP "bump" +#define GOAT3D_MAT_ATTR_REFLECTION "reflection" +#define GOAT3D_MAT_ATTR_TRANSMISSION "transmission" +#define GOAT3D_MAT_ATTR_IOR "ior" +#define GOAT3D_MAT_ATTR_ALPHA "alpha" + +enum goat3d_mesh_attrib { + GOAT3D_MESH_ATTR_VERTEX, + GOAT3D_MESH_ATTR_NORMAL, + GOAT3D_MESH_ATTR_TANGENT, + GOAT3D_MESH_ATTR_TEXCOORD, + GOAT3D_MESH_ATTR_SKIN_WEIGHT, + GOAT3D_MESH_ATTR_SKIN_MATRIX, + GOAT3D_MESH_ATTR_COLOR, + + NUM_GOAT3D_MESH_ATTRIBS +}; + +enum goat3d_node_type { + GOAT3D_NODE_NULL, + GOAT3D_NODE_MESH, + GOAT3D_NODE_LIGHT, + GOAT3D_NODE_CAMERA +}; + +/* immediate mode mesh construction primitive type */ +enum goat3d_im_primitive { + GOAT3D_TRIANGLES, + GOAT3D_QUADS +}; + +enum goat3d_track_type { + GOAT3D_TRACK_VAL, + GOAT3D_TRACK_VEC3, + GOAT3D_TRACK_VEC4, + GOAT3D_TRACK_QUAT, + GOAT3D_TRACK_POS = GOAT3D_TRACK_VEC3 | 0x100, + GOAT3D_TRACK_ROT = GOAT3D_TRACK_QUAT | 0x200, + GOAT3D_TRACK_SCALE = GOAT3D_TRACK_VEC3 | 0x300 +}; + +struct goat3d_key { + long tm; + float val[4]; +}; + +/* track interpolation modes */ +enum goat3d_interp { + GOAT3D_INTERP_STEP, + GOAT3D_INTERP_LINEAR, + GOAT3D_INTERP_CUBIC +}; +/* track extrapolation modes */ +enum goat3d_extrap { + GOAT3D_EXTRAP_EXTEND, + GOAT3D_EXTRAP_CLAMP, + GOAT3D_EXTRAP_REPEAT, + GOAT3D_EXTRAP_PINGPONG +}; + +enum goat3d_option { + GOAT3D_OPT_SAVEXML, /* save in XML format (dropped) */ + GOAT3D_OPT_SAVETEXT, /* save in text format */ + GOAT3D_OPT_SAVEBINDATA, /* save mesh data in text files as binary blobs */ + GOAT3D_OPT_SAVEBIN, /* not implemented yet */ + GOAT3D_OPT_SAVEGLTF, /* not implemented yet */ + GOAT3D_OPT_SAVEGLB, /* not implemented yet */ + + NUM_GOAT3D_OPTIONS +}; + +struct goat3d; +struct goat3d_material; +struct goat3d_mtlattr; +struct goat3d_mesh; +struct goat3d_light; +struct goat3d_camera; +struct goat3d_node; +struct goat3d_anim; +struct goat3d_track; + +struct goat3d_io { + void *cls; /* closure data */ + + long (*read)(void *buf, size_t bytes, void *uptr); + long (*write)(const void *buf, size_t bytes, void *uptr); + long (*seek)(long offs, int whence, void *uptr); +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* construction/destruction */ +GOAT3DAPI struct goat3d *goat3d_create(void); +GOAT3DAPI void goat3d_free(struct goat3d *g); + +GOAT3DAPI void goat3d_setopt(struct goat3d *g, enum goat3d_option opt, int val); +GOAT3DAPI int goat3d_getopt(const struct goat3d *g, enum goat3d_option opt); + +/* load/save */ +GOAT3DAPI int goat3d_load(struct goat3d *g, const char *fname); +GOAT3DAPI int goat3d_save(const struct goat3d *g, const char *fname); + +GOAT3DAPI int goat3d_load_file(struct goat3d *g, FILE *fp); +GOAT3DAPI int goat3d_save_file(const struct goat3d *g, FILE *fp); + +GOAT3DAPI int goat3d_load_io(struct goat3d *g, struct goat3d_io *io); +GOAT3DAPI int goat3d_save_io(const struct goat3d *g, struct goat3d_io *io); + +/* load/save animation files (g must already be loaded to load animations) */ +GOAT3DAPI int goat3d_load_anim(struct goat3d *g, const char *fname); +GOAT3DAPI int goat3d_save_anim(const struct goat3d *g, const char *fname); + +GOAT3DAPI int goat3d_load_anim_file(struct goat3d *g, FILE *fp); +GOAT3DAPI int goat3d_save_anim_file(const struct goat3d *g, FILE *fp); + +GOAT3DAPI int goat3d_load_anim_io(struct goat3d *g, struct goat3d_io *io); +GOAT3DAPI int goat3d_save_anim_io(const struct goat3d *g, struct goat3d_io *io); + +/* misc scene properties */ +GOAT3DAPI int goat3d_set_name(struct goat3d *g, const char *name); +GOAT3DAPI const char *goat3d_get_name(const struct goat3d *g); + +GOAT3DAPI void goat3d_set_ambient(struct goat3d *g, const float *ambient); +GOAT3DAPI void goat3d_set_ambient3f(struct goat3d *g, float ar, float ag, float ab); +GOAT3DAPI const float *goat3d_get_ambient(const struct goat3d *g); + +GOAT3DAPI int goat3d_get_bounds(const struct goat3d *g, float *bmin, float *bmax); + +/* materials */ +GOAT3DAPI int goat3d_add_mtl(struct goat3d *g, struct goat3d_material *mtl); +GOAT3DAPI int goat3d_get_mtl_count(struct goat3d *g); +GOAT3DAPI struct goat3d_material *goat3d_get_mtl(struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_material *goat3d_get_mtl_by_name(struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_material *goat3d_create_mtl(void); +GOAT3DAPI void goat3d_destroy_mtl(struct goat3d_material *mtl); + +GOAT3DAPI int goat3d_set_mtl_name(struct goat3d_material *mtl, const char *name); +GOAT3DAPI const char *goat3d_get_mtl_name(const struct goat3d_material *mtl); + +GOAT3DAPI int goat3d_set_mtl_attrib(struct goat3d_material *mtl, const char *attrib, const float *val); +GOAT3DAPI int goat3d_set_mtl_attrib1f(struct goat3d_material *mtl, const char *attrib, float val); +GOAT3DAPI int goat3d_set_mtl_attrib3f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b); +GOAT3DAPI int goat3d_set_mtl_attrib4f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b, float a); +GOAT3DAPI const float *goat3d_get_mtl_attrib(struct goat3d_material *mtl, const char *attrib); + +GOAT3DAPI int goat3d_set_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib, const char *mapname); +GOAT3DAPI const char *goat3d_get_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib); + +GOAT3DAPI int goat3d_get_mtl_attrib_count(struct goat3d_material *mtl); +GOAT3DAPI const char *goat3d_get_mtl_attrib_name(struct goat3d_material *mtl, int idx); + + +/* meshes */ +GOAT3DAPI int goat3d_add_mesh(struct goat3d *g, struct goat3d_mesh *mesh); +GOAT3DAPI int goat3d_get_mesh_count(struct goat3d *g); +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh(struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh_by_name(struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_mesh *goat3d_create_mesh(void); +GOAT3DAPI void goat3d_destroy_mesh(struct goat3d_mesh *mesh); + +GOAT3DAPI int goat3d_set_mesh_name(struct goat3d_mesh *mesh, const char *name); +GOAT3DAPI const char *goat3d_get_mesh_name(const struct goat3d_mesh *mesh); + +GOAT3DAPI void goat3d_set_mesh_mtl(struct goat3d_mesh *mesh, struct goat3d_material *mtl); +GOAT3DAPI struct goat3d_material *goat3d_get_mesh_mtl(struct goat3d_mesh *mesh); + +GOAT3DAPI int goat3d_get_mesh_vertex_count(struct goat3d_mesh *mesh); +GOAT3DAPI int goat3d_get_mesh_attrib_count(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib); +GOAT3DAPI int goat3d_get_mesh_face_count(struct goat3d_mesh *mesh); + +/* sets all the data for a single vertex attribute array in one go. + * vnum is the number of *vertices* to be set, not the number of floats, ints or whatever + * data is expected to be something different depending on the attribute: + * - GOAT3D_MESH_ATTR_VERTEX - 3 floats per vertex + * - GOAT3D_MESH_ATTR_NORMAL - 3 floats per vertex + * - GOAT3D_MESH_ATTR_TANGENT - 3 floats per vertex + * - GOAT3D_MESH_ATTR_TEXCOORD - 2 floats per vertex + * - GOAT3D_MESH_ATTR_SKIN_WEIGHT - 4 floats per vertex + * - GOAT3D_MESH_ATTR_SKIN_MATRIX - 4 ints per vertex + * - GOAT3D_MESH_ATTR_COLOR - 4 floats per vertex + */ +GOAT3DAPI int goat3d_set_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + const void *data, int vnum); +GOAT3DAPI int goat3d_add_mesh_attrib1f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, float val); +GOAT3DAPI int goat3d_add_mesh_attrib2f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y); +GOAT3DAPI int goat3d_add_mesh_attrib3f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z); +GOAT3DAPI int goat3d_add_mesh_attrib4f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z, float w); +/* returns a pointer to the beginning of the requested mesh attribute array */ +GOAT3DAPI void *goat3d_get_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib); +/* returns a pointer to the requested mesh attribute */ +GOAT3DAPI void *goat3d_get_mesh_attrib(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, int idx); + +/* sets all the faces in one go. data is an array of 3 int vertex indices per face */ +GOAT3DAPI int goat3d_set_mesh_faces(struct goat3d_mesh *mesh, const int *data, int fnum); +GOAT3DAPI int goat3d_add_mesh_face(struct goat3d_mesh *mesh, int a, int b, int c); +/* returns a pointer to the beginning of the face index array */ +GOAT3DAPI int *goat3d_get_mesh_faces(struct goat3d_mesh *mesh); +/* returns a pointer to a face index */ +GOAT3DAPI int *goat3d_get_mesh_face(struct goat3d_mesh *mesh, int idx); + +/* immediate mode OpenGL-like interface for setting mesh data + * NOTE: using this interface will result in no vertex sharing between faces + * NOTE2: the immedate mode interface is not thread-safe, either use locks, or don't + * use it at all in multithreaded situations. + */ +GOAT3DAPI void goat3d_begin(struct goat3d_mesh *mesh, enum goat3d_im_primitive prim); +GOAT3DAPI void goat3d_end(void); +GOAT3DAPI void goat3d_vertex3f(float x, float y, float z); +GOAT3DAPI void goat3d_normal3f(float x, float y, float z); +GOAT3DAPI void goat3d_tangent3f(float x, float y, float z); +GOAT3DAPI void goat3d_texcoord2f(float x, float y); +GOAT3DAPI void goat3d_skin_weight4f(float x, float y, float z, float w); +GOAT3DAPI void goat3d_skin_matrix4i(int x, int y, int z, int w); +GOAT3DAPI void goat3d_color3f(float x, float y, float z); +GOAT3DAPI void goat3d_color4f(float x, float y, float z, float w); + +GOAT3DAPI void goat3d_get_mesh_bounds(const struct goat3d_mesh *mesh, float *bmin, float *bmax); + +/* lights (TODO) */ +GOAT3DAPI int goat3d_add_light(struct goat3d *g, struct goat3d_light *lt); +GOAT3DAPI int goat3d_get_light_count(struct goat3d *g); +GOAT3DAPI struct goat3d_light *goat3d_get_light(struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_light *goat3d_get_light_by_name(struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_light *goat3d_create_light(void); +GOAT3DAPI void goat3d_destroy_light(struct goat3d_light *lt); + +GOAT3DAPI int goat3d_set_light_name(struct goat3d_light *lt, const char *name); +GOAT3DAPI const char *goat3d_get_light_name(const struct goat3d_light *lt); + +/* cameras (TODO) */ +GOAT3DAPI int goat3d_add_camera(struct goat3d *g, struct goat3d_camera *cam); +GOAT3DAPI int goat3d_get_camera_count(struct goat3d *g); +GOAT3DAPI struct goat3d_camera *goat3d_get_camera(struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_camera *goat3d_get_camera_by_name(struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_camera *goat3d_create_camera(void); +GOAT3DAPI void goat3d_destroy_camera(struct goat3d_camera *cam); + +GOAT3DAPI int goat3d_set_camera_name(struct goat3d_camera *cam, const char *name); +GOAT3DAPI const char *goat3d_get_camera_name(const struct goat3d_camera *cam); + +/* nodes */ +GOAT3DAPI int goat3d_add_node(struct goat3d *g, struct goat3d_node *node); +GOAT3DAPI int goat3d_get_node_count(struct goat3d *g); +GOAT3DAPI struct goat3d_node *goat3d_get_node(struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_node *goat3d_get_node_by_name(struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_node *goat3d_create_node(void); +GOAT3DAPI void goat3d_destroy_node(struct goat3d_node *node); + +GOAT3DAPI int goat3d_set_node_name(struct goat3d_node *node, const char *name); +GOAT3DAPI const char *goat3d_get_node_name(const struct goat3d_node *node); + +GOAT3DAPI void goat3d_set_node_object(struct goat3d_node *node, enum goat3d_node_type type, void *obj); +GOAT3DAPI void *goat3d_get_node_object(const struct goat3d_node *node); +GOAT3DAPI enum goat3d_node_type goat3d_get_node_type(const struct goat3d_node *node); + +GOAT3DAPI void goat3d_add_node_child(struct goat3d_node *node, struct goat3d_node *child); +GOAT3DAPI int goat3d_get_node_child_count(const struct goat3d_node *node); +GOAT3DAPI struct goat3d_node *goat3d_get_node_child(const struct goat3d_node *node, int idx); +GOAT3DAPI struct goat3d_node *goat3d_get_node_parent(const struct goat3d_node *node); + +GOAT3DAPI void goat3d_set_node_position(struct goat3d_node *node, float x, float y, float z); +GOAT3DAPI void goat3d_set_node_rotation(struct goat3d_node *node, float qx, float qy, float qz, float qw); +GOAT3DAPI void goat3d_set_node_scaling(struct goat3d_node *node, float sx, float sy, float sz); + +GOAT3DAPI void goat3d_get_node_position(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr); +GOAT3DAPI void goat3d_get_node_rotation(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr, float *wptr); +GOAT3DAPI void goat3d_get_node_scaling(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr); + +GOAT3DAPI void goat3d_set_node_pivot(struct goat3d_node *node, float x, float y, float z); +GOAT3DAPI void goat3d_get_node_pivot(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr); + +GOAT3DAPI void goat3d_get_node_matrix(const struct goat3d_node *node, float *matrix); +/* same as above, but also takes hierarchy into account */ +GOAT3DAPI void goat3d_get_matrix(const struct goat3d_node *node, float *matrix); + +GOAT3DAPI void goat3d_get_node_bounds(const struct goat3d_node *node, float *bmin, float *bmax); + +/* keyframe track */ +GOAT3DAPI struct goat3d_track *goat3d_create_track(void); +GOAT3DAPI void goat3d_destroy_track(struct goat3d_track *trk); + +GOAT3DAPI int goat3d_set_track_name(struct goat3d_track *trk, const char *name); +GOAT3DAPI const char *goat3d_get_track_name(const struct goat3d_track *trk); + +GOAT3DAPI void goat3d_set_track_type(struct goat3d_track *trk, enum goat3d_track_type type); +GOAT3DAPI enum goat3d_track_type goat3d_get_track_type(const struct goat3d_track *trk); + +GOAT3DAPI void goat3d_set_track_node(struct goat3d_track *trk, struct goat3d_node *node); +GOAT3DAPI struct goat3d_node *goat3d_get_track_node(const struct goat3d_track *trk); + +GOAT3DAPI void goat3d_set_track_interp(struct goat3d_track *trk, enum goat3d_interp in); +GOAT3DAPI enum goat3d_interp goat3d_get_track_interp(const struct goat3d_track *trk); +GOAT3DAPI void goat3d_set_track_extrap(struct goat3d_track *trk, enum goat3d_extrap ex); +GOAT3DAPI enum goat3d_extrap goat3d_get_track_extrap(const struct goat3d_track *trk); + +GOAT3DAPI int goat3d_set_track_key(struct goat3d_track *trk, const struct goat3d_key *key); +GOAT3DAPI int goat3d_get_track_key(const struct goat3d_track *trk, int keyidx, struct goat3d_key *key); +GOAT3DAPI int goat3d_get_track_key_count(const struct goat3d_track *trk); + +GOAT3DAPI int goat3d_set_track_val(struct goat3d_track *trk, long msec, float val); +GOAT3DAPI int goat3d_set_track_vec3(struct goat3d_track *trk, long msec, float x, float y, float z); +GOAT3DAPI int goat3d_set_track_vec4(struct goat3d_track *trk, long msec, float x, float y, float z, float w); +GOAT3DAPI int goat3d_set_track_quat(struct goat3d_track *trk, long msec, float x, float y, float z, float w); + +GOAT3DAPI void goat3d_get_track_val(const struct goat3d_track *trk, long msec, float *valp); +GOAT3DAPI void goat3d_get_track_vec3(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp); +GOAT3DAPI void goat3d_get_track_vec4(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp); +GOAT3DAPI void goat3d_get_track_quat(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp); + +GOAT3DAPI long goat3d_get_track_timeline(const struct goat3d_track *trk, long *tstart, long *tend); + +/* animation */ +GOAT3DAPI int goat3d_add_anim(struct goat3d *g, struct goat3d_anim *anim); +GOAT3DAPI int goat3d_get_anim_count(const struct goat3d *g); +GOAT3DAPI struct goat3d_anim *goat3d_get_anim(const struct goat3d *g, int idx); +GOAT3DAPI struct goat3d_anim *goat3d_get_anim_by_name(const struct goat3d *g, const char *name); + +GOAT3DAPI struct goat3d_anim *goat3d_create_anim(void); +GOAT3DAPI void goat3d_destroy_anim(struct goat3d_anim *anim); + +GOAT3DAPI int goat3d_set_anim_name(struct goat3d_anim *anim, const char *name); +GOAT3DAPI const char *goat3d_get_anim_name(const struct goat3d_anim *anim); + +GOAT3DAPI int goat3d_add_anim_track(struct goat3d_anim *anim, struct goat3d_track *trk); +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track(const struct goat3d_anim *anim, int idx); +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_name(const struct goat3d_anim *anim, const char *name); +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_type(const struct goat3d_anim *anim, enum goat3d_track_type type); +GOAT3DAPI int goat3d_get_anim_track_count(const struct goat3d_anim *anim); + +GOAT3DAPI long goat3d_get_anim_timeline(const struct goat3d_anim *anim, long *tstart, long *tend); + +#ifdef __cplusplus +} +#endif + +#endif /* GOAT3D_H_ */ diff --git a/libs/goat3d/src/aabox.c b/libs/goat3d/src/aabox.c new file mode 100644 index 0000000..2bbfb3b --- /dev/null +++ b/libs/goat3d/src/aabox.c @@ -0,0 +1,56 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "aabox.h" + +void g3dimpl_aabox_init(struct aabox *box) +{ + cgm_vcons(&box->bmin, FLT_MAX, FLT_MAX, FLT_MAX); + cgm_vcons(&box->bmax, -FLT_MAX, -FLT_MAX, -FLT_MAX); +} + +void g3dimpl_aabox_cons(struct aabox *box, float x0, float y0, float z0, + float x1, float y1, float z1) +{ + cgm_vcons(&box->bmin, x0, y0, z0); + cgm_vcons(&box->bmax, x1, y1, z1); +} + + +int g3dimpl_aabox_equal(const struct aabox *a, const struct aabox *b) +{ + if(a->bmin.x != b->bmin.x || a->bmin.y != b->bmin.y || a->bmin.z != b->bmin.z) { + return 0; + } + if(a->bmax.x != b->bmax.x || a->bmax.y != b->bmax.y || a->bmax.z != b->bmax.z) { + return 0; + } + return 1; +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +void g3dimpl_aabox_union(struct aabox *res, const struct aabox *a, const struct aabox *b) +{ + res->bmin.x = MIN(a->bmin.x, b->bmin.x); + res->bmin.y = MIN(a->bmin.y, b->bmin.y); + res->bmin.z = MIN(a->bmin.z, b->bmin.z); + res->bmax.x = MAX(a->bmax.x, b->bmax.x); + res->bmax.y = MAX(a->bmax.y, b->bmax.y); + res->bmax.z = MAX(a->bmax.z, b->bmax.z); +} diff --git a/libs/goat3d/src/aabox.h b/libs/goat3d/src/aabox.h new file mode 100644 index 0000000..d9cb818 --- /dev/null +++ b/libs/goat3d/src/aabox.h @@ -0,0 +1,36 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef AABOX_H_ +#define AABOX_H_ + +#include "cgmath/cgmath.h" + +struct aabox { + cgm_vec3 bmin, bmax; +}; + +void g3dimpl_aabox_init(struct aabox *box); +void g3dimpl_aabox_cons(struct aabox *box, float x0, float y0, float z0, + float x1, float y1, float z1); + +int g3dimpl_aabox_equal(const struct aabox *a, const struct aabox *b); + +void g3dimpl_aabox_union(struct aabox *res, const struct aabox *a, + const struct aabox *b); + +#endif /* AABOX_H_ */ diff --git a/libs/goat3d/src/chunk.c b/libs/goat3d/src/chunk.c new file mode 100644 index 0000000..9043357 --- /dev/null +++ b/libs/goat3d/src/chunk.c @@ -0,0 +1,47 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "goat3d.h" +#include "chunk.h" + +void g3dimpl_chunk_header(struct chunk_header *hdr, int id) +{ + hdr->id = id; + hdr->size = sizeof *hdr; +} + +int g3dimpl_write_chunk_header(const struct chunk_header *hdr, struct goat3d_io *io) +{ + io->seek(-(long)hdr->size, SEEK_CUR, io->cls); + if(io->write(hdr, sizeof *hdr, io->cls) < (long)sizeof *hdr) { + return -1; + } + return 0; +} + +int g3dimpl_read_chunk_header(struct chunk_header *hdr, struct goat3d_io *io) +{ + if(io->read(hdr, sizeof *hdr, io->cls) < (long)sizeof *hdr) { + return -1; + } + return 0; +} + +void g3dimpl_skip_chunk(const struct chunk_header *hdr, struct goat3d_io *io) +{ + io->seek(hdr->size - sizeof *hdr, SEEK_CUR, io->cls); +} diff --git a/libs/goat3d/src/chunk.h b/libs/goat3d/src/chunk.h new file mode 100644 index 0000000..505c150 --- /dev/null +++ b/libs/goat3d/src/chunk.h @@ -0,0 +1,146 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef CHUNK_H_ +#define CHUNK_H_ + +#ifndef _MSC_VER +#ifdef __sgi +#include +#else +#include +#endif +#else +typedef unsigned __int32 uint32_t; +#endif + +enum { + CNK_INVALID, /* this shouldn't appear in files */ + CNK_SCENE, /* the root chunk */ + + /* general purpose chunks */ + CNK_INT, + CNK_INT4, + CNK_FLOAT, + CNK_FLOAT3, + CNK_FLOAT4, + CNK_STRING, + + /* --- first level chunks --- */ + /* children of CNK_SCENE */ + CNK_ENV, /* environmental parameters */ + CNK_MTL, /* material */ + CNK_MESH, + CNK_LIGHT, + CNK_CAMERA, + CNK_NODE, + + /* --- second level chunks --- */ + /* children of CNK_ENV */ + CNK_ENV_AMBIENT, /* ambient color, contains a single CNK_FLOAT3 */ + CNK_ENV_FOG, + + /* --- third level chunks --- */ + /* children of CNK_FOG */ + CNK_FOG_COLOR, /* fog color, contains a single CNK_FLOAT3 */ + CNK_FOG_EXP, /* fog exponent, contains a single CNK_FLOAT */ + + /* children of CNK_MTL */ + CNK_MTL_NAME, /* has a single CNK_STRING */ + CNK_MTL_ATTR, /* material attribute, has a CNK_STRING for its name, */ + /* a CNK_MTL_ATTR_VAL, and optionally a CNK_MTL_ATTR_MAP */ + /* children of CNK_MTL_ATTR */ + CNK_MTL_ATTR_NAME, /* has a single CNK_STRING */ + CNK_MTL_ATTR_VAL, /* can have a single CNK_FLOAT, CNK_FLOAT3, or CNK_FLOAT4 */ + CNK_MTL_ATTR_MAP, /* has a single CNK_STRING */ + + /* children of CNK_MESH */ + CNK_MESH_NAME, /* has a single CNK_STRING */ + CNK_MESH_MATERIAL, /* has one of CNK_STRING or CNK_INT to identify the material */ + CNK_MESH_VERTEX_LIST, /* has a series of CNK_FLOAT3 chunks */ + CNK_MESH_NORMAL_LIST, /* has a series of CNK_FLOAT3 chunks */ + CNK_MESH_TANGENT_LIST, /* has a series of CNK_FLOAT3 chunks */ + CNK_MESH_TEXCOORD_LIST, /* has a series of CNK_FLOAT3 chunks */ + CNK_MESH_SKINWEIGHT_LIST, /* has a series of CNK_FLOAT4 chunks (4 skin weights) */ + CNK_MESH_SKINMATRIX_LIST, /* has a series of CNK_INT4 chunks (4 matrix indices) */ + CNK_MESH_COLOR_LIST, /* has a series of CNK_FLOAT4 chunks */ + CNK_MESH_BONES_LIST, /* has a series of CNK_INT or CNK_STRING chunks identifying the bone nodes */ + CNK_MESH_FACE_LIST, /* has a series of CNK_FACE chunks */ + CNK_MESH_FILE, /* optionally mesh data may be in another file, has a CNK_STRING filename */ + + /* child of CNK_MESH_FACE_LIST */ + CNK_MESH_FACE, /* has three CNK_INT chunks */ + + /* children of CNK_LIGHT */ + CNK_LIGHT_NAME, /* has a single CNK_STRING */ + CNK_LIGHT_POS, /* has a single CNK_FLOAT3 */ + CNK_LIGHT_COLOR, /* has a single CNK_FLOAT3 */ + CNK_LIGHT_ATTEN, /* has a single CNK_FLOAT3 (constant, linear, squared attenuation) */ + CNK_LIGHT_DISTANCE, /* has a single CNK_FLOAT */ + CNK_LIGHT_DIR, /* a single CNK_FLOAT3 (for spotlights and dir-lights) */ + CNK_LIGHT_CONE_INNER, /* single CNK_FLOAT, inner cone angle (for spotlights) */ + CNK_LIGHT_CONE_OUTER, /* single CNK_FLOAT, outer cone angle (for spotlights) */ + + /* children of CNK_CAMERA */ + CNK_CAMERA_NAME, /* has a single CNK_STRING */ + CNK_CAMERA_POS, /* single CNK_FLOAT3 */ + CNK_CAMERA_TARGET, /* single CNK_FLOAT3 */ + CNK_CAMERA_FOV, /* single CNK_FLOAT (field of view in radians) */ + CNK_CAMERA_NEARCLIP, /* single CNK_FLOAT (near clipping plane distance) */ + CNK_CAMERA_FARCLIP, /* signle CNK_FLOAT (far clipping plane distance) */ + + /* children of CNK_NODE */ + CNK_NODE_NAME, /* node name, a single CNK_STRING */ + CNK_NODE_PARENT, /* it can have a CNK_INT or a CNK_STRING to identify the parent node */ + + CNK_NODE_MESH, /* it can have a CNK_INT or a CNK_STRING to identify this node's mesh */ + CNK_NODE_LIGHT, /* same as CNK_NODE_MESH */ + CNK_NODE_CAMERA, /* same as CNK_NODE_MESH */ + + CNK_NODE_POS, /* has a CNK_FLOAT3, position vector */ + CNK_NODE_ROT, /* has a CNK_FLOAT4, rotation quaternion (x, y, z imaginary, w real) */ + CNK_NODE_SCALE, /* has a CNK_FLOAT3, scaling */ + CNK_NODE_PIVOT, /* has a CNK_FLOAT3, pivot point */ + + CNK_NODE_MATRIX0, /* has a CNK_FLOAT4, first matrix row (4x3) */ + CNK_NODE_MATRXI1, /* has a CNK_FLOAT4, second matrix row (4x3) */ + CNK_NODE_MATRIX2, /* has a CNK_FLOAT4, third matrix row (4x3) */ + + CNK_ANIM, /* the animation root chunk */ + + MAX_NUM_CHUNKS +}; + +#define UNKNOWN_SIZE ((uint32_t)0xbaadf00d) + +struct chunk_header { + uint32_t id; + uint32_t size; +}; + +struct chunk { + struct chunk_header hdr; + char data[1]; +}; + + +void g3dimpl_chunk_header(struct chunk_header *hdr, int id); +int g3dimpl_write_chunk_header(const struct chunk_header *hdr, struct goat3d_io *io); +int g3dimpl_read_chunk_header(struct chunk_header *hdr, struct goat3d_io *io); +void g3dimpl_skip_chunk(const struct chunk_header *hdr, struct goat3d_io *io); + +#endif /* CHUNK_H_ */ diff --git a/libs/goat3d/src/dynarr.c b/libs/goat3d/src/dynarr.c new file mode 100644 index 0000000..59bbf8c --- /dev/null +++ b/libs/goat3d/src/dynarr.c @@ -0,0 +1,141 @@ +/* dynarr - dynamic resizable C array data structure + * author: John Tsiombikas + * license: public domain + */ +#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; +} + + +void *dynarr_clear(void *da) +{ + return dynarr_resize(da, 0); +} + +/* 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; + } + + if(item) { + memcpy((char*)da + desc->nelem * desc->szelem, item, desc->szelem); + } + desc->nelem++; + 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; +} + +void *dynarr_finalize(void *da) +{ + struct arrdesc *desc = DESC(da); + memmove(desc, da, desc->bufsz); + return desc; +} diff --git a/libs/goat3d/src/dynarr.h b/libs/goat3d/src/dynarr.h new file mode 100644 index 0000000..619feeb --- /dev/null +++ b/libs/goat3d/src/dynarr.h @@ -0,0 +1,90 @@ +/* dynarr - dynamic resizable C array data structure + * author: John Tsiombikas + * license: public domain + */ +#ifndef DYNARR_H_ +#define DYNARR_H_ + +#define dynarr_alloc g3dimpl_dynarr_alloc +#define dynarr_free g3dimpl_dynarr_free +#define dynarr_resize g3dimpl_dynarr_resize +#define dynarr_empty g3dimpl_dynarr_empty +#define dynarr_size g3dimpl_dynarr_size +#define dynarr_clear g3dimpl_dynarr_clear +#define dynarr_push g3dimpl_dynarr_push +#define dynarr_pop g3dimpl_dynarr_pop +#define dynarr_finalize g3dimpl_dynarr_finalize + +/* usage example: + * ------------- + * int *arr = dynarr_alloc(0, sizeof *arr); + * + * int x = 10; + * arr = dynarr_push(arr, &x); + * x = 5; + * arr = dynarr_push(arr, &x); + * x = 42; + * arr = dynarr_push(arr, &x); + * + * for(i=0; i + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include "g3dscn.h" +#include "log.h" + +int g3dimpl_loadmesh(struct goat3d_mesh *mesh, const char *fname) +{ + goat3d_logmsg(LOG_ERROR, "loading external mesh files not implemented yet!\n"); + return -1; +} diff --git a/libs/goat3d/src/g3danm.c b/libs/goat3d/src/g3danm.c new file mode 100644 index 0000000..8fdc41a --- /dev/null +++ b/libs/goat3d/src/g3danm.c @@ -0,0 +1,68 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "g3danm.h" +#include "dynarr.h" +#include "log.h" + +int g3dimpl_anim_init(struct goat3d_anim *anim) +{ + anim->name = 0; + if(!(anim->tracks = dynarr_alloc(0, sizeof *anim->tracks))) { + goat3d_logmsg(LOG_ERROR, "g3dimpl_anim_init: failed to allocate tracks array\n"); + return -1; + } + return 0; +} + +void g3dimpl_anim_destroy(struct goat3d_anim *anim) +{ + int i, num; + + if(!anim) return; + + free(anim->name); + num = dynarr_size(anim->tracks); + for(i=0; itracks[i]); + } + dynarr_free(anim->tracks); +} + + +const char *g3dimpl_trktypestr(enum goat3d_track_type type) +{ + switch(type) { + case GOAT3D_TRACK_VAL: + return "val"; + case GOAT3D_TRACK_VEC3: + return "vec3"; + case GOAT3D_TRACK_VEC4: + return "vec4"; + case GOAT3D_TRACK_QUAT: + return "quat"; + case GOAT3D_TRACK_POS: + return "pos"; + case GOAT3D_TRACK_ROT: + return "rot"; + case GOAT3D_TRACK_SCALE: + return "scale"; + } + return 0; +} + diff --git a/libs/goat3d/src/g3danm.h b/libs/goat3d/src/g3danm.h new file mode 100644 index 0000000..36da018 --- /dev/null +++ b/libs/goat3d/src/g3danm.h @@ -0,0 +1,41 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef G3DANM_H_ +#define G3DANM_H_ + +#include "goat3d.h" +#include "track.h" + +struct goat3d_track { + char *name; + enum goat3d_track_type type; + struct anm_track trk[4]; + struct goat3d_node *node; /* node associated with this track */ +}; + +struct goat3d_anim { + char *name; + struct goat3d_track **tracks; /* dynarr */ +}; + +int g3dimpl_anim_init(struct goat3d_anim *anim); +void g3dimpl_anim_destroy(struct goat3d_anim *anim); + +const char *g3dimpl_trktypestr(enum goat3d_track_type type); + +#endif /* G3DANM_H_ */ diff --git a/libs/goat3d/src/g3dscn.c b/libs/goat3d/src/g3dscn.c new file mode 100644 index 0000000..f58bc19 --- /dev/null +++ b/libs/goat3d/src/g3dscn.c @@ -0,0 +1,212 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "g3dscn.h" +#include "dynarr.h" + +int g3dimpl_obj_init(struct object *o, int type) +{ + static int last_mesh, last_light, last_camera; + struct goat3d_mesh *m; + struct goat3d_light *lt; + struct goat3d_camera *cam; + char *name; + + if(!(name = malloc(64))) { + return -1; + } + + switch(type) { + case OBJTYPE_MESH: + m = (struct goat3d_mesh*)o; + memset(m, 0, sizeof *m); + if(!(m->vertices = dynarr_alloc(0, sizeof *m->vertices))) goto err; + if(!(m->normals = dynarr_alloc(0, sizeof *m->normals))) goto err; + if(!(m->tangents = dynarr_alloc(0, sizeof *m->tangents))) goto err; + if(!(m->texcoords = dynarr_alloc(0, sizeof *m->texcoords))) goto err; + if(!(m->skin_weights = dynarr_alloc(0, sizeof *m->skin_weights))) goto err; + if(!(m->skin_matrices = dynarr_alloc(0, sizeof *m->skin_matrices))) goto err; + if(!(m->colors = dynarr_alloc(0, sizeof *m->colors))) goto err; + if(!(m->faces = dynarr_alloc(0, sizeof *m->faces))) goto err; + if(!(m->bones = dynarr_alloc(0, sizeof *m->bones))) goto err; + sprintf(name, "mesh%d", last_mesh++); + break; + + case OBJTYPE_LIGHT: + lt = (struct goat3d_light*)o; + memset(lt, 0, sizeof *lt); + cgm_vcons(<->color, 1, 1, 1); + cgm_vcons(<->attenuation, 1, 0, 0); + cgm_vcons(<->dir, 0, 0, 1); + lt->inner_cone = cgm_deg_to_rad(30); + lt->outer_cone = cgm_deg_to_rad(45); + sprintf(name, "light%d", last_light++); + break; + + case OBJTYPE_CAMERA: + cam = (struct goat3d_camera*)o; + memset(cam, 0, sizeof *cam); + cam->near_clip = 0.5f; + cam->far_clip = 500.0f; + cgm_vcons(&cam->up, 0, 1, 0); + sprintf(name, "camera%d", last_camera++); + break; + + default: + return -1; + } + + o->name = name; + o->type = type; + cgm_qcons(&o->rot, 0, 0, 0, 1); + cgm_vcons(&o->scale, 1, 1, 1); + return 0; + +err: + free(name); + g3dimpl_obj_destroy(o); + return -1; +} + +void g3dimpl_obj_destroy(struct object *o) +{ + struct goat3d_mesh *m; + + switch(o->type) { + case OBJTYPE_MESH: + m = (struct goat3d_mesh*)o; + dynarr_free(m->vertices); + dynarr_free(m->normals); + dynarr_free(m->tangents); + dynarr_free(m->texcoords); + dynarr_free(m->skin_weights); + dynarr_free(m->skin_matrices); + dynarr_free(m->colors); + dynarr_free(m->faces); + dynarr_free(m->bones); + break; + + default: + break; + } +} + +void g3dimpl_mesh_bounds(struct aabox *bb, struct goat3d_mesh *m, float *xform) +{ + int i, nverts; + + g3dimpl_aabox_init(bb); + + nverts = dynarr_size(m->vertices); + for(i=0; ivertices[i]; + if(xform) cgm_vmul_m4v3(&v, xform); + + if(v.x < bb->bmin.x) bb->bmin.x = v.x; + if(v.y < bb->bmin.y) bb->bmin.y = v.y; + if(v.z < bb->bmin.z) bb->bmin.z = v.z; + + if(v.x > bb->bmax.x) bb->bmax.x = v.x; + if(v.y > bb->bmax.y) bb->bmax.y = v.y; + if(v.z > bb->bmax.z) bb->bmax.z = v.z; + } +} + +int g3dimpl_mtl_init(struct goat3d_material *mtl) +{ + memset(mtl, 0, sizeof *mtl); + if(!(mtl->attrib = dynarr_alloc(0, sizeof *mtl->attrib))) { + return -1; + } + return 0; +} + +void g3dimpl_mtl_destroy(struct goat3d_material *mtl) +{ + int i, num = dynarr_size(mtl->attrib); + for(i=0; iattrib[i].name); + free(mtl->attrib[i].map); + } + dynarr_free(mtl->attrib); +} + +struct material_attrib *g3dimpl_mtl_findattr(struct goat3d_material *mtl, const char *name) +{ + int i, num = dynarr_size(mtl->attrib); + + for(i=0; iattrib[i].name, name) == 0) { + return mtl->attrib + i; + } + } + return 0; +} + +struct material_attrib *g3dimpl_mtl_getattr(struct goat3d_material *mtl, const char *name) +{ + int idx, len; + char *tmpname; + struct material_attrib *tmpattr, *ma; + + if((ma = g3dimpl_mtl_findattr(mtl, name))) { + return ma; + } + + len = strlen(name); + if(!(tmpname = malloc(len + 1))) { + return 0; + } + memcpy(tmpname, name, len + 1); + + idx = dynarr_size(mtl->attrib); + if(!(tmpattr = dynarr_push(mtl->attrib, 0))) { + free(tmpname); + return 0; + } + mtl->attrib = tmpattr; + + ma = mtl->attrib + idx; + ma->name = tmpname; + ma->map = 0; + cgm_wcons(&ma->value, 1, 1, 1, 1); + + return ma; +} + +void g3dimpl_node_bounds(struct aabox *bb, struct goat3d_node *n) +{ + struct object *obj = n->obj; + struct goat3d_node *cn = n->child; + float xform[16]; + + goat3d_get_matrix(n, xform); + + if(obj && obj->type == OBJTYPE_MESH) { + g3dimpl_mesh_bounds(bb, (struct goat3d_mesh*)obj, xform); + } else { + g3dimpl_aabox_init(bb); + } + + while(cn) { + struct aabox cbox; + g3dimpl_node_bounds(&cbox, cn); + g3dimpl_aabox_union(bb, bb, &cbox); + cn = cn->next; + } +} diff --git a/libs/goat3d/src/g3dscn.h b/libs/goat3d/src/g3dscn.h new file mode 100644 index 0000000..468f9d3 --- /dev/null +++ b/libs/goat3d/src/g3dscn.h @@ -0,0 +1,198 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef GOAT3D_SCENE_H_ +#define GOAT3D_SCENE_H_ + +#include "cgmath/cgmath.h" +#include "goat3d.h" +#include "g3danm.h" +#include "aabox.h" + +enum { + OBJTYPE_UNKNOWN, + OBJTYPE_MESH, + OBJTYPE_LIGHT, + OBJTYPE_CAMERA +}; + +enum { + LTYPE_POINT, + LTYPE_DIR, + LTYPE_SPOT +}; + +enum { + CAMTYPE_PRS, + CAMTYPE_TARGET +}; + + +struct face { + int v[3]; +}; + +typedef struct int4 { + int x, y, z, w; +} int4; + +struct material_attrib { + char *name; + cgm_vec4 value; + char *map; +}; + +struct goat3d_material { + char *name; + int idx; + struct material_attrib *attrib; /* dynarr */ +}; + + +#define OBJECT_COMMON \ + int type; \ + char *name; \ + cgm_vec3 pos; \ + cgm_quat rot; \ + cgm_vec3 scale; \ + void *next + +struct object { + OBJECT_COMMON; +}; + +struct goat3d_mesh { + OBJECT_COMMON; + struct goat3d_material *mtl; + + /* dynamic arrays */ + cgm_vec3 *vertices; + cgm_vec3 *normals; + cgm_vec3 *tangents; + cgm_vec2 *texcoords; + cgm_vec4 *skin_weights; + int4 *skin_matrices; + cgm_vec4 *colors; + struct face *faces; + struct goat3d_node **bones; +}; + +struct goat3d_light { + OBJECT_COMMON; + int ltype; + cgm_vec3 color; + cgm_vec3 attenuation; + float max_dist; + cgm_vec3 dir; /* for LTYPE_DIR */ + float inner_cone, outer_cone; /* for LTYPE_SPOT */ +}; + +struct goat3d_camera { + OBJECT_COMMON; + int camtype; + float fov; + float near_clip, far_clip; + cgm_vec3 target, up; +}; + +struct goat3d_node { + char *name; + enum goat3d_node_type type; + void *obj; + int child_count; + + cgm_vec3 pivot; + /* local transformation */ + cgm_vec3 pos, scale; + cgm_quat rot; + /* values from animation evaluation, take precedence over the above if + * has_anim is true + */ + cgm_vec3 apos, ascale; + cgm_quat arot; + int has_anim; + /* matrix computed from the above*/ + float matrix[16]; + int matrix_valid; + + struct goat3d_node *parent; + struct goat3d_node *child; + struct goat3d_node *next; +}; + + +struct goat3d { + unsigned int flags; + char *search_path; + + char *name; + cgm_vec3 ambient; + + /* dynamic arrays */ + struct goat3d_material **materials; + struct goat3d_mesh **meshes; + struct goat3d_light **lights; + struct goat3d_camera **cameras; + struct goat3d_node **nodes; + struct goat3d_anim **anims; + + struct aabox bbox; + int bbox_valid; + + /* namegen */ + unsigned int namecnt[7]; + char namebuf[64]; +}; + +extern int goat3d_log_level; + +/* defined in goat3d.c, declared here to keep them out of the public API */ +int goat3d_init(struct goat3d *g); +void goat3d_destroy(struct goat3d *g); + +void goat3d_clear(struct goat3d *g); + +/* defined in g3dscn.c */ +int g3dimpl_obj_init(struct object *o, int type); +void g3dimpl_obj_destroy(struct object *o); + +void g3dimpl_mesh_bounds(struct aabox *bb, struct goat3d_mesh *m, float *xform); + +int g3dimpl_mtl_init(struct goat3d_material *mtl); +void g3dimpl_mtl_destroy(struct goat3d_material *mtl); +struct material_attrib *g3dimpl_mtl_findattr(struct goat3d_material *mtl, const char *name); +struct material_attrib *g3dimpl_mtl_getattr(struct goat3d_material *mtl, const char *name); + +void g3dimpl_node_bounds(struct aabox *bb, struct goat3d_node *n); + +/* +void io_fprintf(goat3d_io *io, const char *fmt, ...); +void io_vfprintf(goat3d_io *io, const char *fmt, va_list ap); +*/ + +/* defined in read.c */ +int g3dimpl_scnload(struct goat3d *g, struct goat3d_io *io); +int g3dimpl_anmload(struct goat3d *g, struct goat3d_io *io); + +/* defined in write.c */ +int g3dimpl_scnsave(const struct goat3d *g, struct goat3d_io *io); +int g3dimpl_anmsave(const struct goat3d *g, struct goat3d_io *io); + +/* defined in extmesh.c */ +int g3dimpl_loadmesh(struct goat3d_mesh *mesh, const char *fname); + +#endif /* GOAT3D_SCENE_H_ */ diff --git a/libs/goat3d/src/goat3d.c b/libs/goat3d/src/goat3d.c new file mode 100644 index 0000000..9250b3e --- /dev/null +++ b/libs/goat3d/src/goat3d.c @@ -0,0 +1,1914 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include "goat3d.h" +#include "g3dscn.h" +#include "log.h" +#include "dynarr.h" + +static long read_file(void *buf, size_t bytes, void *uptr); +static long write_file(const void *buf, size_t bytes, void *uptr); +static long seek_file(long offs, int whence, void *uptr); +static char *clean_filename(char *str); + +static const char *def_scn_name = "unnamed"; + +#define SETNAME(dest, str) \ + do { \ + char *tmpname; \ + int len = strlen(str); \ + if(!(tmpname = malloc(len + 1))) { \ + return -1; \ + } \ + memcpy(tmpname, str, len + 1); \ + free(dest); \ + dest = tmpname; \ + return 0; \ + } while(0) + + +GOAT3DAPI struct goat3d *goat3d_create(void) +{ + struct goat3d *g; + + if(!(g = malloc(sizeof *g))) { + return 0; + } + if(goat3d_init(g) == -1) { + free(g); + return 0; + } + return g; +} + +GOAT3DAPI void goat3d_free(struct goat3d *g) +{ + goat3d_destroy(g); + free(g); +} + +int goat3d_init(struct goat3d *g) +{ + memset(g, 0, sizeof *g); + + cgm_vcons(&g->ambient, 0.05, 0.05, 0.05); + + if(!(g->materials = dynarr_alloc(0, sizeof *g->materials))) goto err; + if(!(g->meshes = dynarr_alloc(0, sizeof *g->meshes))) goto err; + if(!(g->lights = dynarr_alloc(0, sizeof *g->lights))) goto err; + if(!(g->cameras = dynarr_alloc(0, sizeof *g->cameras))) goto err; + if(!(g->nodes = dynarr_alloc(0, sizeof *g->nodes))) goto err; + if(!(g->anims = dynarr_alloc(0, sizeof *g->anims))) goto err; + + return 0; + +err: + goat3d_destroy(g); + return -1; +} + +void goat3d_destroy(struct goat3d *g) +{ + goat3d_clear(g); + + dynarr_free(g->materials); + dynarr_free(g->meshes); + dynarr_free(g->lights); + dynarr_free(g->cameras); + dynarr_free(g->nodes); + dynarr_free(g->anims); +} + +void goat3d_clear(struct goat3d *g) +{ + int i, num; + + num = dynarr_size(g->materials); + for(i=0; imaterials[i]); + free(g->materials[i]); + } + DYNARR_CLEAR(g->materials); + + num = dynarr_size(g->meshes); + for(i=0; imeshes[i]); + free(g->meshes[i]); + } + DYNARR_CLEAR(g->meshes); + + num = dynarr_size(g->lights); + for(i=0; ilights[i]); + free(g->lights[i]); + } + DYNARR_CLEAR(g->lights); + + num = dynarr_size(g->cameras); + for(i=0; icameras[i]); + free(g->cameras[i]); + } + DYNARR_CLEAR(g->cameras); + + num = dynarr_size(g->nodes); + for(i=0; inodes[i]); + } + DYNARR_CLEAR(g->nodes); + + num = dynarr_size(g->anims); + for(i=0; ianims[i]); + } + + g->name = 0; + g->bbox_valid = 0; +} + +GOAT3DAPI void goat3d_setopt(struct goat3d *g, enum goat3d_option opt, int val) +{ + if(val) { + g->flags |= (1 << (int)opt); + } else { + g->flags &= ~(1 << (int)opt); + } +} + +GOAT3DAPI int goat3d_getopt(const struct goat3d *g, enum goat3d_option opt) +{ + return (g->flags >> (int)opt) & 1; +} + +GOAT3DAPI int goat3d_load(struct goat3d *g, const char *fname) +{ + int len, res; + char *slash; + FILE *fp = fopen(fname, "rb"); + if(!fp) { + goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for reading: %s\n", fname, strerror(errno)); + return -1; + } + + /* if the filename contained any directory components, keep the prefix + * to use it as a search path for external mesh file loading + */ + len = strlen(fname); + if(!(g->search_path = malloc(len + 1))) { + fclose(fp); + return -1; + } + memcpy(g->search_path, fname, len + 1); + + if((slash = strrchr(g->search_path, '/'))) { + *slash = 0; + } else { + if((slash = strrchr(g->search_path, '\\'))) { + *slash = 0; + } else { + free(g->search_path); + g->search_path = 0; + } + } + + if((res = goat3d_load_file(g, fp)) == 0) { + const char *name; + if((name = goat3d_get_name(g)) == def_scn_name) { + goat3d_set_name(g, slash ? slash + 1 : fname); + } + } + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_save(const struct goat3d *g, const char *fname) +{ + int res; + FILE *fp = fopen(fname, "wb"); + if(!fp) { + goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for writing: %s\n", fname, strerror(errno)); + return -1; + } + + res = goat3d_save_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_load_file(struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_load_io(g, &io); +} + +GOAT3DAPI int goat3d_save_file(const struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_save_io(g, &io); +} + +GOAT3DAPI int goat3d_load_io(struct goat3d *g, struct goat3d_io *io) +{ + return g3dimpl_scnload(g, io); +} + +GOAT3DAPI int goat3d_save_io(const struct goat3d *g, struct goat3d_io *io) +{ + if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) { + goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n"); + return -1; + } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) { + /* TODO set treestore output format as text */ + } + return g3dimpl_scnsave(g, io); +} + +/* save/load animations */ +GOAT3DAPI int goat3d_load_anim(struct goat3d *g, const char *fname) +{ + int res; + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + return -1; + } + + res = goat3d_load_anim_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_save_anim(const struct goat3d *g, const char *fname) +{ + int res; + FILE *fp; + + if(!(fp = fopen(fname, "wb"))) { + return -1; + } + + res = goat3d_save_anim_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_load_anim_file(struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_load_anim_io(g, &io); +} + +GOAT3DAPI int goat3d_save_anim_file(const struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_save_anim_io(g, &io); +} + +GOAT3DAPI int goat3d_load_anim_io(struct goat3d *g, struct goat3d_io *io) +{ + return g3dimpl_anmload(g, io); +} + +GOAT3DAPI int goat3d_save_anim_io(const struct goat3d *g, struct goat3d_io *io) +{ + if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) { + goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n"); + return -1; + } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) { + /* TODO set treestore save format as text */ + } + return g3dimpl_anmsave(g, io); +} + + +GOAT3DAPI int goat3d_set_name(struct goat3d *g, const char *name) +{ + int len = strlen(name); + + free(g->name); + if(!(g->name = malloc(len + 1))) { + return -1; + } + memcpy(g->name, name, len + 1); + return 0; +} + +GOAT3DAPI const char *goat3d_get_name(const struct goat3d *g) +{ + return g->name ? g->name : def_scn_name; +} + +GOAT3DAPI void goat3d_set_ambient(struct goat3d *g, const float *amb) +{ + cgm_vcons(&g->ambient, amb[0], amb[1], amb[2]); +} + +GOAT3DAPI void goat3d_set_ambient3f(struct goat3d *g, float ar, float ag, float ab) +{ + cgm_vcons(&g->ambient, ar, ag, ab); +} + +GOAT3DAPI const float *goat3d_get_ambient(const struct goat3d *g) +{ + return &g->ambient.x; +} + +GOAT3DAPI int goat3d_get_bounds(const struct goat3d *g, float *bmin, float *bmax) +{ + int i, num_nodes, num_meshes; + struct aabox bbox; + + if(!g->bbox_valid) { + g3dimpl_aabox_init((struct aabox*)&g->bbox); + + if(dynarr_empty(g->nodes)) { +use_mesh_bounds: + num_meshes = dynarr_size(g->meshes); + for(i=0; imeshes[i], 0); + g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox); + } + } else { + num_nodes = dynarr_size(g->nodes); + for(i=0; inodes[i]->parent) { + continue; + } + g3dimpl_node_bounds(&bbox, g->nodes[i]); + g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox); + } + + /* in case the nodes are junk */ + if(g->bbox.bmin.x > g->bbox.bmax.x) { + goto use_mesh_bounds; + } + } + ((struct goat3d*)g)->bbox_valid = 1; + } + + if(g->bbox.bmin.x > g->bbox.bmax.x) { + return -1; + } + + bmin[0] = g->bbox.bmin.x; + bmin[1] = g->bbox.bmin.y; + bmin[2] = g->bbox.bmin.z; + bmax[0] = g->bbox.bmax.x; + bmax[1] = g->bbox.bmax.y; + bmax[2] = g->bbox.bmax.z; + return 0; +} + +// ---- materials ---- +GOAT3DAPI int goat3d_add_mtl(struct goat3d *g, struct goat3d_material *mtl) +{ + struct goat3d_material **newarr; + mtl->idx = dynarr_size(g->materials); + if(!(newarr = dynarr_push(g->materials, &mtl))) { + return -1; + } + g->materials = newarr; + return 0; +} + +GOAT3DAPI int goat3d_get_mtl_count(struct goat3d *g) +{ + return dynarr_size(g->materials); +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mtl(struct goat3d *g, int idx) +{ + return g->materials[idx]; +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mtl_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->materials); + for(i=0; imaterials[i]->name, name) == 0) { + return g->materials[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_material *goat3d_create_mtl(void) +{ + struct goat3d_material *mtl; + if(!(mtl = malloc(sizeof *mtl))) { + return 0; + } + g3dimpl_mtl_init(mtl); + return mtl; +} + +GOAT3DAPI void goat3d_destroy_mtl(struct goat3d_material *mtl) +{ + g3dimpl_mtl_destroy(mtl); + free(mtl); +} + +GOAT3DAPI int goat3d_set_mtl_name(struct goat3d_material *mtl, const char *name) +{ + SETNAME(mtl->name, name); +} + +GOAT3DAPI const char *goat3d_get_mtl_name(const struct goat3d_material *mtl) +{ + return mtl->name; +} + +GOAT3DAPI int goat3d_set_mtl_attrib(struct goat3d_material *mtl, const char *attrib, const float *val) +{ + struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib); + if(!ma) return -1; + cgm_wcons(&ma->value, val[0], val[1], val[2], val[3]); + return 0; +} + +GOAT3DAPI int goat3d_set_mtl_attrib1f(struct goat3d_material *mtl, const char *attrib, float val) +{ + return goat3d_set_mtl_attrib4f(mtl, attrib, val, 0, 0, 1); +} + +GOAT3DAPI int goat3d_set_mtl_attrib3f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b) +{ + return goat3d_set_mtl_attrib4f(mtl, attrib, r, g, b, 1); +} + +GOAT3DAPI int goat3d_set_mtl_attrib4f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b, float a) +{ + struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib); + if(!ma) return -1; + cgm_wcons(&ma->value, r, g, b, a); + return 0; +} + +GOAT3DAPI const float *goat3d_get_mtl_attrib(struct goat3d_material *mtl, const char *attrib) +{ + struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib); + return ma ? &ma->value.x : 0; +} + +GOAT3DAPI int goat3d_set_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib, const char *mapname) +{ + int len; + char *tmp; + struct material_attrib *ma; + + len = strlen(mapname); + if(!(tmp = malloc(len + 1))) { + return -1; + } + memcpy(tmp, mapname, len + 1); + + if(!(ma = g3dimpl_mtl_getattr(mtl, attrib))) { + free(tmp); + return -1; + } + free(ma->map); + ma->map = tmp; + tmp = clean_filename(ma->map); + if(tmp != ma->map) { + memmove(ma->map, tmp, len - (tmp - ma->map) + 1); + } + return 0; +} + +GOAT3DAPI const char *goat3d_get_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib) +{ + struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib); + return ma->map; +} + +GOAT3DAPI const char *goat3d_get_mtl_attrib_name(struct goat3d_material *mtl, int idx) +{ + return mtl->attrib[idx].name; +} + +GOAT3DAPI int goat3d_get_mtl_attrib_count(struct goat3d_material *mtl) +{ + return dynarr_size(mtl->attrib); +} + +// ---- meshes ---- +GOAT3DAPI int goat3d_add_mesh(struct goat3d *g, struct goat3d_mesh *mesh) +{ + struct goat3d_mesh **arr; + if(!(arr = dynarr_push(g->meshes, &mesh))) { + return -1; + } + g->meshes = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_mesh_count(struct goat3d *g) +{ + return dynarr_size(g->meshes); +} + +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh(struct goat3d *g, int idx) +{ + return g->meshes[idx]; +} + +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->meshes); + for(i=0; imeshes[i]->name, name) == 0) { + return g->meshes[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_mesh *goat3d_create_mesh(void) +{ + struct goat3d_mesh *m; + + if(!(m = malloc(sizeof *m))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)m, OBJTYPE_MESH) == -1) { + free(m); + return 0; + } + return m; +} + +GOAT3DAPI void goat3d_destroy_mesh(struct goat3d_mesh *mesh) +{ + g3dimpl_obj_destroy((struct object*)mesh); + free(mesh); +} + +GOAT3DAPI int goat3d_set_mesh_name(struct goat3d_mesh *mesh, const char *name) +{ + SETNAME(mesh->name, name); +} + +GOAT3DAPI const char *goat3d_get_mesh_name(const struct goat3d_mesh *mesh) +{ + return mesh->name; +} + +GOAT3DAPI void goat3d_set_mesh_mtl(struct goat3d_mesh *mesh, struct goat3d_material *mtl) +{ + mesh->mtl = mtl; +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mesh_mtl(struct goat3d_mesh *mesh) +{ + return mesh->mtl; +} + +GOAT3DAPI int goat3d_get_mesh_vertex_count(struct goat3d_mesh *mesh) +{ + return dynarr_size(mesh->vertices); +} + +GOAT3DAPI int goat3d_get_mesh_attrib_count(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib) +{ + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + return dynarr_size(mesh->vertices); + case GOAT3D_MESH_ATTR_NORMAL: + return dynarr_size(mesh->normals); + case GOAT3D_MESH_ATTR_TANGENT: + return dynarr_size(mesh->tangents); + case GOAT3D_MESH_ATTR_TEXCOORD: + return dynarr_size(mesh->texcoords); + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + return dynarr_size(mesh->skin_weights); + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + return dynarr_size(mesh->skin_matrices); + case GOAT3D_MESH_ATTR_COLOR: + return dynarr_size(mesh->colors); + default: + break; + } + return 0; +} + +GOAT3DAPI int goat3d_get_mesh_face_count(struct goat3d_mesh *mesh) +{ + return dynarr_size(mesh->faces); +} + +#define SET_VERTEX_DATA(arr, p, n) \ + do { \ + void *tmp = dynarr_resize(arr, n); \ + if(!tmp) { \ + goat3d_logmsg(LOG_ERROR, "failed to resize vertex array (%d)\n", n); \ + return -1; \ + } \ + arr = tmp; \ + memcpy(arr, p, n * sizeof *arr); \ + } while(0) + +GOAT3DAPI int goat3d_set_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, const void *data, int vnum) +{ + if(attrib == GOAT3D_MESH_ATTR_VERTEX) { + SET_VERTEX_DATA(mesh->vertices, data, vnum); + return 0; + } + + if(vnum != dynarr_size(mesh->vertices)) { + goat3d_logmsg(LOG_ERROR, "trying to set mesh attrib data with number of elements different than the vertex array\n"); + return -1; + } + + switch(attrib) { + case GOAT3D_MESH_ATTR_NORMAL: + SET_VERTEX_DATA(mesh->normals, data, vnum); + break; + case GOAT3D_MESH_ATTR_TANGENT: + SET_VERTEX_DATA(mesh->tangents, data, vnum); + break; + case GOAT3D_MESH_ATTR_TEXCOORD: + SET_VERTEX_DATA(mesh->texcoords, data, vnum); + break; + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + SET_VERTEX_DATA(mesh->skin_weights, data, vnum); + break; + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + SET_VERTEX_DATA(mesh->skin_matrices, data, vnum); + break; + case GOAT3D_MESH_ATTR_COLOR: + SET_VERTEX_DATA(mesh->colors, data, vnum); + default: + goat3d_logmsg(LOG_ERROR, "trying to set unknown vertex attrib: %d\n", attrib); + return -1; + } + return 0; +} + +GOAT3DAPI int goat3d_add_mesh_attrib1f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float val) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, val, 0, 0, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib2f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, 0, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib3f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, z, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib4f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z, float w) +{ + float vec[4]; + int4 intvec; + void *tmp; + + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->vertices, vec))) { + goto err; + } + mesh->vertices = tmp; + break; + + case GOAT3D_MESH_ATTR_NORMAL: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->normals, vec))) { + goto err; + } + mesh->normals = tmp; + break; + + case GOAT3D_MESH_ATTR_TANGENT: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->tangents, vec))) { + goto err; + } + mesh->tangents = tmp; + break; + + case GOAT3D_MESH_ATTR_TEXCOORD: + cgm_vcons((cgm_vec3*)vec, x, y, 0); + if(!(tmp = dynarr_push(mesh->texcoords, vec))) { + goto err; + } + mesh->texcoords = tmp; + break; + + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + cgm_wcons((cgm_vec4*)vec, x, y, z, w); + if(!(tmp = dynarr_push(mesh->skin_weights, vec))) { + goto err; + } + mesh->skin_weights = tmp; + break; + + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + intvec.x = x; + intvec.y = y; + intvec.z = z; + intvec.w = w; + if(!(tmp = dynarr_push(mesh->skin_matrices, &intvec))) { + goto err; + } + mesh->skin_matrices = tmp; + break; + + case GOAT3D_MESH_ATTR_COLOR: + cgm_wcons((cgm_vec4*)vec, x, y, z, w); + if(!(tmp = dynarr_push(mesh->colors, vec))) { + goto err; + } + mesh->colors = tmp; + + default: + goat3d_logmsg(LOG_ERROR, "trying to add unknown vertex attrib: %d\n", attrib); + return -1; + } + return 0; + +err: + goat3d_logmsg(LOG_ERROR, "failed to push vertex attrib\n"); + return -1; +} + +GOAT3DAPI void *goat3d_get_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib) +{ + return goat3d_get_mesh_attrib(mesh, attrib, 0); +} + +GOAT3DAPI void *goat3d_get_mesh_attrib(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, int idx) +{ + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + return dynarr_empty(mesh->vertices) ? 0 : mesh->vertices + idx; + case GOAT3D_MESH_ATTR_NORMAL: + return dynarr_empty(mesh->normals) ? 0 : mesh->normals + idx; + case GOAT3D_MESH_ATTR_TANGENT: + return dynarr_empty(mesh->tangents) ? 0 : mesh->tangents + idx; + case GOAT3D_MESH_ATTR_TEXCOORD: + return dynarr_empty(mesh->texcoords) ? 0 : mesh->texcoords + idx; + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + return dynarr_empty(mesh->skin_weights) ? 0 : mesh->skin_weights + idx; + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + return dynarr_empty(mesh->skin_matrices) ? 0 : mesh->skin_matrices + idx; + case GOAT3D_MESH_ATTR_COLOR: + return dynarr_empty(mesh->colors) ? 0 : mesh->colors + idx; + default: + break; + } + return 0; +} + + +GOAT3DAPI int goat3d_set_mesh_faces(struct goat3d_mesh *mesh, const int *data, int num) +{ + void *tmp; + if(!(tmp = dynarr_resize(mesh->faces, num))) { + goat3d_logmsg(LOG_ERROR, "failed to resize face array (%d)\n", num); + return -1; + } + mesh->faces = tmp; + memcpy(mesh->faces, data, num * sizeof *mesh->faces); + return 0; +} + +GOAT3DAPI int goat3d_add_mesh_face(struct goat3d_mesh *mesh, int a, int b, int c) +{ + void *tmp; + struct face face; + + face.v[0] = a; + face.v[1] = b; + face.v[2] = c; + + if(!(tmp = dynarr_push(mesh->faces, &face))) { + goat3d_logmsg(LOG_ERROR, "failed to add face\n"); + return -1; + } + mesh->faces = tmp; + return 0; +} + +GOAT3DAPI int *goat3d_get_mesh_faces(struct goat3d_mesh *mesh) +{ + return goat3d_get_mesh_face(mesh, 0); +} + +GOAT3DAPI int *goat3d_get_mesh_face(struct goat3d_mesh *mesh, int idx) +{ + return dynarr_empty(mesh->faces) ? 0 : mesh->faces[idx].v; +} + +// immedate mode state +static enum goat3d_im_primitive im_prim; +static struct goat3d_mesh *im_mesh; +static cgm_vec3 im_norm, im_tang; +static cgm_vec2 im_texcoord; +static cgm_vec4 im_skinw, im_color = {1, 1, 1, 1}; +static int4 im_skinmat; +static int im_use[NUM_GOAT3D_MESH_ATTRIBS]; + + +GOAT3DAPI void goat3d_begin(struct goat3d_mesh *mesh, enum goat3d_im_primitive prim) +{ + DYNARR_CLEAR(mesh->vertices); + DYNARR_CLEAR(mesh->normals); + DYNARR_CLEAR(mesh->tangents); + DYNARR_CLEAR(mesh->texcoords); + DYNARR_CLEAR(mesh->skin_weights); + DYNARR_CLEAR(mesh->skin_matrices); + DYNARR_CLEAR(mesh->colors); + DYNARR_CLEAR(mesh->faces); + + im_mesh = mesh; + memset(im_use, 0, sizeof im_use); + + im_prim = prim; +} + +GOAT3DAPI void goat3d_end(void) +{ + int i, vidx, num_faces, num_quads; + void *tmp; + + switch(im_prim) { + case GOAT3D_TRIANGLES: + { + num_faces = dynarr_size(im_mesh->vertices) / 3; + if(!(tmp = dynarr_resize(im_mesh->faces, num_faces))) { + return; + } + im_mesh->faces = tmp; + + vidx = 0; + for(i=0; ifaces[i].v[0] = vidx++; + im_mesh->faces[i].v[1] = vidx++; + im_mesh->faces[i].v[2] = vidx++; + } + } + break; + + case GOAT3D_QUADS: + { + num_quads = dynarr_size(im_mesh->vertices) / 4; + if(!(tmp = dynarr_resize(im_mesh->faces, num_quads * 2))) { + return; + } + im_mesh->faces = tmp; + + vidx = 0; + for(i=0; ifaces[i * 2].v[0] = vidx; + im_mesh->faces[i * 2].v[1] = vidx + 1; + im_mesh->faces[i * 2].v[2] = vidx + 2; + + im_mesh->faces[i * 2 + 1].v[0] = vidx; + im_mesh->faces[i * 2 + 1].v[1] = vidx + 2; + im_mesh->faces[i * 2 + 1].v[2] = vidx + 3; + + vidx += 4; + } + } + break; + + default: + break; + } +} + +GOAT3DAPI void goat3d_vertex3f(float x, float y, float z) +{ + void *tmp; + cgm_vec3 v; + + cgm_vcons(&v, x, y, z); + if(!(tmp = dynarr_push(im_mesh->vertices, &v))) { + return; + } + im_mesh->vertices = tmp; + + if(im_use[GOAT3D_MESH_ATTR_NORMAL]) { + if((tmp = dynarr_push(im_mesh->normals, &im_norm))) { + im_mesh->normals = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_TANGENT]) { + if((tmp = dynarr_push(im_mesh->tangents, &im_tang))) { + im_mesh->tangents = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_TEXCOORD]) { + if((tmp = dynarr_push(im_mesh->texcoords, &im_texcoord))) { + im_mesh->texcoords = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT]) { + if((tmp = dynarr_push(im_mesh->skin_weights, &im_skinw))) { + im_mesh->skin_weights = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX]) { + if((tmp = dynarr_push(im_mesh->skin_matrices, &im_skinmat))) { + im_mesh->skin_matrices = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_COLOR]) { + if((tmp = dynarr_push(im_mesh->colors, &im_color))) { + im_mesh->colors = tmp; + } + } +} + +GOAT3DAPI void goat3d_normal3f(float x, float y, float z) +{ + cgm_vcons(&im_norm, x, y, z); + im_use[GOAT3D_MESH_ATTR_NORMAL] = 1; +} + +GOAT3DAPI void goat3d_tangent3f(float x, float y, float z) +{ + cgm_vcons(&im_tang, x, y, z); + im_use[GOAT3D_MESH_ATTR_TANGENT] = 1; +} + +GOAT3DAPI void goat3d_texcoord2f(float x, float y) +{ + im_texcoord.x = x; + im_texcoord.y = y; + im_use[GOAT3D_MESH_ATTR_TEXCOORD] = 1; +} + +GOAT3DAPI void goat3d_skin_weight4f(float x, float y, float z, float w) +{ + cgm_wcons(&im_skinw, x, y, z, w); + im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT] = 1; +} + +GOAT3DAPI void goat3d_skin_matrix4i(int x, int y, int z, int w) +{ + im_skinmat.x = x; + im_skinmat.y = y; + im_skinmat.z = z; + im_skinmat.w = w; + im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX] = 1; +} + +GOAT3DAPI void goat3d_color3f(float x, float y, float z) +{ + goat3d_color4f(x, y, z, 1.0f); +} + +GOAT3DAPI void goat3d_color4f(float x, float y, float z, float w) +{ + cgm_wcons(&im_color, x, y, z, w); + im_use[GOAT3D_MESH_ATTR_COLOR] = 1; +} + +GOAT3DAPI void goat3d_get_mesh_bounds(const struct goat3d_mesh *mesh, float *bmin, float *bmax) +{ + struct aabox box; + + g3dimpl_mesh_bounds(&box, (struct goat3d_mesh*)mesh, 0); + + bmin[0] = box.bmin.x; + bmin[1] = box.bmin.y; + bmin[2] = box.bmin.z; + bmax[0] = box.bmax.x; + bmax[1] = box.bmax.y; + bmax[2] = box.bmax.z; +} + +/* lights */ +GOAT3DAPI int goat3d_add_light(struct goat3d *g, struct goat3d_light *lt) +{ + struct goat3d_light **arr; + if(!(arr = dynarr_push(g->lights, <))) { + return -1; + } + g->lights = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_light_count(struct goat3d *g) +{ + return dynarr_size(g->lights); +} + +GOAT3DAPI struct goat3d_light *goat3d_get_light(struct goat3d *g, int idx) +{ + return g->lights[idx]; +} + +GOAT3DAPI struct goat3d_light *goat3d_get_light_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->lights); + for(i=0; ilights[i]->name, name) == 0) { + return g->lights[i]; + } + } + return 0; +} + + +GOAT3DAPI struct goat3d_light *goat3d_create_light(void) +{ + struct goat3d_light *lt; + + if(!(lt = malloc(sizeof *lt))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)lt, OBJTYPE_LIGHT) == -1) { + free(lt); + return 0; + } + return lt; +} + +GOAT3DAPI void goat3d_destroy_light(struct goat3d_light *lt) +{ + g3dimpl_obj_destroy((struct object*)lt); + free(lt); +} + +GOAT3DAPI int goat3d_set_light_name(struct goat3d_light *lt, const char *name) +{ + SETNAME(lt->name, name); +} + +GOAT3DAPI const char *goat3d_get_light_name(const struct goat3d_light *lt) +{ + return lt->name; +} + +/* cameras */ +GOAT3DAPI int goat3d_add_camera(struct goat3d *g, struct goat3d_camera *cam) +{ + struct goat3d_camera **arr; + if(!(arr = dynarr_push(g->cameras, &cam))) { + return -1; + } + g->cameras = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_camera_count(struct goat3d *g) +{ + return dynarr_size(g->cameras); +} + +GOAT3DAPI struct goat3d_camera *goat3d_get_camera(struct goat3d *g, int idx) +{ + return g->cameras[idx]; +} + +GOAT3DAPI struct goat3d_camera *goat3d_get_camera_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->cameras); + for(i=0; icameras[i]->name, name) == 0) { + return g->cameras[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_camera *goat3d_create_camera(void) +{ + struct goat3d_camera *cam; + + if(!(cam = malloc(sizeof *cam))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)cam, OBJTYPE_CAMERA) == -1) { + free(cam); + return 0; + } + return cam; +} + +GOAT3DAPI void goat3d_destroy_camera(struct goat3d_camera *cam) +{ + g3dimpl_obj_destroy((struct object*)cam); + free(cam); +} + +GOAT3DAPI int goat3d_set_camera_name(struct goat3d_camera *cam, const char *name) +{ + SETNAME(cam->name, name); +} + +GOAT3DAPI const char *goat3d_get_camera_name(const struct goat3d_camera *cam) +{ + return cam->name; +} + +/* node */ +GOAT3DAPI int goat3d_add_node(struct goat3d *g, struct goat3d_node *node) +{ + struct goat3d_node **arr; + if(!(arr = dynarr_push(g->nodes, &node))) { + return -1; + } + g->nodes = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_node_count(struct goat3d *g) +{ + return dynarr_size(g->nodes); +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node(struct goat3d *g, int idx) +{ + return g->nodes[idx]; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->nodes); + for(i=0; inodes[i]->name, name) == 0) { + return g->nodes[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_node *goat3d_create_node(void) +{ + struct goat3d_node *node; + + if(!(node = calloc(1, sizeof *node))) { + return 0; + } + node->type = GOAT3D_NODE_NULL; + node->obj = 0; + node->child_count = 0; + + node->rot.w = node->arot.w = 1; + cgm_vcons(&node->scale, 1, 1, 1); + cgm_midentity(node->matrix); + + return node; +} + +GOAT3DAPI void goat3d_destroy_node(struct goat3d_node *node) +{ + if(!node) return; + free(node->name); + free(node); +} + +GOAT3DAPI int goat3d_set_node_name(struct goat3d_node *node, const char *name) +{ + SETNAME(node->name, name); +} + +GOAT3DAPI const char *goat3d_get_node_name(const struct goat3d_node *node) +{ + return node->name; +} + +GOAT3DAPI void goat3d_set_node_object(struct goat3d_node *node, enum goat3d_node_type type, void *obj) +{ + node->obj = obj; + node->type = type; +} + +GOAT3DAPI void *goat3d_get_node_object(const struct goat3d_node *node) +{ + return node->obj; +} + +GOAT3DAPI enum goat3d_node_type goat3d_get_node_type(const struct goat3d_node *node) +{ + return node->type; +} + +GOAT3DAPI void goat3d_add_node_child(struct goat3d_node *node, struct goat3d_node *child) +{ + child->next = node->child; + node->child = child; + child->parent = node; + node->child_count++; + + child->matrix_valid = 0; +} + +GOAT3DAPI int goat3d_get_node_child_count(const struct goat3d_node *node) +{ + return node->child_count; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_child(const struct goat3d_node *node, int idx) +{ + struct goat3d_node *c = node->child; + while(c && idx-- > 0) { + c = c->next; + } + return c; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_parent(const struct goat3d_node *node) +{ + return node->parent; +} + +static void invalidate_subtree(struct goat3d_node *node) +{ + struct goat3d_node *c = node->child; + + while(c) { + invalidate_subtree(c); + c = c->next; + } + node->matrix_valid = 0; +} + + +GOAT3DAPI void goat3d_set_node_position(struct goat3d_node *node, float x, float y, float z) +{ + cgm_vcons(&node->pos, x, y, z); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_set_node_rotation(struct goat3d_node *node, float qx, float qy, float qz, float qw) +{ + cgm_qcons(&node->rot, qx, qy, qz, qw); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_set_node_scaling(struct goat3d_node *node, float sx, float sy, float sz) +{ + cgm_vcons(&node->scale, sx, sy, sz); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_get_node_position(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + if(node->has_anim) { + *xptr = node->apos.x; + *yptr = node->apos.y; + *zptr = node->apos.z; + } else { + *xptr = node->pos.x; + *yptr = node->pos.y; + *zptr = node->pos.z; + } +} + +GOAT3DAPI void goat3d_get_node_rotation(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr, float *wptr) +{ + if(node->has_anim) { + *xptr = node->arot.x; + *yptr = node->arot.y; + *zptr = node->arot.z; + *wptr = node->arot.w; + } else { + *xptr = node->rot.x; + *yptr = node->rot.y; + *zptr = node->rot.z; + *wptr = node->rot.w; + } +} + +GOAT3DAPI void goat3d_get_node_scaling(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + if(node->has_anim) { + *xptr = node->ascale.x; + *yptr = node->ascale.y; + *zptr = node->ascale.z; + } else { + *xptr = node->scale.x; + *yptr = node->scale.y; + *zptr = node->scale.z; + } +} + + +GOAT3DAPI void goat3d_set_node_pivot(struct goat3d_node *node, float px, float py, float pz) +{ + cgm_vcons(&node->pivot, px, py, pz); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_get_node_pivot(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + *xptr = node->pivot.x; + *yptr = node->pivot.y; + *zptr = node->pivot.z; +} + +static void calc_node_matrix(const struct goat3d_node *node, float *mat) +{ + int i; + float rmat[16]; + cgm_vec3 pos, scale; + cgm_quat rot; + + if(node->has_anim) { + pos = node->apos; + rot = node->arot; + scale = node->ascale; + } else { + pos = node->pos; + rot = node->rot; + scale = node->scale; + } + + cgm_mtranslation(mat, node->pivot.x, node->pivot.y, node->pivot.z); + cgm_mrotation_quat(rmat, &rot); + + for(i=0; i<3; i++) { + mat[i] = rmat[i]; + mat[4 + i] = rmat[4 + i]; + mat[8 + i] = rmat[8 + i]; + } + + mat[0] *= scale.x; mat[4] *= scale.y; mat[8] *= scale.z; mat[12] += pos.x; + mat[1] *= scale.x; mat[5] *= scale.y; mat[9] *= scale.z; mat[13] += pos.y; + mat[2] *= scale.x; mat[6] *= scale.y; mat[10] *= scale.z; mat[14] += pos.z; + + cgm_mpretranslate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z); + + /* that's basically: pivot * rotation * translation * scaling * -pivot */ +} + +GOAT3DAPI void goat3d_get_node_matrix(const struct goat3d_node *node, float *matrix) +{ + if(!node->matrix_valid) { + calc_node_matrix(node, (float*)node->matrix); + ((struct goat3d_node*)node)->matrix_valid = 1; + } + memcpy(matrix, node->matrix, sizeof node->matrix); +} + +GOAT3DAPI void goat3d_get_matrix(const struct goat3d_node *node, float *matrix) +{ + goat3d_get_node_matrix(node, matrix); + if(node->parent) { + cgm_mmul(matrix, node->parent->matrix); + } +} + +GOAT3DAPI void goat3d_get_node_bounds(const struct goat3d_node *node, float *bmin, float *bmax) +{ + struct aabox box; + g3dimpl_node_bounds(&box, (struct goat3d_node*)node); + + bmin[0] = box.bmin.x; + bmin[1] = box.bmin.y; + bmin[2] = box.bmin.z; + bmax[0] = box.bmax.x; + bmax[1] = box.bmax.y; + bmax[2] = box.bmax.z; +} + + +/* tracks */ +#define BASETYPE(type) ((int)(type) & 0xff) +static const int key_val_sz[] = {1, 3, 4, 4}; + +GOAT3DAPI struct goat3d_track *goat3d_create_track(void) +{ + int i; + struct goat3d_track *trk; + + if(!(trk = calloc(1, sizeof *trk))) { + return 0; + } + + for(i=0; i<4; i++) { + if(anm_init_track(trk->trk + i) == -1) { + while(--i >= 0) { + anm_destroy_track(trk->trk + i); + } + free(trk); + return 0; + } + } + + return trk; +} + +GOAT3DAPI void goat3d_destroy_track(struct goat3d_track *trk) +{ + int i; + + if(!trk) return; + + free(trk->name); + + for(i=0; i<4; i++) { + anm_destroy_track(trk->trk + i); + } +} + +GOAT3DAPI int goat3d_set_track_name(struct goat3d_track *trk, const char *name) +{ + SETNAME(trk->name, name); +} + +GOAT3DAPI const char *goat3d_get_track_name(const struct goat3d_track *trk) +{ + return trk->name; +} + +GOAT3DAPI void goat3d_set_track_type(struct goat3d_track *trk, enum goat3d_track_type type) +{ + trk->type = type; + + switch(BASETYPE(type)) { + case GOAT3D_TRACK_QUAT: + case GOAT3D_TRACK_VEC4: + anm_set_track_default(trk->trk + 3, 1); + case GOAT3D_TRACK_VEC3: + anm_set_track_default(trk->trk + 1, 0); + anm_set_track_default(trk->trk + 2, 0); + case GOAT3D_TRACK_VAL: + anm_set_track_default(trk->trk + 0, 0); + } +} + +GOAT3DAPI enum goat3d_track_type goat3d_get_track_type(const struct goat3d_track *trk) +{ + return trk->type; +} + +GOAT3DAPI void goat3d_set_track_node(struct goat3d_track *trk, struct goat3d_node *node) +{ + trk->node = node; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_track_node(const struct goat3d_track *trk) +{ + return trk->node; +} + +GOAT3DAPI void goat3d_set_track_interp(struct goat3d_track *trk, enum goat3d_interp in) +{ + int i; + for(i=0; i<4; i++) { + anm_set_track_interpolator(trk->trk + i, in); + } +} + +GOAT3DAPI enum goat3d_interp goat3d_get_track_interp(const struct goat3d_track *trk) +{ + return trk->trk[0].interp; +} + +GOAT3DAPI void goat3d_set_track_extrap(struct goat3d_track *trk, enum goat3d_extrap ex) +{ + int i; + for(i=0; i<4; i++) { + anm_set_track_extrapolator(trk->trk + i, ex); + } +} + +GOAT3DAPI enum goat3d_extrap goat3d_get_track_extrap(const struct goat3d_track *trk) +{ + return trk->trk[0].extrap; +} + +GOAT3DAPI int goat3d_set_track_key(struct goat3d_track *trk, const struct goat3d_key *key) +{ + int i, num; + enum goat3d_track_type basetype; + long tm = ANM_MSEC2TM(key->tm); + + basetype = BASETYPE(trk->type); /* e.g. ROT -> QUAT */ + num = key_val_sz[basetype]; + + for(i=0; itrk + i, tm, key->val[i]) == -1) { + return -1; + } + } + return 0; +} + +GOAT3DAPI int goat3d_get_track_key(const struct goat3d_track *trk, int idx, struct goat3d_key *key) +{ + int i, num; + struct anm_keyframe *akey; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + num = key_val_sz[basetype]; + + for(i=0; itrk + i, idx))) { + return -1; + } + if(i == 0) { + key->tm = ANM_TM2MSEC(akey->time); + } + key->val[i] = akey->val; + } + return 0; +} + +GOAT3DAPI int goat3d_get_track_key_count(const struct goat3d_track *trk) +{ + return trk->trk[0].count; +} + +GOAT3DAPI int goat3d_set_track_val(struct goat3d_track *trk, long msec, float val) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VAL) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_val called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = val; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_vec3(struct goat3d_track *trk, long msec, float x, float y, float z) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC3) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec3 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_vec4(struct goat3d_track *trk, long msec, float x, float y, float z, float w) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC4) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec4 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + key.val[3] = w; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_quat(struct goat3d_track *trk, long msec, float x, float y, float z, float w) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_QUAT) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_quat called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + key.val[3] = w; + return goat3d_set_track_key(trk, &key); +} + + +GOAT3DAPI void goat3d_get_track_val(const struct goat3d_track *trk, long msec, float *valp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VAL) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_val called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *valp = anm_get_value(trk->trk, tm); +} + +GOAT3DAPI void goat3d_get_track_vec3(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC3) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec3 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *xp = anm_get_value(trk->trk, tm); + *yp = anm_get_value(trk->trk + 1, tm); + *zp = anm_get_value(trk->trk + 2, tm); +} + +GOAT3DAPI void goat3d_get_track_vec4(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC4) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec4 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *xp = anm_get_value(trk->trk, tm); + *yp = anm_get_value(trk->trk + 1, tm); + *zp = anm_get_value(trk->trk + 2, tm); + *wp = anm_get_value(trk->trk + 3, tm); +} + +GOAT3DAPI void goat3d_get_track_quat(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp) +{ + enum goat3d_track_type basetype; + float quat[4]; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_QUAT) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_quat called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + anm_get_quat(trk->trk, trk->trk + 1, trk->trk + 2, trk->trk + 3, tm, quat); + *xp = quat[0]; + *yp = quat[1]; + *zp = quat[2]; + *wp = quat[3]; +} + +GOAT3DAPI long goat3d_get_track_timeline(const struct goat3d_track *trk, long *tstart, long *tend) +{ + int i, j, num; + enum goat3d_track_type basetype; + struct anm_keyframe *key; + anm_time_t start = ANM_TIME_MAX; + anm_time_t end = ANM_TIME_MIN; + + basetype = BASETYPE(trk->type); + num = key_val_sz[basetype]; + + for(i=0; itrk[i].count; j++) { + key = anm_get_keyframe(trk->trk + i, j); + if(key->time < start) start = key->time; + if(key->time > end) end = key->time; + } + } + + if(end < start) { + return -1; + } + *tstart = ANM_TM2MSEC(start); + *tend = ANM_TM2MSEC(end); + return *tend - *tstart; +} + +/* animation */ +GOAT3DAPI int goat3d_add_anim(struct goat3d *g, struct goat3d_anim *anim) +{ + struct goat3d_anim **arr; + if(!(arr = dynarr_push(g->anims, &anim))) { + return -1; + } + g->anims = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_anim_count(const struct goat3d *g) +{ + return dynarr_size(g->anims); +} + +GOAT3DAPI struct goat3d_anim *goat3d_get_anim(const struct goat3d *g, int idx) +{ + return g->anims[idx]; +} + +GOAT3DAPI struct goat3d_anim *goat3d_get_anim_by_name(const struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->anims); + for(i=0; ianims[i]->name, name) == 0) { + return g->anims[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_anim *goat3d_create_anim(void) +{ + struct goat3d_anim *anim; + + if(!(anim = malloc(sizeof *anim))) { + return 0; + } + if(g3dimpl_anim_init(anim) == -1) { + free(anim); + return 0; + } + return anim; +} + +GOAT3DAPI void goat3d_destroy_anim(struct goat3d_anim *anim) +{ + g3dimpl_anim_destroy(anim); + free(anim); +} + +GOAT3DAPI int goat3d_set_anim_name(struct goat3d_anim *anim, const char *name) +{ + SETNAME(anim->name, name); +} + +GOAT3DAPI const char *goat3d_get_anim_name(const struct goat3d_anim *anim) +{ + return anim->name; +} + +GOAT3DAPI int goat3d_add_anim_track(struct goat3d_anim *anim, struct goat3d_track *trk) +{ + struct goat3d_track **tmptrk; + + if(!(tmptrk = dynarr_push(anim->tracks, &trk))) { + return -1; + } + anim->tracks = tmptrk; + return 0; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track(const struct goat3d_anim *anim, int idx) +{ + return anim->tracks[idx]; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_name(const struct goat3d_anim *anim, const char *name) +{ + int i, num = dynarr_size(anim->tracks); + for(i=0; itracks[i]->name, name) == 0) { + return anim->tracks[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_type(const struct goat3d_anim *anim, enum goat3d_track_type type) +{ + int i, num = dynarr_size(anim->tracks); + for(i=0; itracks[i]->type == type) { + return anim->tracks[i]; + } + } + return 0; +} + +GOAT3DAPI int goat3d_get_anim_track_count(const struct goat3d_anim *anim) +{ + return dynarr_size(anim->tracks); +} + +GOAT3DAPI long goat3d_get_anim_timeline(const struct goat3d_anim *anim, long *tstart, long *tend) +{ + int i, num = dynarr_size(anim->tracks); + long start, end, trkstart, trkend; + + start = LONG_MAX; + end = LONG_MIN; + + for(i=0; itracks[i], &trkstart, &trkend) != -1) { + if(trkstart < start) start = trkstart; + if(trkend > end) end = trkend; + } + } + + if(end < start) { + return -1; + } + + *tstart = start; + *tend = end; + return end - start; +} + + + + +static long read_file(void *buf, size_t bytes, void *uptr) +{ + return (long)fread(buf, 1, bytes, (FILE*)uptr); +} + +static long write_file(const void *buf, size_t bytes, void *uptr) +{ + return (long)fwrite(buf, 1, bytes, (FILE*)uptr); +} + +static long seek_file(long offs, int whence, void *uptr) +{ + if(fseek((FILE*)uptr, offs, whence) == -1) { + return -1; + } + return ftell((FILE*)uptr); +} + +static char *clean_filename(char *str) +{ + char *last_slash, *ptr; + + if(!(last_slash = strrchr(str, '/'))) { + last_slash = strrchr(str, '\\'); + } + if(last_slash) { + str = last_slash + 1; + } + + ptr = str; + while(*ptr) { + char c = tolower(*ptr); + *ptr++ = c; + } + *ptr = 0; + return str; +} diff --git a/libs/goat3d/src/log.c b/libs/goat3d/src/log.c new file mode 100644 index 0000000..f41f28c --- /dev/null +++ b/libs/goat3d/src/log.c @@ -0,0 +1,36 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include "log.h" + +int goat3d_log_level = 256; + +void goat3d_logmsg(int prio, const char *fmt, ...) +{ + va_list ap; + + if(goat3d_log_level < prio) { + return; + } + + fprintf(stderr, "goat3d: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} diff --git a/libs/goat3d/src/log.h b/libs/goat3d/src/log.h new file mode 100644 index 0000000..19307a0 --- /dev/null +++ b/libs/goat3d/src/log.h @@ -0,0 +1,25 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#ifndef LOG_H_ +#define LOG_H_ + +enum { LOG_ERROR, LOG_WARNING, LOG_INFO }; + +void goat3d_logmsg(int prio, const char *fmt, ...); + +#endif /* LOG_H_ */ diff --git a/libs/goat3d/src/read.c b/libs/goat3d/src/read.c new file mode 100644 index 0000000..368ae75 --- /dev/null +++ b/libs/goat3d/src/read.c @@ -0,0 +1,849 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include "treestor.h" +#include "goat3d.h" +#include "g3dscn.h" +#include "log.h" +#include "dynarr.h" +#include "track.h" + +#if defined(__WATCOMC__) || defined(_WIN32) || defined(__DJGPP__) +#include +#else +#include +#endif + + +struct key { + long tm; + cgm_vec4 val; +}; + +static struct goat3d_material *read_material(struct goat3d *g, struct ts_node *tsmtl); +static char *read_material_attrib(struct material_attrib *attr, struct ts_node *tsmattr); +static struct goat3d_mesh *read_mesh(struct goat3d *g, struct ts_node *tsmesh); +static void *read_veclist(void *arr, int dim, const char *nodename, const char *attrname, struct ts_node *tsnode); +static void *read_intlist(void *arr, int dim, const char *nodename, const char *attrname, struct ts_node *tsnode); +static void *read_bonelist(struct goat3d *g, struct goat3d_node **arr, struct ts_node *tsnode); +static int read_node(struct goat3d *g, struct goat3d_node *node, struct ts_node *tsnode); +static int read_anim(struct goat3d *g, struct ts_node *tsanim); +static struct goat3d_track *read_track(struct goat3d *g, struct ts_node *tstrk); + +GOAT3DAPI void *goat3d_b64decode(const char *str, void *buf, int *bufsz); +#define b64decode goat3d_b64decode + + +int g3dimpl_scnload(struct goat3d *g, struct goat3d_io *io) +{ + int idx; + struct ts_io tsio; + struct ts_node *tsroot, *c; + const char *str; + + tsio.data = io->cls; + tsio.read = io->read; + tsio.write = io->write; + + if(!(tsroot = ts_load_io(&tsio))) { + goat3d_logmsg(LOG_ERROR, "failed to load scene\n"); + return -1; + } + if(strcmp(tsroot->name, "scene") != 0) { + goat3d_logmsg(LOG_ERROR, "invalid scene file, root node is not \"scene\"\n"); + ts_free_tree(tsroot); + return -1; + } + + /* read all materials */ + c = tsroot->child_list; + while(c) { + if(strcmp(c->name, "mtl") == 0) { + struct goat3d_material *mtl = read_material(g, c); + if(mtl) { + goat3d_add_mtl(g, mtl); + } + } + c = c->next; + } + + /* create placeholder nodes, only populating the name field, so that bone + * references in meshes can be resolved. We'll read the rest of the node + * info, including their mesh/light/camera references at the end. + */ + c = tsroot->child_list; + while(c) { + if(strcmp(c->name, "node") == 0) { + struct goat3d_node *node; + + if(!(node = goat3d_create_node())) { + goat3d_logmsg(LOG_ERROR, "failed to allocate node\n"); + c = c->next; + continue; + } + if((str = ts_get_attr_str(c, "name", 0))) { + goat3d_set_node_name(node, str); + } + goat3d_add_node(g, node); + } + c = c->next; + } + + /* read all meshes, cameras, lights, animations */ + c = tsroot->child_list; + while(c) { + if(strcmp(c->name, "mesh") == 0) { + struct goat3d_mesh *mesh = read_mesh(g, c); + if(mesh) { + goat3d_add_mesh(g, mesh); + } + } else if(strcmp(c->name, "anim") == 0) { + read_anim(g, c); + } + + c = c->next; + } + + /* now load the nodes properly */ + idx = 0; + c = tsroot->child_list; + while(c) { + if(strcmp(c->name, "node") == 0) { + struct goat3d_node *node = goat3d_get_node(g, idx++); + assert(node); + read_node(g, node, c); + } + c = c->next; + } + + ts_free_tree(tsroot); + return 0; +} + +int g3dimpl_anmload(struct goat3d *g, struct goat3d_io *io) +{ + /* + struct ts_io tsio; + tsio.data = io->cls; + tsio.read = io->read; + tsio.write = io->write; + */ + return -1; +} + +static struct goat3d_material *read_material(struct goat3d *g, struct ts_node *tsmtl) +{ + struct goat3d_material *mtl; + struct material_attrib mattr, *arr; + struct ts_node *c; + const char *str; + + if(!(mtl = malloc(sizeof *mtl)) || g3dimpl_mtl_init(mtl) == -1) { + free(mtl); + goat3d_logmsg(LOG_ERROR, "read_material: failed to allocate material\n"); + return 0; + } + + if(!(str = ts_get_attr_str(tsmtl, "name", 0)) || !*str) { + /* XXX wait, we can refer to materials by index, why is the name important? */ + goat3d_logmsg(LOG_WARNING, "read_material: ignoring material without a name\n"); + g3dimpl_mtl_destroy(mtl); + return 0; + } + goat3d_set_mtl_name(mtl, str); + + /* read all material attributes */ + c = tsmtl->child_list; + while(c) { + if(strcmp(c->name, "attr") == 0) { + if(read_material_attrib(&mattr, c)) { + if(!(arr = dynarr_push(mtl->attrib, &mattr))) { + goat3d_logmsg(LOG_ERROR, "read_material: failed to resize material attribute array\n"); + g3dimpl_mtl_destroy(mtl); + return 0; + } + mtl->attrib = arr; + } + } + c = c->next; + } + + /*if(dynarr_empty(mtl->attrib)) { + goat3d_logmsg(LOG_WARNING, "read_material: ignoring empty material: %s\n", mtl->name); + g3dimpl_mtl_destroy(mtl); + return 0; + }*/ + return mtl; +} + +static char *read_material_attrib(struct material_attrib *attr, struct ts_node *tsnode) +{ + int i; + struct ts_attr *tsattr; + const char *name, *map; + + memset(attr, 0, sizeof *attr); + + if((tsattr = ts_get_attr(tsnode, "val"))) { + attr->value.w = 1.0f; /* default W to 1 if we get less than a float4 */ + + switch(tsattr->val.type) { + case TS_NUMBER: + attr->value.x = tsattr->val.fnum; + break; + case TS_VECTOR: + assert(tsattr->val.vec_size <= 4); + for(i=0; ival.vec_size; i++) { + (&attr->value.x)[i] = tsattr->val.vec[i]; + } + break; + default: /* no valid val attribute found */ + return 0; + } + } + + if(!(name = ts_get_attr_str(tsnode, "name", 0)) || !*name) { + return 0; + } + if(!(attr->name = malloc(strlen(name) + 1))) { + goat3d_logmsg(LOG_ERROR, "read_material_attrib: failed to allocate name\n"); + return 0; + } + strcpy(attr->name, name); + + if((map = ts_get_attr_str(tsnode, "map", 0)) && *map) { + if(!(attr->map = malloc(strlen(map) + 1))) { + goat3d_logmsg(LOG_ERROR, "read_material_attrib: failed to allocate map name\n"); + free(attr->name); + return 0; + } + strcpy(attr->map, map); + } + return attr->name; +} + +static struct goat3d_mesh *read_mesh(struct goat3d *g, struct ts_node *tsmesh) +{ + struct goat3d_mesh *mesh; + struct goat3d_material *mtl; + struct ts_node *c; + const char *str; + int num; + void *tmp; + + if(!(mesh = malloc(sizeof *mesh)) || g3dimpl_obj_init((struct object*)mesh, OBJTYPE_MESH)) { + goat3d_logmsg(LOG_ERROR, "read_mesh: failed to allocate mesh\n"); + goto fail; + } + + if((str = ts_get_attr_str(tsmesh, "name", 0))) { + goat3d_set_mesh_name(mesh, str); + } + + /* material reference */ + if((num = ts_get_attr_num(tsmesh, "material", -1)) >= 0) { + if((mtl = goat3d_get_mtl(g, num))) { + goat3d_set_mesh_mtl(mesh, mtl); + } else { + goat3d_logmsg(LOG_WARNING, "read_mesh: mesh %s refers to invalid material: %d\n", + mesh->name, num); + } + } else if((str = ts_get_attr_str(tsmesh, "material", 0))) { + if((mtl = goat3d_get_mtl_by_name(g, str))) { + goat3d_set_mesh_mtl(mesh, mtl); + } else { + goat3d_logmsg(LOG_WARNING, "read_mesh: mesh %s refers to invalid material: %s\n", + mesh->name, str); + } + } + + /* external mesh data */ + if((str = ts_get_attr_str(tsmesh, "file", 0))) { + const char *fname = str; + char *pathbuf; + + if(g->search_path) { + pathbuf = alloca(strlen(str) + strlen(g->search_path) + 2); + sprintf(pathbuf, "%s/%s", g->search_path, str); + fname = pathbuf; + } + + if(g3dimpl_loadmesh(mesh, fname) == -1) { + goat3d_logmsg(LOG_ERROR, "read_mesh: failed to load external mesh: %s\n", fname); + goto fail; + } + + /* done loading, we can't have both an external mesh AND internal data, + * if there's anything else hanging under this tsnode, ignore it. + */ + if(tsmesh->child_list) { + goat3d_logmsg(LOG_WARNING, "read_mesh: external mesh node also has unexpected children; ignoring.\n"); + } + return mesh; + } + + c = tsmesh->child_list; + while(c) { + if(strcmp(c->name, "vertex_list") == 0) { + if(!(tmp = read_veclist(mesh->vertices, 3, "vertex", "pos", c))) { + goat3d_logmsg(LOG_ERROR, "read_mesh: failed to read vertex array for mesh %s\n", + mesh->name); + goto fail; + } + mesh->vertices = tmp; + + } else if(strcmp(c->name, "normal_list") == 0) { + if(!(tmp = read_veclist(mesh->normals, 3, "normal", "dir", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read normals array for mesh %s\n", + mesh->name); + } else { + mesh->normals = tmp; + } + + } else if(strcmp(c->name, "tangent_list") == 0) { + if(!(tmp = read_veclist(mesh->tangents, 3, "tangent", "dir", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read tangents array for mesh %s\n", + mesh->name); + } else { + mesh->tangents = tmp; + } + + } else if(strcmp(c->name, "texcoord_list") == 0) { + if(!(tmp = read_veclist(mesh->texcoords, 2, "texcoord", "uv", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read texcoord array for mesh %s\n", + mesh->name); + } else { + mesh->texcoords = tmp; + } + + } else if(strcmp(c->name, "skinweight_list") == 0) { + if(!(tmp = read_veclist(mesh->skin_weights, 4, "skinweight", "weights", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read skin weights array for mesh %s\n", + mesh->name); + } else { + mesh->skin_weights = tmp; + } + + } else if(strcmp(c->name, "skinmatrix_list") == 0) { + if(!(tmp = read_intlist(mesh->skin_matrices, 4, "skinmatrix", "idx", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read skin matrix index array for mesh %s\n", + mesh->name); + } else { + mesh->skin_matrices = tmp; + } + + } else if(strcmp(c->name, "color_list") == 0) { + if(!(tmp = read_veclist(mesh->colors, 4, "color", "color", c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read color array for mesh %s\n", + mesh->name); + } else { + mesh->colors = tmp; + } + + } else if(strcmp(c->name, "bone_list") == 0) { + if(!(tmp = read_bonelist(g, mesh->bones, c))) { + goat3d_logmsg(LOG_WARNING, "read_mesh: failed to read bones array for mesh %s\n", + mesh->name); + } else { + mesh->bones = tmp; + } + + } else if(strcmp(c->name, "face_list") == 0) { + if(!(tmp = read_intlist(mesh->faces, 3, "face", "idx", c))) { + goat3d_logmsg(LOG_ERROR, "read_mesh: failed to read faces array for mesh %s\n", + mesh->name); + goto fail; + } + mesh->faces = tmp; + } + c = c->next; + } + return mesh; + +fail: + g3dimpl_obj_destroy((struct object*)mesh); + return 0; +} + +static int calc_b64_size(const char *s) +{ + int len = strlen(s); + const char *end = s + len; + while(end > s && *--end == '=') len--; + return len * 3 / 4; +} + +static void *read_veclist(void *arr, int dim, const char *nodename, const char *attrname, struct ts_node *tslist) +{ + int i, size, bufsz; + struct ts_node *c; + struct ts_attr *attr; + float vec[4]; + const char *str; + + arr = dynarr_clear(arr); + assert(arr); + + if((size = ts_get_attr_int(tslist, "list_size", -1)) <= 0) { + goat3d_logmsg(LOG_WARNING, "read_veclist: list_size attribute missing or invalid\n"); + size = -1; + } + + if((str = ts_get_attr_str(tslist, "base64", 0))) { + if(size == -1) size = calc_b64_size(str); + if(!(arr = dynarr_resize(arr, size))) { + goat3d_logmsg(LOG_ERROR, "read_veclist: failed to resize %s array\n", + nodename); + return 0; + } + + bufsz = size * dim * sizeof(float); + b64decode(str, arr, &bufsz); + } + + c = tslist->child_list; + while(c) { + if(strcmp(c->name, nodename) != 0) { + c = c->next; + continue; + } + + if((attr = ts_get_attr(c, attrname)) && attr->val.type == TS_VECTOR) { + for(i=0; i= attr->val.vec_size) { + vec[i] = 0; + } else { + vec[i] = attr->val.vec[i]; + } + } + + if(!(arr = dynarr_push(arr, vec))) { + goat3d_logmsg(LOG_ERROR, "read_veclist: failed to resize %s array\n", + nodename); + return 0; + } + } + c = c->next; + } + + if(size > 0 && dynarr_size(arr) != size) { + goat3d_logmsg(LOG_WARNING, "read_veclist: expected %d items, read %d\n", size, dynarr_size(arr)); + } + return arr; +} + +static void *read_intlist(void *arr, int dim, const char *nodename, const char *attrname, struct ts_node *tslist) +{ + int i, size, bufsz; + struct ts_node *c; + struct ts_attr *attr; + int ivec[4]; + const char *str; + + arr = dynarr_clear(arr); + assert(arr); + + if((size = ts_get_attr_int(tslist, "list_size", -1)) <= 0) { + goat3d_logmsg(LOG_WARNING, "read_intlist: list_size attribute missing or invalid\n"); + size = -1; + } + + if((str = ts_get_attr_str(tslist, "base64", 0))) { + if(size == -1) size = calc_b64_size(str); + if(!(arr = dynarr_resize(arr, size))) { + goat3d_logmsg(LOG_ERROR, "read_intlist: failed to resize %s array\n", + nodename); + return 0; + } + + bufsz = size * dim * sizeof(int); + b64decode(str, arr, &bufsz); + } + + c = tslist->child_list; + while(c) { + if(strcmp(c->name, nodename) != 0) { + c = c->next; + continue; + } + + if((attr = ts_get_attr(c, attrname)) && attr->val.type == TS_VECTOR) { + for(i=0; i= attr->val.vec_size) { + ivec[i] = 0; + } else { + ivec[i] = attr->val.vec[i]; + } + } + + if(!(arr = dynarr_push(arr, ivec))) { + goat3d_logmsg(LOG_ERROR, "read_intlist: failed to resize %s array\n", + nodename); + return 0; + } + } + c = c->next; + } + + if(size > 0 && dynarr_size(arr) != size) { + goat3d_logmsg(LOG_WARNING, "read_intlist: expected %d items, read %d\n", size, dynarr_size(arr)); + } + return arr; +} + +static void *read_bonelist(struct goat3d *g, struct goat3d_node **arr, struct ts_node *tslist) +{ + int size, idx; + struct ts_node *c; + struct goat3d_node *bone; + const char *str; + + arr = dynarr_clear(arr); + assert(arr); + + if((size = ts_get_attr_int(tslist, "list_size", -1)) <= 0) { + goat3d_logmsg(LOG_WARNING, "read_bonelist: list_size attribute missing or invalid\n"); + size = -1; + } + + /* TODO base64 data support */ + + c = tslist->child_list; + while(c) { + if(strcmp(c->name, "bone") != 0) { + c = c->next; + continue; + } + + bone = 0; + + if((idx = ts_get_attr_int(c, "bone", -1)) >= 0) { + if(!(bone = goat3d_get_node(g, idx))) { + goat3d_logmsg(LOG_ERROR, "read_bonelist: reference to invalid bone: %d\n", idx); + return 0; + } + + } else if((str = ts_get_attr_str(c, "bone", 0))) { + if(!(bone = goat3d_get_node_by_name(g, str))) { + goat3d_logmsg(LOG_ERROR, "read_bonelist: reference to invalid bone: %s\n", str); + return 0; + } + } + + if(bone && !(arr = dynarr_push(arr, &bone))) { + goat3d_logmsg(LOG_ERROR, "read_bonelist: failed to resize bone array\n"); + return 0; + } + c = c->next; + } + + if(size > 0 && dynarr_size(arr) != size) { + goat3d_logmsg(LOG_WARNING, "read_bonelist: expected %d items, read %d\n", size, dynarr_size(arr)); + } + return arr; +} + +#define GETREF(ptr, typestr, getname) \ + do { \ + ptr = 0; \ + if((idx = ts_get_attr_int(tsnode, typestr, -1)) >= 0) { \ + if(!(ptr = goat3d_get_##getname(g, idx))) { \ + goat3d_logmsg(LOG_ERROR, "read_node: ignoring reference to invalid " typestr ": %d\n", idx); \ + } \ + } else if((str = ts_get_attr_str(tsnode, typestr, 0))) { \ + if(!(ptr = goat3d_get_##getname##_by_name(g, str))) { \ + goat3d_logmsg(LOG_ERROR, "read_node: ignoring reference to invalid " typestr ": %s\n", str); \ + } \ + } \ + } while(0) + +static int read_node(struct goat3d *g, struct goat3d_node *node, struct ts_node *tsnode) +{ + int idx; + const char *str; + struct goat3d_node *parent; + float *vec; + + GETREF(parent, "parent", node); + if(parent) { + goat3d_add_node_child(parent, node); + } + + node->type = GOAT3D_NODE_MESH; + GETREF(node->obj, "mesh", mesh); + if(!node->obj) { + node->type = GOAT3D_NODE_LIGHT; + GETREF(node->obj, "light", light); + } + if(!node->obj) { + node->type = GOAT3D_NODE_CAMERA; + GETREF(node->obj, "camera", camera); + } + if(!node->obj) { + node->type = GOAT3D_NODE_NULL; + } + + if((vec = ts_get_attr_vec(tsnode, "pos", 0))) { + goat3d_set_node_position(node, vec[0], vec[1], vec[2]); + } + if((vec = ts_get_attr_vec(tsnode, "rot", 0))) { + goat3d_set_node_rotation(node, vec[0], vec[1], vec[2], vec[3]); + } + if((vec = ts_get_attr_vec(tsnode, "scale", 0))) { + goat3d_set_node_scaling(node, vec[0], vec[1], vec[2]); + } + if((vec = ts_get_attr_vec(tsnode, "pivot", 0))) { + goat3d_set_node_pivot(node, vec[0], vec[1], vec[2]); + } + + return 0; +} + +static int read_anim(struct goat3d *g, struct ts_node *tsanim) +{ + struct ts_node *c; + const char *str; + struct goat3d_anim *anim; + struct goat3d_track *trk; + + if(!(str = ts_get_attr_str(tsanim, "name", 0))) { + goat3d_logmsg(LOG_WARNING, "read_anim: ignoring animation without a name\n"); + return -1; + } + + if(!(anim = goat3d_create_anim())) { + goat3d_logmsg(LOG_ERROR, "read_anim: failed to initialize animation: %s\n", str); + return -1; + } + goat3d_set_anim_name(anim, str); + + c = tsanim->child_list; + while(c) { + if(strcmp(c->name, "track") == 0) { + if(!(trk = read_track(g, c))) { + c = c->next; + continue; + } + goat3d_add_anim_track(anim, trk); + } + c = c->next; + } + + goat3d_add_anim(g, anim); + return 0; +} + +static int parsetype(const char *str) +{ + if(strcmp(str, "pos") == 0) return GOAT3D_TRACK_POS; + if(strcmp(str, "rot") == 0) return GOAT3D_TRACK_ROT; + if(strcmp(str, "scale") == 0) return GOAT3D_TRACK_SCALE; + if(strcmp(str, "val") == 0) return GOAT3D_TRACK_VAL; + if(strcmp(str, "vec3") == 0) return GOAT3D_TRACK_VEC3; + if(strcmp(str, "vec4") == 0) return GOAT3D_TRACK_VEC4; + if(strcmp(str, "quat") == 0) return GOAT3D_TRACK_QUAT; + return -1; +} + +static int parseinterp(const char *str) +{ + if(strcmp(str, "step") == 0) return GOAT3D_INTERP_STEP; + if(strcmp(str, "linear") == 0) return GOAT3D_INTERP_LINEAR; + if(strcmp(str, "cubic") == 0) return GOAT3D_INTERP_CUBIC; + return -1; +} + +static int parseextrap(const char *str) +{ + if(strcmp(str, "extend") == 0) return GOAT3D_EXTRAP_EXTEND; + if(strcmp(str, "clamp") == 0) return GOAT3D_EXTRAP_CLAMP; + if(strcmp(str, "repeat") == 0) return GOAT3D_EXTRAP_REPEAT; + if(strcmp(str, "pingpong") == 0) return GOAT3D_EXTRAP_PINGPONG; + return -1; +} + +static struct goat3d_track *read_track(struct goat3d *g, struct ts_node *tstrk) +{ + int i, idx; + const char *str; + struct goat3d_track *trk; + struct goat3d_node *node; + struct goat3d_key key; + int type, in, ex; + struct ts_node *c; + struct ts_attr *tsattr; + + if(!(str = ts_get_attr_str(tstrk, "type", 0)) || (type = parsetype(str)) == -1) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring track with missing or invalid type attribute\n"); + return 0; + } + + if((idx = ts_get_attr_int(tstrk, "node", -1)) >= 0) { + if(!(node = goat3d_get_node(g, idx))) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring track with invalid node reference (%d)\n", idx); + return 0; + } + } else if((str = ts_get_attr_str(tstrk, "node", 0))) { + if(!(node = goat3d_get_node_by_name(g, str))) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring track with invalid node reference (%s)\n", str); + return 0; + } + } else { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring track with missing node reference\n"); + return 0; + } + + if(!(trk = goat3d_create_track())) { + goat3d_logmsg(LOG_ERROR, "read_track: failed to create new keyframe track\n"); + return 0; + } + goat3d_set_track_node(trk, node); + goat3d_set_track_type(trk, type); + + if((str = ts_get_attr_str(tstrk, "name", 0))) { + goat3d_set_track_name(trk, str); + } + + if((str = ts_get_attr_str(tstrk, "interp", 0))) { + if((in = parseinterp(str)) == -1) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring invalid interpolation mode: %s\n", str); + } else { + goat3d_set_track_interp(trk, in); + } + } + if((str = ts_get_attr_str(tstrk, "extrap", 0))) { + if((ex = parseextrap(str)) == -1) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring invalid extrapolation mode: %s\n", str); + } else { + goat3d_set_track_extrap(trk, ex); + } + } + + c = tstrk->child_list; + while(c) { + if(strcmp(c->name, "key") == 0) { + if((key.tm = ts_get_attr_int(c, "time", INT_MIN)) == INT_MIN) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring keyframe with missing or invalid time (%s)\n", + ts_get_attr_str(c, "time", "")); + c = c->next; + continue; + } + if(!(tsattr = ts_get_attr(c, "value")) || (tsattr->val.type != TS_NUMBER && + tsattr->val.type != TS_VECTOR)) { + goat3d_logmsg(LOG_WARNING, "read_track: ignoring keyframe with missing or invalid value (%s)\n", + ts_get_attr_str(c, "value", "")); + c = c->next; + continue; + } + + if(tsattr->val.type == TS_NUMBER) { + key.val[0] = tsattr->val.fnum; + } else { + for(i=0; i<4; i++) { + if(i < tsattr->val.vec_size) { + key.val[i] = tsattr->val.vec[i]; + } else { + key.val[i] = 0; + } + } + } + goat3d_set_track_key(trk, &key); + } + c = c->next; + } + + /* force lazy re-sorting of keyframes if necessary */ + goat3d_get_track_key(trk, 0, &key); + + return trk; +} + +static int b64bits(int c) +{ + if(c >= 'A' && c <= 'Z') { + return c - 'A'; + } + if(c >= 'a' && c <= 'z') { + return c - 'a' + 26; + } + if(c >= '0' && c <= '9') { + return c - '0' + 52; + } + if(c == '+') return 62; + if(c == '/') return 63; + + return -1; +} + +GOAT3DAPI void *goat3d_b64decode(const char *str, void *buf, int *bufsz) +{ + unsigned char *dest, *end; + unsigned char acc; + int bits, sz; + unsigned int gidx; + + if(buf) { + sz = *bufsz; + } else { + sz = calc_b64_size(str); + if(!(buf = malloc(sz))) { + return 0; + } + if(bufsz) *bufsz = sz; + } + dest = buf; + end = (unsigned char*)buf + sz; + + sz = 0; + gidx = 0; + acc = 0; + while(*str) { + if((bits = b64bits(*str++)) == -1) { + continue; + } + + switch(gidx++ & 3) { + case 0: + acc = bits << 2; + break; + case 1: + if(dest < end) *dest = acc | (bits >> 4); + dest++; + acc = bits << 4; + break; + case 2: + if(dest < end) *dest = acc | (bits >> 2); + dest++; + acc = bits << 6; + break; + case 3: + if(dest < end) *dest = acc | bits; + dest++; + default: + break; + } + } + + if(gidx & 3) { + if(dest < end) *dest = acc; + dest++; + } + + if(bufsz) *bufsz = dest - (unsigned char*)buf; + return buf; +} diff --git a/libs/goat3d/src/track.c b/libs/goat3d/src/track.c new file mode 100644 index 0000000..90d07cc --- /dev/null +++ b/libs/goat3d/src/track.c @@ -0,0 +1,415 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "track.h" +#include "dynarr.h" + +#include "cgmath/cgmath.h" + +static int find_prev_key(const 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->keys_sorted = 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; + dest->keys_sorted = src->keys_sorted; +} + +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(const 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(const 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; +} + +static int keycmp(const void *a, const void *b) +{ + return ((struct anm_keyframe*)a)->time - ((struct anm_keyframe*)b)->time; +} + +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 if necessary */ + void *tmp; + if(!(tmp = dynarr_push(track->keys, key))) { + return -1; + } + track->keys = tmp; + idx = track->count++; + if(idx > 0 && track->keys[idx - 1].time > key->time) { + /* key shold not go to the end, mark for re-sorting */ + track->keys_sorted = 0; + } + } + return 0; +} + +#define lazysort_keys(track) \ + if(track->count > 1 && !track->keys_sorted) { \ + qsort(track->keys, track->count, sizeof *track->keys, keycmp); \ + ((struct anm_track*)track)->keys_sorted = 1; \ + } + +struct anm_keyframe *anm_get_keyframe(const struct anm_track *track, int idx) +{ + if(idx < 0 || idx >= track->count) { + return 0; + } + lazysort_keys(track); + return track->keys + idx; +} + +int anm_get_key_interval(const struct anm_track *track, anm_time_t tm) +{ + int last; + + lazysort_keys(track); + + 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(const 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(const 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; + } + lazysort_keys(track); + + 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); +} + + +void anm_get_quat(const struct anm_track *xtrk, const struct anm_track *ytrk, + const struct anm_track *ztrk, const struct anm_track *wtrk, anm_time_t tm, float *qres) +{ + int idx0, idx1, last_idx; + anm_time_t tstart, tend; + float t, dt; + cgm_quat q1, q2; + + if(!xtrk->count) { + qres[0] = xtrk->def_val; + qres[1] = ytrk->def_val; + qres[2] = ztrk->def_val; + qres[3] = wtrk->def_val; + return; + } + + lazysort_keys(xtrk); + lazysort_keys(ytrk); + lazysort_keys(ztrk); + lazysort_keys(wtrk); + + last_idx = xtrk->count - 1; + + tstart = xtrk->keys[0].time; + tend = xtrk->keys[last_idx].time; + + if(tstart == tend) { + qres[0] = xtrk->keys[0].val; + qres[1] = ytrk->keys[0].val; + qres[2] = ztrk->keys[0].val; + qres[3] = wtrk->keys[0].val; + return; + } + + tm = anm_remap_time(xtrk, tm, tstart, tend); + + idx0 = anm_get_key_interval(xtrk, tm); + assert(idx0 >= 0 && idx0 < xtrk->count); + idx1 = idx0 + 1; + + if(idx0 == last_idx) { + qres[0] = xtrk->keys[idx0].val; + qres[1] = ytrk->keys[idx0].val; + qres[2] = ztrk->keys[idx0].val; + qres[3] = wtrk->keys[idx0].val; + return; + } + + dt = (float)(xtrk->keys[idx1].time - xtrk->keys[idx0].time); + t = (float)(tm - xtrk->keys[idx0].time) / dt; + + q1.x = xtrk->keys[idx0].val; + q1.y = ytrk->keys[idx0].val; + q1.z = ztrk->keys[idx0].val; + q1.w = wtrk->keys[idx0].val; + + q2.x = xtrk->keys[idx1].val; + q2.y = ytrk->keys[idx1].val; + q2.z = ztrk->keys[idx1].val; + q2.w = wtrk->keys[idx1].val; + + cgm_qslerp((cgm_quat*)qres, &q1, &q2, 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 : 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 --git a/libs/goat3d/src/track.h b/libs/goat3d/src/track.h new file mode 100644 index 0000000..17f044b --- /dev/null +++ b/libs/goat3d/src/track.h @@ -0,0 +1,131 @@ +/* +libanim - hierarchical keyframe animation library +Copyright (C) 2012-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published +by the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +/* 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_MIN LONG_MIN +#define ANM_TIME_MAX LONG_MAX +#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; + + int keys_sorted; +}; + +#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); + +int anm_set_track_name(struct anm_track *track, const char *name); +const char *anm_get_track_name(const 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(const 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(const 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(const 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(const struct anm_track *track, anm_time_t tm); + +/* evaluates a set of 4 tracks treated as a quaternion, to perform slerp instead + * of linear interpolation. Result is returned through the last argument, which + * is expected to point to an array of 4 floats (x,y,z,w) + */ +void anm_get_quat(const struct anm_track *xtrk, const struct anm_track *ytrk, + const struct anm_track *ztrk, const struct anm_track *wtrk, anm_time_t tm, float *qres); + +#ifdef __cplusplus +} +#endif + + +#endif /* LIBANIM_TRACK_H_ */ diff --git a/libs/goat3d/src/write.c b/libs/goat3d/src/write.c new file mode 100644 index 0000000..b144be5 --- /dev/null +++ b/libs/goat3d/src/write.c @@ -0,0 +1,736 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include "g3dscn.h" +#include "log.h" +#include "dynarr.h" +#include "treestor.h" + +/* type passed to namegen */ +enum { MTL, MESH, LIGHT, CAMERA, NODE, ANIM, TRACK }; + +static struct ts_node *create_mtltree(struct goat3d *g, const struct goat3d_material *mtl); +static struct ts_node *create_meshtree(struct goat3d *g, const struct goat3d_mesh *mesh); +static struct ts_node *create_lighttree(struct goat3d *g, const struct goat3d_light *light); +static struct ts_node *create_camtree(struct goat3d *g, const struct goat3d_camera *cam); +static struct ts_node *create_nodetree(struct goat3d *g, const struct goat3d_node *node); +static struct ts_node *create_animtree(struct goat3d *g, const struct goat3d_anim *anim); +static struct ts_node *create_tracktree(struct goat3d *g, const struct goat3d_track *trk); + +static void init_namegen(struct goat3d *g); +static const char *namegen(struct goat3d *g, const char *name, int type); + +GOAT3DAPI char *goat3d_b64encode(const void *data, int size, char *buf, int *bufsz); +#define b64encode goat3d_b64encode + +#define create_tsnode(n, p, nstr) \ + do { \ + int len = strlen(nstr); \ + if(!((n) = ts_alloc_node())) { \ + goat3d_logmsg(LOG_ERROR, "failed to create treestore node\n"); \ + goto err; \ + } \ + if(!((n)->name = malloc(len + 1))) { \ + goat3d_logmsg(LOG_ERROR, "failed to allocate node name string\n"); \ + ts_free_node(n); \ + goto err; \ + } \ + memcpy((n)->name, (nstr), len + 1); \ + if(p) { \ + ts_add_child((p), (n)); \ + } \ + } while(0) + +#define create_tsattr(a, n, nstr, atype) \ + do { \ + if(!((a) = ts_alloc_attr())) { \ + goat3d_logmsg(LOG_ERROR, "failed to create treestore attribute\n"); \ + goto err; \ + } \ + if(ts_set_attr_name(a, nstr) == -1) { \ + goat3d_logmsg(LOG_ERROR, "failed to allocate attrib name string\n"); \ + ts_free_attr(a); \ + goto err; \ + } \ + (a)->val.type = (atype); \ + if(n) { \ + ts_add_attr((n), (a)); \ + } \ + } while(0) + + + +int g3dimpl_scnsave(const struct goat3d *g, struct goat3d_io *io) +{ + int i, num; + struct ts_io tsio; + struct ts_node *tsroot = 0, *tsn, *tsenv; + struct ts_attr *tsa; + + tsio.data = io->cls; + tsio.read = io->read; + tsio.write = io->write; + + init_namegen((struct goat3d*)g); + + create_tsnode(tsroot, 0, "scene"); + + /* environment */ + create_tsnode(tsenv, tsroot, "env"); + create_tsattr(tsa, tsenv, "ambient", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, g->ambient.x, g->ambient.y, g->ambient.z); + /* TODO: fog */ + + num = dynarr_size(g->materials); + for(i=0; imaterials[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + num = dynarr_size(g->meshes); + for(i=0; imeshes[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + num = dynarr_size(g->lights); + for(i=0; ilights[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + num = dynarr_size(g->cameras); + for(i=0; icameras[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + num = dynarr_size(g->nodes); + for(i=0; inodes[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + num = dynarr_size(g->anims); + for(i=0; ianims[i]))) { + goto err; + } + ts_add_child(tsroot, tsn); + } + + if(ts_save_io(tsroot, &tsio) == -1) { + goat3d_logmsg(LOG_ERROR, "g3dimpl_scnsave: failed\n"); + goto err; + } + return 0; + +err: + ts_free_tree(tsroot); + return -1; +} + +int g3dimpl_anmsave(const struct goat3d *g, struct goat3d_io *io) +{ + return -1; +} + +static struct ts_node *create_mtltree(struct goat3d *g, const struct goat3d_material *mtl) +{ + int i, num_attr; + struct ts_node *tsn, *tsmtl = 0; + struct ts_attr *tsa; + + create_tsnode(tsmtl, 0, "mtl"); + create_tsattr(tsa, tsmtl, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, mtl->name, MTL)) == -1) { + goto err; + } + + num_attr = dynarr_size(mtl->attrib); + for(i=0; iattrib + i; + + create_tsnode(tsn, tsmtl, "attr"); + create_tsattr(tsa, tsn, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, attr->name) == -1) { + goto err; + } + create_tsattr(tsa, tsn, "val", TS_VECTOR); + ts_set_valuefv(&tsa->val, 4, attr->value.x, attr->value.y, attr->value.z, attr->value.w); + if(attr->map) { + create_tsattr(tsa, tsn, "map", TS_STRING); + if(ts_set_value_str(&tsa->val, attr->map) == -1) { + goto err; + } + } + } + return tsmtl; + +err: + ts_free_tree(tsmtl); + return 0; +} + +static struct ts_node *create_meshtree(struct goat3d *g, const struct goat3d_mesh *mesh) +{ + int i, num; + struct ts_node *tsmesh = 0, *tslist, *tsitem; + struct ts_attr *tsa; + + create_tsnode(tsmesh, 0, "mesh"); + create_tsattr(tsa, tsmesh, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, mesh->name, MESH)) == -1) { + goto err; + } + + if(mesh->mtl) { + if(mesh->mtl->name) { + create_tsattr(tsa, tsmesh, "material", TS_STRING); + if(ts_set_value_str(&tsa->val, mesh->mtl->name) == -1) { + goto err; + } + } else { + create_tsattr(tsa, tsmesh, "material", TS_NUMBER); + ts_set_valuei(&tsa->val, mesh->mtl->idx); + } + } + + /* TODO option of saving separate mesh files */ + + if((num = dynarr_size(mesh->vertices))) { + create_tsnode(tslist, tsmesh, "vertex_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->vertices, num * 3 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; ivertices + i; + create_tsnode(tsitem, tslist, "vertex"); + create_tsattr(tsa, tsitem, "pos", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, vptr->x, vptr->y, vptr->z); + } + } + } + + if((num = dynarr_size(mesh->normals))) { + create_tsnode(tslist, tsmesh, "normal_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->normals, num * 3 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; inormals + i; + create_tsnode(tsitem, tslist, "normal"); + create_tsattr(tsa, tsitem, "dir", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, nptr->x, nptr->y, nptr->z); + } + } + } + + if((num = dynarr_size(mesh->tangents))) { + create_tsnode(tslist, tsmesh, "tangent_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->tangents, num * 3 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; itangents + i; + create_tsnode(tsitem, tslist, "tangent"); + create_tsattr(tsa, tsitem, "dir", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, tptr->x, tptr->y, tptr->z); + } + } + } + + if((num = dynarr_size(mesh->texcoords))) { + create_tsnode(tslist, tsmesh, "texcoord_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->texcoords, num * 3 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; itexcoords + i; + create_tsnode(tsitem, tslist, "texcoord"); + create_tsattr(tsa, tsitem, "uv", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, uvptr->x, uvptr->y, 0.0f); + } + } + } + + if((num = dynarr_size(mesh->skin_weights))) { + create_tsnode(tslist, tsmesh, "skinweight_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->skin_weights, num * 4 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; iskin_weights + i; + create_tsnode(tsitem, tslist, "skinweight"); + create_tsattr(tsa, tsitem, "weights", TS_VECTOR); + ts_set_valuefv(&tsa->val, 4, wptr->x, wptr->y, wptr->z, wptr->w); + } + } + } + + if((num = dynarr_size(mesh->skin_matrices))) { + create_tsnode(tslist, tsmesh, "skinmatrix_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->skin_matrices, num * 4 * sizeof(int), 0, 0))) { + goto err; + } + } else { + for(i=0; iskin_matrices + i; + create_tsnode(tsitem, tslist, "skinmatrix"); + create_tsattr(tsa, tsitem, "idx", TS_VECTOR); + ts_set_valueiv(&tsa->val, 4, iptr->x, iptr->y, iptr->z, iptr->w); + } + } + } + + if((num = dynarr_size(mesh->colors))) { + create_tsnode(tslist, tsmesh, "color_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->colors, num * 4 * sizeof(float), 0, 0))) { + goto err; + } + } else { + for(i=0; icolors + i; + create_tsnode(tsitem, tslist, "color"); + create_tsattr(tsa, tsitem, "color", TS_VECTOR); + ts_set_valuefv(&tsa->val, 4, cptr->x, cptr->y, cptr->z, cptr->w); + } + } + } + + if((num = dynarr_size(mesh->bones))) { + create_tsnode(tslist, tsmesh, "bone_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + /* TODO: base64 option */ + for(i=0; ival, mesh->bones[i]->name) == -1) { + goto err; + } + } + } + + if((num = dynarr_size(mesh->faces))) { + create_tsnode(tslist, tsmesh, "face_list"); + create_tsattr(tsa, tslist, "list_size", TS_NUMBER); + ts_set_valuei(&tsa->val, num); + + if(goat3d_getopt(g, GOAT3D_OPT_SAVEBINDATA)) { + create_tsattr(tsa, tslist, "base64", TS_STRING); + if(!(tsa->val.str = b64encode(mesh->faces, num * 3 * sizeof(int), 0, 0))) { + goto err; + } + } else { + for(i=0; ifaces + i; + create_tsnode(tsitem, tslist, "face"); + create_tsattr(tsa, tsitem, "idx", TS_VECTOR); + ts_set_valueiv(&tsa->val, 3, fptr->v[0], fptr->v[1], fptr->v[2]); + } + } + } + + return tsmesh; + +err: + ts_free_tree(tsmesh); + return 0; +} + +static struct ts_node *create_lighttree(struct goat3d *g, const struct goat3d_light *light) +{ + struct ts_node *tslight = 0; + struct ts_attr *tsa; + + create_tsnode(tslight, 0, "light"); + create_tsattr(tsa, tslight, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, light->name, LIGHT)) == -1) { + goto err; + } + + if(light->ltype != LTYPE_DIR) { + create_tsattr(tsa, tslight, "pos", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, light->pos.x, light->pos.y, light->pos.z); + } + + if(light->ltype != LTYPE_POINT) { + create_tsattr(tsa, tslight, "dir", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, light->dir.x, light->dir.y, light->dir.z); + } + + if(light->ltype == LTYPE_SPOT) { + create_tsattr(tsa, tslight, "cone_inner", TS_NUMBER); + ts_set_valuef(&tsa->val, light->inner_cone); + create_tsattr(tsa, tslight, "cone_outer", TS_NUMBER); + ts_set_valuef(&tsa->val, light->outer_cone); + } + + create_tsattr(tsa, tslight, "color", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, light->color.x, light->color.y, light->color.z); + + create_tsattr(tsa, tslight, "atten", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, light->attenuation.x, light->attenuation.y, light->attenuation.z); + + create_tsattr(tsa, tslight, "distance", TS_NUMBER); + ts_set_valuef(&tsa->val, light->max_dist); + + return tslight; + +err: + ts_free_tree(tslight); + return 0; +} + +static struct ts_node *create_camtree(struct goat3d *g, const struct goat3d_camera *cam) +{ + struct ts_node *tscam = 0; + struct ts_attr *tsa; + + create_tsnode(tscam, 0, "camera"); + create_tsattr(tsa, tscam, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, cam->name, CAMERA)) == -1) { + goto err; + } + + create_tsattr(tsa, tscam, "pos", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, cam->pos.x, cam->pos.y, cam->pos.z); + + if(cam->camtype == CAMTYPE_TARGET) { + create_tsattr(tsa, tscam, "target", TS_VECTOR); + ts_set_valuefv(&tsa->val, 3, cam->target.x, cam->target.y, cam->target.z); + } + + create_tsattr(tsa, tscam, "fov", TS_NUMBER); + ts_set_valuef(&tsa->val, cam->fov); + + create_tsattr(tsa, tscam, "nearclip", TS_NUMBER); + ts_set_valuef(&tsa->val, cam->near_clip); + + create_tsattr(tsa, tscam, "farclip", TS_NUMBER); + ts_set_valuef(&tsa->val, cam->far_clip); + + return tscam; + +err: + ts_free_tree(tscam); + return 0; +} + +static struct ts_node *create_nodetree(struct goat3d *g, const struct goat3d_node *node) +{ + struct ts_node *tsnode = 0; + struct ts_attr *tsa; + struct goat3d_node *par; + static const char *objtypestr[] = {"null", "mesh", "light", "camera"}; + float vec[4]; + float xform[16]; + + create_tsnode(tsnode, 0, "node"); + create_tsattr(tsa, tsnode, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, node->name, NODE)) == -1) { + goto err; + } + + if((par = goat3d_get_node_parent(node))) { + create_tsattr(tsa, tsnode, "parent", TS_STRING); + if(ts_set_value_str(&tsa->val, goat3d_get_node_name(par)) == -1) { + goto err; + } + } + + if(node->obj && node->type != GOAT3D_NODE_NULL) { + create_tsattr(tsa, tsnode, objtypestr[node->type], TS_STRING); + if(ts_set_value_str(&tsa->val, ((struct object*)node->obj)->name) == -1) { + goto err; + } + } + + goat3d_get_node_position(node, vec, vec + 1, vec + 2); + create_tsattr(tsa, tsnode, "pos", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 3, vec); + + goat3d_get_node_rotation(node, vec, vec + 1, vec + 2, vec + 3); + create_tsattr(tsa, tsnode, "rot", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 4, vec); + + goat3d_get_node_scaling(node, vec, vec + 1, vec + 2); + create_tsattr(tsa, tsnode, "scale", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 3, vec); + + goat3d_get_node_pivot(node, vec, vec + 1, vec + 2); + create_tsattr(tsa, tsnode, "pivot", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 3, vec); + + goat3d_get_node_matrix(node, xform); + cgm_mtranspose(xform); + create_tsattr(tsa, tsnode, "matrix0", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 4, xform); + create_tsattr(tsa, tsnode, "matrix1", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 4, xform + 4); + create_tsattr(tsa, tsnode, "matrix2", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, 4, xform + 8); + + return tsnode; + +err: + ts_free_tree(tsnode); + return 0; +} + +static struct ts_node *create_animtree(struct goat3d *g, const struct goat3d_anim *anim) +{ + int i, num_trk; + struct ts_node *tsanim, *tstrk; + struct ts_attr *tsa; + + create_tsnode(tsanim, 0, "anim"); + create_tsattr(tsa, tsanim, "name", TS_STRING); + if(ts_set_value_str(&tsa->val, namegen(g, anim->name, ANIM)) == -1) { + goto err; + } + + + num_trk = goat3d_get_anim_track_count(anim); + for(i=0; ival, namegen(g, trk->name, TRACK)) == -1) { + goto err; + } + + create_tsattr(tsa, tstrk, "type", TS_STRING); + if(ts_set_value_str(&tsa->val, g3dimpl_trktypestr(trk->type)) == -1) { + goto err; + } + basetype = trk->type & 0xff; + + create_tsattr(tsa, tstrk, "interp", TS_STRING); + if(ts_set_value_str(&tsa->val, instr[trk->trk[0].interp]) == -1) { + goto err; + } + create_tsattr(tsa, tstrk, "extrap", TS_STRING); + if(ts_set_value_str(&tsa->val, exstr[trk->trk[0].extrap]) == -1) { + goto err; + } + + if(trk->node) { + create_tsattr(tsa, tstrk, "node", TS_STRING); + if(ts_set_value_str(&tsa->val, trk->node->name) == -1) { + goto err; + } + } + + num_keys = goat3d_get_track_key_count(trk); + for(i=0; ival, key.tm); + + if(basetype == GOAT3D_TRACK_VAL) { + create_tsattr(tsa, tskey, "value", TS_NUMBER); + ts_set_valuef(&tsa->val, key.val[0]); + } else { + static const int typecount[] = {1, 3, 4, 4}; + create_tsattr(tsa, tskey, "value", TS_VECTOR); + ts_set_valuef_arr(&tsa->val, typecount[basetype], key.val); + } + } + + return tstrk; + +err: + ts_free_tree(tstrk); + return 0; +} + + + +static void init_namegen(struct goat3d *g) +{ + memset(g->namecnt, 0, sizeof g->namecnt); +} + +static const char *namegen(struct goat3d *g, const char *name, int type) +{ + static const char *fmt[] = {"material%03u", "mesh%03u", "light%03u", + "camera%03u", "node%03u", "animation%03u", "track%03u"}; + + if(name) { + /* if an actual name happens to match our pattern, make sure to skip it + * for the auto-generated names + */ + int n; + if(sscanf(name, fmt[type], &n)) { + g->namecnt[type] = n; + } + return name; + } + + sprintf(g->namebuf, fmt[type], g->namecnt[type]++); + return g->namebuf; +} + +static const char *enctab = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +GOAT3DAPI char *goat3d_b64encode(const void *data, int size, char *buf, int *bufsz) +{ + const unsigned char *src = data; + char *dest; + int i; + int outsz = size * 4 / 3 + 1; + int bitgrp[4]; + int leftover; + + if(buf) { + if(*bufsz < outsz) { + *bufsz = outsz; + return 0; + } + } else { + if(bufsz) *bufsz = outsz; + + /* reserve more space for up to two padding bytes */ + if(!(buf = malloc(outsz + 2))) { + return 0; + } + } + dest = buf; + + leftover = 0; + while(size > 0) { + bitgrp[0] = (src[0] & 0xfc) >> 2; + bitgrp[1] = (src[0] & 3) << 4; + if(--size <= 0) { + leftover = 2; + break; + } + bitgrp[1] |= src[1] >> 4; + bitgrp[2] = (src[1] & 0xf) << 2; + if(--size <= 0) { + leftover = 3; + break; + } + bitgrp[2] |= src[2] >> 6; + bitgrp[3] = src[2] & 0x3f; + + dest[0] = enctab[bitgrp[0]]; + dest[1] = enctab[bitgrp[1]]; + dest[2] = enctab[bitgrp[2]]; + dest[3] = enctab[bitgrp[3]]; + + dest += 4; + src += 3; + leftover = 0; + size--; + } + + if(leftover) { + for(i=0; i<4; i++) { + if(i < leftover) { + *dest++ = enctab[bitgrp[i]]; + } else { + *dest++ = '='; + } + } + } + + *dest = 0; + return buf; +} diff --git a/libs/imago/Makefile b/libs/imago/Makefile index 06b91dc..5e91e5b 100644 --- a/libs/imago/Makefile +++ b/libs/imago/Makefile @@ -20,14 +20,17 @@ jobj = jpeglib/jcapimin.o jpeglib/jcapistd.o jpeglib/jccoefct.o jpeglib/jccolor. jpeglib/jfdctint.o jpeglib/jidctflt.o jpeglib/jidctfst.o jpeglib/jidctint.o \ jpeglib/jidctred.o jpeglib/jmemmgr.o jpeglib/jmemnobs.o jpeglib/jquant1.o \ jpeglib/jquant2.o jpeglib/jutils.o -src = $(mobj) $(zobj) $(pobj) $(jobj) -alib = ../unix/libimago.a +obj = $(mobj) $(zobj) $(pobj) $(jobj) +alib = ../unix/imago.a CFLAGS = -O3 -Izlib -Ilibpng -Ijpeglib $(pic) $(alib): $(obj) $(AR) rcs $@ $(obj) +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + .PHONY: clean clean: rm -f $(obj) $(alib) diff --git a/libs/treestor/LICENSE b/libs/treestor/LICENSE new file mode 100644 index 0000000..536e666 --- /dev/null +++ b/libs/treestor/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2016 John Tsiombikas + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/libs/treestor/Makefile b/libs/treestor/Makefile new file mode 100644 index 0000000..6c2ea75 --- /dev/null +++ b/libs/treestor/Makefile @@ -0,0 +1,14 @@ +obj = src/treestor.o src/text.o src/dynarr.o +alib = ../unix/treestor.a + +CFLAGS = -O3 -Iinclude + +$(alib): $(obj) + $(AR) rcs $@ $(obj) + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.PHONY: clean +clean: + rm -f $(obj) $(alib) diff --git a/libs/treestor/README.md b/libs/treestor/README.md new file mode 100644 index 0000000..f75efa9 --- /dev/null +++ b/libs/treestor/README.md @@ -0,0 +1,38 @@ +libtreestore +============ + +Libtreestore is a simple C library for reading/writing hierarchical data in a +json-like text format, or a chunk-based binary format. + +A better way to describe the text format is like XML without the CDATA, and with +curly braces instead of tags: + +``` +rootnode { + some_attribute = "some_string_value" + some_numeric_attrib = 10 + vector_attrib = [255, 128, 0] + array_attrib = ["tom", "dick", "harry"] + + # you can have multiple nodes with the same name + childnode { + childattr = "whatever" + } + childnode { + another_childattr = "xyzzy" + } +} +``` + +License +------- +Copyright (C) 2016-2019 John Tsiombikas + +Libtreestore is free software. Feel free to use, modify, and/or redistribute +it, under the terms of the MIT/X11 license. See LICENSE for detauls. + +Issues +------ +At the moment only the text format has been implemented. + +More info soon... diff --git a/libs/treestor/include/treestor.h b/libs/treestor/include/treestor.h new file mode 100644 index 0000000..842a489 --- /dev/null +++ b/libs/treestor/include/treestor.h @@ -0,0 +1,167 @@ +#ifndef TREESTORE_H_ +#define TREESTORE_H_ + +#include +#include +#include + +#ifdef __cplusplus +#define TS_DEFVAL(x) =(x) +extern "C" { +#else +#define TS_DEFVAL(x) +#endif + +/** set of user-supplied I/O functions, for ts_load_io/ts_save_io */ +struct ts_io { + void *data; + + long (*read)(void *buf, size_t bytes, void *uptr); + long (*write)(const void *buf, size_t bytes, void *uptr); +}; + +enum ts_value_type { TS_STRING, TS_NUMBER, TS_VECTOR, TS_ARRAY }; + +/** treestore node attribute value */ +struct ts_value { + enum ts_value_type type; + + char *str; /**< string values will have this set */ + int inum; /**< numeric values will have this set */ + float fnum; /**< numeric values will have this set */ + + /** vector values (arrays containing ONLY numbers) will have this set */ + float *vec; /**< elements of the vector */ + int vec_size; /**< size of the vector (in elements), same as array_size */ + + /** array values (including vectors) will have this set */ + struct ts_value *array; /**< elements of the array */ + int array_size; /**< size of the array (in elements) */ +}; + +int ts_init_value(struct ts_value *tsv); +void ts_destroy_value(struct ts_value *tsv); + +struct ts_value *ts_alloc_value(void); /**< also calls ts_init_value */ +void ts_free_value(struct ts_value *tsv); /**< also calls ts_destroy_value */ + +/** perform a deep-copy of a ts_value */ +int ts_copy_value(struct ts_value *dest, struct ts_value *src); + +/** set a ts_value as a string */ +int ts_set_value_str(struct ts_value *tsv, const char *str); + +/** set a ts_value from a list of integers */ +int ts_set_valuei_arr(struct ts_value *tsv, int count, const int *arr); +int ts_set_valueiv(struct ts_value *tsv, int count, ...); +int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap); +int ts_set_valuei(struct ts_value *tsv, int inum); /**< equiv: ts_set_valueiv(val, 1, inum) */ + +/** set a ts_value from a list of floats */ +int ts_set_valuef_arr(struct ts_value *tsv, int count, const float *arr); +int ts_set_valuefv(struct ts_value *tsv, int count, ...); +int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap); +int ts_set_valuef(struct ts_value *tsv, float fnum); /**< equiv: ts_set_valuefv(val, 1, fnum) */ + +/** set a ts_value from a list of ts_value pointers. they are deep-copied as per ts_copy_value */ +int ts_set_value_arr(struct ts_value *tsv, int count, const struct ts_value *arr); +int ts_set_valuev(struct ts_value *tsv, int count, ...); +int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap); + + +/** treestore node attribute */ +struct ts_attr { + char *name; + struct ts_value val; + + struct ts_attr *next; +}; + +int ts_init_attr(struct ts_attr *attr); +void ts_destroy_attr(struct ts_attr *attr); + +struct ts_attr *ts_alloc_attr(void); /**< also calls ts_init_attr */ +void ts_free_attr(struct ts_attr *attr); /**< also calls ts_destroy_attr */ + +/** perform a deep-copy of a ts_attr */ +int ts_copy_attr(struct ts_attr *dest, struct ts_attr *src); + +int ts_set_attr_name(struct ts_attr *attr, const char *name); + + + +/** treestore node */ +struct ts_node { + char *name; + + int attr_count; + struct ts_attr *attr_list, *attr_tail; + + int child_count; + struct ts_node *child_list, *child_tail; + struct ts_node *parent; + + struct ts_node *next; /* next sibling */ +}; + +int ts_init_node(struct ts_node *node); +void ts_destroy_node(struct ts_node *node); + +struct ts_node *ts_alloc_node(void); /**< also calls ts_init_node */ +void ts_free_node(struct ts_node *n); /**< also calls ts_destroy_node */ + +/** recursively destroy all the nodes of the tree */ +void ts_free_tree(struct ts_node *tree); + +int ts_set_node_name(struct ts_node *node, const char *name); + +void ts_add_attr(struct ts_node *node, struct ts_attr *attr); +struct ts_attr *ts_get_attr(struct ts_node *node, const char *name); + +const char *ts_get_attr_str(struct ts_node *node, const char *aname, + const char *def_val TS_DEFVAL(0)); +float ts_get_attr_num(struct ts_node *node, const char *aname, + float def_val TS_DEFVAL(0.0f)); +int ts_get_attr_int(struct ts_node *node, const char *aname, + int def_val TS_DEFVAL(0.0f)); +float *ts_get_attr_vec(struct ts_node *node, const char *aname, + float *def_val TS_DEFVAL(0)); +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, + struct ts_value *def_val TS_DEFVAL(0)); + + +void ts_add_child(struct ts_node *node, struct ts_node *child); +int ts_remove_child(struct ts_node *node, struct ts_node *child); +struct ts_node *ts_get_child(struct ts_node *node, const char *name); + +/* load/save by opening the specified file */ +struct ts_node *ts_load(const char *fname); +int ts_save(struct ts_node *tree, const char *fname); + +/* load/save using the supplied FILE pointer */ +struct ts_node *ts_load_file(FILE *fp); +int ts_save_file(struct ts_node *tree, FILE *fp); + +/* load/save using custom I/O functions */ +struct ts_node *ts_load_io(struct ts_io *io); +int ts_save_io(struct ts_node *tree, struct ts_io *io); + + +struct ts_attr *ts_lookup(struct ts_node *root, const char *path); +const char *ts_lookup_str(struct ts_node *root, const char *path, + const char *def_val TS_DEFVAL(0)); +float ts_lookup_num(struct ts_node *root, const char *path, + float def_val TS_DEFVAL(0.0f)); +int ts_lookup_int(struct ts_node *root, const char *path, + int def_val TS_DEFVAL(0)); +float *ts_lookup_vec(struct ts_node *root, const char *path, + float *def_val TS_DEFVAL(0)); +struct ts_value *ts_lookup_array(struct ts_node *root, const char *path, + struct ts_value *def_val TS_DEFVAL(0)); + + +#ifdef __cplusplus +} +#endif + +#endif /* TREESTORE_H_ */ diff --git a/libs/treestor/src/dynarr.c b/libs/treestor/src/dynarr.c new file mode 100644 index 0000000..2d3f611 --- /dev/null +++ b/libs/treestor/src/dynarr.c @@ -0,0 +1,133 @@ +/* dynarr - dynamic resizable C array data structure + * author: John Tsiombikas + * license: public domain + */ +#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 *ts_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 ts_dynarr_free(void *da) +{ + if(da) { + free(DESC(da)); + } +} + +void *ts_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 ts_dynarr_empty(void *da) +{ + return DESC(da)->nelem ? 0 : 1; +} + +int ts_dynarr_size(void *da) +{ + return DESC(da)->nelem; +} + + +void *ts_dynarr_clear(void *da) +{ + return ts_dynarr_resize(da, 0); +} + +/* stack semantics */ +void *ts_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 = ts_dynarr_resize(da, newsz))) { + fprintf(stderr, "failed to resize\n"); + return da; + } + da = tmp; + desc = DESC(da); + desc->nelem = nelem; + } + + if(item) { + memcpy((char*)da + desc->nelem++ * desc->szelem, item, desc->szelem); + } + return da; +} + +void *ts_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 = ts_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 --git a/libs/treestor/src/dynarr.h b/libs/treestor/src/dynarr.h new file mode 100644 index 0000000..513a431 --- /dev/null +++ b/libs/treestor/src/dynarr.h @@ -0,0 +1,69 @@ +/* dynarr - dynamic resizable C array data structure + * author: John Tsiombikas + * license: public domain + */ +#ifndef DYNARR_H_ +#define DYNARR_H_ + +/* usage example: + * ------------- + * int *arr = ts_dynarr_alloc(0, sizeof *arr); + * + * int x = 10; + * arr = ts_dynarr_push(arr, &x); + * x = 5; + * arr = ts_dynarr_push(arr, &x); + * x = 42; + * arr = ts_dynarr_push(arr, &x); + * + * for(i=0; i +#include +#include +#include +#include +#include "treestor.h" +#include "dynarr.h" + +struct parser { + struct ts_io *io; + int nline; + char *token; + int nextc; +}; + +enum { TOK_SYM, TOK_ID, TOK_NUM, TOK_STR }; + +static struct ts_node *read_node(struct parser *pstate); +static int read_array(struct parser *pstate, struct ts_value *tsv, char endsym); +static int next_token(struct parser *pstate); + +static int print_attr(struct ts_attr *attr, struct ts_io *io, int level); +static char *value_to_str(struct ts_value *value); +static int tree_level(struct ts_node *n); +static const char *indent(int x); +static const char *toktypestr(int type); + +#define EXPECT(type) \ + do { \ + if(next_token(pst) != (type)) { \ + fprintf(stderr, "expected %s token\n", toktypestr(type)); \ + goto err; \ + } \ + } while(0) + +#define EXPECT_SYM(c) \ + do { \ + if(next_token(pst) != TOK_SYM || pst->token[0] != (c)) { \ + fprintf(stderr, "expected symbol: %c\n", c); \ + goto err; \ + } \ + } while(0) + + +struct ts_node *ts_text_load(struct ts_io *io) +{ + char *root_name; + struct parser pstate, *pst = &pstate; + struct ts_node *node = 0; + + pstate.io = io; + pstate.nline = 0; + pstate.nextc = -1; + if(!(pstate.token = ts_dynarr_alloc(0, 1))) { + perror("failed to allocate token string"); + return 0; + } + + EXPECT(TOK_ID); + if(!(root_name = strdup(pst->token))) { + perror("failed to allocate root node name"); + ts_dynarr_free(pst->token); + return 0; + } + EXPECT_SYM('{'); + if(!(node = read_node(pst))) { + ts_dynarr_free(pst->token); + return 0; + } + node->name = root_name; + +err: + ts_dynarr_free(pst->token); + return node; +} + +static int read_value(struct parser *pst, int toktype, struct ts_value *val) +{ + switch(toktype) { + case TOK_NUM: + ts_set_valuef(val, atof(pst->token)); + break; + + case TOK_SYM: + if(pst->token[0] == '[' || pst->token[0] == '{') { + char endsym = pst->token[0] + 2; /* end symbol is dist 2 from either '[' or '{' */ + if(read_array(pst, val, endsym) == -1) { + return -1; + } + } else { + fprintf(stderr, "read_node: unexpected rhs symbol: %c\n", pst->token[0]); + } + break; + + case TOK_ID: + case TOK_STR: + default: + ts_set_value_str(val, pst->token); + } + + return 0; +} + +static struct ts_node *read_node(struct parser *pst) +{ + int type; + struct ts_node *node; + + if(!(node = ts_alloc_node())) { + perror("failed to allocate treestore node"); + return 0; + } + + while((type = next_token(pst)) == TOK_ID) { + char *id; + + if(!(id = strdup(pst->token))) { + goto err; + } + + EXPECT(TOK_SYM); + + if(pst->token[0] == '=') { + /* attribute */ + struct ts_attr *attr; + int type; + + if(!(attr = ts_alloc_attr())) { + goto err; + } + + if((type = next_token(pst)) == -1) { + ts_free_attr(attr); + fprintf(stderr, "read_node: unexpected EOF\n"); + goto err; + } + + if(read_value(pst, type, &attr->val) == -1) { + ts_free_attr(attr); + fprintf(stderr, "failed to read value\n"); + goto err; + } + attr->name = id; + ts_add_attr(node, attr); + + } else if(pst->token[0] == '{') { + /* child */ + struct ts_node *child; + + if(!(child = read_node(pst))) { + ts_free_node(node); + return 0; + } + + child->name = id; + ts_add_child(node, child); + + } else { + fprintf(stderr, "unexpected token: %s\n", pst->token); + goto err; + } + } + + if(type != TOK_SYM || pst->token[0] != '}') { + fprintf(stderr, "expected closing brace\n"); + goto err; + } + return node; + +err: + fprintf(stderr, "treestore read_node failed\n"); + ts_free_node(node); + return 0; +} + +static int read_array(struct parser *pst, struct ts_value *tsv, char endsym) +{ + int type; + struct ts_value values[32]; + int i, nval = 0; + int res; + + while((type = next_token(pst)) != -1) { + ts_init_value(values + nval); + if(read_value(pst, type, values + nval) == -1) { + return -1; + } + if(nval < 31) { + ++nval; + } else { + ts_destroy_value(values + nval); + } + + type = next_token(pst); + if(!(type == TOK_SYM && (pst->token[0] == ',' || pst->token[0] == endsym))) { + fprintf(stderr, "read_array: expected comma or end symbol ('%c')\n", endsym); + return -1; + } + if(pst->token[0] == endsym) { + break; /* we're done */ + } + } + + if(!nval) { + return -1; + } + + res = ts_set_value_arr(tsv, nval, values); + + for(i=0; inextc >= 0) { + c = pst->nextc; + pst->nextc = -1; + } else { + if(pst->io->read(&c, 1, pst->io->data) < 1) { + return -1; + } + } + return c; +} + +static void ungetchar(char c, struct parser *pst) +{ + assert(pst->nextc == -1); + pst->nextc = c; +} + +static int next_token(struct parser *pst) +{ + int c; + + DYNARR_CLEAR(pst->token); + + /* skip whitespace */ + while((c = nextchar(pst)) != -1) { + if(c == '#') { /* skip to end of line */ + while((c = nextchar(pst)) != -1 && c != '\n'); + if(c == -1) return -1; + } + if(!isspace(c)) break; + if(c == '\n') ++pst->nline; + } + if(c == -1) return -1; + + DYNARR_STRPUSH(pst->token, c); + + if(isdigit(c) || c == '-' || c == '+') { + /* token is a number */ + int found_dot = 0; + while((c = nextchar(pst)) != -1 && + (isdigit(c) || (c == '.' && !found_dot))) { + DYNARR_STRPUSH(pst->token, c); + if(c == '.') found_dot = 1; + } + if(c != -1) ungetchar(c, pst); + return TOK_NUM; + } + if(isalpha(c)) { + /* token is an identifier */ + while((c = nextchar(pst)) != -1 && (isalnum(c) || c == '_')) { + DYNARR_STRPUSH(pst->token, c); + } + if(c != -1) ungetchar(c, pst); + return TOK_ID; + } + if(c == '"') { + /* token is a string constant, remove the opening quote */ + DYNARR_STRPOP(pst->token); + while((c = nextchar(pst)) != -1 && c != '"') { + DYNARR_STRPUSH(pst->token, c); + if(c == '\n') ++pst->nline; + } + if(c != '"') { + return -1; + } + return TOK_STR; + } + return TOK_SYM; +} + +int ts_text_save(struct ts_node *tree, struct ts_io *io) +{ + char *buf; + struct ts_node *c; + struct ts_attr *attr; + int lvl = tree_level(tree); + int sz, inline_attr, res = -1; + + if(!(buf = malloc(lvl + strlen(tree->name) + 4))) { + perror("ts_text_save failed to allocate buffer"); + goto end; + } + + if(tree->child_list || (tree->attr_list && tree->attr_list->next)) { + inline_attr = 0; + } else { + inline_attr = 1; + } + + sz = sprintf(buf, "%s%s {", indent(lvl), tree->name); + if(!inline_attr) { + strcat(buf, "\n"); + sz++; + } + if(io->write(buf, sz, io->data) < sz) { + goto end; + } + + attr = tree->attr_list; + while(attr) { + if(print_attr(attr, io, inline_attr ? -1 : lvl) == -1) { + goto end; + } + attr = attr->next; + } + + c = tree->child_list; + while(c) { + if(ts_text_save(c, io) == -1) { + goto end; + } + c = c->next; + } + + if(inline_attr) { + sz = sprintf(buf, "}\n"); + } else { + sz = sprintf(buf, "%s}\n", indent(lvl)); + } + if(io->write(buf, sz, io->data) < sz) { + goto end; + } + res = 0; +end: + free(buf); + return res; +} + +static int print_attr(struct ts_attr *attr, struct ts_io *io, int level) +{ + char *buf, *val; + int sz; + + if(!(val = value_to_str(&attr->val))) { + return -1; + } + + sz = (level >= 0 ? level : 0) + strlen(attr->name) + ts_dynarr_size(val) + 5; + if(!(buf = malloc(sz))) { + perror("print_attr: failed to allocate name buffer"); + ts_dynarr_free(val); + } + + if(level >= 0) { + sz = sprintf(buf, "%s%s = %s\n", indent(level + 1), attr->name, val); + } else { + sz = sprintf(buf, " %s = %s ", attr->name, val); + } + if(io->write(buf, sz, io->data) < sz) { + ts_dynarr_free(val); + free(buf); + return -1; + } + ts_dynarr_free(val); + free(buf); + return 0; +} + +static char *append_dynstr(char *dest, char *s) +{ + while(*s) { + DYNARR_STRPUSH(dest, *s++); + } + return dest; +} + +static char *value_to_str(struct ts_value *value) +{ + int i; + char buf[128]; + char *str, *valstr; + + if(!(str = ts_dynarr_alloc(0, 1))) { + return 0; + } + + switch(value->type) { + case TS_NUMBER: + sprintf(buf, "%g", value->fnum); + str = append_dynstr(str, buf); + break; + + case TS_VECTOR: + DYNARR_STRPUSH(str, '['); + for(i=0; ivec_size; i++) { + if(i == 0) { + sprintf(buf, "%g", value->vec[i]); + } else { + sprintf(buf, ", %g", value->vec[i]); + } + str = append_dynstr(str, buf); + } + DYNARR_STRPUSH(str, ']'); + break; + + case TS_ARRAY: + DYNARR_STRPUSH(str, '['); + for(i=0; iarray_size; i++) { + if(i > 0) { + str = append_dynstr(str, ", "); + } + if(!(valstr = value_to_str(value->array + i))) { + ts_dynarr_free(str); + return 0; + } + str = append_dynstr(str, valstr); + ts_dynarr_free(valstr); + } + DYNARR_STRPUSH(str, ']'); + break; + + default: + sprintf(buf, "\"%s\"", value->str); + str = append_dynstr(str, buf); + } + + return str; +} + +static int tree_level(struct ts_node *n) +{ + if(!n->parent) return 0; + return tree_level(n->parent) + 1; +} + +static const char *indent(int x) +{ + static const char buf[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + const char *end = buf + sizeof buf - 1; + return x > sizeof buf - 1 ? buf : end - x; +} + +static const char *toktypestr(int type) +{ + switch(type) { + case TOK_ID: + return "identifier"; + case TOK_NUM: + return "number"; + case TOK_STR: + return "string"; + case TOK_SYM: + return "symbol"; + } + return "unknown"; +} diff --git a/libs/treestor/src/treestor.c b/libs/treestor/src/treestor.c new file mode 100644 index 0000000..57b06ed --- /dev/null +++ b/libs/treestor/src/treestor.c @@ -0,0 +1,809 @@ +#include +#include +#include +#include +#include +#include "treestor.h" + +#ifdef WIN32 +#include +#else +#include +#endif + +struct ts_node *ts_text_load(struct ts_io *io); +int ts_text_save(struct ts_node *tree, struct ts_io *io); + +static long io_read(void *buf, size_t bytes, void *uptr); +static long io_write(const void *buf, size_t bytes, void *uptr); + + +/* ---- ts_value implementation ---- */ + +int ts_init_value(struct ts_value *tsv) +{ + memset(tsv, 0, sizeof *tsv); + return 0; +} + +void ts_destroy_value(struct ts_value *tsv) +{ + int i; + + free(tsv->str); + free(tsv->vec); + + for(i=0; iarray_size; i++) { + ts_destroy_value(tsv->array + i); + } + free(tsv->array); +} + + +struct ts_value *ts_alloc_value(void) +{ + struct ts_value *v = malloc(sizeof *v); + if(!v || ts_init_value(v) == -1) { + free(v); + return 0; + } + return v; +} + +void ts_free_value(struct ts_value *tsv) +{ + ts_destroy_value(tsv); + free(tsv); +} + + +int ts_copy_value(struct ts_value *dest, struct ts_value *src) +{ + int i; + + if(dest == src) return 0; + + *dest = *src; + + dest->str = 0; + dest->vec = 0; + dest->array = 0; + + if(src->str) { + if(!(dest->str = malloc(strlen(src->str) + 1))) { + goto fail; + } + strcpy(dest->str, src->str); + } + if(src->vec && src->vec_size > 0) { + if(!(dest->vec = malloc(src->vec_size * sizeof *src->vec))) { + goto fail; + } + memcpy(dest->vec, src->vec, src->vec_size * sizeof *src->vec); + } + if(src->array && src->array_size > 0) { + if(!(dest->array = calloc(src->array_size, sizeof *src->array))) { + goto fail; + } + for(i=0; iarray_size; i++) { + if(ts_copy_value(dest->array + i, src->array + i) == -1) { + goto fail; + } + } + } + return 0; + +fail: + free(dest->str); + free(dest->vec); + if(dest->array) { + for(i=0; iarray_size; i++) { + ts_destroy_value(dest->array + i); + } + free(dest->array); + } + return -1; +} + +#define MAKE_NUMSTR_FUNC(type, fmt) \ + static char *make_##type##str(type x) \ + { \ + static char scrap[128]; \ + char *str; \ + int sz = snprintf(scrap, sizeof scrap, fmt, x); \ + if(!(str = malloc(sz + 1))) return 0; \ + sprintf(str, fmt, x); \ + return str; \ + } + +MAKE_NUMSTR_FUNC(int, "%d") +MAKE_NUMSTR_FUNC(float, "%g") + + +struct val_list_node { + struct ts_value val; + struct val_list_node *next; +}; + +int ts_set_value_str(struct ts_value *tsv, const char *str) +{ + if(tsv->str) { + ts_destroy_value(tsv); + if(ts_init_value(tsv) == -1) { + return -1; + } + } + + tsv->type = TS_STRING; + if(!(tsv->str = malloc(strlen(str) + 1))) { + return -1; + } + strcpy(tsv->str, str); + +#if 0 + /* try to parse the string and see if it fits any of the value types */ + if(*str == '[' || *str == '{') { + /* try to parse as a vector */ + struct val_list_node *list = 0, *tail = 0, *node; + int nelem = 0; + char endsym = *str++ + 2; /* ']' is '[' + 2 and '}' is '{' + 2 */ + + while(*str && *str != endsym) { + float val = strtod(str, &endp); + if(endp == str || !(node = malloc(sizeof *node))) { + break; + } + ts_init_value(&node->val); + ts_set_valuef(&node->val, val); + node->next = 0; + + if(list) { + tail->next = node; + tail = node; + } else { + list = tail = node; + } + ++nelem; + str = endp; + } + + if(nelem && (tsv->array = malloc(nelem * sizeof *tsv->array)) && + (tsv->vec = malloc(nelem * sizeof *tsv->vec))) { + int idx = 0; + while(list) { + node = list; + list = list->next; + + tsv->array[idx] = node->val; + tsv->vec[idx] = node->val.fnum; + ++idx; + free(node); + } + tsv->type = TS_VECTOR; + } + + } else if((tsv->fnum = strtod(str, &endp)), endp != str) { + /* it's a number I guess... */ + tsv->type = TS_NUMBER; + } +#endif + + return 0; +} + +int ts_set_valuei_arr(struct ts_value *tsv, int count, const int *arr) +{ + int i; + + if(count < 1) return -1; + if(count == 1) { + if(!(tsv->str = make_intstr(*arr))) { + return -1; + } + + tsv->type = TS_NUMBER; + tsv->fnum = (float)*arr; + tsv->inum = *arr; + return 0; + } + + /* otherwise it's an array, we need to create the ts_value array, and + * the simplified vector + */ + if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) { + return -1; + } + tsv->vec_size = count; + + for(i=0; ivec[i] = arr[i]; + } + + if(!(tsv->array = malloc(count * sizeof *tsv->array))) { + free(tsv->vec); + } + tsv->array_size = count; + + for(i=0; iarray + i); + ts_set_valuef(tsv->array + i, arr[i]); + } + + tsv->type = TS_VECTOR; + return 0; +} + +int ts_set_valueiv(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valueiv_va(tsv, count, ap); + va_end(ap); + return res; +} + +int ts_set_valueiv_va(struct ts_value *tsv, int count, va_list ap) +{ + int i, *vec; + + if(count < 1) return -1; + if(count == 1) { + int num = va_arg(ap, int); + ts_set_valuei(tsv, num); + return 0; + } + + vec = alloca(count * sizeof *vec); + for(i=0; istr = make_floatstr(*arr))) { + return -1; + } + + tsv->type = TS_NUMBER; + tsv->fnum = *arr; + tsv->inum = (int)*arr; + return 0; + } + + /* otherwise it's an array, we need to create the ts_value array, and + * the simplified vector + */ + if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) { + return -1; + } + tsv->vec_size = count; + + for(i=0; ivec[i] = arr[i]; + } + + if(!(tsv->array = malloc(count * sizeof *tsv->array))) { + free(tsv->vec); + } + tsv->array_size = count; + + for(i=0; iarray + i); + ts_set_valuef(tsv->array + i, arr[i]); + } + + tsv->type = TS_VECTOR; + return 0; +} + +int ts_set_valuefv(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valuefv_va(tsv, count, ap); + va_end(ap); + return res; +} + +int ts_set_valuefv_va(struct ts_value *tsv, int count, va_list ap) +{ + int i; + float *vec; + + if(count < 1) return -1; + if(count == 1) { + float num = va_arg(ap, double); + ts_set_valuef(tsv, num); + return 0; + } + + vec = alloca(count * sizeof *vec); + for(i=0; iarray = malloc(count * sizeof *tsv->array))) { + return -1; + } + tsv->array_size = count; + + for(i=0; iarray + i, (struct ts_value*)arr + i) == -1) { + while(--i >= 0) { + ts_destroy_value(tsv->array + i); + } + free(tsv->array); + tsv->array = 0; + return -1; + } + } + + if(allnum) { + if(!(tsv->vec = malloc(count * sizeof *tsv->vec))) { + ts_destroy_value(tsv); + return -1; + } + tsv->type = TS_VECTOR; + tsv->vec_size = count; + + for(i=0; ivec[i] = tsv->array[i].fnum; + } + } else { + tsv->type = TS_ARRAY; + } + return 0; +} + +int ts_set_valuev(struct ts_value *tsv, int count, ...) +{ + int res; + va_list ap; + va_start(ap, count); + res = ts_set_valuev_va(tsv, count, ap); + va_end(ap); + return res; +} + +int ts_set_valuev_va(struct ts_value *tsv, int count, va_list ap) +{ + int i; + + if(count <= 1) return -1; + + if(!(tsv->array = malloc(count * sizeof *tsv->array))) { + return -1; + } + tsv->array_size = count; + + for(i=0; iarray + i, src) == -1) { + while(--i >= 0) { + ts_destroy_value(tsv->array + i); + } + free(tsv->array); + tsv->array = 0; + return -1; + } + } + return 0; +} + + +/* ---- ts_attr implementation ---- */ + +int ts_init_attr(struct ts_attr *attr) +{ + memset(attr, 0, sizeof *attr); + return ts_init_value(&attr->val); +} + +void ts_destroy_attr(struct ts_attr *attr) +{ + free(attr->name); + ts_destroy_value(&attr->val); +} + +struct ts_attr *ts_alloc_attr(void) +{ + struct ts_attr *attr = malloc(sizeof *attr); + if(!attr || ts_init_attr(attr) == -1) { + free(attr); + return 0; + } + return attr; +} + +void ts_free_attr(struct ts_attr *attr) +{ + ts_destroy_attr(attr); + free(attr); +} + +int ts_copy_attr(struct ts_attr *dest, struct ts_attr *src) +{ + if(dest == src) return 0; + + if(ts_set_attr_name(dest, src->name) == -1) { + return -1; + } + + if(ts_copy_value(&dest->val, &src->val) == -1) { + ts_destroy_attr(dest); + return -1; + } + return 0; +} + +int ts_set_attr_name(struct ts_attr *attr, const char *name) +{ + char *n = malloc(strlen(name) + 1); + if(!n) return -1; + strcpy(n, name); + + free(attr->name); + attr->name = n; + return 0; +} + + +/* ---- ts_node implementation ---- */ + +int ts_init_node(struct ts_node *node) +{ + memset(node, 0, sizeof *node); + return 0; +} + +void ts_destroy_node(struct ts_node *node) +{ + if(!node) return; + + free(node->name); + + while(node->attr_list) { + struct ts_attr *attr = node->attr_list; + node->attr_list = node->attr_list->next; + ts_free_attr(attr); + } +} + +struct ts_node *ts_alloc_node(void) +{ + struct ts_node *node = malloc(sizeof *node); + if(!node || ts_init_node(node) == -1) { + free(node); + return 0; + } + return node; +} + +void ts_free_node(struct ts_node *node) +{ + ts_destroy_node(node); + free(node); +} + +void ts_free_tree(struct ts_node *tree) +{ + if(!tree) return; + + while(tree->child_list) { + struct ts_node *child = tree->child_list; + tree->child_list = tree->child_list->next; + ts_free_tree(child); + } + + ts_free_node(tree); +} + +int ts_set_node_name(struct ts_node *node, const char *name) +{ + char *n = malloc(strlen(name) + 1); + if(!n) return -1; + strcpy(n, name); + + free(node->name); + node->name = n; + return 0; +} + +void ts_add_attr(struct ts_node *node, struct ts_attr *attr) +{ + attr->next = 0; + if(node->attr_list) { + node->attr_tail->next = attr; + node->attr_tail = attr; + } else { + node->attr_list = node->attr_tail = attr; + } + node->attr_count++; +} + +struct ts_attr *ts_get_attr(struct ts_node *node, const char *name) +{ + struct ts_attr *attr = node->attr_list; + while(attr) { + if(strcmp(attr->name, name) == 0) { + return attr; + } + attr = attr->next; + } + return 0; +} + +const char *ts_get_attr_str(struct ts_node *node, const char *aname, const char *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.str) { + return def_val; + } + return attr->val.str; +} + +float ts_get_attr_num(struct ts_node *node, const char *aname, float def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.fnum; +} + +int ts_get_attr_int(struct ts_node *node, const char *aname, int def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.inum; +} + +float *ts_get_attr_vec(struct ts_node *node, const char *aname, float *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.vec) { + return def_val; + } + return attr->val.vec; +} + +struct ts_value *ts_get_attr_array(struct ts_node *node, const char *aname, struct ts_value *def_val) +{ + struct ts_attr *attr = ts_get_attr(node, aname); + if(!attr || !attr->val.array) { + return def_val; + } + return attr->val.array; +} + +void ts_add_child(struct ts_node *node, struct ts_node *child) +{ + if(child->parent) { + if(child->parent == node) return; + ts_remove_child(child->parent, child); + } + child->parent = node; + child->next = 0; + + if(node->child_list) { + node->child_tail->next = child; + node->child_tail = child; + } else { + node->child_list = node->child_tail = child; + } + node->child_count++; +} + +int ts_remove_child(struct ts_node *node, struct ts_node *child) +{ + struct ts_node dummy, *iter = &dummy; + dummy.next = node->child_list; + + while(iter->next && iter->next != child) { + iter = iter->next; + } + if(!iter->next) { + return -1; + } + + child->parent = 0; + + iter->next = child->next; + if(!iter->next) { + node->child_tail = iter; + } + node->child_list = dummy.next; + node->child_count--; + assert(node->child_count >= 0); + return 0; +} + +struct ts_node *ts_get_child(struct ts_node *node, const char *name) +{ + struct ts_node *res = node->child_list; + while(res) { + if(strcmp(res->name, name) == 0) { + return res; + } + res = res->next; + } + return 0; +} + +struct ts_node *ts_load(const char *fname) +{ + FILE *fp; + struct ts_node *root; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "ts_load: failed to open file: %s: %s\n", fname, strerror(errno)); + return 0; + } + + root = ts_load_file(fp); + fclose(fp); + return root; +} + +struct ts_node *ts_load_file(FILE *fp) +{ + struct ts_io io = {0}; + io.data = fp; + io.read = io_read; + + return ts_load_io(&io); +} + +struct ts_node *ts_load_io(struct ts_io *io) +{ + return ts_text_load(io); +} + +int ts_save(struct ts_node *tree, const char *fname) +{ + FILE *fp; + int res; + + if(!(fp = fopen(fname, "wb"))) { + fprintf(stderr, "ts_save: failed to open file: %s: %s\n", fname, strerror(errno)); + return 0; + } + res = ts_save_file(tree, fp); + fclose(fp); + return res; +} + +int ts_save_file(struct ts_node *tree, FILE *fp) +{ + struct ts_io io = {0}; + io.data = fp; + io.write = io_write; + + return ts_save_io(tree, &io); +} + +int ts_save_io(struct ts_node *tree, struct ts_io *io) +{ + return ts_text_save(tree, io); +} + +static const char *pathtok(const char *path, char *tok) +{ + int len; + const char *dot = strchr(path, '.'); + if(!dot) { + strcpy(tok, path); + return 0; + } + + len = dot - path; + memcpy(tok, path, len); + tok[len] = 0; + return dot + 1; +} + +struct ts_attr *ts_lookup(struct ts_node *node, const char *path) +{ + char *name = alloca(strlen(path) + 1); + + if(!node) return 0; + + if(!(path = pathtok(path, name)) || strcmp(name, node->name) != 0) { + return 0; + } + + while((path = pathtok(path, name)) && (node = ts_get_child(node, name))); + + if(path || !node) return 0; + return ts_get_attr(node, name); +} + +const char *ts_lookup_str(struct ts_node *root, const char *path, const char *def_val) +{ + struct ts_attr *attr = ts_lookup(root, path); + if(!attr || !attr->val.str) { + return def_val; + } + return attr->val.str; +} + +float ts_lookup_num(struct ts_node *root, const char *path, float def_val) +{ + struct ts_attr *attr = ts_lookup(root, path); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.fnum; +} + +int ts_lookup_int(struct ts_node *root, const char *path, int def_val) +{ + struct ts_attr *attr = ts_lookup(root, path); + if(!attr || attr->val.type != TS_NUMBER) { + return def_val; + } + return attr->val.inum; +} + +float *ts_lookup_vec(struct ts_node *root, const char *path, float *def_val) +{ + struct ts_attr *attr = ts_lookup(root, path); + if(!attr || !attr->val.vec) { + return def_val; + } + return attr->val.vec; +} + +struct ts_value *ts_lookup_array(struct ts_node *node, const char *path, struct ts_value *def_val) +{ + struct ts_attr *attr = ts_lookup(node, path); + if(!attr || !attr->val.array) { + return def_val; + } + return attr->val.array; +} + +static long io_read(void *buf, size_t bytes, void *uptr) +{ + size_t sz = fread(buf, 1, bytes, uptr); + if(sz < bytes && errno) return -1; + return sz; +} + +static long io_write(const void *buf, size_t bytes, void *uptr) +{ + size_t sz = fwrite(buf, 1, bytes, uptr); + if(sz < bytes && errno) return -1; + return sz; +} diff --git a/src/scr_game.c b/src/scr_game.c index 0b5f2b9..112b6ff 100644 --- a/src/scr_game.c +++ b/src/scr_game.c @@ -5,6 +5,7 @@ #include "miniglut.h" #include "game.h" #include "util.h" +#include "goat3d.h" static int ginit(void); static void gdestroy(void); @@ -27,13 +28,44 @@ struct game_screen scr_game = { static float cam_theta, cam_phi = 20, cam_dist = 10; static float cam_pan[3]; +static struct goat3d *gscn; +static int dlist; + + static int ginit(void) { + int i, num, nfaces; + + if(!(gscn = goat3d_create()) || goat3d_load(gscn, "data/track1.g3d")) { + return -1; + } + + dlist = glGenLists(1); + glNewList(dlist, GL_COMPILE); + num = goat3d_get_node_count(gscn); + for(i=0; i