diff --git a/korman/operators/op_mesh.py b/korman/operators/op_mesh.py index b22e465..85958bb 100644 --- a/korman/operators/op_mesh.py +++ b/korman/operators/op_mesh.py @@ -26,6 +26,143 @@ class PlasmaMeshOperator: return context.scene.render.engine == "PLASMA_GAME" and context.mode == "OBJECT" +FLARE_MATERIAL_BASE_NAME = "FLAREGEN" + +def store_material_selection(self, value): + if bpy.data.materials.get(value, None): + bpy.context.scene.plasma_scene.last_flare_material = value + + +class PlasmaAddFlareOperator(PlasmaMeshOperator, bpy.types.Operator): + bl_idname = "mesh.plasma_flare_add" + bl_label = "Add Lamp Flare" + bl_category = "Plasma" + bl_description = "Adds a new Plasma Lamp Flare" + bl_options = {"REGISTER", "UNDO"} + + # Allows user to specify their own name stem + flare_name = bpy.props.StringProperty(name="Name", + description="Flare name stem", + default="Flare", + options=set()) + flare_distance = bpy.props.FloatProperty(name="Distance", + description="Flare's distance from the illuminating object", + min=0.1, max=2.0, step=10, precision=1, default=1.0, + options=set()) + flare_material_name = bpy.props.StringProperty(name="Material", + description="A specially-crafted material to use for this flare", + default=FLARE_MATERIAL_BASE_NAME, + update=store_material_selection, + options=set()) + + @classmethod + def poll(cls, context): + return super().poll(context) and context.mode == "OBJECT" + + def draw(self, context): + layout = self.layout + box = layout.box() + box.label("Flare Name:") + row = box.row() + row.alert = not self.flare_name + row.prop(self, "flare_name", text="") + + box = layout.box() + box.label("Geometry:") + row = box.row() + row.prop(self, "flare_distance") + + box = layout.box() + box.label("Material:") + row = box.row() + row.prop_search(self, "flare_material_name", bpy.data, "materials", text="") + + def execute(self, context): + if context.space_data.local_view: + bpy.ops.view3d.localview() + self.create_flare_objects() + return {"FINISHED"} + + def create_flare_objects(self): + bpyscene = bpy.context.scene + cursor_shift = mathutils.Matrix.Translation(bpy.context.scene.cursor_location) + + for obj in bpy.data.objects: + obj.select = False + + # Create Empty, rotated to horizontal + flare_root = bpy.data.objects.new("{}".format(self.name_stem), None) + flare_root.empty_draw_type = "SINGLE_ARROW" + flare_root.matrix_world = cursor_shift + flare_root.rotation_euler[0] = math.radians(90) + flare_root.plasma_object.enabled = True + flare_root.select = True + bpy.context.scene.objects.link(flare_root) + bpyscene.objects.active = flare_root + + # Enable VFM on Empty + bpy.ops.object.plasma_modifier_add(types="viewfacemod") + flare_root.plasma_modifiers.viewfacemod.preset_options = "Sprite" + + # Create a textured Plane + with utils.bmesh_object("{}_Visible".format(self.name_stem)) as (flare_plane, bm): + flare_plane.hide_render = True + flare_plane.plasma_object.enabled = True + bpyscene.objects.active = flare_plane + + # Make the actual plane mesh, facing away from the empty + bmesh.ops.create_grid(bm, size=(0.5 + self.flare_distance * 0.5), matrix=mathutils.Matrix.Rotation(math.radians(180.0), 4, 'X')) + bmesh.ops.transform(bm, matrix=mathutils.Matrix.Translation((0.0, 0.0, -self.flare_distance)), space=flare_plane.matrix_world, verts=bm.verts) + bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY") + + # Give the plane a basic UV unwrap, so that it's texture-ready + bpy.ops.object.editmode_toggle() + bpy.ops.uv.smart_project() + bpy.ops.object.editmode_toggle() + + # Create AUTOGEN_FLARE material and texture, setting No-Z-Write + auto_mat = self.find_create_material() + flare_plane.data.materials.append(auto_mat) + + # Parent Plane to Empty + flare_plane.parent = flare_root + + # Enable Opacity Fader + bpy.ops.object.plasma_modifier_add(types="fademod") + flare_plane.plasma_modifiers.fademod.fader_type = "FadeOpacity" + flare_plane.plasma_modifiers.fademod.fade_in_time = 0.25 + flare_plane.plasma_modifiers.fademod.fade_out_time = 0.25 + flare_plane.plasma_modifiers.fademod.bounds_center = True + + def find_create_material(self): + # If the selected flare material exists, use it + auto_mat = bpy.data.materials.get(self.flare_material_name, None) + + if auto_mat is None: + # Generate a new flare material and texture + auto_mat = bpy.data.materials.new(name=FLARE_MATERIAL_BASE_NAME) + auto_mat.use_shadeless = True + auto_mat.use_shadows = False + auto_mat.use_cast_shadows = False + self.flare_material_name = auto_mat.name + + auto_tex = bpy.data.textures.new(name=FLARE_MATERIAL_BASE_NAME, type="IMAGE") + auto_tex.use_alpha = True + auto_tex.plasma_layer.skip_depth_write = True + auto_tex.plasma_layer.skip_depth_test = True + auto_tex.plasma_layer.z_bias = True + + new_slot = auto_mat.texture_slots.add() + new_slot.texture = auto_tex + new_slot.blend_type = "ADD" + + return auto_mat + + @property + def name_stem(self): + return self.flare_name if self.flare_name else "Flare" + + class PlasmaAddLadderMeshOperator(PlasmaMeshOperator, bpy.types.Operator): bl_idname = "mesh.plasma_ladder_add" bl_label = "Add Ladder" diff --git a/korman/ui/ui_menus.py b/korman/ui/ui_menus.py index 7a4f0b2..1073a98 100644 --- a/korman/ui/ui_menus.py +++ b/korman/ui/ui_menus.py @@ -29,6 +29,7 @@ class PlasmaAddMenu(PlasmaMenu, bpy.types.Menu): def draw(self, context): layout = self.layout + layout.operator("mesh.plasma_flare_add", text="Lamp Flare", icon="PARTICLES") layout.operator("mesh.plasma_ladder_add", text="Ladder", icon="COLLAPSEMENU") layout.operator("mesh.plasma_linkingbook_add", text="Linking Book", icon="FILE_IMAGE")