示例#1
0
Value* reload_texture_cf (Value** arg_list, int count)
{
	// Make sure we have the correct number of arguments (1)
	check_arg_count(reload_texture, 1, count);
	char *message = "NelReloadTexture [BitmapTex]";
	//type_check (arg_list[0], TextureMap, message);

	// Get a good interface pointer
	Interface *ip = MAXScript_interface;

	theCNelExport.init (false, false, ip, true);

	// The 2 filenames
	Texmap *texmap = arg_list[0]->to_texmap ();

	// BitmapTex ?
	if (texmap->ClassID() == Class_ID (BMTEX_CLASS_ID, 0))
	{
		// Cast
		BitmapTex *bitmap = (BitmapTex*)texmap;

		// Reload
		bitmap->ReloadBitmapAndUpdate ();

		// Tell the bitmap has changed
		BroadcastNotification (NOTIFY_BITMAP_CHANGED, (void *)bitmap->GetMapName());
		
		return &true_value;
	}

	return &false_value;
}
示例#2
0
BitmapTex* SceneExportUtil::getStdMatBitmapTex( StdMat* stdmat, int id )
{
	StdMat2* stdmat2 = 0;
	int channel = id;
	if ( stdmat->SupportsShaders() )
	{
		stdmat2 = static_cast<StdMat2*>( stdmat );
		channel = stdmat2->StdIDToChannel( id );
	}

	if ( stdmat->MapEnabled(channel) )
	{
		Texmap*	tex	= stdmat->GetSubTexmap(channel);
		if ( tex && tex->ClassID() == Class_ID(BMTEX_CLASS_ID,0) &&
			(!stdmat2 || 2 == stdmat2->GetMapState(channel)) )
		{
			BitmapTex* bmptex = static_cast<BitmapTex*>(tex);
			if ( bmptex->GetMapName() )
			{
				return bmptex;
			}
		}
	}
	return 0;
}
void plDistribComponent_old::ISetProbTexmap(plDistributor& distrib)
{
    distrib.SetProbabilityBitmapTex(nil);

    Texmap* tex = fCompPB->GetTexmap(kProbTexmap);
    if( tex )
    {
        BitmapTex* bmt = GetIBitmapTextInterface(tex);
        if( bmt )
            distrib.SetProbabilityBitmapTex(bmt);
        else if( tex->ClassID() == LAYER_TEX_CLASS_ID )
            distrib.SetProbabilityLayerTex((plLayerTex*)tex);
    }
}
示例#4
0
bool SGP_MaxInterface::GetStdMtlChannelBitmapFileName( StdMat* pStdMat, int nChannel, TCHAR szFileName[] )
{
	if( !pStdMat )
	{
		assert( false );
		return false;
	}
	Texmap *tx = pStdMat->GetSubTexmap(nChannel);
	if( !tx  )
		return false;
	if(tx->ClassID() != Class_ID(BMTEX_CLASS_ID,0))
		return false;
	BitmapTex *bmt = (BitmapTex*)tx;
	_tcscpy( szFileName, bmt->GetMapName() );
	return true;
}
示例#5
0
/*
====================
GatherMesh
====================
*/
void G3DMExport::GatherMesh(INode* i_node)
{
	// convert to the triangle type
	Mesh* i_mesh = NULL;
	Object* obj = i_node->EvalWorldState(mTime).obj;
	if(obj && ( obj->SuperClassID() == GEOMOBJECT_CLASS_ID ))
	{
		if(obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) 
		{ 
			TriObject *tri_obj = (TriObject*)obj->ConvertToType(mTime, Class_ID(TRIOBJ_CLASS_ID, 0)); MAX_CHECK(tri_obj);
			i_mesh = &tri_obj->mesh;
		}
	}
	if(i_mesh==NULL||i_mesh->getNumFaces()==0||i_mesh->getNumVerts()==0) return;

	MESH mesh;

	// get the mesh name
	mesh.name = i_node->GetName();

	// get the material
	mesh.texture = "textures/default.tga";
	Mtl* mtl = i_node->GetMtl();
	if(mtl && (mtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0)) && ((StdMat*)mtl)->MapEnabled(ID_DI)) 
	{
		Texmap *texmap = mtl->GetSubTexmap(ID_DI);
		if(texmap && texmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00))
		{
			mesh.texture = UnifySlashes(((BitmapTex *)texmap)->GetMapName());
			if( !strstr( mesh.texture.c_str(), mPath.c_str() ) )
			{
				G3DAssert("The material(%s) is error : the texture path(%s) is illegal!",mtl->GetName(), mesh.texture.c_str());
				return;
			}
			else
			{
				mesh.texture = strstr(mesh.texture.c_str(),mPath.c_str()) + strlen(mPath.c_str());
			}
		}
	}

	// if it has uvs
	int map_count = i_mesh->getNumMaps();
	bool has_uvs = i_mesh->getNumTVerts() && i_mesh->tvFace;
	if(!(has_uvs&&map_count)) return;

	// get the transform
	Matrix3 transform = i_node->GetObjectTM(mTime);

	// get the points
	mesh.points.assign(i_mesh->verts, i_mesh->verts+i_mesh->getNumVerts());

	// get the triangles
	for(int i = 0; i < i_mesh->getNumFaces(); i++)
	{
		Face& face = i_mesh->faces[i];

		TRIANGLE tri;		
		tri.smoothing = face.smGroup;
		for(int j = 0; j < 3; j++)
		{
			VTNIS v;
			v.pos = transform * i_mesh->verts[face.v[j]];

			// get the uv
			UVVert * map_verts = i_mesh->mapVerts(1);
			TVFace * map_faces = i_mesh->mapFaces(1);
			v.uv = reinterpret_cast<Point2&>(map_verts[map_faces[i].t[j]]);
			v.uv.y = 1 - v.uv.y;

			// initialize the normal
			v.normal = Point3::Origin;

			// get the vertex index
			v.index = face.v[j];

			// get the smoothing group
			v.smoothing = face.smGroup;			

			// set the index for the triangle
			tri.index0[j] = v.index;

			// reassemble the vertex list
			tri.index1[j] = AddVertex(mesh, v);
		}

		// add the triangle to the table
		mesh.triangles.push_back(tri);
	}

	// build the index map
	for( int i = 0; i < mesh.vertexes.size(); i++ )
	{
		mesh.vertex_index_map[mesh.vertexes[i].index].push_back(i);
	}

	// build the normal space
	BuildNormal(mesh);

	// calculate the bounding box	
	mesh.box.Init();
	for(int i = 0; i < mesh.vertexes.size(); i++)
	{
		mesh.box += mesh.vertexes[i].pos;
	}

	// add the mesh to the table
	mMeshes.push_back(mesh);
}
示例#6
0
void plPassMtl::ShadeWithBackground(ShadeContext &sc, Color background, bool useVtxAlpha /* = true */)
{
#if 1

    // old
#if 0
    Color lightCol,rescol, diffIllum0;
    RGBA mval;
    Point3 N0,P;
    BOOL bumped = FALSE;
    int i;

    if (gbufID) 
        sc.SetGBufferID(gbufID);
    
    if (sc.mode == SCMODE_SHADOW) {
        float opac = 0.0;
        for (i=0; i < NumSubTexmaps(); i++)     {
            if (SubTexmapOn(i)) {
                hsMaxLayerBase *hsmLay = (hsMaxLayerBase *)GetSubTexmap(i);
                opac += hsmLay->GetOpacity(t);
            }
        }
        
        float f = 1.0f - opac;
        sc.out.t = Color(f,f,f);
        return;
    }
    
    N0 = sc.Normal();
    P = sc.P();
#endif

    TimeValue t = sc.CurTime();
    Color color(0, 0, 0);
    float alpha = 0.0;

    // Evaluate Base layer
    Texmap *map = fLayersPB->GetTexmap(kPassLayBase);
    if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID 
                || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) )
    {
        plLayerTex *layer = (plLayerTex*)map;
        AColor evalColor = layer->EvalColor(sc);

        color = evalColor;
        alpha = evalColor.a;
    }

    // Evaluate Top layer, if it's on
    if (fLayersPB->GetInt(kPassLayTopOn))
    {
        Texmap *map = fLayersPB->GetTexmap(kPassLayTop);
        if (map && ( map->ClassID() == LAYER_TEX_CLASS_ID 
                    || map->ClassID() == STATIC_ENV_LAYER_CLASS_ID 
                    || map->ClassID() == ANGLE_ATTEN_LAYER_CLASS_ID) )
        {
            plPlasmaMAXLayer *layer = (plPlasmaMAXLayer*)map;
            AColor evalColor = layer->EvalColor(sc);

            // Blend layers
            if( !layer->DiscardColor() )
            {
                int blendType = fLayersPB->GetInt(kPassLayBlend);
                switch (blendType)
                {
                case kBlendAdd:
                    color += evalColor * evalColor.a;
                    break;
                case kBlendAlpha:
                    color = (1.0f - evalColor.a) * color + evalColor.a * evalColor;
                    break;
                case kBlendMult:
                    color *= evalColor;
                    break;
                default:    // No blend...
                    color = evalColor;
                    break;
                }
            }
            if( !layer->DiscardAlpha() )
            {
                int alphaType = fLayersPB->GetInt(kPassLayOutputBlend);
                switch( alphaType )
                {
                case kAlphaMultiply:
                    alpha *= evalColor.a;
                    break;
                case kAlphaAdd:
                    alpha += evalColor.a;
                    break;
                case kAlphaDiscard:
                default:
                    break;
                }
            }
        }
    }

#if 1
    AColor black;
    black.Black();
    AColor white;
    white.White();


    SIllumParams ip;
    if (fBasicPB->GetInt(kPassBasEmissive))
    {
        // Emissive objects don't get shaded
        ip.diffIllum = fBasicPB->GetColor(kPassBasColorAmb, t) * color;
        ip.diffIllum.ClampMinMax();
        ip.specIllum = black;
    }
    else
    {
        //
        // Shading setup
        //

        // Setup the parameters for the shader
        ip.amb = fBasicPB->GetColor(kPassBasColorAmb, t);
        ip.diff = fBasicPB->GetColor(kPassBasColor, t) * color;
        ip.diffIllum = black;
        ip.specIllum = black;
        ip.N = sc.Normal();
        ip.V = sc.V();


        //
        // Specularity
        //
        if (fBasicPB->GetInt(kPassBasUseSpec, t))
        {
            ip.sh_str = 1.f;
            ip.spec = fBasicPB->GetColor( kPassBasSpecColor, t );
            ip.ph_exp = (float)pow(2.0f,float(fBasicPB->GetInt(kPassBasShine, t)) / 10.0f);
            ip.shine = float(fBasicPB->GetInt(kPassBasShine, t)) / 100.0f;
        }
        else
        {
            ip.spec = black;
            ip.sh_str = 0;
            ip.ph_exp = 0;
            ip.shine = 0;
        }
        ip.softThresh = 0;

        //

        // Do the shading
        Shader *myShader = GetShader(SHADER_BLINN);
        myShader->Illum(sc, ip);

        // Override shader parameters
        if (fAdvPB->GetInt(kPBAdvNoShade))
        {
            ip.diffIllum = black;
            ip.specIllum = black;
        }
        if (fAdvPB->GetInt(kPBAdvWhite))
        {
            ip.diffIllum = white;
            ip.specIllum = black;
        }

        ip.specIllum.ClampMinMax();
        ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum;
        ip.diffIllum.ClampMinMax();
    }

//  AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac)
#endif

    // Get opacity and combine with alpha
    float opac = float(fBasicPB->GetInt(kPassBasOpacity, t)) / 100.0f;
    alpha *= opac;

    float vtxAlpha = 1.0f;
    if (useVtxAlpha && GetOutputBlend() == plPassMtlBase::kBlendAlpha)
    {
        Point3 p;
        GetInterpVtxValue(MAP_ALPHA, sc, p);
        vtxAlpha = p.x;
    }
    alpha *= vtxAlpha;

    // MAX will do the additive/alpha/no blending for us based on what Requirements()
    // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c,
    // we have to multiply our output color by the alpha.
    // If we ever need a more complicated blending function, you can request the
    // background color via Requirements() (otherwise it's just black) and then do
    // the blending yourself; however, if the transparency isn't set, the shadows
    // will be opaque, so be careful.
    Color outC = ip.diffIllum + ip.specIllum;

    sc.out.c = ( outC * alpha );
    sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha );

#endif
}
示例#7
0
bool sMaterial::ConvertMTL(Mtl *mtl)
{
	char filename[64];
	char file_ext[16];
	char filename_with_ext[128];

	m_EmissiveColor = mtl->GetSelfIllumColor();
	m_AmbientColor = mtl->GetAmbient();
	m_DiffuseColor = mtl->GetDiffuse();
	m_SpecularColor = mtl->GetSpecular();
	m_fShininess = mtl->GetShininess();

	m_iNumTextures = 0;

	m_BlendMode = "replace";

	if ( mtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0) ) 
	{
		StdMat* std = (StdMat*)mtl;

		float fOpacity = std->GetOpacity(0);
		if ( fOpacity < 1.0f )
		{
			switch (std->GetTransparencyType()) 
			{
			case TRANSP_FILTER: 
				m_BlendMode = "blend";
				break;
			case TRANSP_SUBTRACTIVE: 
				m_BlendMode = "subtract";
				break;
			case TRANSP_ADDITIVE: 
				m_BlendMode = "add";
				break;
			default: 
				m_BlendMode = "replace";
				break;
			}	
		}

		m_bCullFace = !std->GetTwoSided();
	}

	for (int i=0; i<mtl->NumSubTexmaps(); i++) 
	{
		Texmap *tex = mtl->GetSubTexmap(i);
		if ( tex && tex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00) ) 
		{
			bool valid_channel = false;
			int texture_type = -1;

			switch(i)
			{
			case 0: // ambientmap/lightmap
				texture_type = TEXTURE_LIGHTMAP;
				break;
			case 1: // diffusemap
				texture_type = TEXTURE_DIFFUSE;
				break;
			case 9: // environment
				texture_type = TEXTURE_ENVIRONMENT;
				break;
			default:
				// not supported by fixed pipeline 3D rendering
				break;
			}

			if ( texture_type >= 0 )
			{
				TSTR mapName = ((BitmapTex *)tex)->GetMapName();
				_splitpath(mapName, NULL, NULL, filename, file_ext);
				sprintf(filename_with_ext, "%s%s", filename, file_ext);
				m_Textures[texture_type] = filename_with_ext;
				m_MapChannel[texture_type] = tex->GetMapChannel()-1;
			}
		}
	}

	return true;
}
AWDBlock * MaxAWDExporter::ExportCameraAndTextureExporter(INode * node, double * mtxData, AWDSceneBlock * parent, BlockSettings * blockSettings)
{
    awd_float64 * transform_mtx_camera = (double *)malloc(12*sizeof(awd_float64));
    awd_float64 store1 = mtxData[3];
    awd_float64 store2 = mtxData[4];
    awd_float64 store3 = mtxData[5];
    transform_mtx_camera[0] = mtxData[0];
    transform_mtx_camera[1] = mtxData[1];
    transform_mtx_camera[2] = mtxData[2];
    transform_mtx_camera[3] = mtxData[6];
    transform_mtx_camera[4] = mtxData[7];
    transform_mtx_camera[5] = mtxData[8];
    transform_mtx_camera[6] = store1*-1;
    transform_mtx_camera[7] = store2*-1;
    transform_mtx_camera[8] = store3*-1;
    transform_mtx_camera[9] = mtxData[9];
    transform_mtx_camera[10] = mtxData[10];
    transform_mtx_camera[11] = mtxData[11];
    Object *obj;
    obj = node->GetObjectRef();
    SClass_ID sid=obj->SuperClassID();
    getBaseObjectAndID( obj, sid );
    CameraObject *camObject= (CameraObject *) obj;
    double fov=camObject->GetFOV(0);
    bool isOrtho=camObject->IsOrtho();
    double clipNear=camObject->GetClipDist(0,CAM_HITHER_CLIP);
    double clipFar=camObject->GetClipDist(0,CAM_YON_CLIP);
    char * camName_ptr=W2A(node->GetName());
    AWD_lens_type camType=AWD_LENS_PERSPECTIVE;
    if (isOrtho)
        camType=AWD_LENS_ORTHO;
    AWDCamera * awdCamera = new AWDCamera(camName_ptr, strlen(camName_ptr), camType, transform_mtx_camera);
    AWDTextureProjector * textureProjector= new AWDTextureProjector(camName_ptr, strlen(camName_ptr), mtxData);
    AWDBitmapTexture * projectionTexture = NULL;
    free(camName_ptr);
    if(!isOrtho){
        //double aspectRatio=maxInterface->GetRendApect();
        double aspectRatio=1/double(maxInterface->GetRendImageAspect());
        double horizontalFOV=double(fov* (double(double(180)/(double(3.14159265358979323846)))));
        double verticalFOV=horizontalFOV * double(aspectRatio);
        awdCamera->set_lens_fov(verticalFOV);
    }

    awdCamera->set_lens_near(clipNear * blockSettings->get_scale());
    awdCamera->set_lens_far(clipFar * blockSettings->get_scale());
    bool exportCamera=true;
    bool exportTextureProjector=false;

    BaseObject* node_bo = (BaseObject*)node->GetObjectRef();
    IDerivedObject* node_der = NULL;
    char * settingsNodeID_ptr=NULL;

    if((node_bo->SuperClassID() == GEN_DERIVOB_CLASS_ID) || (node_bo->SuperClassID() == WSM_DERIVOB_CLASS_ID) || (node_bo->SuperClassID() == DERIVOB_CLASS_ID ))
    {
        node_der = ( IDerivedObject* ) node->GetObjectRef();
        if (node_der!=NULL){
            int nMods = node_der->NumModifiers();
            for (int m = 0; m<nMods; m++){
                Modifier* node_mod = node_der->GetModifier(m);
                if (node_mod->IsEnabled()){
                    MSTR className;
                    node_mod->GetClassName(className);
                    char * className_ptr=W2A(className);
                    if (ATTREQ(className_ptr,"AWDCamera")){
                        IParamBlock2* pb = GetParamBlock2ByName((ReferenceMaker*)node_mod, "main");
                        if(pb!=NULL){
                            int numBlockparams=pb->NumParams();
                            int p=0;
                            for (p=0; p<numBlockparams; p++) {
                                ParamID pid = pb->IndextoID(p);
                                ParamDef def = pb->GetParamDef(pid);
                                ParamType2 paramtype = pb->GetParameterType(pid);
                                char * paramName=W2A(def.int_name);
                                if (paramtype==TYPE_STRING) {
                                    if (ATTREQ(paramName, "thisAWDID"))
                                        settingsNodeID_ptr = W2A(pb->GetStr(pid));
                                }
                                if (paramtype==TYPE_BOOL){
                                    if (ATTREQ(paramName, "exportCamera"))
                                        exportCamera = (0 != pb->GetInt(pid));
                                    if (ATTREQ(paramName, "exportTextureProjector"))
                                        exportTextureProjector = (0 != pb->GetInt(pid));
                                }
                                free(paramName);
                            }
                        }
                        if(exportCamera){
                            AWD_lens_type lens_type = AWD_LENS_PERSPECTIVE;
                            int lensType=1;
                            int projectionHeight=1;
                            int offcenterX_pos=1;
                            int offcenterX_neg=1;
                            int offcenterY_pos=1;
                            int offcenterY_neg=1;
                            IParamBlock2*  pb = GetParamBlock2ByName((ReferenceMaker*)node_mod, "camera_params");
                            if(pb!=NULL){
                                int numBlockparams=pb->NumParams();
                                int p=0;
                                for (p=0; p<numBlockparams; p++) {
                                    ParamID pid = pb->IndextoID(p);
                                    ParamDef def = pb->GetParamDef(pid);
                                    ParamType2 paramtype = pb->GetParameterType(pid);
                                    char * paramName=W2A(def.int_name);
                                    if (paramtype==TYPE_INT){
                                        if (ATTREQ(paramName, "lensType"))
                                            lensType = pb->GetInt(pid);
                                        if (ATTREQ(paramName, "projectionHeight"))
                                            projectionHeight = pb->GetInt(pid);
                                        if (ATTREQ(paramName, "offcenterX_pos"))
                                            offcenterX_pos = pb->GetInt(pid);
                                        if (ATTREQ(paramName, "offcenterX_neg"))
                                            offcenterX_neg = pb->GetInt(pid);
                                        if (ATTREQ(paramName, "offcenterY_pos"))
                                            offcenterY_pos = pb->GetInt(pid);
                                        if (ATTREQ(paramName, "offcenterY_neg"))
                                            offcenterY_neg = pb->GetInt(pid);
                                    }
                                    free(paramName);
                                }
                                if(lensType==2){
                                    lens_type=AWD_LENS_ORTHO;
                                    awdCamera->set_lens_proj_height(projectionHeight);
                                }
                                else if (lensType==3){
                                    lens_type=AWD_LENS_ORTHOOFFCENTER;
                                    awdCamera->set_lens_offset(offcenterX_pos, offcenterX_neg, offcenterY_neg, offcenterY_pos );
                                }
                                awdCamera->set_lens_type(lens_type);
                            }
                        }
                        if(exportTextureProjector){
                            IParamBlock2*  pb = GetParamBlock2ByName((ReferenceMaker*)node_mod, "texture_projector_params");
                            if(pb!=NULL){
                                int numBlockparams=pb->NumParams();
                                int p=0;
                                for (p=0; p<numBlockparams; p++) {
                                    ParamID pid = pb->IndextoID(p);
                                    ParamDef def = pb->GetParamDef(pid);
                                    ParamType2 paramtype = pb->GetParameterType(pid);
                                    char * paramName=W2A(def.int_name);
                                    if (paramtype==TYPE_TEXMAP){
                                        if (ATTREQ(paramName,"projectionTexture") ){
                                            Texmap *projectionTexmap = pb->GetTexmap(pid);
                                            if (projectionTexmap != NULL && projectionTexmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
                                                projectionTexture=ExportBitmapTexture((BitmapTex *)projectionTexmap, NULL, UNDEFINEDTEXTYPE, FORTEXTUREPROJECTOR);
                                                if (projectionTexture!=NULL)
                                                    textureProjector->set_texture(projectionTexture);
                                                else{
                                                    textureProjector->make_invalide();
                                                    exportTextureProjector=false;
                                                }
                                            }
                                        }
                                    }
                                    if (paramtype==TYPE_FLOAT){
                                        if (ATTREQ(paramName, "aspect_ratio"))
                                            textureProjector->set_aspect_ratio(pb->GetFloat(pid));
                                    }
                                    free(paramName);
                                }
                            }
                            else{
                                textureProjector->make_invalide();
                                exportTextureProjector=false;
                            }
                        }
                    }
                    free(className_ptr);
                }
            }
        }
    }
    else{
    }
    if(exportCamera){
        if (parent) {
            parent->add_child(awdCamera);
        }
        else {
            awd->add_scene_block(awdCamera);
        }
    }
    else{
        delete awdCamera;
    }

    if(exportTextureProjector){
        textureProjectorCache->Set(settingsNodeID_ptr, textureProjector);
        if (parent) {
            parent->add_child(textureProjector);
        }
        else {
            awd->add_scene_block(textureProjector);
        }
        if(projectionTexture!=NULL)
            awd->add_texture(projectionTexture);
    }
    else{
        delete textureProjector;
        if(projectionTexture!=NULL)
            delete projectionTexture;
    }
    if(settingsNodeID_ptr!=NULL)
        free(settingsNodeID_ptr);
    if((exportCamera)&&(!exportTextureProjector))
        return awdCamera;
    else if((!exportCamera)&&(exportTextureProjector)&&(textureProjector!=NULL))
        return textureProjector;
    else if((exportCamera)&&(exportTextureProjector))
        return awdCamera;
    return NULL;
}
示例#9
0
int ExportQuake3Model(const TCHAR *filename, ExpInterface *ei, Interface *gi, int start_time, std::list<ExportNode> lTags, std::list<ExportNode> lMeshes)
{
	FILE *file;
	int i, j, totalTags, totalMeshes, current_time = 0;
	long pos_current, totalTris = 0, totalVerts = 0;
	std::list<FrameRange>::iterator range_i;
	std::vector<Point3> lFrameBBoxMin;
	std::vector<Point3> lFrameBBoxMax;
	long pos_tagstart;
	long pos_tagend;
	long pos_filesize;
	long pos_framestart;
	int lazynamesfixed = 0;
	const Point3 x_axis(1, 0, 0);
	const Point3 z_axis(0, 0, 1);

	SceneEnumProc checkScene(ei->theScene, start_time, gi);
	totalTags = (int)lTags.size();
	if (g_tag_for_pivot)
		totalTags++;
	totalMeshes = (int)lMeshes.size();

	// open file
	file = _tfopen(filename, _T("wb"));
	if (!file)
	{
		ExportError("Cannot open file '%s'.", filename);
		return FALSE;
	}
	ExportDebug("%s:", filename);

	// sync pattern and version
	putChars("IDP3", 4, file);
	put32(15, file);
	putChars("Darkplaces MD3 Exporter", 64, file);
	put32(0, file);   // flags
	
	// MD3 header
	ExportState("Writing MD3 header");
	put32(g_total_frames, file);      // how many frames
	put32(totalTags, file);	  // tagsnum
	put32(totalMeshes, file); // meshnum
	put32(1, file);   // maxskinnum
	put32(108, file); // headersize
	pos_tagstart = ftell(file); put32(0, file);   // tagstart
	pos_tagend	= ftell(file);  put32(256, file); // tagend
	pos_filesize = ftell(file); put32(512, file); // filesize
	ExportDebug("    %i frames, %i tags, %i meshes", g_total_frames, totalTags, totalMeshes);

	// frame info
	// bbox arrays get filled while exported mesh and written back then
	ExportState("Writing frame info");
	pos_framestart = ftell(file);
	lFrameBBoxMin.resize(g_total_frames);
	lFrameBBoxMax.resize(g_total_frames);
	for (i = 0; i < g_total_frames; i++)
	{
		// init frame data
		lFrameBBoxMin[i].Set(0, 0, 0);
		lFrameBBoxMax[i].Set(0, 0, 0);
		// put data
		putFloat(-1.0f, file); // bbox min vector
		putFloat(-1.0f, file);
		putFloat(-1.0f, file);	
		putFloat( 1.0f, file); // bbox max vector
		putFloat(1.0f, file);
		putFloat(1.0f, file);
		putFloat(0.0f, file);  // local origin (usually 0 0 0)
		putFloat(0.0f, file);
		putFloat(0.0f, file);
		putFloat(1.0f, file);  // radius of bounding sphere
		putChars("", 16, file);
	}

	// tags
	pos_current = ftell(file);
	fseek(file, pos_tagstart, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);
	
	// for each frame range cycle all frames and write out each tag
	long pos_tags = pos_current;
	if (totalTags)
	{
		long current_frame = 0;
		ExportState("Writing %i tags", totalTags);
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi);
				current_time = current_scene.time;

				// write out tags
				if (lTags.size())
				{
					for (std::list<ExportNode>::iterator tag_i = lTags.begin(); tag_i != lTags.end(); tag_i++)
					{
						INode *node	= current_scene[tag_i->i]->node;
						Matrix3	tm = node->GetObjTMAfterWSM(current_time);

						ExportState("Writing '%s' frame %i of %i", tag_i->name, i, g_total_frames);

						// tagname
						putChars(tag_i->name, 64, file);
						// origin, rotation matrix
						Point3 row = tm.GetRow(3);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(0);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(1);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(2);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
					}
				}

				// write the center of mass tag_pivot which is avg of all objects's pivots
				if (g_tag_for_pivot)
				{
					ExportState("Writing 'tag_pivot' frame %i of %i", i, g_total_frames);

					// write the null data as tag_pivot need to be written after actual geometry
					// (it needs information on frame bound boxes to get proper blendings)
					putChars("tag_pivot", 64, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
				}
			}
		}
	}

	// write the tag object offsets
	pos_current = ftell(file);
	fseek(file, pos_tagend, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);

	// allocate the structs used to calculate tag_pivot
	std::vector<Point3> tag_pivot_origin;
	std::vector<double> tag_pivot_volume;
	if (g_tag_for_pivot)
	{
		tag_pivot_origin.resize(g_total_frames);
		tag_pivot_volume.resize(g_total_frames);
	}

	// mesh objects
	// for each mesh object write uv and frames
	SceneEnumProc scratch(ei->theScene, start_time, gi);
	ExportState("Writing %i meshes", (int)lMeshes.size());
	for (std::list<ExportNode>::iterator mesh_i = lMeshes.begin(); mesh_i != lMeshes.end(); mesh_i++)
	{
		bool needsDel;

		ExportState("Start mesh #%i", mesh_i);
		INode *node = checkScene[mesh_i->i]->node;
		Matrix3 tm	= node->GetObjTMAfterWSM(start_time);
		TriObject *tri = GetTriObjectFromNode(node, start_time, needsDel);
		if (!tri)
			continue;

		// get mesh, compute normals
		Mesh &mesh = tri->GetMesh();
		MeshNormalSpec *meshNormalSpec = mesh.GetSpecifiedNormals();
		if (meshNormalSpec)
		{
			if (!meshNormalSpec->GetNumFaces())
				meshNormalSpec = NULL;
			else
			{
				meshNormalSpec->SetParent(&mesh);
				meshNormalSpec->CheckNormals();
			}
		}
		mesh.checkNormals(TRUE);

		// fix lazy object names
		ExportState("Attempt to fix mesh name '%s'", mesh_i->name);
		char  meshname[64];
		size_t meshnamelen = min(63, strlen(mesh_i->name));
		memset(meshname, 0, 64);
		strncpy(meshname, mesh_i->name, meshnamelen);
		meshname[meshnamelen] = 0;
		if (!strncmp("Box", meshname, 3)    || !strncmp("Sphere", meshname, 6)  || !strncmp("Cylinder", meshname, 8) ||
            !strncmp("Torus", meshname, 5)  || !strncmp("Cone", meshname, 4)    || !strncmp("GeoSphere", meshname, 9) ||
			!strncmp("Tube", meshname, 4)   || !strncmp("Pyramid", meshname, 7) || !strncmp("Plane", meshname, 5) ||
			!strncmp("Teapot", meshname, 6) || !strncmp("Object", meshname, 6))
		{
name_conflict:
			lazynamesfixed++;
			if (lazynamesfixed == 1)
				strcpy(meshname, "base");
			else
				sprintf(meshname, "base%i", lazynamesfixed);

			// check if it's not used by another mesh
			for (std::list<ExportNode>::iterator m_i = lMeshes.begin(); m_i != lMeshes.end(); m_i++)
				if (!strncmp(m_i->name, meshname, strlen(meshname)))
					goto name_conflict;
			// approve name
			ExportWarning("Lazy object name '%s' (mesh renamed to '%s').", node->GetName(), meshname);
		}

		// special mesh check
		bool shadow_or_collision = false;
		if (g_mesh_special)
			  if (!strncmp("collision", meshname, 9) || !strncmp("shadow", meshname, 6))
				shadow_or_collision = true;

		// get material
		const char *shadername = NULL;
		Texmap *tex = 0;
		Mtl *mtl = 0;
		if (!shadow_or_collision)
		{
			mtl = node->GetMtl();
			if (mtl)
			{
				// check for multi-material
				if (mtl->IsMultiMtl())
				{
					// check if it's truly multi material
					// we do support multi-material with only one texture (some importers set it)
					bool multi_material = false;
					MtlID matId = mesh.faces[0].getMatID();
					for (i = 1; i < mesh.getNumFaces(); i++)
						if (mesh.faces[i].getMatID() != matId)
							multi_material = true;

					if (multi_material)
						if (g_mesh_multimaterials == MULTIMATERIALS_NONE)
							ExportWarning("Object '%s' is multimaterial and using multiple materials on its faces, that case is not yet supported (truncating to first submaterial).", node->GetName());
					
					// switch to submaterial
					mtl = mtl->GetSubMtl(matId);
				}

				// get shader from material if supplied
				char *materialname = GetChar(mtl->GetName());
				if (g_mesh_materialasshader && (strstr(materialname, "/") != NULL || strstr(materialname, "\\") != NULL))
					shadername = GetChar(mtl->GetName());
				else
				{
					// get texture
					tex = mtl->GetSubTexmap(ID_DI);
					if (tex)
					{
						if (tex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00))
						{
							shadername = GetChar(((BitmapTex *)tex)->GetMapName());
							if (shadername == NULL || !shadername[0])
								ExportWarning("Object '%s' material '%s' has no bitmap.", tex->GetName(), node->GetName());
						}
						else
						{
							tex = NULL;
							ExportWarning("Object '%s' has material with wrong texture type (only Bitmap are supported).", node->GetName());
						}
					}
					else
						ExportWarning("Object '%s' has material but no texture.", node->GetName());
				}
			}
			else
				ExportWarning("Object '%s' has no material.", node->GetName());
		}

		long pos_meshstart = ftell(file);

		// surface object
		ExportState("Writing mesh '%s' header", meshname);
		putChars("IDP3", 4, file);
		putChars(meshname, 64, file);
		put32(0, file); // flags
		put32(g_total_frames, file);                          // framecount
		put32(1, file);                                       // skincount
		long pos_vertexnum = ftell(file); put32(0, file);     // vertexcount
		put32(mesh.getNumFaces(), file);                      // trianglecount
		long pos_trianglestart = ftell(file); put32(0, file); // start triangles
		put32(108, file);                                     // header size
		long pos_texvecstart = ftell(file); put32(0, file);   // texvecstart
		long pos_vertexstart = ftell(file); put32(16, file);  // vertexstart
		long pos_meshsize = ftell(file); put32(32, file);	  // meshsize

		// write out a single 'skin'
		ExportState("Writing mesh %s texture", meshname);
		if (shadow_or_collision)
			putChars(meshname, 64, file);
		else if (shadername) 
			putMaterial(shadername, mtl, tex, file);
		else
			putChars("noshader", 64, file);
		put32(0, file); // flags

		// build geometry
		ExportState("Building vertexes/triangles");
		std::vector<ExportVertex>vVertexes;
		std::vector<ExportTriangle>vTriangles;
		vVertexes.resize(mesh.getNumVerts());
		int vExtraVerts = mesh.getNumVerts();
		for (i = 0; i < mesh.getNumVerts(); i++)
		{
			vVertexes[i].vert = i;
			vVertexes[i].normalfilled = false;
			// todo: check for coincident verts
		}
		int vNumExtraVerts = 0;

		// check normals
		if (!mesh.normalsBuilt && !shadow_or_collision)
			ExportWarning("Object '%s' does not have normals contructed.", node->GetName());

		// get info for triangles
		const float normal_epsilon = 0.01f;
		vTriangles.resize(mesh.getNumFaces());
		for (i = 0; i < mesh.getNumFaces(); i++)
		{
			DWORD smGroup = mesh.faces[i].getSmGroup();
			ExportState("Mesh %s: checking normals for face %i of %i", meshname, i, mesh.getNumFaces());
			for (j = 0; j < 3; j++)
			{
				int vert = mesh.faces[i].getVert(j);
				vTriangles[i].e[j] = vert;
				// find a right normal for this vertex and save its 'address'
				int vni;
				Point3 vn;
				if (!mesh.normalsBuilt || shadow_or_collision)
				{
					vn.Set(0, 0, 0);
					vni = 0;
				}
				else
				{
					int numNormals;
					RVertex *rv = mesh.getRVertPtr(vert);
					if (meshNormalSpec)
					{  
						ExportState("face %i vert %i have normal specified", i, j);
						// mesh have explicit normals (i.e. Edit Normals modifier)
						vn = meshNormalSpec->GetNormal(i, j);
						vni = meshNormalSpec->GetNormalIndex(i, j);
					}
					else if (rv && rv->rFlags & SPECIFIED_NORMAL)
					{
						ExportState("face %i vert %i have SPECIFIED_NORMAL flag", i, j);
						// SPECIFIED_NORMAL flag
						vn = rv->rn.getNormal();
						vni = 0;
					}
					else if (rv && (numNormals = rv->rFlags & NORCT_MASK) && smGroup)
					{
						// If there is only one vertex is found in the rn member.
						if (numNormals == 1)
						{
							ExportState("face %i vert %i have solid smooth group", i, j);
							vn = rv->rn.getNormal();
							vni = 0;
							
						}
						else
						{
							ExportState("face %i vert %i have mixed smoothing groups", i, j);
							// If two or more vertices are there you need to step through them
							// and find the vertex with the same smoothing group as the current face.
							// You will find multiple normals in the ern member.
							for (int k = 0; k < numNormals; k++)
							{
								if (rv->ern[k].getSmGroup() & smGroup)
								{
									vn = rv->ern[k].getNormal();
									vni = 1 + k;
								}
							}
						}
					}
					else
					{
						ExportState("face %i vert %i flat shaded", i, j);
						// Get the normal from the Face if no smoothing groups are there
						vn = mesh.getFaceNormal(i);
						vni = 0 - (i + 1);
					}
				}

				// subdivide to get all normals right
				if (!vVertexes[vert].normalfilled)
				{
					vVertexes[vert].normal = vn;
					vVertexes[vert].normalindex = vni;
					vVertexes[vert].normalfilled = true;
				}
				else if ((vVertexes[vert].normal - vn).Length() >= normal_epsilon)
				{
					// current vertex not matching normal - it was already filled by different smoothing group
					// find a vert in extra verts in case it was already created
					bool vert_found = false;
					for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++)
					{
						if (vVertexes[ev].vert == vert && (vVertexes[ev].normal - vn).Length() < normal_epsilon)
						{
							vert_found = true;
							vTriangles[i].e[j] = ev;
							break;
						}
					}
					// we havent found a vertex, create new
					if (!vert_found)
					{
						ExportVertex NewVert;
						NewVert.vert = vVertexes[vert].vert;
						NewVert.normal = vn;
						NewVert.normalindex = vni;
						NewVert.normalfilled = true;
						vTriangles[i].e[j] = (int)vVertexes.size();
						vVertexes.push_back(NewVert);
						vNumExtraVerts++;
					}
				}
			}
		}
		int vNumExtraVertsForSmoothGroups = vNumExtraVerts;

		// generate UV map
		// VorteX: use direct maps reading since getNumTVerts()/getTVert is deprecated
		//  max sets two default mesh maps: 0 - vertex color, 1 : UVW, 2 & up are custom ones
		ExportState("Building UV map");
		std::vector<ExportUV>vUVMap;
		vUVMap.resize(vVertexes.size());
		int meshMap = 1;
		if (!mesh.mapSupport(meshMap) || !mesh.getNumMapVerts(meshMap) || shadow_or_collision)
		{
			for (i = 0; i < mesh.getNumVerts(); i++)
			{
				vUVMap[i].u = 0.5;
				vUVMap[i].v = 0.5;
			}
			if (!shadow_or_collision)
				ExportWarning("No UV mapping was found on object '%s'.", node->GetName());
		}
		else
		{
			UVVert *meshUV = mesh.mapVerts(meshMap);
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				ExportState("Mesh %s: converting tvert for face %i of %i", meshname, i, (int)vTriangles.size());
				// for 3 face vertexes
				for (j = 0; j < 3; j++)
				{
					int vert = vTriangles[i].e[j];
					int tv = mesh.tvFace[i].t[j];
					UVVert &UV = meshUV[tv];

					if (!vUVMap[vert].filled)
					{
						// fill uvMap vertex
						vUVMap[vert].u = UV.x;
						vUVMap[vert].v = UV.y;
						vUVMap[vert].filled = true;
						vUVMap[vert].tvert = tv;
					}
					else if (tv != vUVMap[vert].tvert)
					{
						// uvMap slot for this vertex has been filled
						// we should arrange triangle to other vertex, which not filled and having same shading and uv
						// check if any of the extra vertices can fit
						bool vert_found = false;
						for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++)
						{
							if (vVertexes[ev].vert == vert && vUVMap[vert].u == UV.x &&vUVMap[vert].v == UV.y  && (vVertexes[ev].normal - vVertexes[vert].normal).Length() < normal_epsilon)
							{
								vert_found = true;
								vTriangles[i].e[j] = vVertexes[ev].vert;
								break;
							}
						}
						if (!vert_found)
						{
							// create new vert
							ExportVertex NewVert;
							NewVert.vert = vVertexes[vert].vert;
							NewVert.normal = vVertexes[vert].normal;
							NewVert.normalindex = vVertexes[vert].normalindex;
							NewVert.normalfilled = vVertexes[vert].normalfilled;
							vTriangles[i].e[j] = (int)vVertexes.size();
							vVertexes.push_back(NewVert);
							vNumExtraVerts++;
							// create new TVert
							ExportUV newUV;
							newUV.filled = true;
							newUV.u = UV.x;
							newUV.v = UV.y;
							newUV.tvert = tv;
							vUVMap.push_back(newUV);
						}
					}
				}
			}
		}
		int vNumExtraVertsForUV = (vNumExtraVerts - vNumExtraVertsForSmoothGroups);

		// print some debug stats
		ExportDebug("    mesh %s: %i vertexes +%i %s +%i UV, %i triangles", meshname, ((int)vVertexes.size() - vNumExtraVerts), vNumExtraVertsForSmoothGroups, meshNormalSpec ? "EditNormals" : "SmoothGroups", vNumExtraVertsForUV, (int)vTriangles.size());

		// fill in triangle start
		pos_current = ftell(file);
		fseek(file, pos_trianglestart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);

		// detect if object have negative scale (mirrored)
		// in this canse we should rearrange triangles counterclockwise
		// so stuff will not be inverted
		ExportState("Mesh %s: writing %i triangles", meshname, (int)vTriangles.size());
		if (DotProd(CrossProd(tm.GetRow(0), tm.GetRow(1)), tm.GetRow(2)) < 0.0)
		{
			ExportWarning("Object '%s' is mirrored (having negative scale on it's transformation)", node->GetName());
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				put32(vTriangles[i].b, file);	// vertex index
				put32(vTriangles[i].c, file);	// for 3 vertices
				put32(vTriangles[i].a, file);	// of triangle
			}
		}
		else
		{
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				put32(vTriangles[i].a, file);	// vertex index
				put32(vTriangles[i].c, file);	// for 3 vertices
				put32(vTriangles[i].b, file);	// of triangle
			}
		}

		// fill in texvecstart
		// write out UV mapping coords.
		ExportState("Mesh %s: writing %i UV vertexes", meshname, (int)vUVMap.size());
		pos_current = ftell(file);
		fseek(file, pos_texvecstart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);
		for (i = 0; i < (int)vUVMap.size(); i++)
		{
			putFloat(vUVMap[i].u, file); // texture coord u,v
			putFloat(1.0f - vUVMap[i].v, file);	// for vertex
		}
		vUVMap.clear();

		// fill in vertexstart
		pos_current = ftell(file);
		fseek(file, pos_vertexstart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);

		// fill in vertexnum
		pos_current = ftell(file);
		fseek(file, pos_vertexnum, SEEK_SET);
		put32((int)vVertexes.size(), file);
		fseek(file, pos_current, SEEK_SET);

		// write out for each frame the position of each vertex
		long current_frame = 0;
		ExportState("Mesh %s: writing %i frames", meshname, g_total_frames);
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				bool _needsDel;

				// get triobject for current frame
				SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi);
				current_time = current_scene.time;
				INode *_node = current_scene[mesh_i->i]->node;
				TriObject *_tri	= GetTriObjectFromNode(_node, current_time, _needsDel);
				if (!_tri)
					continue;

				// get mesh, compute normals
				Mesh &_mesh	= _tri->GetMesh();
				MeshNormalSpec *_meshNormalSpec = _mesh.GetSpecifiedNormals();
				if (_meshNormalSpec)
				{
					if (!_meshNormalSpec->GetNumFaces())
						_meshNormalSpec = NULL;
					else
					{
						_meshNormalSpec->SetParent(&_mesh);
						_meshNormalSpec->CheckNormals();
					}
				}
				_mesh.checkNormals(TRUE);

				// get transformations for current frame
				Matrix3 _tm	= _node->GetObjTMAfterWSM(current_time);

				ExportState("Mesh %s: writing frame %i of %i", meshname, current_frame, g_total_frames);

				Point3 BoxMin(0, 0, 0);
				Point3 BoxMax(0, 0, 0);
				for (j = 0; j < (int)vVertexes.size(); j++) // number of vertices
				{
					ExportState("Mesh %s: transform vertex %i of %i", meshname, j, (int)vVertexes.size());

					int vert = vVertexes[j].vert;
					Point3 &v = _tm.PointTransform(_mesh.getVert(vert));
					
					// populate bbox data
					if (!shadow_or_collision)
					{
						BoxMin.x = min(BoxMin.x, v.x);
						BoxMin.y = min(BoxMin.y, v.y);
						BoxMin.z = min(BoxMin.z, v.z);
						BoxMax.x = max(BoxMax.x, v.x);
						BoxMax.y = max(BoxMax.y, v.y);
						BoxMax.z = max(BoxMax.z, v.z);
					}

					// write vertex
					double f;
					f = v.x * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);
					f = v.y * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);
					f = v.z * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);

					// get normal
					ExportState("Mesh %s: transform vertex normal %i of %i", meshname, j, (int)vVertexes.size());
					Point3 n;
					if (_meshNormalSpec) // mesh have explicit normals (i.e. Edit Normals modifier)
						n = _meshNormalSpec->Normal(vVertexes[j].normalindex);
					else if (!vVertexes[j].normalfilled || !_mesh.normalsBuilt)
						n = _mesh.getNormal(vert);
					else
					{
						RVertex *rv = _mesh.getRVertPtr(vert);
						if (vVertexes[j].normalindex < 0)
							n = _mesh.getFaceNormal((0 - vVertexes[j].normalindex) - 1);
						else if (vVertexes[j].normalindex == 0)
							n = rv->rn.getNormal();
						else 
							n = rv->ern[vVertexes[j].normalindex - 1].getNormal();
					}

					// transform normal
					Point3 &nt = _tm.VectorTransform(n).Normalize();

					// encode a normal vector into a 16-bit latitude-longitude value
					double lng = acos(nt.z) * 255 / (2 * pi);
					double lat = atan2(nt.y, nt.x) * 255 / (2 * pi);
					put16((((int)lat & 0xFF) << 8) | ((int)lng & 0xFF), file);
				}

				// blend the pivot positions for tag_pivot using mesh's volumes for blending power
				if (g_tag_for_pivot && !shadow_or_collision)
				{
					ExportState("Mesh %s: writing tag_pivot", meshname);

					Point3 Size = BoxMax - BoxMin;
					double BoxVolume = pow(Size.x * Size.y * Size.z, 0.333f);

					// blend matrices
					float blend = (float)(BoxVolume / (BoxVolume + tag_pivot_volume[current_frame]));
					float iblend = 1 - blend;
					tag_pivot_volume[current_frame]   = tag_pivot_volume[current_frame] + BoxVolume;
					Point3 row = _tm.GetRow(3) - _node->GetObjOffsetPos();
					tag_pivot_origin[current_frame].x = tag_pivot_origin[current_frame].x * iblend + row.x * blend;
					tag_pivot_origin[current_frame].y = tag_pivot_origin[current_frame].y * iblend + row.y * blend;
					tag_pivot_origin[current_frame].z = tag_pivot_origin[current_frame].z * iblend + row.z * blend;
				}

				// populate bbox data for frames
				lFrameBBoxMin[current_frame].x = min(lFrameBBoxMin[current_frame].x, BoxMin.x);
				lFrameBBoxMin[current_frame].y = min(lFrameBBoxMin[current_frame].y, BoxMin.y);
				lFrameBBoxMin[current_frame].z = min(lFrameBBoxMin[current_frame].z, BoxMin.z);
				lFrameBBoxMax[current_frame].x = max(lFrameBBoxMax[current_frame].x, BoxMax.x);
				lFrameBBoxMax[current_frame].y = max(lFrameBBoxMax[current_frame].y, BoxMax.y);
				lFrameBBoxMax[current_frame].z = max(lFrameBBoxMax[current_frame].z, BoxMax.z);

				// delete the working object, if necessary.
				if (_needsDel)
					delete _tri;
			}
		}

		// delete if necessary
		if (needsDel)
			delete tri;

		// fill in meshsize
		pos_current = ftell(file);
		fseek(file, pos_meshsize, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);  

		// reset back to first frame
		SceneEnumProc scratch(ei->theScene, start_time, gi);
		totalTris += (long)vTriangles.size();
		totalVerts += (long)vVertexes.size();
		vTriangles.clear();
		vVertexes.clear();
	}

	// write tag_pivot
	ExportState("Writing tag_pivot positions");
	if (g_tag_for_pivot)
	{
		pos_current = ftell(file);
		long current_frame = 0;
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				fseek(file, pos_tags + totalTags*112*current_frame + (int)lTags.size()*112 + 64, SEEK_SET);
				// origin
				putFloat(tag_pivot_origin[current_frame].x, file);
				putFloat(tag_pivot_origin[current_frame].y, file);
				putFloat(tag_pivot_origin[current_frame].z, file);
			}
		}
		fseek(file, pos_current, SEEK_SET);
	}
	tag_pivot_volume.clear();
	tag_pivot_origin.clear();

	// write frame data
	ExportState("Writing culling info");
	long current_frame = 0;
	pos_current = ftell(file);
	for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
	{
		for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
		{
			fseek(file, pos_framestart + current_frame*56, SEEK_SET);
			putFloat(lFrameBBoxMin[current_frame].x, file);	// bbox min vector
			putFloat(lFrameBBoxMin[current_frame].y, file);
			putFloat(lFrameBBoxMin[current_frame].z, file);	
			putFloat(lFrameBBoxMax[current_frame].x, file); // bbox max vector
			putFloat(lFrameBBoxMax[current_frame].y, file);
			putFloat(lFrameBBoxMax[current_frame].z, file);
			putFloat(0, file); // local origin (usually 0 0 0)
			putFloat(0, file);
			putFloat(0, file);
			putFloat(max(lFrameBBoxMin[current_frame].Length(), lFrameBBoxMax[current_frame].Length()) , file); // radius of bounding sphere
		}
	}
	fseek(file, pos_current, SEEK_SET);
	lFrameBBoxMin.clear();
	lFrameBBoxMax.clear();

	// fill in filesize
	pos_current = ftell(file);
	fseek(file, pos_filesize, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);

	fclose(file);

	ExportDebug("    total: %i vertexes, %i triangles", totalVerts, totalTris);

	return TRUE;
}
示例#10
0
void DxStdMtl2::LoadTextureData(IHLSLCodeGenerator * codeGen)
{
	Bitmap * bmap;
	BitmapInfo stBI;

	TimeValue t = GetCOREInterface()->GetTime();
	int nWidth,nHeight;

	int numberOfTextures = elementContainer.NumberofElementsByType(EffectElements::kEleTex);
	for(int i=0; i<numberOfTextures;i++)
	{
		bool bBump;
		TextureElement * texEle = static_cast<TextureElement*>(elementContainer.GetElementByType(i,EffectElements::kEleTex));

		TSTR mapType = texEle->GetMapName();
		Texmap *texmap = codeGen->GetShaderDefinedTexmap(map,mapType.data(),bBump);

		if(texmap)
		{
			BMM_Color_64 *p;
			nWidth = nHeight = DIMDEFAULT;
			BitmapDimensions(nWidth,nHeight,texmap);
			// load and create the D3D texture;
/*			if(texmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
			{
				BitmapTex *pBT;
				Bitmap *pTex;
				pBT = (BitmapTex *)texmap;
				pTex = pBT->GetBitmap(t);
				if (pTex)
				{
					nWidth = getClosestPowerOf2(pTex->Width());
					nHeight = getClosestPowerOf2(pTex->Height());
				}

			}
*/				
			stBI.SetType(BMM_TRUE_32);
			stBI.SetWidth(nWidth);
			stBI.SetHeight(nHeight);        
			bmap = TheManager->Create(&stBI);

			if (bmap)
			{
//				LPDIRECT3DTEXTURE9 pRenderTex = texEle->GetD3DTexture();

				texmap->RenderBitmap(t, bmap, MAPSCALE3D * 2.0f);
				p = new BMM_Color_64[nWidth*nHeight];

				for (int y = 0; y < nHeight; y++)
					bmap->GetLinearPixels(0, y, nWidth, p + y * nWidth);
			
				if(texEle->pTex)
				{
					D3DSURFACE_DESC stLD;
					texEle->pTex->GetLevelDesc(0, &stLD);
					if (stLD.Width != nWidth || stLD.Height != nHeight)
					{
						SAFE_RELEASE(texEle->pTex);
					}

				}
				if(!texEle->pTex)
					pd3dDevice->CreateTexture(nWidth,nHeight, 0,D3DUSAGE_AUTOGENMIPMAP,	D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,&texEle->pTex, NULL);

				if(texEle->pTex)
				{
					PIXELFMT *pT;
					D3DLOCKED_RECT stLR;
					texEle->pTex->LockRect(0, &stLR, 0, 0);
					pT = (PIXELFMT *)stLR.pBits;

					for (int i = 0; i < nWidth * nHeight; i++)
					{
						pT[i].r = p[i].r >> 8;
						pT[i].g = p[i].g >> 8;
						pT[i].b = p[i].b >> 8;
						pT[i].a = p[i].a >> 8;
					}
					texEle->pTex->UnlockRect(0);
				
					if(bBump && texmap->ClassID() != GNORMAL_CLASS_ID)
					{
//						LPDIRECT3DTEXTURE9 normalTex = texEle->GetD3DBumpTexture();
						
						if(texEle->pBumpTex)
						{
							D3DSURFACE_DESC stLD;
							texEle->pBumpTex->GetLevelDesc(0, &stLD);
							if (stLD.Width != nWidth || stLD.Height != nHeight)
							{
								SAFE_RELEASE(texEle->pBumpTex);
							}
						}
						if(!texEle->pBumpTex)
							pd3dDevice->CreateTexture(nWidth,nHeight, 0,D3DUSAGE_AUTOGENMIPMAP,	D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,&texEle->pBumpTex, NULL);

						D3DXComputeNormalMap(texEle->pBumpTex,texEle->pTex,NULL, NULL, D3DX_CHANNEL_RED,30.0f);

						if(texEle->GetParamHandle())
						{
							pEffectParser->LoadTexture(texEle->pBumpTex, texEle->GetParameterName());
//							pEffect->SetTexture(texEle->GetParamHandle(),texEle->pBumpTex);
//							D3DXSaveTextureToFile("c:\\temp\\normal_notgnormal.dds", D3DXIFF_DDS, texEle->pBumpTex, NULL);
							SAFE_RELEASE(texEle->pBumpTex);
						}
					}
					else
					{
						if(texEle->GetParamHandle())
						{
							pEffectParser->LoadTexture(texEle->pTex, texEle->GetParameterName());
//							pEffect->SetTexture(texEle->GetParamHandle(),texEle->pTex);
//							D3DXSaveTextureToFile("c:\\temp\\normal_gnormal.dds", D3DXIFF_DDS, texEle->pTex, NULL);
							SAFE_RELEASE(texEle->pTex);
						}

					}
				}
				bmap->DeleteThis();

			}
			delete p;
		}
		else
		{
示例#11
0
//=================================================================
// Methods for DumpModelTEP
//
int DumpModelTEP::callback(INode *pnode)
{
	Object*	pobj;
	int	fHasMat = TRUE;

	// clear physique export parameters
	m_mcExport = NULL;
	m_phyExport = NULL;
    m_phyMod = NULL;

	ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");

	if (::FNodeMarkedToSkip(pnode))
		return TREE_CONTINUE;
	
	int iNode = ::GetIndexOfINode(pnode);
	TSTR strNodeName(pnode->GetName());
	
	// The Footsteps node apparently MUST have a dummy mesh attached!  Ignore it explicitly.
	if (FStrEq((char*)strNodeName, "Bip01 Footsteps"))
		return TREE_CONTINUE;

	// Helper nodes don't have meshes
	pobj = pnode->GetObjectRef();
	if (pobj->SuperClassID() == HELPER_CLASS_ID)
		return TREE_CONTINUE;

	// The model's root is a child of the real "scene root"
	INode *pnodeParent = pnode->GetParentNode();
	BOOL fNodeIsRoot = pnodeParent->IsRootNode( );

	// Get node's material: should be a multi/sub (if it has a material at all)
	Mtl *pmtlNode = pnode->GetMtl();
	if (pmtlNode == NULL)
	{
		return TREE_CONTINUE;
		fHasMat = FALSE;
	}
	else if (!(pmtlNode->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlNode->IsMultiMtl()))
	{
		// sprintf(st_szDBG, "ERROR--Material on node %s isn't a Multi/Sub-Object", (char*)strNodeName);
		// ASSERT_AND_ABORT(FALSE, st_szDBG);
		fHasMat = FALSE;
	}
	
	// Get Node's object, convert to a triangle-mesh object, so I can access the Faces
	ObjectState os = pnode->EvalWorldState(m_tvToDump);
	pobj = os.obj;
	TriObject *ptriobj;
	BOOL fConvertedToTriObject = 
		pobj->CanConvertToType(triObjectClassID) &&
		(ptriobj = (TriObject*)pobj->ConvertToType(m_tvToDump, triObjectClassID)) != NULL;
	if (!fConvertedToTriObject)
		return TREE_CONTINUE;
	Mesh *pmesh = &ptriobj->mesh;

	// Shouldn't have gotten this far if it's a helper object
	if (pobj->SuperClassID() == HELPER_CLASS_ID)
	{
		sprintf(st_szDBG, "ERROR--Helper node %s has an attached mesh, and it shouldn't.", (char*)strNodeName);
		ASSERT_AND_ABORT(FALSE, st_szDBG);
	}

	// Ensure that the vertex normals are up-to-date
	pmesh->buildNormals();

	// We want the vertex coordinates in World-space, not object-space
	Matrix3 mat3ObjectTM = pnode->GetObjectTM(m_tvToDump);


	// initialize physique export parameters
    m_phyMod = FindPhysiqueModifier(pnode);
    if (m_phyMod)
	{
		// Physique Modifier exists for given Node
	    m_phyExport = (IPhysiqueExport *)m_phyMod->GetInterface(I_PHYINTERFACE);

        if (m_phyExport)
        {
            // create a ModContext Export Interface for the specific node of the Physique Modifier
           m_mcExport = (IPhyContextExport *)m_phyExport->GetContextInterface(pnode);

		   if (m_mcExport)
		   {
		       // convert all vertices to Rigid 
                m_mcExport->ConvertToRigid(TRUE);
		   }
		}
	}

	// Dump the triangle face info
	int cFaces = pmesh->getNumFaces();
	for (int iFace = 0; iFace < cFaces; iFace++)
	{
		Face*	pface		= &pmesh->faces[iFace];
		TVFace*	ptvface		= &pmesh->tvFace[iFace];
		DWORD	smGroupFace	= pface->getSmGroup();

		// Get face's 3 indexes into the Mesh's vertex array(s).
		DWORD iVertex0 = pface->getVert(0);
		DWORD iVertex1 = pface->getVert(1);
		DWORD iVertex2 = pface->getVert(2);
		ASSERT_AND_ABORT((int)iVertex0 < pmesh->getNumVerts(), "Bogus Vertex 0 index");
		ASSERT_AND_ABORT((int)iVertex1 < pmesh->getNumVerts(), "Bogus Vertex 1 index");
		ASSERT_AND_ABORT((int)iVertex2 < pmesh->getNumVerts(), "Bogus Vertex 2 index");
		
		// Get the 3 Vertex's for this face
		Point3 pt3Vertex0 = pmesh->getVert(iVertex0);
		Point3 pt3Vertex1 = pmesh->getVert(iVertex1);
		Point3 pt3Vertex2 = pmesh->getVert(iVertex2);

		// Get the 3 RVertex's for this face
		// NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
		RVertex *prvertex0 = pmesh->getRVertPtr(iVertex0);
		RVertex *prvertex1 = pmesh->getRVertPtr(iVertex1);
		RVertex *prvertex2 = pmesh->getRVertPtr(iVertex2);
		
		// Find appropriate normals for each RVertex
		// A vertex can be part of multiple faces, so the "smoothing group"
		// is used to locate the normal for this face's use of the vertex.
		Point3 pt3Vertex0Normal;
		Point3 pt3Vertex1Normal;
		Point3 pt3Vertex2Normal;
		if (smGroupFace) 
		{
			pt3Vertex0Normal = Pt3GetRVertexNormal(prvertex0, smGroupFace);
			pt3Vertex1Normal = Pt3GetRVertexNormal(prvertex1, smGroupFace);
			pt3Vertex2Normal = Pt3GetRVertexNormal(prvertex2, smGroupFace);
		}
		else 
		{
			pt3Vertex0Normal = pmesh->getFaceNormal( iFace );
			pt3Vertex1Normal = pmesh->getFaceNormal( iFace );
			pt3Vertex2Normal = pmesh->getFaceNormal( iFace );
		}
		ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus orig normal 0" );
		ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus orig normal 1" );
		ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus orig normal 2" );
	
		// Get Face's sub-material from node's material, to get the bitmap name.
		// And no, there isn't a simpler way to get the bitmap name, you have to
		// dig down through all these levels.
		TCHAR szBitmapName[256] = "null.bmp";
		if (fHasMat)
		{
			MtlID mtlidFace = pface->getMatID();
			if (mtlidFace >= pmtlNode->NumSubMtls())
			{
				sprintf(st_szDBG, "ERROR--Bogus sub-material index %d in node %s; highest valid index is %d",
					mtlidFace, (char*)strNodeName, pmtlNode->NumSubMtls()-1);
				// ASSERT_AND_ABORT(FALSE, st_szDBG);
				mtlidFace = 0;
			}
			Mtl *pmtlFace = pmtlNode->GetSubMtl(mtlidFace);
			ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
 
			if ((pmtlFace->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlFace->IsMultiMtl()))
			{
				// it's a sub-sub material.  Gads.
				pmtlFace = pmtlFace->GetSubMtl(mtlidFace);			
				ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
			}

			if (!(pmtlFace->ClassID() == Class_ID(DMTL_CLASS_ID, 0)))
			{

				sprintf(st_szDBG,
					"ERROR--Sub-material with index %d (used in node %s) isn't a 'default/standard' material [%x].",
					mtlidFace, (char*)strNodeName, pmtlFace->ClassID());
				ASSERT_AND_ABORT(FALSE, st_szDBG);
			}
			StdMat *pstdmtlFace = (StdMat*)pmtlFace;
			Texmap *ptexmap = pstdmtlFace->GetSubTexmap(ID_DI);
			// ASSERT_AND_ABORT(ptexmap != NULL, "NULL diffuse texture")
			if (ptexmap != NULL) 
			{
				if (!(ptexmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)))
				{
					sprintf(st_szDBG,
						"ERROR--Sub-material with index %d (used in node %s) doesn't have a bitmap as its diffuse texture.",
						mtlidFace, (char*)strNodeName);
					ASSERT_AND_ABORT(FALSE, st_szDBG);
				}
				BitmapTex *pbmptex = (BitmapTex*)ptexmap;
				strcpy(szBitmapName, pbmptex->GetMapName());
				TSTR strPath, strFile;
				SplitPathFile(TSTR(szBitmapName), &strPath, &strFile);
				strcpy(szBitmapName,strFile);
			}
		}

		UVVert UVvertex0( 0, 0, 0 );
		UVVert UVvertex1( 1, 0, 0 );
		UVVert UVvertex2( 0, 1, 0 );
		
		// All faces must have textures assigned to them
		if (pface->flags & HAS_TVERTS)
		{
			// Get TVface's 3 indexes into the Mesh's TVertex array(s).
			DWORD iTVertex0 = ptvface->getTVert(0);
			DWORD iTVertex1 = ptvface->getTVert(1);
			DWORD iTVertex2 = ptvface->getTVert(2);
			ASSERT_AND_ABORT((int)iTVertex0 < pmesh->getNumTVerts(), "Bogus TVertex 0 index");
			ASSERT_AND_ABORT((int)iTVertex1 < pmesh->getNumTVerts(), "Bogus TVertex 1 index");
			ASSERT_AND_ABORT((int)iTVertex2 < pmesh->getNumTVerts(), "Bogus TVertex 2 index");

			// Get the 3 TVertex's for this TVFace
			// NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
			UVvertex0 = pmesh->getTVert(iTVertex0);
			UVvertex1 = pmesh->getTVert(iTVertex1);
			UVvertex2 = pmesh->getTVert(iTVertex2);
		}
		else 
		{
			//sprintf(st_szDBG, "ERROR--Node %s has a textureless face.  All faces must have an applied texture.", (char*)strNodeName);
			//ASSERT_AND_ABORT(FALSE, st_szDBG);
		}
		
		/*
		const char *szExpectedExtension = ".bmp";
		if (stricmp(szBitmapName+strlen(szBitmapName)-strlen(szExpectedExtension), szExpectedExtension) != 0)
			{
			sprintf(st_szDBG, "Node %s uses %s, which is not a %s file", (char*)strNodeName, szBitmapName, szExpectedExtension);
			ASSERT_AND_ABORT(FALSE, st_szDBG);
			}
		*/

		// Determine owning bones for the vertices.
		int iNodeV0, iNodeV1, iNodeV2;
		if (m_mcExport)
		{
			// The Physique add-in allows vertices to be assigned to bones arbitrarily
			iNodeV0 = InodeOfPhyVectex( iVertex0 );
			iNodeV1 = InodeOfPhyVectex( iVertex1 );
			iNodeV2 = InodeOfPhyVectex( iVertex2 );
		}
		else
		{
			// Simple 3dsMax model: the vertices are owned by the object, and hence the node
			iNodeV0 = iNode;
			iNodeV1 = iNode;
			iNodeV2 = iNode;
		}
		
		// Rotate the face vertices out of object-space, and into world-space space
		Point3 v0 = pt3Vertex0 * mat3ObjectTM;
		Point3 v1 = pt3Vertex1 * mat3ObjectTM;
		Point3 v2 = pt3Vertex2 * mat3ObjectTM;


		Matrix3 mat3ObjectNTM = mat3ObjectTM;
		mat3ObjectNTM.NoScale( );
		ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus pre normal 0" );
		pt3Vertex0Normal = VectorTransform(mat3ObjectNTM, pt3Vertex0Normal);
		ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus post normal 0" );
		ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus pre normal 1" );
		pt3Vertex1Normal = VectorTransform(mat3ObjectNTM, pt3Vertex1Normal);
		ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus post normal 1" );
		ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus pre normal 2" );
		pt3Vertex2Normal = VectorTransform(mat3ObjectNTM, pt3Vertex2Normal);
		ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus post normal 2" );

		// Finally dump the bitmap name and 3 lines of face info
		fprintf(m_pfile, "%s\n", szBitmapName);
		fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
				iNodeV0, v0.x, v0.y, v0.z,
				pt3Vertex0Normal.x, pt3Vertex0Normal.y, pt3Vertex0Normal.z,
				UVvertex0.x, UVvertex0.y);
		fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
				iNodeV1, v1.x, v1.y, v1.z,
				pt3Vertex1Normal.x, pt3Vertex1Normal.y, pt3Vertex1Normal.z,
				UVvertex1.x, UVvertex1.y);
		fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f\n",
				iNodeV2, v2.x, v2.y, v2.z,
				pt3Vertex2Normal.x, pt3Vertex2Normal.y, pt3Vertex2Normal.z,
				UVvertex2.x, UVvertex2.y);
	}

	cleanup( );	
	return TREE_CONTINUE;
}
示例#12
0
void plParticleMtl::ShadeWithBackground(ShadeContext &sc, Color background)
{
#if 1
    TimeValue t = sc.CurTime();
    Color color(0, 0, 0);
    float alpha = 0.0;

    // Evaluate Base layer
    Texmap *map = fBasicPB->GetTexmap(kTexmap);
    if (map && map->ClassID() == LAYER_TEX_CLASS_ID)
    {
        plLayerTex *layer = (plLayerTex*)map;
        AColor evalColor = layer->EvalColor(sc);

        color = evalColor;
        alpha = evalColor.a;
    }

#if 1
    AColor black;
    black.Black();
    AColor white;
    white.White();


    SIllumParams ip;
    if( fBasicPB->GetInt( kNormal ) == kEmissive )
    {
        // Emissive objects don't get shaded
        ip.diffIllum = fBasicPB->GetColor(kColorAmb, t) * color;
        ip.diffIllum.ClampMinMax();
        ip.specIllum = black;
    }
    else
    {
        //
        // Shading setup
        //

        // Setup the parameters for the shader
        ip.amb = black;
        ip.diff = fBasicPB->GetColor(kColor, t) * color;
        ip.spec = white;
        ip.diffIllum = black;
        ip.specIllum = black;
        ip.N = sc.Normal();
        ip.V = sc.V();


        //
        // Specularity
        //
        ip.sh_str = 0;
        ip.ph_exp = 0;
        ip.shine = 0;

        ip.softThresh = 0;



        // Do the shading
        Shader *myShader = GetShader(SHADER_BLINN);
        myShader->Illum(sc, ip);

        ip.diffIllum.ClampMinMax();
        ip.specIllum.ClampMinMax();
        ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum;
    }

//  AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac)
#endif

    // Get opacity and combine with alpha
    float opac = float(fBasicPB->GetInt(kOpacity, t)) / 100.0f;
    //float opac = 1.0f;
    alpha *= opac;

    // MAX will do the additive/alpha/no blending for us based on what Requirements()
    // we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c,
    // we have to multiply our output color by the alpha.
    // If we ever need a more complicated blending function, you can request the
    // background color via Requirements() (otherwise it's just black) and then do
    // the blending yourself; however, if the transparency isn't set, the shadows
    // will be opaque, so be careful.
    Color outC = ip.diffIllum + ip.specIllum;

    sc.out.c = ( outC * alpha );
    sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha );

#endif
}
示例#13
0
// --[  Method  ]---------------------------------------------------------------
//
//  - Class     : CStravaganzaMaxTools
//
//  - prototype : bool BuildShaders()
//
//  - Purpose   : Builds the shader list from MAX's materials.
//                Preview mode requires texture files to be stored with full
//                path in order to load them. When we export, we only store the
//                filename. Another thing is that in the export mode, we copy
//                all textures into the path specified by the user if that
//                option is checked.
//
// -----------------------------------------------------------------------------
bool CStravaganzaMaxTools::BuildShaders()
{
	std::vector<Mtl*>::iterator it;

	assert(m_vecShaders.empty());

	if(!m_bPreview && m_bCopyTextures && m_strTexturePath == "")
	{
		CLogger::NotifyWindow("Textures won't be copied\nSpecify a valid output texture path first");
	}

	LOG.Write("\n\n-Building shaders: ");

	for(it = m_vecMaterials.begin(); it != m_vecMaterials.end(); ++it)
	{
		Mtl* pMaxMaterial = *it;
		assert(pMaxMaterial);

		LOG.Write("\n    %s", pMaxMaterial->GetName().data());
		CShaderStandard* pShaderStd = new CShaderStandard;
		pShaderStd->SetName(pMaxMaterial->GetName().data());

		// Properties

		StdMat2 *pMaxStandardMtl = NULL;
		StdMat2 *pMaxBakedMtl    = NULL;

		float fAlpha;

		if(pMaxMaterial->ClassID() == Class_ID(DMTL_CLASS_ID, 0))
		{
			pMaxStandardMtl = (StdMat2 *)pMaxMaterial;
		}
		else if(pMaxMaterial->ClassID() == Class_ID(BAKE_SHELL_CLASS_ID, 0))
		{
			pMaxStandardMtl = (StdMat2 *)pMaxMaterial->GetSubMtl(0);
			pMaxBakedMtl    = (StdMat2 *)pMaxMaterial->GetSubMtl(1);
		}

		if(pMaxStandardMtl)
		{
			// Standard material

			fAlpha = pMaxStandardMtl->GetOpacity(0);

			Shader* pMaxShader = pMaxStandardMtl->GetShader();

			CVector4 v4Specular = ColorToVector4(pMaxStandardMtl->GetSpecular(0), 0.0f) * pMaxShader->GetSpecularLevel(0, 0);

			pShaderStd->SetAmbient  (ColorToVector4(pMaxStandardMtl->GetAmbient(0),  0.0f));
			pShaderStd->SetDiffuse  (ColorToVector4(pMaxStandardMtl->GetDiffuse(0),  fAlpha));
			pShaderStd->SetSpecular (v4Specular);
			pShaderStd->SetShininess(pMaxShader->GetGlossiness(0, 0) * 128.0f);

			if(pMaxStandardMtl->GetTwoSided() == TRUE)
			{
				pShaderStd->SetTwoSided(true);
			}

			// Need to cast to StdMat2 in order to get access to IsFaceted().
			// ¿Is StdMat2 always the interface for standard materials?
			if(((StdMat2*)pMaxStandardMtl)->IsFaceted())
			{
				pShaderStd->SetFaceted(true);
			}

			if(pMaxStandardMtl->GetWire() == TRUE)
			{
				pShaderStd->SetPostWire(true);
				pShaderStd->SetWireLineThickness(pMaxStandardMtl->GetWireSize(0));
			}
		}
		else
		{
			// Material != Standard

			fAlpha = 1.0f; // pMaxMaterial->GetXParency();

			pShaderStd->SetAmbient  (ColorToVector4(pMaxMaterial->GetAmbient(),  0.0f));
			pShaderStd->SetDiffuse  (ColorToVector4(pMaxMaterial->GetDiffuse(),  fAlpha));
			pShaderStd->SetSpecular (CVector4(0.0f, 0.0f, 0.0f, 0.0f));
			pShaderStd->SetShininess(0.0f);
		}

		// Layers

		if(!pMaxStandardMtl)
		{
			m_vecShaders.push_back(pShaderStd);
			continue;
		}

		bool bDiffuseMap32Bits = false;
		StdMat2 *pStandardMtl;

		for(int i = 0; i < 3; i++)
		{
			int nMap;

			pStandardMtl = pMaxStandardMtl;

			// 0 = diffuse, 1 == bump, 2 = lightmap (self illumination slot) or envmap (reflection slot)

			if(i == 0)
			{
				nMap = ID_DI;
			}
			else if(i == 1)
			{
				nMap = ID_BU;

				// If its a baked material, get the bump map from there

				if(pMaxBakedMtl)
				{
					pStandardMtl = pMaxBakedMtl;
				}
			}
			else if(i == 2)
			{
				bool bBaked = false;

				// If its a baked material, get the map2 (lightmap) from there

				if(pMaxBakedMtl)
				{
					if(pMaxBakedMtl->GetMapState(ID_SI) == MAXMAPSTATE_ENABLED)
					{
						bBaked       = true;
						nMap         = ID_SI;
						pStandardMtl = pMaxBakedMtl;
					}
				}

				if(!bBaked)
				{
					if(pStandardMtl->GetMapState(ID_SI) == MAXMAPSTATE_ENABLED)
					{
						nMap = ID_SI;
					}
					else
					{
						nMap = ID_RL;
					}
				}
			}

			// Check validity

			if(pStandardMtl->GetMapState(nMap) != MAXMAPSTATE_ENABLED)
			{
				if(i == 0)
				{
					LOG.Write("\n        No diffuse. Skipping.");
					break;
				}

				continue;
			}

			Texmap* pMaxTexmap = pStandardMtl->GetSubTexmap(nMap);

			if(!pMaxTexmap)
			{
				if(i == 0)
				{
					LOG.Write("\n        No diffuse. Skipping.");
					break;
				}

				continue;
			}

			// Get texmaps

			std::vector<std::string> vecTextures, vecPaths;

			CShaderStandard::SLayerInfo  layerInfo;
			CShaderStandard::SBitmapInfo bitmapInfo;

			if(pMaxTexmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
			{
				BitmapTex* pMaxBitmapTex = (BitmapTex*)pMaxTexmap;
				Bitmap*    pMaxBitmap    = pMaxBitmapTex->GetBitmap(SECONDS_TO_TICKS(m_fStartTime));
				StdUVGen*  pMaxUVGen     = pMaxBitmapTex->GetUVGen();

				if(!pMaxBitmap)
				{
					if(i == 0)
					{
						LOG.Write("\n        Invalid diffuse. Skipping.");
						break;
					}
					continue;
				}

				assert(pMaxUVGen);

				BitmapInfo bi = pMaxBitmap->Storage()->bi;

				// bi.Name() returns the full path
				// bi.Filename() returns just the filename

				vecTextures.push_back(bi.Filename());
				vecPaths.   push_back(bi.Name());

				LOG.Write("\n        Bitmap %s", vecTextures[0].data());

				// Check if diffuse texture has alpha channel

				if(i == 0)
				{
					CBitmap    bitmap;
					CInputFile bitmapFile;

					if(!bitmapFile.Open(bi.Name(), false))
					{
						CLogger::NotifyWindow("WARNING - CStravaganzaMaxTools::BuildShaders():\nUnable to load file %s", bi.Name());
					}
					else
					{
						if(!bitmap.Load(&bitmapFile, GetFileExt(bi.Name())))
						{
							CLogger::NotifyWindow("WARNING - CStravaganzaMaxTools::BuildShaders():\nUnable to load bitmap %s", bi.Name());
						}
						else
						{
							if(bitmap.GetBpp() == 32)
							{
								bDiffuseMap32Bits = true;
								LOG.Write(" (with alpha channel)");
							}
							bitmap.Free();
						}
						bitmapFile.Close();
					}
				}

				// Ok, copy properties

				layerInfo.texInfo.bLoop        = false;
				layerInfo.texInfo.eTextureType = UtilGL::Texturing::CTexture::TEXTURE2D;

				bitmapInfo.strFile         = m_bPreview ? bi.Name() : bi.Filename();
				bitmapInfo.bTile           = ((pMaxUVGen->GetTextureTiling() & (U_WRAP | V_WRAP)) == (U_WRAP | V_WRAP)) ? true : false;
				bitmapInfo.fSeconds        = 0.0f;
				bitmapInfo.bForceFiltering = false;
				bitmapInfo.eFilter         = UtilGL::Texturing::FILTER_TRILINEAR; // won't be used (forcefiltering = false)
				
				layerInfo.texInfo.m_vecBitmaps.push_back(bitmapInfo);

				layerInfo.eTexEnv          = nMap == ID_RL ? CShaderStandard::TEXENV_ADD : CShaderStandard::TEXENV_MODULATE;
				layerInfo.eUVGen           = pMaxUVGen->GetCoordMapping(0) == UVMAP_SPHERE_ENV ? CShaderStandard::UVGEN_ENVMAPPING : CShaderStandard::UVGEN_EXPLICITMAPPING;
				layerInfo.uMapChannel      = pMaxUVGen->GetMapChannel();
				layerInfo.v3ScrollSpeed    = CVector3(0.0f, 0.0f, 0.0f);
				layerInfo.v3RotationSpeed  = CVector3(0.0f, 0.0f, 0.0f);
				layerInfo.v3ScrollOffset   = CVector3(pMaxUVGen->GetUOffs(0), pMaxUVGen->GetVOffs(0), 0.0f);
				layerInfo.v3RotationOffset = CVector3(pMaxUVGen->GetUAng(0),  pMaxUVGen->GetVAng(0),  pMaxUVGen->GetWAng(0));
			}
			else if(pMaxTexmap->ClassID() == Class_ID(ACUBIC_CLASS_ID, 0))
			{
				ACubic*       pMaxCubic  = (ACubic*)pMaxTexmap;
				IParamBlock2* pBlock     = pMaxCubic->pblock;
				Interval      validRange = m_pMaxInterface->GetAnimRange();

				for(int nFace = 0; nFace < 6; nFace++)
				{
					int nMaxFace;

					switch(nFace)
					{
					case 0: nMaxFace = 3; break;
					case 1: nMaxFace = 2; break;
					case 2: nMaxFace = 1; break;
					case 3: nMaxFace = 0; break;
					case 4: nMaxFace = 5; break;
					case 5: nMaxFace = 4; break;
					}

					TCHAR *name;
					pBlock->GetValue(acubic_bitmap_names, TICKS_TO_SECONDS(m_fStartTime), name, validRange, nMaxFace);

					vecPaths.push_back(name);

					CStr path, file, ext;
					SplitFilename(CStr(name), &path, &file, &ext);

					std::string strFile = std::string(file.data()) + ext.data();

					vecTextures.push_back(strFile);

					bitmapInfo.strFile         = m_bPreview ? name : strFile;
					bitmapInfo.bTile           = false;
					bitmapInfo.fSeconds        = 0.0f;
					bitmapInfo.bForceFiltering = false;
					bitmapInfo.eFilter         = UtilGL::Texturing::FILTER_TRILINEAR;
					
					layerInfo.texInfo.m_vecBitmaps.push_back(bitmapInfo);
				}

				layerInfo.texInfo.bLoop        = false;
				layerInfo.texInfo.eTextureType = UtilGL::Texturing::CTexture::TEXTURECUBEMAP;

				layerInfo.eTexEnv          = nMap == ID_RL ? CShaderStandard::TEXENV_ADD : CShaderStandard::TEXENV_MODULATE;
				layerInfo.eUVGen           = CShaderStandard::UVGEN_ENVMAPPING;
				layerInfo.uMapChannel      = 0;
				layerInfo.v3ScrollSpeed    = CVector3(0.0f, 0.0f, 0.0f);
				layerInfo.v3RotationSpeed  = CVector3(0.0f, 0.0f, 0.0f);
				layerInfo.v3ScrollOffset   = CVector3(0.0f, 0.0f, 0.0f);
				layerInfo.v3RotationOffset = CVector3(0.0f, 0.0f, 0.0f);
			}
			else
			{
				if(i == 0)
				{
					LOG.Write("\n        No diffuse. Skipping.");
					break;
				}
				continue;
			}

			if(!m_bPreview && m_bCopyTextures && m_strTexturePath != "")
			{
				for(int nTex = 0; nTex != vecTextures.size(); nTex++)
				{
					// Copy textures into the specified folder

					std::string strDestPath = m_strTexturePath;

					if(strDestPath[strDestPath.length() - 1] != '\\')
					{
						strDestPath.append("\\", 1);
					}

					strDestPath.append(vecTextures[nTex]);

					if(!CopyFile(vecPaths[nTex].data(), strDestPath.data(), FALSE))
					{
						CLogger::NotifyWindow("Unable to copy %s to\n%s", vecPaths[i], strDestPath.data());
					}
				}
			}

			if(layerInfo.eUVGen == CShaderStandard::UVGEN_ENVMAPPING && i == 1)
			{
				CLogger::NotifyWindow("%s : Bump with spheremapping not supported", pShaderStd->GetName().data());
			}
			else
			{
				// Add layer

				switch(i)
				{
				case 0: pShaderStd->SetLayer(CShaderStandard::LAYER_DIFF, layerInfo); break;
				case 1: pShaderStd->SetLayer(CShaderStandard::LAYER_BUMP, layerInfo); break;
				case 2: pShaderStd->SetLayer(CShaderStandard::LAYER_MAP2, layerInfo); break;
				}
			}
		}

		// ¿Do we need blending?

		if(ARE_EQUAL(fAlpha, 1.0f) && !bDiffuseMap32Bits)
		{
			pShaderStd->SetBlendSrcFactor(UtilGL::States::BLEND_ONE);
			pShaderStd->SetBlendDstFactor(UtilGL::States::BLEND_ZERO);
		}
		else
		{
			pShaderStd->SetBlendSrcFactor(UtilGL::States::BLEND_SRCALPHA);
			pShaderStd->SetBlendDstFactor(UtilGL::States::BLEND_INVSRCALPHA);
		}

		// Add shader

		m_vecShaders.push_back(pShaderStd);
	}

	return true;
}
示例#14
0
/*
====================
GatherSkin
====================
*/
void G3DSExport::GatherSkin(INode* i_node)
{
	SKIN skin;

	// get the name of the node
	skin.name = i_node->GetName();

	// get the skin interface
	Modifier *modifier = GetModifier(i_node,SKIN_CLASSID);
	ISkin* i_skin = (ISkin*)modifier->GetInterface(I_SKIN);
	MAX_CHECK(i_skin);

	// convert to the triangle type
	Mesh* i_mesh = NULL;
	Object* obj = i_node->EvalWorldState(mTime).obj;
	if(obj && ( obj->SuperClassID() == GEOMOBJECT_CLASS_ID ))
	{
		if(obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) 
		{ 
			TriObject *tri_obj = (TriObject*)obj->ConvertToType(mTime, Class_ID(TRIOBJ_CLASS_ID, 0)); MAX_CHECK(tri_obj);
			i_mesh = &tri_obj->mesh;
		}
	}
	MAX_CHECK(i_mesh&&i_mesh->getNumFaces()&&i_mesh->getNumVerts());

	// get the material
	skin.texture = "textures/default.tga";
	Mtl* mtl = i_node->GetMtl();
	if(mtl && (mtl->ClassID()==Class_ID(DMTL_CLASS_ID, 0)) && ((StdMat*)mtl)->MapEnabled(ID_DI)) 
	{
		Texmap *texmap = mtl->GetSubTexmap(ID_DI);
		if(texmap && texmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00))
		{
			skin.texture = UnifySlashes(((BitmapTex *)texmap)->GetMapName());
			if( !strstr( skin.texture.c_str(), mPath.c_str() ) )
			{
				G3DAssert("The material(%s) is error : the texture path(%s) is illegal!",mtl->GetName(), skin.texture.c_str());
			}
			else
			{
				skin.texture = strstr(skin.texture.c_str(),mPath.c_str()) + strlen(mPath.c_str());
			}
		}
	}

	// if it has uvs
	int map_count = i_mesh->getNumMaps();
	bool has_uvs = i_mesh->getNumTVerts() && i_mesh->tvFace;
	if(!(has_uvs&&map_count)) { G3DAssert("The skin(%s) has not the uv coordinates.",skin.name.c_str()); return; }

	// get the transform
	Matrix3 mesh_matrix = i_node->GetObjectTM(mTime);
	Matrix3 node_matrix = i_node->GetNodeTM(mTime);
	Matrix3 transform = mesh_matrix * Inverse(node_matrix);

	// get the points
	skin.points.assign(i_mesh->verts, i_mesh->verts+i_mesh->getNumVerts());

	// get the triangles
	for(int i = 0; i < i_mesh->getNumFaces(); i++)
	{
		Face& face = i_mesh->faces[i];

		TRIANGLE tri;		
		tri.smoothing = face.smGroup;
		for(int j = 0; j < 3; j++)
		{
			VPTNIS v;
			v.pos = transform * i_mesh->verts[face.v[j]];

			// get the uv
			UVVert * map_verts = i_mesh->mapVerts(1);
			TVFace * map_faces = i_mesh->mapFaces(1);
			v.uv = reinterpret_cast<Point2&>(map_verts[map_faces[i].t[j]]);
			v.uv.y = 1 - v.uv.y;

			// initialize the normal
			v.normal = Point3::Origin;

			// get the vertex index
			v.index = face.v[j];

			// get the smoothing group
			v.smoothing = face.smGroup;			

			// set the index for the triangle
			tri.index0[j] = v.index;

			// reassemble the vertex list
			tri.index1[j] = AddVertex(skin, v);
		}

		// add the triangle to the table
		skin.triangles.push_back(tri);
	}

	// build the index map
	for( int i = 0; i < skin.vertexes.size(); i++ )
	{
		skin.vertex_index_map[skin.vertexes[i].index].push_back(i);
	}

	// get the skin context data
	ISkinContextData* i_skin_context_data = i_skin->GetContextInterface(i_node);
	if(i_skin_context_data == NULL) { G3DAssert("The skin(%s) has not the weight.",skin.name.c_str()); return; }

	// gets the initial matrix of the skinned object 
	Matrix3 initial_object_transform;
	i_skin->GetSkinInitTM(i_node, initial_object_transform, true);

	// process the points
	int num_points = i_skin_context_data->GetNumPoints();
	for(int i = 0; i < num_points; i++)
	{
		MAX_CHECK(i < skin.points.size());

		VPIW viw;

		// get the initial point				
		viw.pos = initial_object_transform * skin.points[i];

		// process the weights		
		std::multimap< float, int > weights;

		// get the number of bones that control this vertex
		int num_bones = i_skin_context_data->GetNumAssignedBones(i);
		if(num_bones>0)
		{
			for (int j = 0; j < num_bones; j++)
			{
				Matrix3 transform;

				// get the assigned bone of the point 
				INode* i_bone_node = i_skin->GetBone(i_skin_context_data->GetAssignedBone(i, j));
				MAX_CHECK(i_bone_node != NULL);

				// get the weight of the bone
				float weight = i_skin_context_data->GetBoneWeight(i, j);

				// add the weight to the table
				weights.insert(std::make_pair(weight, AddBone(skin,i_bone_node)));
			}
		}
		else
		{
			// add the weight to the table
			weights.insert(std::make_pair(1.f, AddBone(skin,i_node)));
		}

		// recalculate the weights
		float weight0 = 0.f, weight1 = 0.f, weight2 = 0.f;
		int index0 = 0, index1 = 0, index2 = 0;
		std::multimap< float, int >::iterator it = weights.end();
		it--;
		weight0 = it->first;
		index0 = it->second;
		if(it != weights.begin())
		{
			it--;
			weight1 = it->first;
			index1 = it->second;
			if(it != weights.begin())
			{
				it--;
				weight2 = it->first;
				index2 = it->second;
			}
		}		
		float sum_weights = weight0 + weight1 + weight2;

		// store the skin weights	
		viw.weight[0]	= weight0/sum_weights;
		viw.index[0]	= index0;
		viw.weight[1]	= weight1/sum_weights;
		viw.index[1]	= index1;
		viw.weight[2]	= weight2/sum_weights;
		viw.index[2]	= index2;
		skin.weights.push_back(viw);
	}

	// get the initial transforms
	skin.transforms.resize(skin.bones.size());
	for(int i = 0; i < skin.bones.size(); i++)
	{
		INode* node = skin.bones[i];
		Matrix3 mat;
		if (SKIN_INVALID_NODE_PTR == i_skin->GetBoneInitTM( node, mat ))
		{
			if (SKIN_INVALID_NODE_PTR == i_skin->GetSkinInitTM( node, mat ))
			{
				mat.IdentityMatrix();
			}
		}
		skin.transforms[i] = Inverse(mat);
	}

	// there is a 75 bone limit for each skinned object.
	if(skin.bones.size()>75)
	{
		G3DAssert("There are more %d bones in the skin(%s).",skin.bones.size(), i_node->GetName());
		return;
	}

	// reset the skin vertex position
	for(int i = 0; i < skin.vertexes.size(); i++)
	{
		VPTNIS& v0 = skin.vertexes[i];
		VPIW& v1 = skin.weights[v0.index];
		v0.pos = v1.pos;
	}

	// build the normal space
	BuildNormal(skin);

	// calculate the bounding box	
	skin.box.Init();
	for(int i = 0; i < skin.vertexes.size(); i++)
	{
		Point3 pt = node_matrix * skin.vertexes[i].pos;
		skin.box += pt;
	}

	// add the skin to the table
	mSkins.push_back(skin);
}
示例#15
0
bool SGP_MaxInterface::GetMtlAnim( StdMat* pStdMtl, ColorTrack& track, int nChannel )
{
	if( pStdMtl == NULL )
	{
		assert( false && "std mtl is NULL" );
		return false;
	}

	int nFrameCount = 0;
	TimeValue nStartTick = GetStartTick();
	TimeValue nEndTick = GetEndTick();
	int nTickPerFrame = GetTickPerFrame();



	track.bTiling = false;

	StdUVGen *uv = NULL;

	Texmap *tx = pStdMtl->GetSubTexmap(nChannel);
	if( tx )
	{
		if( tx->ClassID() == Class_ID( BMTEX_CLASS_ID, 0 ) )
		{
			BitmapTex *bmt = (BitmapTex*)tx;
			uv = bmt->GetUVGen();
			if( uv )
			{
				track.nUTile = (int)uv->GetUScl(0);
				track.nVTile = (int)uv->GetVScl(0);
				if( track.nUTile == 1 && track.nVTile == 1 )
					track.bTiling = false;
				else
					track.bTiling = true;
				track.nStartFrame = bmt->GetStartTime();
				track.fPlaybackRate = bmt->GetPlaybackRate();
				track.nLoopMode = bmt->GetEndCondition();

				if( uv->GetUAng( 0 ) != 0.0f ||
					uv->GetVAng( 0 ) != 0.0f )
				{
					track.fUSpeed = uv->GetUAng( 0 ) / piOver180;
					track.fVSpeed = uv->GetVAng( 0 ) / piOver180;
					track.bUVMoving = true;
				}
				else
					track.bUVMoving = false;
			}
		}
	}


	TimeValue t;
	for( t = nStartTick; t <= nEndTick; t += nTickPerFrame )
		nFrameCount++;


	track.ColorKeyFrame.resize( nFrameCount );

	t = nStartTick;
	for( int i = 0; i < nFrameCount; i++, t += nTickPerFrame )
	{
		SGP_ColorKey key;
		memset( &key, 0x00, sizeof( key ) );
		Color diffuse	= pStdMtl->GetDiffuse( t );
		Color ambient	= pStdMtl->GetAmbient( t );
		Color specular	= pStdMtl->GetSpecular( t );

		Color filter	= pStdMtl->GetFilter( t );
		float alpha		= pStdMtl->GetOpacity( t );
		float shinstr	= pStdMtl->GetShinStr(t);
		float selfillum = pStdMtl->GetSelfIllum( t );

		float uoffset	= 0;
		float voffset	= 0;
		if( uv )
		{
			uoffset	= uv->GetUOffs( t );
			voffset	= uv->GetVOffs( t );
		}
/*
		int	nTransparencyType = pStdMtl->GetTransparencyType();

		key.dwBlendMode = 0;
		switch( nTransparencyType )
		{
		case TRANSP_SUBTRACTIVE:
			key.dwBlendMode |= HR3D_MDX2_MODULATE;
			break;
		case TRANSP_ADDITIVE:
			key.dwBlendMode |= HR3D_MDX2_ADD;
			break;
		case TRANSP_FILTER:
			key.dwBlendMode |= HR3D_MDX2_MODULATE2X;
			break;
		default:
			break;
		};
*/
		key.dr = diffuse.r;
		key.dg = diffuse.g;
		key.db = diffuse.b;

		key.da = alpha;


		if( uv )
		{
			key.uoffset = uv->GetUOffs( t );
			key.voffset = uv->GetVOffs( t );
		}
		else
		{
			key.uoffset = 0;
			key.voffset = 0;
		}

		track.ColorKeyFrame.getReference(i) = key;
	}

	return true;
}