HMP

From GameStudio Wiki

Jump to: navigation, search

Contents

The HMP5 terrain format

A terrain is basically a rectangular mesh of height values with one or several surface textures. It is a variant of the GameStudio MDL5 Model format, without all the data structures that are unnecessary for terrain.

HMP file header

Once the file header is read, all the other terrain parts can be found just by calculating their position in the file. Here is the format of the .HMP file header:

typedef float vec3[3];

typedef struct {
	char version[4]; // "HMP4" or "HMP5"; only the newer HMP5 format is described here
	long nu1; // always 2
	vec3 scale; // heightpoint scale factors
	vec3 offset; // heightpoint offset
	long nu6; // not used
	float ftrisize_x; // triangle X size
	float ftrisize_y; // triangle Y size
	float fnumverts_x; // number of mesh coordinates in X direction
	long numskins ; // number of skin textures
	long nu8,nu9; // not used
	long numverts; // total number of mesh coordinates
	long nu10; // not used
	long numframes; // number of frames
	long nu11; // not used
	long flags; // always 0
	long nu12; // not used
} hmp_header;

The size of this header is 0x54 bytes (84).

The "HMP4" format is used by the A6 engine prior to 5.230, while the new "HMP5" format is used by the A6 engine since version 5.230. The number of vertices in the rectangular mesh can be determined by

int numverts_x = (int) fnumverts_x;
int numverts_y = numverts/numverts_x;
After the file header follow the textures and then the array of height values.

HMP texture format

The terrain surface textures are flat pictures. There can be more than one texture. By default, the first texture is the terrain skin, and the second texture is the detail map if it has a different size. Further textures (up to 8) are used for material effects and shaders. You will find the first texture just after the model header, at offset baseskin = 0x54. There are numskins textures to read. The texture and pixel formats are the same as for MDL skins, and are described in detail in the MDL5 format description.

HMP height values

A terrain contains a set of animation frames, which each is a set of height values. Normally only the first frame is used, because terrain does not animate. Each mesh vertex is defined by a height value and a normal.

typedef byte unsigned char;
typedef word unsigned short;
typedef struct {
	word z; // height value, packed on 0..65536
	byte lightnormalindex; // index of the vertex normal
	byte unused; // not used
} hmp_trivertx_t;

To get the real Z coordinate from the packed coordinates, multiply it by the Z scaling factor, and add the Z offset. Both the scaling factor and the offset can be found in the mdl_header struct. Thus the formula for calculating the real height positions is:

float height = (scale[2] * z) + offset[2];

The x/y scale and offset vectors determine the terrains x and y boundaries, like this:

float xmin = offset[0];float xmax = scale[0] * 65536 + offset[0];float ymin = offset[1]; float ymax = scale[1] * 65536 + offset[1];

The X and Y position of the vertex results of the number of the vertex in the mesh, and thus must not be stored. The lightnormalindex field is an index to the actual vertex normal vector, just like in the MDL format description. A whole frame has the following structure:

typedef struct {
	long type; // always 2
	mdl_trivertxs_t bboxmin,bboxmax; // bounding box of the frame - see mdl description
	char name[16]; // name of the frame, used for animation
	hmp_trivertx_t height[numverts]; // array of height values
} hmp_frame_t;


A not animated terrain has only one frame, but can have several skin textures, like a MDL. By default, the first skin is used for the terrain texture and the second skin for the detail map. Depending on the terrain material or shader assigned in the GameStudio script, the final terrain texture can be composed from up to 8 skin textures.

Functions how to write HMPs

C++: For this code you have to include the hmp5.h and bind the hmp5.lib. You can find these files in the latest WED / MED SDK of Wlad (can be downloaded from the download page).


HMP5W DllHmpW;

//starts writing of an HMP
DLLFUNC var WriteHMP (STRING* filename)
{
	void*			handle;
	handle = DllHmpW.Open(filename->chars);
	if(handle == NULL) { return(_VAR(0)); }
	return(_VAR(1));
}

//Writes the Header of an HMP
DLLFUNC var WriteHmpHeader(var header_info[9],VECTOR* min,VECTOR* max)
{
	HMP5_HEADER* header; 

	header = &DllHmpW.Head();
	DllHmpW.Head().fnumverts_x = _FLOAT(header_info[0]);
	DllHmpW.Head().ftrisize_x = _FLOAT(header_info[1]);
	DllHmpW.Head().ftrisize_y = _FLOAT(header_info[2]);

	float v_origin[3];
	float v_scale[3];

	v_origin[0] = _FLOAT(min->x); v_origin[1] = _FLOAT(min->y); 
       v_origin[2] = _FLOAT(min->z);

	v_scale[0] = _FLOAT(max->x) - v_origin[0]; 
       v_scale[1] = _FLOAT(max->y) - v_origin[1];
       v_scale[2] = _FLOAT(max->z) - v_origin[2];

	v_scale[0] /= 65535.0f;
	v_scale[1] /= 65535.0f;
	v_scale[2] /= 65535.0f; 

	DllHmpW.Head().scale[0] = v_scale[0];
	DllHmpW.Head().scale[1] = v_scale[1];
	DllHmpW.Head().scale[2] = v_scale[2];
	DllHmpW.Head().scale_origin[0] = v_origin[0];
	DllHmpW.Head().scale_origin[1] = v_origin[1];
	DllHmpW.Head().scale_origin[2] = v_origin[2]; 

	return(_VAR(1));
}

//finish hmp writing 
DLLFUNC var WriteHmpClose()
{
	bool a = DllHmpW.Close();
	return(_VAR((int)a));
}

//starts a frame
DLLFUNC var WriteHmpFrame(STRING* framename)
{
	bool a = DllHmpW.Frame(framename->chars);
	return(_VAR((int)a));
}

//writes one vertex
DLLFUNC var WriteHmpVertex(var height)
{
	unsigned short packed_z = 0;
	if(DllHmpW.Head().scale[2] == 0)
	{
		packed_z = 0;
	}
	else
	{
		packed_z = (short) ((_FLOAT(height) - DllHmpW.Head().scale_origin[2]) / DllHmpW.Head().scale[2]);
	}

	bool a = DllHmpW.Vertex(packed_z,84,0);
	return(_VAR(packed_z));
}

#define	SKINTYP_8BIT_PAL256			0x00
#define	SKINTYP_16BIT_565				0x02
#define	SKINTYP_16BIT_4444			0x03
#define	SKINTYP_24BIT_888				0x04
#define SKINTYP_32BIT_8888			0x05

#define	SKINTYP_MIPFLAG				0x08
#define	SKINTYP_MATERIAL				0x10
#define	SKINTYP_MATERIAL_ASCDEF		0x20
#define	SKINTYP_RGBFLAG				0x80 

#define	SKINTYP_16BIT_565M			(SKINTYP_16BIT_565 | SKINTYP_MIPFLAG)
#define	SKINTYP_16BIT_4444M			(SKINTYP_16BIT_4444 | SKINTYP_MIPFLAG)
#define	SKINTYP_24BIT_888M			(SKINTYP_24BIT_888 | SKINTYP_MIPFLAG)
#define	SKINTYP_32BIT_8888M			(SKINTYP_32BIT_8888 | SKINTYP_MIPFLAG)


//generates a mipmap
bool Create_MipMap(BYTE* mip,BYTE* bmp,int width, int height,int bpp, int scalefactor)
{
	int a = 0; int b = 0; int c = 0;
	while(b < width*height*bpp)
	{
		c = 0;
		while(c < bpp)
		{
			mip[a+c] = bmp[b+c];
			c += 1;
		}
	 	a += 1;
	 	b += bpp*scalefactor;
	} 

	return(true);
}

//writes a skin
DLLFUNC var WriteHmpSkin(STRING* str,BMAP* bmp)
{
	int bpp = 0;
	int type = 0;
	bool mips = 0;
	int format = _INT(bmap_lock(bmp,0));
	switch ( format )
	{
	case 565: type = SKINTYP_16BIT_565*((bmp->flags & BMPF_MIPMAPS) ==0) 
       + SKINTYP_16BIT_565M*((bmp->flags & BMPF_MIPMAPS) !=0);  break; 
	case 4444: type = SKINTYP_16BIT_4444*((bmp->flags & BMPF_MIPMAPS) ==0) 
       + SKINTYP_16BIT_4444M*((bmp->flags & BMPF_MIPMAPS)!=0); break;
	case 888: type = SKINTYP_24BIT_888*((bmp->flags & BMPF_MIPMAPS) ==0) 
       + SKINTYP_24BIT_888M*((bmp->flags & BMPF_MIPMAPS)!=0); break;
	case 8888: type = SKINTYP_32BIT_8888*((bmp->flags & BMPF_MIPMAPS) ==0) 
       + SKINTYP_32BIT_8888M*((bmp->flags & BMPF_MIPMAPS)!=0); break;
	default: 
		return _VAR(-1);
	}; 

	if(bmp->bytespp == 3 && type == SKINTYP_32BIT_8888M)
	{
		type = SKINTYP_24BIT_888M;
	}
	if(bmp->bytespp == 3 && type == SKINTYP_32BIT_8888)
	{
		type = SKINTYP_24BIT_888;
	}  

	mips = (bool) ((int)bmp->flags & BMPF_MIPMAPS);
	bpp = bmp->bytespp;
	if(!bpp || !type) { return(_VAR(-1)); } 

	int size = (_INT(bmap_width(bmp))) * (_INT(bmap_height(bmp))) * bpp;
	BYTE* nmip = new BYTE[size];
	for(int d=0;d<size;d++)
		nmip[d] = bmp->pixels[d]; 

	bool erfolg;
	int mtrl_typ = type;
	DllHmpW.Skin ( mtrl_typ, str->chars);
	DllHmpW.SkinSize ( _INT(bmap_width(bmp)), _INT(bmap_height(bmp)), bpp, mips );
	if(mips == true)
	{
	 	int size = (_INT(bmap_width(bmp))/2) * (_INT(bmap_height(bmp))/2) * bpp;
		BYTE* mip2 = new BYTE[size];
		Create_MipMap(mip2,bmp->pixels,_INT(bmap_width(bmp)),_INT(bmap_height(bmp)),bpp,4); 

		size = (_INT(bmap_width(bmp))/4) * (_INT(bmap_height(bmp))/4) * bpp;
		BYTE* mip3 = new BYTE[size];
		Create_MipMap(mip3,bmp->pixels,_INT(bmap_width(bmp)),_INT(bmap_height(bmp)),bpp,16); 

		size = (_INT(bmap_width(bmp))/8) * (_INT(bmap_height(bmp))/8) * bpp;
		BYTE* mip4 = new BYTE[size];
		Create_MipMap(mip4,bmp->pixels,_INT(bmap_width(bmp)),_INT(bmap_height(bmp)),bpp,64); 

		erfolg = DllHmpW.SkinBits ( nmip, mip2, mip3, mip4); 

		delete mip2;
		delete mip3;
		delete mip4;
	}
	else
	{
		erfolg = DllHmpW.SkinBits ( nmip, NULL,NULL,NULL);
	} 

	DllHmpW.SkinDone();
	bmap_unlock(bmp);  

	return(_VAR((int)erfolg));
}

//writes an empty skin (1 skin is needed)
DLLFUNC var WriteSkinEmpty(var width2,var height2,STRING* str)
{
	int width = _INT(width2);
	int height = _INT(height2);
	int type = SKINTYP_8BIT_PAL256;
	int bpp = 1;

	DllHmpW.Skin ( type, str->chars);
	DllHmpW.SkinSize ( width, height, bpp, false );
	int size = width*height*bpp;
	BYTE* skin = new BYTE[size];
	memset(skin,0,size);  

	DllHmpW.SkinBits (skin, NULL, NULL, NULL);
	DllHmpW.SkinDone(); 

	return(_VAR(1));
}
 
//Close an open frame
DLLFUNC var WriteHmpFrameEnd()
{
	bool a = DllHmpW.FrameDone();
	return(_VAR((int)a));
}


Here is a sample C-Script code to write a HMP:

function Write_Terrain(ent,filename,mode_)
{
	var a = 0; var b = 0;
	var _min[3];
	var _max[3];
	var _Headerinfo[3];
	p_Entity = ent; //p_entity = entity pointer
	
	Terrain_GetSize(p_Entity,Terrain_Info); //get Size (Num Verts X / Tri Size X/Y) via a help function
	_Headerinfo[0] = Terrain_Info[0]; //Num Verts X
       _Headerinfo[1] = Terrain_Info[2] / p_Entity.scale_x; //triangle sizeX
	_Headerinfo[2] = Terrain_Info[3] / p_Entity.scale_y; //triangle sizeY
	
	WriteHMP(filename); //call dll function
	
	c_setminmax(p_Entity);
	
	_min.z = 70000;
	a = 1;
	b = 0;
	temp = ent_frames(p_Entity);
        //check all frames for min / max values of the ent
	while(b < ent_frames(p_Entity))
	{
		while(a <= ent_vertices(p_Entity))
		{
			vec_for_mesh(temp,p_Entity,a);
			if(temp.x < _min.x) { _min.x = temp.x; }
			if(temp.y < _min.y) { _min.y = temp.y; }
			if(temp.z < _min.z) { _min.z = temp.z; }
			if(temp.x > _max.x) { _max.x = temp.x; }
			if(temp.y > _max.y) { _max.y = temp.y; }
			if(temp.z > _max.z) { _max.z = temp.z; }
			a += 1;
		}
		a = 0;
		b += 1;
	}
	
	vec_set(temp.x,_Headerinfo.x);
        //write header
	WriteHmpHeader(temp.x,_min,_max);
	
	a = 1;
        //write skins
	while(a <= ent_skins(p_Entity))
	{
		p_Bmp = 0; //bitmap pointer
		p_Bmp = bmap_for_entity(p_Entity,a); 

		if(p_Bmp != 0)
		{
			str_cpy(material_HelpStr1,"Skin");
			str_for_num(HelpStr1,a); str_cat(material_HelpStr1,HelpStr1);
			WriteHmpSkin(material_HelpStr1,p_Bmp);
		}
	 	a += 1;
	} 

	b = 0;
        //write frames
	while(b < ent_frames(p_Entity))
	{
		a = 1;
		str_cpy(material_HelpStr1,"Frame");
		str_for_num(HelpStr1,a); str_cat(material_HelpStr1,HelpStr1);
		WriteHmpFrame(material_HelpStr1);
		while(a <= ent_vertices(p_Entity))
		{
			vec_for_mesh(temp.x,p_Entity,a);
			WriteHmpVertex(temp.z);
			a += 1;
		}
		WriteHmpFrameEnd();
		a = 0;
		b += 1;
	}
	WriteHmpClose();
	return(1);
}
Personal tools