@ -32,6 +32,7 @@ from . import utils
if TYPE_CHECKING :
from . convert import Exporter
from . logger import _ExportLogger as ExportLogger
from . . properties . modifiers . game_gui import *
class Clipping ( NamedTuple ) :
@ -48,9 +49,13 @@ class GuiConverter:
if TYPE_CHECKING :
_parent : weakref . ref [ Exporter ] = . . .
_pages : Dict [ str , Any ] = . . .
_mods_exported : Set [ str ] = . . .
def __init__ ( self , parent : Optional [ Exporter ] = None ) :
self . _parent = weakref . ref ( parent ) if parent is not None else None
self . _pages = { }
self . _mods_exported = set ( )
# Go ahead and prepare the GUI transparent material for future use.
if parent is not None :
@ -203,6 +208,93 @@ class GuiConverter:
w2c [ 2 , i ] * = - 1.0
return PostEffectModMatrices ( c2w , w2c )
def check_pre_export ( self , name : str , * * kwargs ) :
previous = self . _pages . setdefault ( name , kwargs )
if previous != kwargs :
diff = set ( previous . items ( ) ) - set ( kwargs . items ( ) )
raise ExportError ( f " GUI Page ' { name } ' has target modifiers with conflicting settings: \n { diff } " )
def create_note_gui ( self , gui_page : str , gui_camera : bpy . types . Object ) :
if not gui_page in self . _mods_exported :
guidialog_object = utils . create_empty_object ( f " { gui_page } _NoteDialog " )
guidialog_object . plasma_object . enabled = True
guidialog_object . plasma_object . page = gui_page
yield guidialog_object
guidialog_mod : PlasmaGameGuiDialogModifier = guidialog_object . plasma_modifiers . gui_dialog
guidialog_mod . enabled = True
guidialog_mod . is_modal = True
if gui_camera is not None :
guidialog_mod . camera_object = gui_camera
else :
# Abuse the GUI Dialog's lookat caLculation to make us a camera that looks at everything
# the artist has placed into the GUI page. We want to do this NOW because we will very
# soon be adding more objects into the GUI page.
camera_object = yield utils . create_camera_object ( f " { gui_page } _GUICamera " )
camera_object . data . angle = math . radians ( 45.0 )
camera_object . data . lens_unit = " FOV "
visible_objects = [
i for i in self . _parent ( ) . get_objects ( gui_page )
if i . type == " MESH " and i . data . materials
]
camera_object . matrix_world = self . calc_camera_matrix (
bpy . context . scene ,
visible_objects ,
camera_object . data . angle
)
clipping = self . calc_clipping (
camera_object . matrix_world ,
bpy . context . scene ,
visible_objects ,
camera_object . data . angle
)
camera_object . data . clip_start = clipping . hither
camera_object . data . clip_end = clipping . yonder
guidialog_mod . camera_object = camera_object
# Begin creating the object for the clickoff plane. We want to yield it immediately
# to the exporter in case something goes wrong during the export, allowing the stale
# object to be cleaned up.
click_plane_object = utils . BMeshObject ( f " { gui_page } _Exit " )
click_plane_object . matrix_world = guidialog_mod . camera_object . matrix_world
click_plane_object . plasma_object . enabled = True
click_plane_object . plasma_object . page = gui_page
yield click_plane_object
# We have a camera on guidialog_mod.camera_object. We will now use it to generate the
# points for the click-off plane button.
# TODO: Allow this to be configurable to 4:3, 16:9, or 21:9?
with ExitStack ( ) as stack :
stack . enter_context ( self . generate_camera_render_settings ( bpy . context . scene ) )
toggle = stack . enter_context ( helpers . GoodNeighbor ( ) )
# Temporarily adjust the clipping plane out to the farthest point we can find to ensure
# that the click-off button ecompasses everything. This is a bit heavy-handed, but if
# you want more refined control, you won't be using this helper.
clipping = max ( ( guidialog_mod . camera_object . data . clip_start , guidialog_mod . camera_object . data . clip_end ) )
toggle . track ( guidialog_mod . camera_object . data , " clip_start " , clipping - 0.1 )
view_frame = guidialog_mod . camera_object . data . view_frame ( bpy . context . scene )
click_plane_object . data . materials . append ( self . transparent_material )
with click_plane_object as click_plane_mesh :
verts = [ click_plane_mesh . verts . new ( i ) for i in view_frame ]
face = click_plane_mesh . faces . new ( verts )
# TODO: Ensure the face is pointing toward the camera!
# I feel like we should be fine by assuming that Blender returns the viewframe
# verts in the correct order, but this is Blender... So test that assumption carefully.
# TODO: Apparently not!
face . normal_flip ( )
# We've now created the mesh object - handle the GUI Button stuff
click_plane_object . plasma_modifiers . gui_button . enabled = True
# NOTE: We will be using xDialogToggle.py, so we use a special tag ID instead of the
# close dialog procedure.
click_plane_object . plasma_modifiers . gui_control . tag_id = 99
self . _mods_exported . add ( gui_page )
@contextmanager
def generate_camera_render_settings ( self , scene : bpy . types . Scene ) - > Iterator [ None ] :
# Set the render info to basically TV NTSC 4:3, which will set Blender's camera