ScanMountGoat / ldr_tools_blender

LDraw Blender importer addon
MIT License
11 stars 3 forks source link

Bump Map for Lego-logo on studs #30

Open tobblx opened 2 months ago

tobblx commented 2 months ago

First of all I want 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.")
ScanMountGoat commented 2 months ago

This would be a nice feature. It looks like your script is essentially applying a planar UV projection to the cylinder for each stud. One approach is to generate these UV coordinates using shading nodes or geometry nodes. I already have an attribute for detecting if a face is part of a stud for slope materials.

For modifying the mesh directly, it makes sense to do this with numpy arrays or in Rust for performance. I'll need to play around with this some to see if there are better ways of doing this. Let me me know if you have any more ideas for how to improve imports.