Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added rust_material/rust_material.zip
Binary file not shown.
110 changes: 110 additions & 0 deletions rust_material/rust_material/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
bl_info = {
"name": "Rust Material Generator",
"author": "Saikiran Shet",
"version": (1, 2),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > Rust Material",
"description": "Generates a rust material with textures and interactive controls",
"category": "Material",
}

import bpy
import importlib

# safe import so reloading in Blender text editor doesn't break
from . import rust_generator
importlib.reload(rust_generator)
from .rust_generator import create_rust_material

# ------------------ Property Group ------------------ #
class RustProperties(bpy.types.PropertyGroup):
roughness: bpy.props.FloatProperty(
name="Roughness",
description="Adjust material roughness",
default=0.5,
min=0.0,
max=1.0,
step=0.01
)
color_tint: bpy.props.FloatVectorProperty(
name="Color Tint",
description="Tint the rust material color",
subtype='COLOR',
size=3,
min=0.0,
max=1.0,
default=(1.0, 1.0, 1.0)
)
normal_strength: bpy.props.FloatProperty(
name="Normal Strength",
description="Strength of the normal map",
default=1.0,
min=0.0,
max=5.0,
step=0.1
)
displacement_height: bpy.props.FloatProperty(
name="Displacement Height",
description="Height multiplier for displacement",
default=0.05,
min=0.0,
max=1.0,
step=0.01
)

# ------------------ Operator ------------------ #
class RUST_OT_generate(bpy.types.Operator):
bl_idname = "rust.generate_material"
bl_label = "Generate Rust Material"
bl_description = "Create or update a rust material with textures"

def execute(self, context):
props = context.scene.rust_props
try:
create_rust_material(
roughness=props.roughness,
color_tint=props.color_tint,
normal_strength=props.normal_strength,
disp_height=props.displacement_height
)
self.report({'INFO'}, "Rust Material Created / Updated Successfully!")
except Exception as e:
# also print to console for full traceback
import traceback
traceback.print_exc()
self.report({'ERROR'}, f"Rust addon error: {str(e)}")
return {'FINISHED'}

# ------------------ Panel ------------------ #
class RUST_PT_panel(bpy.types.Panel):
bl_label = "Rust Material Generator"
bl_idname = "RUST_PT_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Rust Material'

def draw(self, context):
layout = self.layout
props = context.scene.rust_props

layout.prop(props, "roughness")
layout.prop(props, "normal_strength")
layout.prop(props, "displacement_height")
layout.prop(props, "color_tint")
layout.operator("rust.generate_material", text="Create / Update Rust Material")

# ------------------ Registration ------------------ #
classes = [RustProperties, RUST_OT_generate, RUST_PT_panel]

def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.rust_props = bpy.props.PointerProperty(type=RustProperties)

def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.rust_props

if __name__ == "__main__":
register()
Binary file added rust_material/rust_material/assets/Metal056C.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
126 changes: 126 additions & 0 deletions rust_material/rust_material/rust_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import bpy
import os

# filenames expected in assets folder (edit if your names differ)
TEXTURES = {
"Base Color": "Metal056C_4K-JPG_Color.jpg",
"Normal": "Metal056C_4K-JPG_NormalDX.jpg",
"Displacement": "Metal056C_4K-JPG_Displacement.jpg",
"Metalness": "Metal056C_4K-JPG_Metalness.jpg",
}

def asset_path(filename):
addon_dir = os.path.dirname(__file__)
return os.path.join(addon_dir, "assets", filename)

def load_image_safe(filename):
"""Load an image if present. Returns image or None and prints helpful message."""
path = asset_path(filename)
if os.path.exists(path):
try:
# reuse loaded image if already in bpy.data.images
for img in bpy.data.images:
if os.path.abspath(bpy.path.abspath(img.filepath)) == os.path.abspath(path):
return img
return bpy.data.images.load(path)
except Exception as e:
raise RuntimeError(f"Failed to load image '{path}': {e}")
else:
# do not raise here — return None so we can fall back
print(f"[Rust Addon] Texture not found: {path}")
return None

def ensure_non_color(image):
"""Set colorspace appropriately for normal and non-color maps."""
if image is None:
return
try:
# Normal maps and data maps should use Non-Color
image.colorspace_settings.name = 'Non-Color'
except Exception:
# older Blender versions or other issues may throw — ignore
pass

def create_rust_material(roughness=0.5, color_tint=(1.0,1.0,1.0), normal_strength=1.0, disp_height=0.05):
mat_name = "RustMaterial"
# create or reuse material
mat = bpy.data.materials.get(mat_name) or bpy.data.materials.new(mat_name)
mat.use_nodes = True
nodes = mat.node_tree.nodes
links = mat.node_tree.links

# clear nodes cleanly
nodes.clear()

# Output and Principled
output = nodes.new("ShaderNodeOutputMaterial"); output.location = (600, 0)
bsdf = nodes.new("ShaderNodeBsdfPrincipled"); bsdf.location = (300, 0)
bsdf.inputs['Roughness'].default_value = roughness
links.new(bsdf.outputs['BSDF'], output.inputs['Surface'])

# Tint node (mix multiply)
tint = nodes.new("ShaderNodeMixRGB"); tint.blend_type = 'MULTIPLY'
tint.inputs['Fac'].default_value = 1.0
tint.inputs['Color2'].default_value = (*color_tint, 1.0)
tint.location = (0, 200)
links.new(tint.outputs['Color'], bsdf.inputs['Base Color'])

# load textures (safe)
imgs = {}
for key, fname in TEXTURES.items():
imgs[key] = load_image_safe(fname)

# Base Color
if imgs["Base Color"]:
tex_base = nodes.new("ShaderNodeTexImage"); tex_base.image = imgs["Base Color"]
tex_base.location = (-400, 200)
links.new(tex_base.outputs['Color'], tint.inputs['Color1'])
else:
# no texture: set tint directly on base color
bsdf.inputs['Base Color'].default_value = (*color_tint, 1.0)

# Normal
if imgs["Normal"]:
tex_norm = nodes.new("ShaderNodeTexImage"); tex_norm.image = imgs["Normal"]
tex_norm.location = (-400, 0)
ensure_non_color(tex_norm.image)
tex_norm.image.colorspace_settings.name = 'Non-Color'
normal_map = nodes.new("ShaderNodeNormalMap"); normal_map.location = (-150, 0)
normal_map.inputs['Strength'].default_value = normal_strength
links.new(tex_norm.outputs['Color'], normal_map.inputs['Color'])
links.new(normal_map.outputs['Normal'], bsdf.inputs['Normal'])

# Metalness
if imgs["Metalness"]:
tex_metal = nodes.new("ShaderNodeTexImage"); tex_metal.image = imgs["Metalness"]
tex_metal.location = (-400, -200)
ensure_non_color(tex_metal.image)
links.new(tex_metal.outputs['Color'], bsdf.inputs['Metallic'])
else:
bsdf.inputs['Metallic'].default_value = 0.0

# Displacement (connect to Material Output displacement)
if imgs["Displacement"]:
tex_disp = nodes.new("ShaderNodeTexImage"); tex_disp.image = imgs["Displacement"]
tex_disp.location = (-400, -400)
ensure_non_color(tex_disp.image)
# Convert color to height (in case it's colored): use RGB to BW or separate
rgb2bw = nodes.new("ShaderNodeRGBToBW"); rgb2bw.location = (-200, -400)
links.new(tex_disp.outputs['Color'], rgb2bw.inputs['Color'])
disp_node = nodes.new("ShaderNodeDisplacement"); disp_node.location = (100, -400)
disp_node.inputs['Scale'].default_value = disp_height
links.new(rgb2bw.outputs['Val'], disp_node.inputs['Height'])
links.new(disp_node.outputs['Displacement'], output.inputs['Displacement'])
# Also set material settings for displacement (if using cycles)
try:
# enable displacement in material settings if available
mat.cycles.displacement_method = 'BOTH'
except Exception:
pass

# tidy node locations a bit (optional)
for n in nodes:
if n.location.x < -500:
n.location.x = -500

print("[Rust Addon] Material created/updated:", mat_name)
Binary file added rust_material/test_rust_gen.blend
Binary file not shown.