VERSION = 'prealpha' import bpy import bpy_extras import mathutils __ENUM_POI_TYPE__ = { ('CASTLE', 'Castle', 'creepy castle', 1), ('RUIN', 'Ruin', 'repressive ruins', 2), ('CITY', 'City', 'callous city', 3), ('VILLAGE', 'Village', 'vengeful village', 4), ('SWAMP', 'Swamp', 'scornful swamp', 5), } class GaiaException(Exception): pass class GaiaNotActivated(GaiaException): message = 'Gaia is not activated in this scene!' # This allows you to right click on a button and link to documentation def gaia_manual_map(): return ( 'https://docs.phryk.net/gaia/latest/index.html', #prefix ( # mappings ('gaia.scene_enable', '#scene_enable'), ('gaia.poi_add', '#poi_add'), ) ) def gaia_enabled(): if 'gaia_enabled' in bpy.context.scene: return bpy.context.scene['gaia_enabled'] == VERSION return False def gaia_collection(name=None): """ returns gaia root collection, or sub-collection if `name` is passed but only if Gaia is activated in this scene. """ root_collection_name = '__gaia__' if gaia_enabled(): if not 'gaia_collections' in bpy.context.scene: bpy.context.scene['gaia_collections'] = {} # check if root Gaia collection exists if 'root' in bpy.context.scene['gaia_collections'] and bpy.context.scene['gaia_collections']['root'] in bpy.data.collections: root_collection = bpy.data.collections[bpy.context.scene['gaia_collections']['root']] else: # add root Gaia collection if not root_collection = bpy.data.collections.new(root_collection_name) bpy.context.scene['gaia_collections']['root'] = root_collection.name bpy.context.scene.collection.children.link(root_collection) if name is None: return root_collection else: if name in bpy.context.scene['gaia_collections'] and bpy.context.scene['gaia_collections'][name] in bpy.data.collections: return bpy.data.collections[bpy.context.scene['gaia_collections'][name]] else: collection = bpy.data.collections.new(name) bpy.context.scene['gaia_collections'][name] = collection.name root_collection.children.link(collection) return collection raise GaiaNotActivated() def collection_get_recursive(collection, otype=None): objects = [] for o in collection.objects.values(): #if otype is None or isinstance(o, otype): # since we can't create new bpy.types isinstance is not accurate enough (will match any subclasses, which if passed Object to filter for empties will just not do anything) if type(o) == otype: # strict (same class) type checking objects.append(o) for c in collection.children.values(): objects += collection_get_recursive(c, type=type) return objects class SceneEnable(bpy.types.Operator): bl_idname = "gaia.scene_enable" bl_label = "Enable Gaia in this scene" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): context.scene['gaia_enabled'] = VERSION return {'FINISHED'} class POIAdd(bpy.types.Operator, bpy_extras.object_utils.AddObjectHelper): """Create a new Point of Interest""" bl_idname = "gaia.poi_add" bl_label = "Add Point of Interest" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): col = gaia_collection('POI') bpy.ops.object.empty_add(type='SPHERE') obj = bpy.context.active_object obj.name = 'New POI' col.objects.link(obj) bpy.context.scene.collection.objects.unlink(obj) return {'FINISHED'} class POIGraph(bpy.types.Operator): bl_idname = "gaia.poi_graph" bl_label = "Build POI graph" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): self.report({'INFO'}, "FNORP") poicol = gaia_collection('POI') pois = collection_get_recursive(poicol, otype=bpy.types.Object) # Object means it'll filter for empties vertices = [] for poi in pois: self.report({'INFO'}, "POI!") self.report({'INFO'}, poi.name) vertices.append(poi.location) vertices_2d = [mathutils.Vector((vert[0], vert[1])) for vert in vertices] tri_verts, tri_edges, tri_faces, orig_verts, orig_edges, orig_faces = mathutils.geometry.delaunay_2d_cdt(vertices_2d, [], [], 1, 0.01) self.report({'INFO'}, f"TRIANGULATION RESULT: {tri_edges}, {tri_faces}") mesh = bpy.data.meshes.new('POIGraph') mesh.from_pydata(vertices, tri_edges, tri_faces) mesh.update() meshobj = bpy.data.objects.new('POIGraph', mesh) meshcol = gaia_collection('Meshes') meshcol.objects.link(meshobj) return {'FINISHED'} def poi_add_button(self, context): self.layout.operator( POIAdd.bl_idname, text="Point of Interest", icon='PLUGIN' ) def poi_graph_button(self, context): self.layout.operator( POIGraph.bl_idname, text="Generate POI graph", icon='PLUGIN' ) class POIPanel(bpy.types.Panel): bl_idname = "OBJECT_PT_GAIA_POI" bl_label = "Gaia POI Properties" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "object" def draw(self, context): layout = self.layout if context.active_object.type != 'EMPTY': row = layout.row() row.label(text="This is not a Point of Interest.") else: row = layout.row() row.prop(bpy.context.active_object, 'POI_type') class GaiaPanel(bpy.types.Panel): bl_idname = "VIEW3D_PT_GAIA" bl_label = "Gaia" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = 'Gaia' def draw(self, context): layout = self.layout if not 'gaia_enabled' in bpy.context.scene: row = layout.row() row.label(text="Gaia not enabled.") row = layout.row() row.operator("gaia.scene_enable") else: # gaia *is* enabled if bpy.context.scene['gaia_enabled'] != VERSION: row = layout.row() row.label(text="Gaia enabled, but with a different version!") row = layout.row() row.label(text=f"Scene is on '{bpy.context.scene['gaia_enabled']}'.") row = layout.row() row.label(text=f"We are on '{VERSION}'.") row = layout.row() row.operator("gaia.scene_enable") else: row = layout.row() row.label(text="Gaia enabled – place step 1 button here") def register(): bpy.types.Object.POI_type = bpy.props.EnumProperty(name="POI type", items=__ENUM_POI_TYPE__) bpy.utils.register_class(GaiaPanel) bpy.utils.register_class(SceneEnable) bpy.utils.register_class(POIAdd) bpy.utils.register_class(POIGraph) bpy.utils.register_class(POIPanel) bpy.types.VIEW3D_MT_mesh_add.append(poi_add_button) GaiaPanel.append(poi_graph_button) bpy.utils.register_manual_map(gaia_manual_map) def unregister(): bpy.utils.unregister_class(GaiaPanel) bpy.utils.unregister_class(SceneEnable) bpy.utils.unregister_class(POIAdd) bpy.utils.unregister_class(POIGraph) bpy.utils.unregister_class(POIPanel) bpy.types.VIEW3D_MT_mesh_add.remove(poi_add_button) GaiaPanel.remove(poi_graph_button) bpy.utils.unregister_manual_map(add_object_manual_map) del(bpy.types.Object.POI_type) if __name__ == "__main__": register()