3D Shape Generator

In my second experiment, I wanted to work with curves and save old coordinates so that I could reuse them for similar curve shapes.

3D Shape AI Node


 


1. Create a Python node named "shape_node_GPT," adding the below code in the content area. 
import openai
import hou

# Current Python SOP node
node = hou.pwd()

# Geometry container
geo = hou.node("/obj/geo2")
if not geo:
    geo = hou.node("/obj").createNode("geo", "geo2")

# Set OpenAI API key from houdini.env
openai.api_key = hou.getenv("OPENAI_API_KEY")

# User prompt
user_prompt = node.parm("prompt").eval()

# System message
shape_message_node = hou.node("/obj/geo2/shape_message_node")
if shape_message_node:
    system_message = shape_message_node.parm("message").eval()
else:
    hou.ui.displayMessage("Error: shape_message_node not found!", severity=hou.severityType.Error)
    system_message = ""

# OpenAI call
def get_completion(user_prompt, system_message):
    try:
        messages = [
            {"role": "system", "content": system_message},
            {"role": "user", "content": user_prompt}
        ]
        response = openai.ChatCompletion.create(
            model="gpt-4-turbo-preview",
            messages=messages,
            temperature=1.0,
            max_tokens=500
        )
        return response["choices"][0]["message"]["content"].strip()
    except Exception as e:
        return str(e)

# Hide previously generated AI nodes safely (don't delete while cooking)
for child in geo.children():
    if "ai_" in child.name().lower():
        try:
            child.setDisplayFlag(False)
            child.setRenderFlag(False)
        except:
            pass

# Get AI result
response = get_completion(user_prompt, system_message)
lines = response.split("\n")

created_nodes = {}

# Create shapes and apply transforms
for line in lines:
    parts = line.split()

    # CREATE
    if "create" in line.lower():
        shape_type = parts[1].lower()
        params = {parts[i]: float(parts[i + 1]) for i in range(3, len(parts), 2)}
        shape_node = None

        if shape_type == "sphere":
            shape_node = geo.createNode("sphere", f"ai_sphere_{len(created_nodes)}")
            shape_node.parm("radx").set(params.get("radius", 1.0))
            shape_node.parm("rady").set(params.get("radius", 1.0))
            shape_node.parm("radz").set(params.get("radius", 1.0))

        elif shape_type == "box":
            shape_node = geo.createNode("box", f"ai_box_{len(created_nodes)}")
            shape_node.parm("sizex").set(params.get("size_x", 1.0))
            shape_node.parm("sizey").set(params.get("size_y", 1.0))
            shape_node.parm("sizez").set(params.get("size_z", 1.0))

        elif shape_type == "cylinder":
            shape_node = geo.createNode("tube", f"ai_cylinder_{len(created_nodes)}")
            shape_node.parm("height").set(params.get("height", 2.0))
            shape_node.parm("rad").set(params.get("radius", 0.5))

        elif shape_type == "torus":
            shape_node = geo.createNode("torus", f"ai_torus_{len(created_nodes)}")
            shape_node.parm("rad1").set(params.get("major_radius", 1.0))
            shape_node.parm("rad2").set(params.get("minor_radius", 0.3))

        elif shape_type == "cone":
            shape_node = geo.createNode("tube", f"ai_cone_{len(created_nodes)}")
            shape_node.parm("height").set(params.get("height", 2.0))
            shape_node.parm("rad1").set(params.get("radius_top", 0.5))
            shape_node.parm("rad2").set(params.get("radius_bottom", 1.0))

        if shape_node:
            created_nodes[f"{shape_type}_{len(created_nodes)}"] = shape_node

    # APPLY
    elif "apply" in line.lower():
        transform_type = parts[1].lower()
        params = {parts[i]: float(parts[i + 1]) for i in range(3, len(parts), 2)}
        last_node = list(created_nodes.values())[-1] if created_nodes else None

        if last_node:
            xform = geo.createNode("xform", f"ai_{transform_type}_{last_node.name()}")
            xform.setInput(0, last_node)

            if transform_type == "translate":
                xform.parm("tx").set(params.get("x", 0.0))
                xform.parm("ty").set(params.get("y", 0.0))
                xform.parm("tz").set(params.get("z", 0.0))

            elif transform_type == "rotate":
                xform.parm("rx").set(params.get("axis_x", 0.0) * params.get("degrees", 0.0))
                xform.parm("ry").set(params.get("axis_y", 0.0) * params.get("degrees", 0.0))
                xform.parm("rz").set(params.get("axis_z", 0.0) * params.get("degrees", 0.0))

            created_nodes[f"{transform_type}_{len(created_nodes)}"] = xform

# Merge all generated nodes into one final output
if created_nodes:
    merge_node = geo.createNode("merge", "ai_merge_all")
    for i, node in enumerate(created_nodes.values()):
        merge_node.setInput(i, node)

    out_node = geo.node("OUT")
    if not out_node:
        out_node = geo.createNode("null", "OUT")
        out_node.setDisplayFlag(True)
        out_node.setRenderFlag(True)

    out_node.setInput(0, merge_node)

# Auto-layout the generated network
geo.layoutChildren()

2. Also, create a Prompt parameter inside the Python node and a cook button with this script in it: "hou.node('/obj/geo2/shape_node_GPT').cook(force=True). "



3. Create a null node named "shape_message_node" and edit the parameter by clicking the gear icon at the top and selecting "Edit Parameter Interface" to create a string inside the node. 





Pastes the below prompt in the string:
You are an AI specializing in procedural 3D shape generation for Houdini.
Interpret the user's text prompt and generate structured Houdini node instructions.

Rules:
- Output only shape creation steps, no extra text.
- Supported shapes: Sphere, Box, Torus, Cylinder, Cone, Grid.
- If the user provides a size, adjust parameters accordingly.
- Format example: Create sphere with radius 2 Apply translate with x 1 y 2 z 0 Apply rotate with axis_x 0 axis_y 1 axis_z 0 degrees 45

DO NOT include explanations or comments, ONLY the structured shape instructions.

4. Create a Rules board because AI has limitations, and because of that, the words need to be precise at some points.

You are an AI specializing in procedural 3D shape generation for Houdini.
Interpret the user's text prompt and generate structured Houdini node instructions.

Rules:
- Output only shape creation steps, no extra text.
- Supported shapes: Sphere, Box, Torus, Cylinder, Cone, Grid.
- If the user provides a size, adjust parameters accordingly.
- Format example: Create sphere with radius 2 Apply translate with x 1 y 2 z 0 Apply rotate with axis_x 0 axis_y 1 axis_z 0 degrees 45

DO NOT include explanations or comments, ONLY the structured shape instructions.


Note: To get the "Cook" button next to the prompt area, mark "Horizontally Join to Next Parameter" in Prompt or any node earlier than the cook node.



Thank You, Houdini School, for explaining the foundations!
If you want to learn how to use Houdini, they are one of the best places to start with your VFX career.

Comments

Popular posts from this blog

Present Best Way to Install MLOPs in Houdini

Color Ramp Generator

Researching topics for My Thesis (Master's Edition)