cbhacks / CrashEdit

Unofficial Crash Bandicoot 1/2/3 Level Editor
https://www.cbhacks.com/tools/crashedit/
Other
135 stars 25 forks source link

Support exporting animation frames and scenery to OBJ with textures #158

Open Almamu opened 1 year ago

Almamu commented 1 year ago

This PR adds the following featurers

Once the OBJ is exported it can be imported in any 3D software. Due to the OBJ format being a bit lacking, there's some minor adjustements that need to happen once imported into your 3D tool of choice. I'd recommend using blender and this script to get the best results possible:

import bpy

# apply the node tree changes to the material
def update_material (materialRef):
    image = materialRef.node_tree.nodes['Image Texture']
    bsdf = materialRef.node_tree.nodes['Principled BSDF']
    # remove the output links for the image
    for link in image.outputs['Color'].links:
        materialRef.node_tree.links.remove (link)
    # connect the image's alpha to the principled's alpha
    materialRef.node_tree.links.new (image.outputs['Alpha'], bsdf.inputs['Alpha'])
    # add the color attribute
    color = materialRef.node_tree.nodes.new ('ShaderNodeVertexColor')
    # set the layer name, use hardcoded value for now
    color.layer_name = 'Color'
    # add the color mix
    mix = materialRef.node_tree.nodes.new ('ShaderNodeMix')
    # setup settings
    mix.data_type = 'RGBA'
    materialRef.blend_method = 'BLEND'
    materialRef.show_transparent_back = False
    # now link inputs, depending on the blending mode these have to be performed differently
    if image.image.name.endswith("b0.bmp"):
        mix.inputs[0].default_value = 1
        mix.blend_type = 'MULTIPLY'
        materialRef.node_tree.links.new (image.outputs['Color'], mix.inputs[6])
        materialRef.node_tree.links.new (color.outputs['Color'], mix.inputs[7])
    elif image.image.name.endswith("b1.bmp"):
        mix.inputs[0].default_value = 0
        mix.blend_type = 'ADD'
        materialRef.node_tree.links.new (image.outputs['Color'], mix.inputs[6])
        materialRef.node_tree.links.new (color.outputs['Color'], mix.inputs[7])
        materialRef.node_tree.links.new (color.outputs['Color'], bsdf.inputs['Alpha'])
    elif image.image.name.endswith("b2.bmp"):
        mix.inputs[0].default_value = 0
        mix.blend_type = 'SUBTRACT'
        materialRef.node_tree.links.new (image.outputs['Color'], mix.inputs[6])
        materialRef.node_tree.links.new (color.outputs['Color'], mix.inputs[7])
    else:
        mix.inputs[0].default_value = 1
        mix.blend_type = 'MULTIPLY'
        materialRef.node_tree.links.new (image.outputs['Color'], mix.inputs[6])
        materialRef.node_tree.links.new (color.outputs['Color'], mix.inputs[7])

    materialRef.node_tree.links.new (mix.outputs[2], bsdf.inputs[0])
    image.interpolation = 'Closest'
    return

def can_update_material (materialRef):
    if not materialRef:
        return False
    if not materialRef.node_tree:
        return False
    if not materialRef.node_tree.nodes:
        return False
    if not len(materialRef.node_tree.nodes) == 3:
        return False
    if not 'Image Texture' in materialRef.node_tree.nodes:
        return False
    if not 'Material Output' in materialRef.node_tree.nodes:
        return False
    if not 'Principled BSDF' in materialRef.node_tree.nodes:
        return False

for material in bpy.data.materials:
    if can_update_material (material) is False:
        continue

    update_material (material)

The script will update all the materials in use and create the proper Color Attribute and color mixing required for everything to display properly.

It might be possible to improve the export in the future, like using object groups for exporting all frames of an animation as one single obj file, instead of multiple, or pack the textures into one texture atlas to make better usage of the texture space, but it should be a good starting point for anyone looking to export animations and scenery.