Skip to content

Instantly share code, notes, and snippets.

@ji-podhead
Created October 7, 2025 14:19
Show Gist options
  • Select an option

  • Save ji-podhead/9487f6cc25f80b2861dc0855c36f9976 to your computer and use it in GitHub Desktop.

Select an option

Save ji-podhead/9487f6cc25f80b2861dc0855c36f9976 to your computer and use it in GitHub Desktop.
blender visualize point cloud
import bpy
import json
import numpy as np # Wird nicht direkt benötigt, da json.load verwendet wird
from mathutils import Vector
from time import time
def visualize_particles_as_instances(json_filepath, particle_scale=1, instance_radius=0.05, collection_name="VoxelParticleInstances"):
"""
Visualisiert Partikelpositionen aus einer JSON-Datei in Blender,
indem es Low-Poly Icosphären auf den Vertices eines Punktwolken-Meshes instanziiert.
Basierend auf TLouskys Ansatz.
Args:
json_filepath (str): Der Pfad zur JSON-Datei mit den Voxel-Daten.
particle_scale (float): Ein Skalierungsfaktor für die Partikelpositionen (z.B. 1 für keine Skalierung, 25 für Vergrösserung).
instance_radius (float): Der Radius der instanziierten Icosphären.
collection_name (str): Der Name der Collection, in die die Objekte eingefügt werden.
"""
t = time()
C = bpy.context
# Sicherstellen, dass die Datei existiert und laden
try:
with open(json_filepath, 'r') as fh:
data = json.load(fh)
except FileNotFoundError:
print(f"Fehler: Datei nicht gefunden unter '{json_filepath}'")
return
except json.JSONDecodeError:
print(f"Fehler: Konnte JSON-Daten aus '{json_filepath}' nicht lesen. Ist die Datei gültig?")
return
positions = data.get("positions")
if not positions or len(positions) % 3 != 0:
print("Fehler: Ungültige oder fehlende 'positions'-Daten in der JSON-Datei.")
return
num_verts = len(positions) // 3
print(f'Lade {num_verts} Partikel aus "{json_filepath}"...')
# Collection erstellen oder holen
if collection_name not in bpy.data.collections:
particle_collection = bpy.data.collections.new(collection_name)
C.scene.collection.children.link(particle_collection)
else:
particle_collection = bpy.data.collections[collection_name]
# Optional: Bestehende Objekte in dieser Collection löschen, um Überschneidungen zu vermeiden
for obj_to_remove in [o for o in particle_collection.objects if o.name.startswith(('pc_instancer', 'Icosphere_Instance_Base'))]:
bpy.data.objects.remove(obj_to_remove, do_unlink=True)
print(f"Vorhandene Objekte in '{collection_name}' bereinigt.")
# Create and arrange mesh data for the point cloud
verts = []
for i in range(num_verts):
x = positions[i*3] * particle_scale
y = positions[i*3+1] * particle_scale
z = positions[i*3+2] * particle_scale
verts.append(Vector((x, y, z)))
me = bpy.data.meshes.new('pc_data_mesh')
me.from_pydata(verts, [], [])
# Create mesh object for the point cloud and link to scene collection
pc_obj = bpy.data.objects.new( 'pc_instancer', me )
particle_collection.objects.link( pc_obj ) # Link to our specific collection
# Add minimal icosphere to use as instance object
# Sicherstellen, dass die icosphere in der aktuellen Szene erstellt wird, um sie direkt zu bearbeiten
# und dann in die Ziel-Collection zu verschieben
# Temporäre Collection für icosphere, wenn nicht im Kontext der Scene Collection
bpy.ops.mesh.primitive_ico_sphere_add( subdivisions=1, radius=instance_radius, location=(0,0,0) )
is_obj = C.object
is_obj.name = "Icosphere_Instance_Base"
# Verschiebe die Icosphere in unsere Partikel-Collection und entferne sie aus der aktuellen Collection
if is_obj.name not in particle_collection.objects:
# Finde die Collection, in der die Icosphere gerade ist (oft die Scene Collection)
for col in is_obj.users_collection:
col.objects.unlink(is_obj)
particle_collection.objects.link(is_obj)
# Material für die Instanzen hinzufügen (standardmässig weiss/grau)
if "VoxelInstanceMaterial" not in bpy.data.materials:
instance_mat = bpy.data.materials.new(name="VoxelInstanceMaterial")
instance_mat.use_nodes = True
principled_bsdf = instance_mat.node_tree.nodes.get('Principled BSDF')
if principled_bsdf:
principled_bsdf.inputs['Base Color'].default_value = (0.5, 0.5, 0.5, 1.0) # Grau
principled_bsdf.inputs['Roughness'].default_value = 0.7
else:
instance_mat = bpy.data.materials["VoxelInstanceMaterial"]
if not is_obj.data.materials:
is_obj.data.materials.append(instance_mat)
else:
is_obj.data.materials[0] = instance_mat
# Set instancing props for the point cloud object
pc_obj.instance_type = 'VERTS'
# Wichtig: show_instancer_for_viewport auf True lassen, sonst sieht man nichts
pc_obj.show_instancer_for_viewport = True # Zeigt die Instanzen im Viewport
pc_obj.show_instancer_for_render = True # Rendert die Instanzen
# Verstecke das Basis-Instanz-Objekt selbst, da wir nur seine Instanzen sehen wollen
is_obj.hide_set(True)
is_obj.hide_render = True
# Set instance parenting (parent icosphere to verts)
# Wähle zuerst den Instancer (pc_obj) und dann die Instanz (is_obj) aus
bpy.ops.object.select_all(action='DESELECT')
pc_obj.select_set(True)
C.view_layer.objects.active = pc_obj
# Hier der entscheidende Schritt: Child (is_obj) wird Parent (pc_obj)
# in der Instanz-Hierarchie hinzugefügt.
# Wähle zuerst das Kind (die Instanz) und dann den Elternteil (den Instancer).
is_obj.select_set(True)
C.view_layer.objects.active = pc_obj # Das aktive Objekt sollte der Instancer sein
# Parent setzen (Instanz-Objekt wird zum Child des Instancer-Objekts)
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
print(f'Total time = {time() - t} seconds' )
print(f"Partikel als Instanzen in Collection '{collection_name}' erstellt.")
print("Die Basis-Icosphere ('Icosphere_Instance_Base') ist im Viewport und Render versteckt.")
# --- ANWENDUNG DES SKRIPTS ---
if __name__ == "__main__":
# Pfad zur JSON-Datei (passe diesen an, wo dein Export-Skript die Datei speichert!)
json_input_file = bpy.path.abspath("//voxel_data.json")
# Skalierungsfaktor für die Positionen (wenn die Koordinaten sehr klein sind)
position_scale_factor = 1.0 # 1.0 für keine zusätzliche Skalierung
# Radius der instanziierten Partikel
particle_instance_radius = 0.05
# Vorherige Auswahl aufheben
bpy.ops.object.select_all(action='DESELECT')
visualize_particles_as_instances(json_input_file,
particle_scale=position_scale_factor,
instance_radius=particle_instance_radius)
print("Skript beendet.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment