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 openaiimport hou# Current Python SOP nodenode = hou.pwd()# Geometry containergeo = hou.node("/obj/geo2")if not geo:geo = hou.node("/obj").createNode("geo", "geo2")# Set OpenAI API key from houdini.envopenai.api_key = hou.getenv("OPENAI_API_KEY")# User promptuser_prompt = node.parm("prompt").eval()# System messageshape_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 calldef 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 resultresponse = get_completion(user_prompt, system_message)lines = response.split("\n")created_nodes = {}# Create shapes and apply transformsfor line in lines:parts = line.split()# CREATEif "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 = Noneif 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# APPLYelif "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 Noneif 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 outputif 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 networkgeo.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
Post a Comment