ScanMountGoat / ssbh_editor

View, edit, and validate models for Smash Ultimate
MIT License
32 stars 4 forks source link

Bump Map for Lego-logo on studs #194

Closed tobblx closed 4 months ago

tobblx commented 4 months ago

First of all I have to say: What a great ldraw importer plugin. Thank you very much for your work! The cycles renderings look amazing. I still have one small wish, which I have missed with most of the ldraw importers used so far: It would be good if you could use a central normal map / bump map for the Lego studs. So there is no extra geometry required, which are lots of additional vertices per part. The imported models could then also better be used in game-engines. This would require the UV coordinates for the vertices of the Lego studs to be set. Perhaps this could be done directly when creating the meshes. I have created a script for this for testing. It sets the UV-Coordinates of the identified stud-vertices in all selected objects. (They are identified by having 8, 16 or 48 connecting edges - the edge-length must be 6) UV coordinates in the range of x /y = 10.0 - 11.0 are set for the studs. This means that there can be no conflicts with other parts whose UV coordinates are between 0 and 1, for example. If required, the Lego stud bump map can then be added to the existing BUMP map in the shader. I hope I could give you some inspiration for an extension to the already great importer. :-) (See examples in the attachments) Lego_logo_Bump_Map_Examples.zip Example_Rendering_With_Bump_Map_Logos Example_Shader_Nodes_Modification

import bpy
import bmesh

def set_uv_from_vertex_coordinates(allVerts, obj):
    min_x = min(vert.co.x for vert in allVerts)
    max_x = max(vert.co.x for vert in allVerts)
    min_y = min(vert.co.y for vert in allVerts)
    max_y = max(vert.co.y for vert in allVerts)
    min_z = min(vert.co.z for vert in allVerts)
    max_z = max(vert.co.z for vert in allVerts)

    range_x = max_x - min_x
    range_y = max_y - min_y
    range_z = max_z - min_z

    if range_x<0.001:
        print("range_x<0.001 bei "+obj.name)
        return
    if range_z<0.001:
        print("range_z<0.001 bei "+obj.name)
        return

    for vert in allVerts:
        for loop in vert.link_loops:
            u = (vert.co.x - min_x) / range_x +10
            v = (vert.co.z - min_z) / range_z +10
            loop[uv_layer].uv = (u,v)

# Sicherstellen, dass ein Objekt ausgewählt ist und es sich um ein Mesh-Objekt handelt
for obj in bpy.context.selected_objects:
    if obj is None or obj.type != 'MESH':
        #raise ValueError("Kein Mesh-Objekt ausgewählt")
        continue

    # Sicherstellen, dass das Objekt sich im Objektmodus befindet
    bpy.ops.object.mode_set(mode='OBJECT')

    # Erstellen eines BMesh Objekts aus dem aktiven Mesh
    bm = bmesh.new()
    bm.from_mesh(obj.data)

    # Sicherstellen, dass das Mesh eine UV-Map hat
    uv_layer = bm.loops.layers.uv.verify()

    # Schleife über alle Vertices
    for v in bm.verts:
        # Anzahl der angrenzenden Vertices ermitteln
        connected_vertices = [e.other_vert(v) for e in v.link_edges]
        if len(connected_vertices) == 8 or len(connected_vertices) == 16 or len(connected_vertices) == 48 :
            allEdgeLengthsAreCorrect = True;
            for e in v.link_edges:
                edgeLength=e.calc_length()
                if edgeLength<5.9 or edgeLength>6.1:
                    allEdgeLengthsAreCorrect = False
            if allEdgeLengthsAreCorrect:
                #print(str(len(connected_vertices)) +": "+ str(v.link_edges[0].calc_length()))
                allVerts = tuple(connected_vertices + [v])
                set_uv_from_vertex_coordinates(allVerts, obj)

    # Zurückschreiben der BMesh-Daten in das Mesh und Aktualisieren
    bm.to_mesh(obj.data)
    bm.free()

    # Aktualisieren des Meshes
    obj.data.update()

print("UV-Koordinaten wurden erfolgreich gesetzt.")