Hey, all. I know I’m certainly not the first to accomplish this (David Winter’s been doing it a while with his player uniforms), but I figured I’d go ahead and post a code sample that will paint texture layers one on top of the other on a render surface, then bake them out to a texture, ready to be applied to your friendly neighborhood uniform, character model, vehicle, or what have you.
The code will allow you to add each texture layer with:
The system works just as well for baking out multi-layered normal maps, too: very useful for adding dents to vehicles, or wrinkles to faces! Just remember to normalize your normal map after baking it all down! And although I haven’t really done anything with it, I’m perfectly sure that this would work fine for changing specular maps (adding sweat/blood), bump maps, and alpha maps.
Public Class texBaker
'Dimensions used to store the width and height of the render surface we'll be painting to
Private bakeWidth As Integer
Private bakeHeight As Integer
'Each texture we want to bake on will be stored in a layer, and will be painted in the order it's added
Private Structure bakeLayers
Dim texID As Integer
Dim texAlpha As Single
Dim texColor As MTV3D65.TV_3DVECTOR
Dim hasMask As Boolean
Dim maskID As Integer
End Structure
Private bakeLayer() As bakeLayers
Private iLayers As Integer
'Before we start baking, we initialize our values and clear our layers
Public Sub begin(ByVal width As Integer, ByVal height As Integer)
bakeWidth = width
bakeHeight = height
iLayers = -1
ReDim bakeLayer(0)
End Sub
'As the user adds each layer, we create a new layer and fill it with the appropriate information
Public Sub addLayer(ByVal textureID As Integer, ByVal textureAlpha As Single, ByVal texturecolor As MTV3D65.TV_3DVECTOR, Optional ByVal maskID As Integer = -1)
iLayers += 1
ReDim Preserve bakeLayer(iLayers)
bakeLayer(iLayers).texID = textureID
bakeLayer(iLayers).texAlpha = textureAlpha
bakeLayer(iLayers).texColor = texturecolor
bakeLayer(iLayers).maskID = maskID
End Sub
Public Function bake(Optional ByVal bakedTextureName As String = "") As Integer
'When the user is ready to bake out a texture, we make sure there are layers ready to be baked, then...
If iLayers > -1 Then
'We create our render surface and begin painting on each layer
Dim bakeSurface As New MTV3D65.TVRenderSurface
bakeSurface = tvSCENE.CreateRenderSurface(bakeWidth, bakeHeight)
bakeSurface.StartRender()
Dim i
For i = 0 To iLayers
'If a layer has a mask assigned to it, we take the diffuse texture and add an alpha channel to it
If bakeLayer(i).maskID <> -1 Then
bakeLayer(i).texID = tvTEXTURES.AddAlphaChannel(bakeLayer(i).texID, bakeLayer(i).maskID, "Fex's texBaker's temporary alpha'd texture")
End If
'We paint on each layer with the appropriate color and opacity
tvSCREEN.Draw_Texture(bakeLayer(i).texID, 0, 0, bakeWidth - 1, bakeHeight - 1, tvGLOBALS.RGBA(bakeLayer(i).texColor.x, bakeLayer(i).texColor.y, bakeLayer(i).texColor.z, bakeLayer(i).texAlpha), tvGLOBALS.RGBA(bakeLayer(i).texColor.x, bakeLayer(i).texColor.y, bakeLayer(i).texColor.z, bakeLayer(i).texAlpha), tvGLOBALS.RGBA(bakeLayer(i).texColor.x, bakeLayer(i).texColor.y, bakeLayer(i).texColor.z, bakeLayer(i).texAlpha), tvGLOBALS.RGBA(bakeLayer(i).texColor.x, bakeLayer(i).texColor.y, bakeLayer(i).texColor.z, bakeLayer(i).texAlpha))
'If we created a new texture with an alpha channel just a second ago, we clean it up here
If bakeLayer(i).maskID <> -1 Then
tvTEXTURES.DeleteTexture(tvGLOBALS.GetTex("texBaker's temporary alpha'd texture"))
End If
Next
bakeSurface.EndRender()
'Once we're done baking down all of our layers, we save out our baked render surface as a texture
If tvTEXTURES.TextureExists(bakedTextureName) Then tvTEXTURES.DeleteTexture(tvGLOBALS.GetTex(bakedTextureName))
bake = bakeSurface.CreateStaticTextureFromRenderSurface(bakeWidth, bakeHeight, MTV3D65.CONST_TV_COLORKEY.TV_COLORKEY_USE_ALPHA_CHANNEL, bakedTextureName)
'Cleanup time!
bakeSurface.Destroy()
End If
End Function
End Class
Public tvBAKER As New texBaker
'When I change the diffuse map progress bar values:
tvBAKER.begin(256, 256)
tvBAKER.addLayer(tvGLOBALS.GetTex("headBase"), 1, vector(1, 1, 1))
tvBAKER.addLayer(tvGLOBALS.GetTex("facialHair"), ProgressBar1.Value / 100, vector(60 / 256, 40 / 256, 35 / 256), tvGLOBALS.GetTex("facialHairMask"))
testHead.SetTexture(tvBAKER.bake("bakedDiffuseTexture"), 0)
'When I change the normal map progress bar values:
tvBAKER.begin(256, 256)
tvBAKER.addLayer(tvGLOBALS.GetTex("headNormal"), 1, vector(1, 1, 1))
tvBAKER.addLayer(tvGLOBALS.GetTex("wrinkleNormal"), ProgressBar2.Value / 100, vector(1, 1, 1))
testHead.SetTextureEx(MTV3D65.CONST_TV_LAYER.TV_LAYER_NORMALMAP, tvBAKER.bake("bakedNormalTexture"), 0)