Skip to main content
GameDev.net gamedev.net
🔒 Locked

Blender -> SDKMesh: multiple UV maps

Started by Daerst Dec 10, 2014 at 2:56 PM 7 replies 6.3k views
Original Post
Daerst
Daerst

Hi there,

I'm prototyping a Global Illumination technique by extending one of NVidia's demos (DX11 Samples - Diffuse Global Illumination). The demo uses the CDXUTSDKMesh class to load and render the scene.

What I did so far:

  • Create a test scene in Blender.
  • Unwrap it for texturing and export it using the .x exporter by Chris Foster.
  • Use the DirectX MeshConvert utility to convert my .x file to .sdkmesh format.

This works fine so far and has allowed me to fix some carelessness on NVidia's part (e.g. lots of unwanted normal interpolation on flat surfaces and no (!) tangents, although they use normal mapping in the demo...). Now, my technique requires two different UV maps (texturing + lightmapping) for my test scene. What I've tried:

  • Create a second UV map.
  • The .x exporter only handles the active UV map, so I extended it to output the second UV map as follows (pseudo-C++, although it was actually implemented in Python):
    
    FVFData
    {
        258;
        NUM_VERTICES * 2;
        reinterpret_cast<unsigned long>(uv[0].x),
        reinterpret_cast<unsigned long>(uv[1].x),
        ...;
    }
  • Use MeshConvert with /tcount 2, which creates a second UV channel. Whatever I do, the values of this channel are always 0. The tool does not seem to care about the FVFData at all.
  • IDEA: Use the vertex color and just put the UVs in the RG channels. Unfortunately, MeshConvert doesn't seem to care the vertex color either.
  • IDEA: Load two separate meshes, one with each UV map, and somehow merge them in code. Since the vertex buffers are not writeable, I have not found a way to do this.

Any hints, experiences or further ideas are appreciated.

Thanks
David

Daerst
Daerst

Some progress on my end, documented in case anyone needs it...

I have dumped the uncooperative MeshConvert in favor of the DirectX Content Exporter. I downloaded the source and the Autodesk FBX SDK 2011.3.1 and compiled the tool without problems in Visual Studio 2012. I am now able to convert .fbx files to .sdkmesh, which is way more flexible than the .x solution. If you plan to do the same, be wary of the -compressvertexdata- and -force32bitindices+ parameters. The first is enabled by default, an can be disabled using the minus at the end. If you don't do this, you normals etc. are automatically compressed into smaller formats. Since MeshConvert doesn't have such functionality, it's easiest to disable it at first and enable it later if you need it. The same goes for the latter parameter, forcing indices to be 32 bit. If you don't enable this, the Content Exporter uses 16 bit indices if possible.

I now receive values for a second UV channel in my program, but they are horribly wrong. My texture UV channel looks good, while my lightmap UV channel is messed up. If I switch them, so that the lightmap is UV channel 0, it suddenly looks ok but the texture UV channel is distorted. I have no idea whether the Blender FBX exporter or DirectX Content Exporter messes it up, but something is still horribly wrong. If I duplicate my UV channel and output exactly the same UV channel twice, everything looks good. Any ideas?

Buckeye
Buckeye

Thanks for updating your progress. Over the years, there hasn't been much posted about using the FVFData template. I've personally never gotten FVFData instances to work, so I can only guess.

However, the FVFData struct you posted specifies an FVF code 258 equivalent to 0x102, or D3DFVF_XYZ | D3DFVF_TEX1 **, but the data is only texture coordinates.

** I.e., from the docs:

#define D3DFVF_XYZ 0x002

#define D3DFVF_TEX1 0x100

The FVFData template is described as "Specifies the mesh data minus the position data." [ emphasis mine - buckeye ] Explicitly including D3DFVF_XYZ may be confusing the issue.

You might want to play with the FVF code in your script, perhaps starting with 256 ( just D3DFVF_TEX1 ). N.B., perhaps others can give you more info on the use of the texture FVFs, but (if you haven't already looked at it) you may find this link of interest, in particular, the section "Texture Flags."

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.
Daerst
Daerst

Thanks for your reply, Buckeye. 258 is a value I found online and also encountered when using MeshConvert to convert from ascii .x to ascii .x - kinda pointless, but with the /tcount 2 option you FVFData with code 258 and all values 0. I don't know why, but it seems to be a broad agreement. I tried some other values. Random values prevent MeshConvert from reading the file, so he at least tries to parse it. 256 and 258 work, 257 doesn't. Everything leads to zeroes in UV channel 1. I think MeshConvert just doesn't use the data,

Regarding the FBX pipeline, I posted the error to MSDN and hope to get an answer there. Quoting myself from here:

The FBX exporter automatically collapses UV values which are the same / very close, and each vertex has an index into the list of UV values. So far, so good. If this mapping is the same, both UV maps are imported correctly. If the mapping is different (e.g. if UV channel 0 has two triangles [vertices are already duplicated] mapped as a quad, yielding 4 values in the list of UV values, and UV channel 1 has the same two triangles mapped separately, yielding 6 values in the list of UV values), my second UV map is distorted. Some values in UV channel 1 are mixed up, and for some I have no idea where they're coming from.

Buckeye
Buckeye

FYI, as you've already concluded, the MeshConvert utility (the DX SDK June 2010 version) does not provide for multiple tex coords, or user data. Examination of the source code indicates it uses D3DXLoadMeshFromX, and then clones the loaded mesh, specifying the cloned vertex format elements as position, normal, texcoord, tangent and binormal with usage indices of 0 only. [ EDIT: I.e., a second set of tex coords, or similar user-defined data, in the vertex would have to be specified with D3DDECLUSAGE_TEXCOORD and usage index 1. ] The SDKMesh output eventually derives from data in that cloned mesh so the "extra" data is never passed on.

The creation of the SDKMesh is a bit convoluted (TL;DR), so I'm not sure if it would even have a chance of working - but, if you have a bit of adventure in you, you might want to add an additional element for tex coord with a usage index of 1 to the cloned mesh element decl, to see if it will load the second set of tex coords.

EDIT2: Perhaps another useless suggestion, but, if you're inclined to try the change to cloned mesh decl mentioned, you might try first loading your x-file with D3DXLoadMeshFromX, and, if successful, analyze the resulting mesh vertex declaration to see if the FVFData is even recognized at that point in the process.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.
Daerst
Daerst

Got it :) You really need to only add a single line and modify the offsets in MeshConvert's LoaderXFile.cpp:


D3DVERTEXELEMENT9 declTanBi[] = 
{
    { 0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
    { 0, 12, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0 },
    { 0, 24, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
    { 0, 32, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
    { 0, 40, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT,  0 },
    { 0, 52, D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 },
    D3DDECL_END()
};

This will do for me since all my meshes have exactly 2 UV channels. In practice, this should depend on SETTINGS::NumTexCoords (the output vertex declaration already does, so no need to change anything there).

Here's what I added to Blender's X exporter (export_x.py):


        # Write additional UV coordinates using FVFData
        FVFCode = 258
        Index = 0
        for UVLayer in Mesh.uv_layers:
            if UVLayer == Mesh.uv_layers.active:
                continue # skip active layer, we already had that

            self.Exporter.File.Write("FVFData {{ // {} UV coordinates {}\n" \
                .format(self.SafeName, UVLayer.name))
            self.Exporter.File.Indent()
            self.Exporter.File.Write("{};\n".format(FVFCode))
            self.Exporter.File.Write("{};\n".format(2 * VertexCount))
            for Polygon in Mesh.polygons:
                Vertices = []
                for Vertex in [UVLayer.data[Vertex] for Vertex in
                    Polygon.loop_indices]:
                    Vertices.append(tuple(Vertex.uv))
                for Vertex in Vertices:
                    self.Exporter.File.Write("{},\n".format( struct.unpack("<I", struct.pack("<f", Vertex[0]))[0] ))
                    self.Exporter.File.Write("{}".format( struct.unpack("<I", struct.pack("<f", 1 - Vertex[1]))[0] ))
                    Index += 1
                    if Index == VertexCount:
                        self.Exporter.File.Write(";\n", Indent=False)
                    else:
                        self.Exporter.File.Write(",\n", Indent=False)

            self.Exporter.File.Unindent()
            self.Exporter.File.Write("}} // End of {} UV coordinates {}\n".format(
                self.SafeName, UVLayer.name))

Again, this will only work for two UV channels, since for three or more you would have to use DeclData I think. The creepy struct.pack / struct.unpack stuff reinterprets a float as an integer, as expected by the FVFData.

Thanks for your hints, Buckeye!

Buckeye
Buckeye




Thanks for your hints, Buckeye!

Thanks for letting me use you as a guinea pig! wink.png

BTW, what FVF code did you end up with in FVFData?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.
Daerst
Daerst

258 works fine. For science I'll try 256 next week and see if that works too.

Buckeye
Buckeye




258 works fine. For science I'll try 256 ...

Thanks. "For science.." laugh.png

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly. You don't forget how to play when you grow old; you grow old when you forget how to play.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.