

#ifndef __skelmodel_h__
#define __skelmodel_h__

#ifndef __BYTE_SWAPPING_H__
#include "byte_swapping.h"
#endif

#ifndef __ttageditform_h__
#include "TTagEditForm.h"
#endif

#define MAX_SKMD_MODELS 16
#define MAX_SKMD_MODELNAME 31
#define MAX_SKMD_BONENAME 63
#define MAX_SKMD_PREMADELOD 12
#define MAX_SKMD_BONES 256

#define MINIMUM_LOD_INCREMENT 50

enum {
	_skmd_lod_has_npatch_fill_bit,
	
	_skmd_lod_has_npatch_fill_flag = FLAG(_skmd_lod_has_npatch_fill_bit)
};

struct skmd_lod {
	word weighted_vert_count;
	word min_triangles;
	word max_triangles;
	word weight_ref_count;
	word flags;
	word PADDING;
	
	// internal pointers used only when loaded
	word *weights_per_bone;
	word *edge_collapse;	// negative offset by skmd_lodref.min_vertices
	word *min_vertices_for_tri;
	word *weight_refs;
	byte *weights_per_vertex;
	float (*weights)[4];
	word triangles[1/* really max_triangles */][3];
	
	// the data to which the above point
	
	// word vertices_per_bone[skmd_modelheader.bone_count];
	// word edge_collapse[skmd_lodref.max_vertices - skmd_lodref.min_vertices];
	// word min_vertices_for_tri[max_triangles];
	// word vertex_refs[4 * quadvert_count];
	// byte weight_count[skmd_lodref.max_vertices];
	
	
	// align-to-16-bytes
	
	// skmd_quadvert verts[quadvert_count];
};


struct skmd_lodref {
	word min_vertices;
	word max_vertices;
	unsigned offset;	// from skmd_modelheader start
	unsigned length;
	struct skmd_lod *data;	// used when loaded
};


enum {
	_skmd_bone_attach_type_bit_0,
	_skmd_bone_attach_type_bit_1,
	_skmd_bone_shadow_dimension_bit_0,
	_skmd_bone_shadow_dimension_bit_1,
	_skmd_bone_shadowgen_enabled_bit,
	_skmd_bone_shadowgen_children_bit,
	_skmd_bone_shadow_flatten_bit,
	
	_skmd_bone_attach_type_is_skelmodel = 0,
	_skmd_bone_attach_type_is_light = 1,
	_skmd_bone_attach_type_is_local_projectiles = 2,
	
	_skmd_bone_shadow_dimension_x = 0,
	_skmd_bone_shadow_dimension_y = 1,
	_skmd_bone_shadow_dimension_z = 2,
	_skmd_bone_shadow_dimension_none = 3,
	
	_skmd_bone_attach_type_mask = FLAG(_skmd_bone_attach_type_bit_0)|FLAG(_skmd_bone_attach_type_bit_1),
	_skmd_bone_shadow_dimension_mask = FLAG(_skmd_bone_shadow_dimension_bit_0)|FLAG(_skmd_bone_shadow_dimension_bit_1),
	_skmd_bone_shadowgen_enabled_flag = FLAG(_skmd_bone_shadowgen_enabled_bit),
	_skmd_bone_shadowgen_children_flag = FLAG(_skmd_bone_shadowgen_children_bit),
	_skmd_bone_shadow_flatten_flag = FLAG(_skmd_bone_shadow_flatten_bit)
};


struct skmd_bone
{
	char name[MAX_SKMD_BONENAME+1];
	unsigned flags;
	
	signed short parent;	// can be -1 == no parent
	signed short anim_bone; // can be -1 == none; run-time
	
	float float_orient[4];
	float float_offset[3];
	
	signed short fixed_orient[4];
	signed short fixed_offset[3];
	
	signed short attachment_index; // run-time
	tag attachment_id;
	
	float bounds[3][2];
	
	float shadow_near;
	float shadow_far;
	float shadow_width;
	
	tag custom_shadow_tag;
	byte custom_shadow_stack;
	byte custom_shadow_level;
	short shadow_texture_entry_index;
	
	float tree_mass;
	float tree_resistance;
	float tree_dampening;
};

enum
{
	_skmd_complex_collision_tri_face_plane = 0,
	_skmd_complex_collision_tri_edge01_plane,
	_skmd_complex_collision_tri_edge12_plane,
	_skmd_complex_collision_tri_edge20_plane,
	NUMBER_OF_SKMD_COMPLEX_COLLISION_TRI_PLANES
};

struct skmd_complex_collision_tri
{
	short normals[NUMBER_OF_SKMD_COMPLEX_COLLISION_TRI_PLANES][3];
	long constants[NUMBER_OF_SKMD_COMPLEX_COLLISION_TRI_PLANES];
	/* for a plane i and a world_point3d (x,y,z) on that plane, 
	 * (normals[i][0]*x) + (normals[i][1]*y) + (normals[i][2]*z) + constants[i] == 0
	 * normals are of TRIG_MAGNITUDE, and for edge triangles the opposite
	 * point is on the positive side of the plane.
	 */
};

struct skmd_complex_collision_box
{
	short_world_rectangle3d bounds;
	short sub_box_first_index, sub_box_count;
	short triangle_first_index, triangle_count;
};


struct skmd_modelheader {
	char name[MAX_SKMD_MODELNAME+1];
	tag texture_stack;
	word hard_min_vertices;
	word user_min_vertices;
	word max_vertices;
	word lod_count;
	word bone_count;
	word bone_fixed_shift;
	word anim_offset_scale;
	word anim_offset_shift;
	float model_scale;
	world_distance collision_radius;
	world_distance collision_height0;
	world_distance collision_height1;
	world_distance pick_radius;
	world_distance pick_height0;
	world_distance pick_height1;
	world_distance hilight_radius;
	world_distance hilight_height0;
	world_distance hilight_height1;
	word complex_collision_tri_count;
	word complex_collision_box_count;
	
	byte padding[62];
	
	short loaded_texturestacks_index;
	float (*uv)[2];	// internal pointer used when loaded
	struct skmd_complex_collision_box *complex_collision_boxes;	// internal pointer used when loaded
	struct skmd_complex_collision_tri *complex_collision_tris;	// internal pointer used when loaded
	word *real_vertex_counts; // negative offset by hard_min_vertices
	struct skmd_lodref lods[MAX_SKMD_PREMADELOD];
	struct skmd_bone bones[1];	// variable, bone_count
	
	// align-to-16-bytes
	// float uv[max_vertices][2];
	// skmd_complex_collision_Data complex_collision[complex_collision_count];
	// word real_vertex_count[max_vertices + 1 - hard_min_vertices];
};

enum {
	_skmd_model_front_and_back_bit,
	_skmd_complex_collision_bit,
	
	_skmd_model_front_and_back_flag = FLAG(_skmd_model_front_and_back_bit),
	_skmd_complex_collision_flag = FLAG(_skmd_complex_collision_bit)
};


struct skmd_modelref {
	unsigned offset;
	unsigned length;
	word flags;
	word probability;
	struct skmd_modelheader *model;	// internal pointer used when loaded
};


struct skmd_tag_header {
	tag animation;
	unsigned animation_checksum;
	word model_count;
	word flags;
	
	struct skmd_modelref models[MAX_SKMD_MODELS];
};


unsigned import_skmd_modelheader( skmd_modelheader *mh );	// returns length
unsigned import_skmd_lod( int bone_count, int min_vertex, int max_vertex, skmd_lod *lod );	// returns length
unsigned setup_skmd_lod_ptrs( int bone_count, int min_vertex, int max_vertex, skmd_lod *lod );	// returns length
unsigned setup_skmd_modelheader_ptrs( skmd_modelheader *mh );
void build_complex_skelmodel_collision( skmd_modelheader *mh );

extern byte_swap_code bs_skmd_tag_header[];
extern byte_swap_code bs_skmd_modelheader[];
extern byte_swap_code bs_skmd_bone[];
extern byte_swap_code bs_skmd_complex_collision_tri[];
extern byte_swap_code bs_skmd_complex_collision_box[];


struct object_bone {
	float world_pos[3];
	float q[4];
	float m[3][3];
};

void generate_skel_mesh( const object_bone *bones, int bone_count,
						const skmd_lodref *ref, int target_vert_count,
						void *out_vert, void *out_norm,
						unsigned short *out_tris, int *out_tri_count );


bool ExtractSKMDNames( tag_entry *e, class XGStringList *out );

class TskmdEditForm: public TTagEditForm {
	public:
							TskmdEditForm( struct tag_entry *e );
		virtual				~TskmdEditForm();
		
		DECLAREDISPATCH
		virtual long		DoControlClick( long arg, void *ptr );
		virtual long		DoListValue( long arg, void *ptr );
		virtual long		DoSliderValue( long arg, void *ptr );
		virtual long		DoKeyClick( long arg, void *ptr );
		
		virtual void		CommitToTag();
		virtual void		ReloadTag();
		virtual bool		NeedCommit();
		
		void				AddModel( class XGFileSpecifier &fs );
		void				ReAddModel( class XGFileSpecifier &fs, int index );
		
		void				EditAttachments( struct tag_file *source, skmd_modelheader *model );
		void				EditShadows( struct tag_file *source, skmd_modelheader *model );
		void				EditWindPhysics( struct tag_file *source, skmd_modelheader *model );
		
	private:
		void free_all();
		void UpdateModelList();
		void SetMainValues( int val = -1 );
		void SetModelValues( int which_model );
		void SetModelViewer( int which_model, int vertices );
		void SetMinVertices( int which_model, int minimum );
		void UpdateSizes( int val, bool rescale );
		void UpdateCollision( int val );
		void UpdateClickRing( int val );
		void UpdateHilightRing( int val );
		void RedrawModelView();
		skmd_tag_header		models;
		bool fNeedCommit;
		
		double cached_collision_radius;
		double cached_collision_height0;
		double cached_collision_height1;
		double cached_pick_radius;
		double cached_pick_height0;
		double cached_pick_height1;
		double cached_hilight_radius;
		double cached_hilight_height0;
		double cached_hilight_height1;
		double cached_model_scale;
};





#endif

