Question

I'm using the standard .fbx importer with custom shaders in XNA. The .fbx model is UV wrapped properly and is textured appropriately when I use BasicEffect. However when I use my custom effect I have to load the texture in as a parameter, and it is not mapped correctly.

Questions: 1) How can I texture my .fbx model properly using the included texture's coordinates with custom effects? 2) Is there a way to access the texture from the loaded .fbx model object? Where does this texture go?

Note: I've studied custom content pipelines and don't believe writing my own Fbx importer/processor will be efficient. However if someone can descriptively supply me with firsthand experience of this being the answer than I will use the custom pipeline.

Thank you for your time and for reading this post.

Was it helpful?

Solution

This is an old question, but I had to figure this out myself yesterday so I thought I'd post a followup:

If you're using the default FBX content processor and have the DefaultEffect property set to BasicEffect, you can get the Texture2D for the object via:

texture = ((BasicEffect)model.Meshes[0].Effects[0]).Texture;

Note that the each mesh in the model may have a different texture.

The texture coordinates are stored in the MeshPart'sVertexBuffer along with position, etc. I've seen two vertex declarations. For a model/mesh that used a single texture (bitmap material in 3DS Max), the vertex declaration was VertexPositionNormalTexture.

For a model that had two textures (a bitmap and an opacity/alpha map), the declaration had the elements:

Position
Normal
Texture (usage index 0)
Texture (usage index 1)

or, wrapped into an IVertexType structure,

public struct VertexPositionNormalTextureTexture : IVertexType
{
    public Vector3 Position;
    public Vector3 Normal;
    public Vector4 Texture0;
    public Vector4 Texture1;

    public static VertexDeclaration VertexDeclaration
    {
        get
        {
            return new VertexDeclaration
            (
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.Position, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.Normal, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 0)
            ,
            new VertexElement(0,VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 1)
            );
        }
    }


    VertexDeclaration IVertexType.VertexDeclaration
    {
        get { return VertexDeclaration; }
    }
}

and the equivelant HLSL structure:

struct VertexPositionNormalTextureTexture
{
    float3 Position : POSITION0;
    float3 Normal : NORMAL0;
    float4 Texture0 : TEXCOORD0;
    float4 Texture1 : TEXCOORD1;
};

Note that I changed .Position and .Normal from Vector4 and Vector3 to float4 and float3 before I posted this, and haven't tested it. It's possible that they may need to be changed back to Vector4 and float4.

Of course, you'll need a sampler and some basic logic in your pixel shader to read each texture. Assuming you've set two effect parameters xTexture0 and xTexture1 to Texture2D objects containing your color texture and opacity map,

// texture sampler
sampler Sampler0 = sampler_state
{
    Texture = (xTexture0);
};

sampler Sampler1 = sampler_state
{
    Texture = (xTexture1);
};

and here's a simple two-texture pixel shader. If you only want one texture, just read from the first sampler and return the value, or apply lighting, etc.

float4 TwoTexturePixelShader(VertexPositionNormalTextureTexture input) : COLOR0
{
    float4 texel0;
    float4 texel1;
    float4 pixel;

    // check global effect parameter to see if we want textures turned on
    // (useful for debugging geometry weirdness)
    if (TexturesEnabled)
    {
        texel0 = tex2D(Sampler0, input.Texture0);
        texel1 = tex2D(Sampler1, input.Texture1);       
        /// Assume texel1 is an alpha map, so just multiple texel0 by that alpha.
        pixel.rgb=texel0.rgb;
        pixel.a=texel0.a;
    }
    else
        /// return 100% green
        pixel = float4(0,1,0,1);

    return pixel;

} 

The relevant points here are that the texture coordinates are already present in the FBX and are already stored in each MeshPart's VertexBuffer, so all you need to do is extract the texture, pass it into your shader as a global effect parameter, and proceed as normal.

OTHER TIPS

The reason it's not working is because you have to set the effect's parameters manually instead of relying on the basiceffect (which would have had the shader parameters set in the content pipeline). Now, I'm not familiar with your shader so I couldn't prescribe code to solve your problem ...

To answer your second question, you can get around it in a roundabout sort of way. Because the model loads in the content pipeline with basiceffect by default, the texture is imported and assigned to the shader's parameters inside of the pipeline. So if you want to access it you'd have to look at the modelmeshpart's default effect property. Here is a forum post that describes this process.

The more correct answer would be a compromise between a full on custom importer and just using the default. You can make a custom modelprocessor that inherits from the existing one. In there, you can import your own custom effect, along with your custom textures and whatever parameters you need to set. and set it on the modelcontent. There was an article (either on Shawn Hargreave's blog, or on msdn) that showed how to do this, but I'm failing to find it at the moment unfortunately.

good luck!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top