Objects: add Volume object type, and prototypes for Hair and PointCloud
Only the volume object is exposed in the user interface. It is based on OpenVDB internally. Drawing and rendering code will follow in another commit. https://wiki.blender.org/wiki/Source/Objects/Volume https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Volumes Hair and PointCloud object types are hidden behind a WITH_NEW_OBJECT_TYPES build option. These are unfinished, and included only to make it easier to cooperate on development in the future and avoid tricky merges. https://wiki.blender.org/wiki/Source/Objects/New_Object_Types Ref T73201, T68981 Differential Revision: https://developer.blender.org/D6945
This commit is contained in:
@@ -35,14 +35,17 @@ _modules = [
|
||||
"properties_data_curve",
|
||||
"properties_data_empty",
|
||||
"properties_data_gpencil",
|
||||
"properties_data_hair",
|
||||
"properties_data_light",
|
||||
"properties_data_lattice",
|
||||
"properties_data_mesh",
|
||||
"properties_data_metaball",
|
||||
"properties_data_modifier",
|
||||
"properties_data_pointcloud",
|
||||
"properties_data_shaderfx",
|
||||
"properties_data_lightprobe",
|
||||
"properties_data_speaker",
|
||||
"properties_data_volume",
|
||||
"properties_mask_common",
|
||||
"properties_material",
|
||||
"properties_material_gpencil",
|
||||
|
||||
78
release/scripts/startup/bl_ui/properties_data_hair.py
Normal file
78
release/scripts/startup/bl_ui/properties_data_hair.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'hair') and context.hair and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_hair(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
hair = context.hair
|
||||
space = context.space_data
|
||||
|
||||
if ob:
|
||||
layout.template_ID(ob, "data")
|
||||
elif hair:
|
||||
layout.template_ID(space, "pin_id")
|
||||
|
||||
|
||||
class DATA_PT_hair(DataButtonsPanel, Panel):
|
||||
bl_label = "Hair"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
hair = context.hair
|
||||
pass
|
||||
|
||||
class DATA_PT_custom_props_hair(DataButtonsPanel, PropertyPanel, Panel):
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
_context_path = "object.data"
|
||||
_property_type = bpy.types.Hair if hasattr(bpy.types, "Hair") else None
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_context_hair,
|
||||
DATA_PT_hair,
|
||||
DATA_PT_custom_props_hair,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
78
release/scripts/startup/bl_ui/properties_data_pointcloud.py
Normal file
78
release/scripts/startup/bl_ui/properties_data_pointcloud.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return hasattr(context, 'pointcloud') and context.pointcloud and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_pointcloud(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
pointcloud = context.pointcloud
|
||||
space = context.space_data
|
||||
|
||||
if ob:
|
||||
layout.template_ID(ob, "data")
|
||||
elif pointcloud:
|
||||
layout.template_ID(space, "pin_id")
|
||||
|
||||
|
||||
class DATA_PT_pointcloud(DataButtonsPanel, Panel):
|
||||
bl_label = "Point Cloud"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pointcloud = context.pointcloud
|
||||
pass
|
||||
|
||||
class DATA_PT_custom_props_pointcloud(DataButtonsPanel, PropertyPanel, Panel):
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
_context_path = "object.data"
|
||||
_property_type = bpy.types.PointCloud if hasattr(bpy.types, "PointCloud") else None
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_context_pointcloud,
|
||||
DATA_PT_pointcloud,
|
||||
DATA_PT_custom_props_pointcloud,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
173
release/scripts/startup/bl_ui/properties_data_volume.py
Normal file
173
release/scripts/startup/bl_ui/properties_data_volume.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# ##### BEGIN GPL LICENSE BLOCK #####
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
from rna_prop_ui import PropertyPanel
|
||||
|
||||
|
||||
class DataButtonsPanel:
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
engine = context.scene.render.engine
|
||||
return context.volume and (engine in cls.COMPAT_ENGINES)
|
||||
|
||||
|
||||
class DATA_PT_context_volume(DataButtonsPanel, Panel):
|
||||
bl_label = ""
|
||||
bl_options = {'HIDE_HEADER'}
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
volume = context.volume
|
||||
space = context.space_data
|
||||
|
||||
if ob:
|
||||
layout.template_ID(ob, "data")
|
||||
elif volume:
|
||||
layout.template_ID(space, "pin_id")
|
||||
|
||||
|
||||
class DATA_PT_volume_file(DataButtonsPanel, Panel):
|
||||
bl_label = "OpenVDB File"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
volume = context.volume
|
||||
volume.grids.load()
|
||||
|
||||
layout.prop(volume, "filepath", text="")
|
||||
|
||||
if len(volume.filepath):
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(volume, "is_sequence")
|
||||
if volume.is_sequence:
|
||||
col.prop(volume, "frame_duration", text="Frames")
|
||||
col.prop(volume, "frame_start", text="Start")
|
||||
col.prop(volume, "frame_offset", text="Offset")
|
||||
col.prop(volume, "sequence_mode", text="Mode")
|
||||
|
||||
error_msg = volume.grids.error_message
|
||||
if len(error_msg):
|
||||
layout.separator()
|
||||
col = layout.column(align=True)
|
||||
col.label(text="Failed to load volume:")
|
||||
col.label(text=error_msg)
|
||||
|
||||
|
||||
class VOLUME_UL_grids(UIList):
|
||||
def draw_item(self, context, layout, data, grid, icon, active_data, active_propname, index):
|
||||
name = grid.name
|
||||
data_type = grid.bl_rna.properties['data_type'].enum_items[grid.data_type]
|
||||
|
||||
layout.label(text=name)
|
||||
row = layout.row()
|
||||
row.alignment = 'RIGHT'
|
||||
row.active = False
|
||||
row.label(text=data_type.name)
|
||||
|
||||
|
||||
class DATA_PT_volume_grids(DataButtonsPanel, Panel):
|
||||
bl_label = "Grids"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
volume = context.volume
|
||||
volume.grids.load()
|
||||
|
||||
layout.template_list("VOLUME_UL_grids", "grids", volume, "grids", volume.grids, "active_index", rows=3)
|
||||
|
||||
|
||||
class DATA_PT_volume_render(DataButtonsPanel, Panel):
|
||||
bl_label = "Render"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
scene = context.scene
|
||||
volume = context.volume
|
||||
render = volume.render
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(render, "space")
|
||||
col.prop(render, "step_size")
|
||||
|
||||
if scene.render.engine == 'CYCLES':
|
||||
col = layout.column(align=True)
|
||||
col.prop(render, "clipping")
|
||||
|
||||
|
||||
class DATA_PT_volume_viewport_display(DataButtonsPanel, Panel):
|
||||
bl_label = "Viewport Display"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
volume = context.volume
|
||||
display = volume.display
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop(display, "wireframe_type")
|
||||
sub = col.row()
|
||||
sub.active = display.wireframe_type in {'BOXES', 'POINTS'}
|
||||
sub.prop(display, "wireframe_detail", text="Detail")
|
||||
|
||||
layout.prop(display, "density")
|
||||
|
||||
|
||||
class DATA_PT_custom_props_volume(DataButtonsPanel, PropertyPanel, Panel):
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
_context_path = "object.data"
|
||||
_property_type = bpy.types.Volume
|
||||
|
||||
|
||||
classes = (
|
||||
DATA_PT_context_volume,
|
||||
DATA_PT_volume_grids,
|
||||
DATA_PT_volume_file,
|
||||
DATA_PT_volume_viewport_display,
|
||||
DATA_PT_volume_render,
|
||||
DATA_PT_custom_props_volume,
|
||||
VOLUME_UL_grids,
|
||||
)
|
||||
|
||||
if __name__ == "__main__": # only for live edit.
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
@@ -216,7 +216,7 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel):
|
||||
|
||||
obj = context.object
|
||||
obj_type = obj.type
|
||||
is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'})
|
||||
is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'})
|
||||
is_wire = (obj_type in {'CAMERA', 'EMPTY'})
|
||||
is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE')
|
||||
is_dupli = (obj.instance_type != 'NONE')
|
||||
|
||||
@@ -126,6 +126,12 @@ class DopesheetFilterPopoverBase:
|
||||
flow.prop(dopesheet, "show_lattices", text="Lattices")
|
||||
if bpy.data.metaballs:
|
||||
flow.prop(dopesheet, "show_metaballs", text="Metaballs")
|
||||
if hasattr(bpy.data, "hairs") and bpy.data.hairs:
|
||||
flow.prop(dopesheet, "show_hairs", text="Hairs")
|
||||
if hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds:
|
||||
flow.prop(dopesheet, "show_pointclouds", text="Point Clouds")
|
||||
if bpy.data.volumes:
|
||||
flow.prop(dopesheet, "show_volumes", text="Volumes")
|
||||
|
||||
# data types
|
||||
flow.prop(dopesheet, "show_worlds", text="Worlds")
|
||||
|
||||
@@ -137,6 +137,9 @@ class FILEBROWSER_PT_filter(Panel):
|
||||
row = col.row()
|
||||
row.label(icon='FILE_TEXT')
|
||||
row.prop(params, "use_filter_text", text="Text Files", toggle=0)
|
||||
row = col.row()
|
||||
row.label(icon='FILE_VOLUME')
|
||||
row.prop(params, "use_filter_volume", text="Volume Files", toggle=0)
|
||||
|
||||
col.separator()
|
||||
|
||||
|
||||
@@ -76,7 +76,8 @@ class NODE_HT_header(Header):
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'GPENCIL'}
|
||||
types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META',
|
||||
'GPENCIL', 'VOLUME', 'HAIR', 'POINTCLOUD'}
|
||||
# disable material slot buttons when pinned, cannot find correct slot within id_from (#36589)
|
||||
# disable also when the selected object does not support materials
|
||||
has_material_slots = not snode.pin and ob_type in types_that_support_material
|
||||
|
||||
@@ -397,6 +397,9 @@ class OUTLINER_PT_filter(Panel):
|
||||
if (
|
||||
bpy.data.curves or
|
||||
bpy.data.metaballs or
|
||||
(hasattr(bpy.data, "hairs") and bpy.data.hairs) or
|
||||
(hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds) or
|
||||
bpy.data.volumes or
|
||||
bpy.data.lightprobes or
|
||||
bpy.data.lattices or
|
||||
bpy.data.fonts or
|
||||
|
||||
@@ -390,18 +390,23 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa
|
||||
col.prop(edit, "use_duplicate_armature", text="Armature")
|
||||
col.prop(edit, "use_duplicate_curve", text="Curve")
|
||||
# col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented.
|
||||
col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil")
|
||||
if hasattr(edit, "use_duplicate_hair"):
|
||||
col.prop(edit, "use_duplicate_hair", text="Hair")
|
||||
col.prop(edit, "use_duplicate_light", text="Light")
|
||||
col.prop(edit, "use_duplicate_lightprobe", text="Light Probe")
|
||||
col = flow.column()
|
||||
col.prop(edit, "use_duplicate_lightprobe", text="Light Probe")
|
||||
col.prop(edit, "use_duplicate_material", text="Material")
|
||||
col.prop(edit, "use_duplicate_mesh", text="Mesh")
|
||||
col.prop(edit, "use_duplicate_metaball", text="Metaball")
|
||||
col.prop(edit, "use_duplicate_particle", text="Particle")
|
||||
col = flow.column()
|
||||
if hasattr(edit, "use_duplicate_pointcloud"):
|
||||
col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud")
|
||||
col.prop(edit, "use_duplicate_surface", text="Surface")
|
||||
col.prop(edit, "use_duplicate_text", text="Text")
|
||||
# col.prop(edit, "use_duplicate_texture", text="Texture") # Not implemented.
|
||||
col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil")
|
||||
col.prop(edit, "use_duplicate_volume", text="Volume")
|
||||
|
||||
|
||||
class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel):
|
||||
|
||||
@@ -2157,6 +2157,16 @@ class VIEW3D_MT_camera_add(Menu):
|
||||
layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA')
|
||||
|
||||
|
||||
class VIEW3D_MT_volume_add(Menu):
|
||||
bl_idname = "VIEW3D_MT_volume_add"
|
||||
bl_label = "Volume"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
layout.operator("object.volume_import", text="Import OpenVDB...", icon='OUTLINER_DATA_VOLUME')
|
||||
layout.operator("object.volume_add", text="Empty", icon='OUTLINER_DATA_VOLUME')
|
||||
|
||||
|
||||
class VIEW3D_MT_add(Menu):
|
||||
bl_label = "Add"
|
||||
bl_translation_context = i18n_contexts.operator_default
|
||||
@@ -2179,6 +2189,11 @@ class VIEW3D_MT_add(Menu):
|
||||
layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
|
||||
layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
|
||||
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
|
||||
if hasattr(bpy.data, "hairs"):
|
||||
layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR')
|
||||
if hasattr(bpy.data, "pointclouds"):
|
||||
layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
|
||||
layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME')
|
||||
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
|
||||
|
||||
layout.separator()
|
||||
@@ -5428,6 +5443,9 @@ class VIEW3D_PT_object_type_visibility(Panel):
|
||||
("surf", "Surface"),
|
||||
("meta", "Meta"),
|
||||
("font", "Text"),
|
||||
("hair", "Hair"),
|
||||
("pointcloud", "Point Cloud"),
|
||||
("volume", "Volume"),
|
||||
("grease_pencil", "Grease Pencil"),
|
||||
(None, None),
|
||||
# Other
|
||||
@@ -5445,6 +5463,11 @@ class VIEW3D_PT_object_type_visibility(Panel):
|
||||
col.separator()
|
||||
continue
|
||||
|
||||
if attr == "hair" and not hasattr(bpy.data, "hairs"):
|
||||
continue
|
||||
elif attr == "pointcloud" and not hasattr(bpy.data, "pointclouds"):
|
||||
continue
|
||||
|
||||
attr_v = "show_object_viewport_" f"{attr:s}"
|
||||
attr_s = "show_object_select_" f"{attr:s}"
|
||||
|
||||
@@ -7254,6 +7277,7 @@ classes = (
|
||||
VIEW3D_MT_light_add,
|
||||
VIEW3D_MT_lightprobe_add,
|
||||
VIEW3D_MT_camera_add,
|
||||
VIEW3D_MT_volume_add,
|
||||
VIEW3D_MT_add,
|
||||
VIEW3D_MT_image_add,
|
||||
VIEW3D_MT_object,
|
||||
|
||||
Reference in New Issue
Block a user