Browse Source

Add localized text modifier.

pull/276/head
Adam Johnson 3 years ago
parent
commit
a31b8ac964
Signed by: Hoikas
GPG Key ID: 0B6515D6FF6F271E
  1. 7
      korman/exporter/material.py
  2. 86
      korman/properties/modifiers/gui.py
  3. 148
      korman/properties/modifiers/render.py
  4. 45
      korman/ui/modifiers/render.py

7
korman/exporter/material.py

@ -799,7 +799,12 @@ class MaterialConverter:
# when the exporter tells us to finalize all our shit # when the exporter tells us to finalize all our shit
if texture.image is None: if texture.image is None:
dtm = self._mgr.find_create_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo) dtm = self._mgr.find_create_object(plDynamicTextMap, name="{}_DynText".format(layer.key.name), bl=bo)
dtm.hasAlpha = texture.use_alpha if texture.use_alpha:
dtm.hasAlpha = True
if not state.blendFlags & hsGMatState.kBlendMask:
state.blendFlags |= hsGMatState.kBlendAlpha
else:
dtm.hasAlpha = False
dtm.visWidth = int(layer_props.dynatext_resolution) dtm.visWidth = int(layer_props.dynatext_resolution)
dtm.visHeight = int(layer_props.dynatext_resolution) dtm.visHeight = int(layer_props.dynatext_resolution)
layer.texture = dtm.key layer.texture = dtm.key

86
korman/properties/modifiers/gui.py

@ -14,11 +14,13 @@
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import math
import bmesh import bmesh
from bpy.props import *
import mathutils import mathutils
import math
from pathlib import Path from pathlib import Path
from bpy.props import *
from PyHSPlasma import * from PyHSPlasma import *
from ...addon_prefs import game_versions from ...addon_prefs import game_versions
@ -113,14 +115,50 @@ class PlasmaJournalTranslation(bpy.types.PropertyGroup):
items=languages, items=languages,
default=_DEFAULT_LANGUAGE_NAME, default=_DEFAULT_LANGUAGE_NAME,
options=set()) options=set())
text_id = PointerProperty(name="Journal Contents", text_id = PointerProperty(name="Contents",
description="Text data block containing the journal's contents for this language", description="Text data block containing the text for this language",
type=bpy.types.Text, type=bpy.types.Text,
poll=_poll_nonpytext, poll=_poll_nonpytext,
options=set()) options=set())
class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz): class TranslationMixin:
def _get_translation(self):
# Ensure there is always a default (read: English) translation available.
default_idx, default = next(((idx, translation) for idx, translation in enumerate(self.translations)
if translation.language == _DEFAULT_LANGUAGE_NAME), (None, None))
if default is None:
default_idx = len(self.translations)
default = self.translations.add()
default.language = _DEFAULT_LANGUAGE_NAME
if self.active_translation_index < len(self.translations):
language = self.translations[self.active_translation_index].language
else:
self.active_translation_index = default_idx
language = default.language
# Due to the fact that we are using IDs to keep the data from becoming insane on new
# additions, we must return the integer id...
return next((idx for key, _, _, idx in languages if key == language))
def _set_translation(self, value):
# We were given an int here, must change to a string
language_name = next((key for key, _, _, i in languages if i == value))
idx = next((idx for idx, translation in enumerate(self.translations)
if translation.language == language_name), None)
if idx is None:
self.active_translation_index = len(self.translations)
translation = self.translations.add()
translation.language = language_name
else:
self.active_translation_index = idx
@property
def translations(self):
raise RuntimeError("TranslationMixin subclass needs a translation getter!")
class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz, TranslationMixin):
pl_id = "journalbookmod" pl_id = "journalbookmod"
bl_category = "GUI" bl_category = "GUI"
@ -156,36 +194,6 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
type=bpy.types.Object, type=bpy.types.Object,
poll=idprops.poll_mesh_objects) poll=idprops.poll_mesh_objects)
def _get_translation(self):
# Ensure there is always a default (read: English) translation available.
default_idx, default = next(((idx, translation) for idx, translation in enumerate(self.journal_translations)
if translation.language == _DEFAULT_LANGUAGE_NAME), (None, None))
if default is None:
default_idx = len(self.journal_translations)
default = self.journal_translations.add()
default.language = _DEFAULT_LANGUAGE_NAME
if self.active_translation_index < len(self.journal_translations):
language = self.journal_translations[self.active_translation_index].language
else:
self.active_translation_index = default_idx
language = default.language
# Due to the fact that we are using IDs to keep the data from becoming insane on new
# additions, we must return the integer id...
return next((idx for key, _, _, idx in languages if key == language))
def _set_translation(self, value):
# We were given an int here, must change to a string
language_name = next((key for key, _, _, i in languages if i == value))
idx = next((idx for idx, translation in enumerate(self.journal_translations)
if translation.language == language_name), None)
if idx is None:
self.active_translation_index = len(self.journal_translations)
translation = self.journal_translations.add()
translation.language = language_name
else:
self.active_translation_index = idx
journal_translations = CollectionProperty(name="Journal Translations", journal_translations = CollectionProperty(name="Journal Translations",
type=PlasmaJournalTranslation, type=PlasmaJournalTranslation,
options=set()) options=set())
@ -193,7 +201,8 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
active_translation = EnumProperty(name="Language", active_translation = EnumProperty(name="Language",
description="Language of this translation", description="Language of this translation",
items=languages, items=languages,
get=_get_translation, set=_set_translation, get=TranslationMixin._get_translation,
set=TranslationMixin._set_translation,
options=set()) options=set())
def pre_export(self, exporter, bo): def pre_export(self, exporter, bo):
@ -307,6 +316,11 @@ class PlasmaJournalBookModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz
# We are too late in the export to be harvested automatically, so let's be explicit # We are too late in the export to be harvested automatically, so let's be explicit
return True return True
@property
def translations(self):
# Backwards compatibility thunk.
return self.journal_translations
linking_pfms = { linking_pfms = {
pvPots : { pvPots : {

148
korman/properties/modifiers/render.py

@ -19,10 +19,11 @@ from bpy.props import *
import functools import functools
from PyHSPlasma import * from PyHSPlasma import *
from .base import PlasmaModifierProperties, PlasmaModifierUpgradable from .base import PlasmaModifierProperties, PlasmaModifierLogicWiz, PlasmaModifierUpgradable
from ...exporter.etlight import _NUM_RENDER_LAYERS from ...exporter.etlight import _NUM_RENDER_LAYERS
from ...exporter import utils from ...exporter import utils
from ...exporter.explosions import ExportError from ...exporter.explosions import ExportError
from .gui import languages, PlasmaJournalTranslation, TranslationMixin
from ... import idprops from ... import idprops
class PlasmaBlendOntoObject(bpy.types.PropertyGroup): class PlasmaBlendOntoObject(bpy.types.PropertyGroup):
@ -604,6 +605,151 @@ class PlasmaLightingMod(PlasmaModifierProperties):
return False return False
_LOCALIZED_TEXT_PFM = (
{ 'id': 1, 'type': "ptAttribDynamicMap", 'name': "dynTextMap", },
{ 'id': 2, 'type': "ptAttribString", 'name': "locPath" },
{ 'id': 3, 'type': "ptAttribString", 'name': "fontFace" },
{ 'id': 4, 'type': "ptAttribInt", 'name': "fontSize" },
{ 'id': 5, 'type': "ptAttribFloat", 'name': "fontColorR" },
{ 'id': 6, 'type': "ptAttribFloat", 'name': "fontColorG" },
{ 'id': 7, 'type': "ptAttribFloat", 'name': "fontColorB" },
{ 'id': 8, 'type': "ptAttribFloat", 'name': "fontColorA" },
{ 'id': 9, 'type': "ptAttribInt", 'name': "marginTop" },
{ 'id': 10, 'type': "ptAttribInt", 'name': "marginLeft" },
{ 'id': 11, 'type': "ptAttribInt", 'name': "marginBottom" },
{ 'id': 12, 'type': "ptAttribInt", 'name': "marginRight" },
{ 'id': 13, 'type': "ptAttribInt", 'name': "lineSpacing" },
# Yes, it's really a ptAttribDropDownList, but those are only for use in
# artist generated node trees.
{ 'id': 14, 'type': "ptAttribString", 'name': "justify" },
{ 'id': 15, 'type': "ptAttribFloat", 'name': "clearColorR" },
{ 'id': 16, 'type': "ptAttribFloat", 'name': "clearColorG" },
{ 'id': 17, 'type': "ptAttribFloat", 'name': "clearColorB" },
{ 'id': 18, 'type': "ptAttribFloat", 'name': "clearColorA" },
)
class PlasmaLocalizedTextModifier(PlasmaModifierProperties, PlasmaModifierLogicWiz, TranslationMixin):
pl_id = "dynatext"
bl_category = "Render"
bl_label = "Localized Text"
bl_description = ""
bl_icon = "TEXT"
translations = CollectionProperty(name="Translations",
type=PlasmaJournalTranslation,
options=set())
active_translation_index = IntProperty(options={"HIDDEN"})
active_translation = EnumProperty(name="Language",
description="Language of this translation",
items=languages,
get=TranslationMixin._get_translation,
set=TranslationMixin._set_translation,
options=set())
def _poll_dyna_text(self, value: bpy.types.Texture) -> bool:
if value.type != "IMAGE":
return False
if value.image is not None:
return False
tex_materials = frozenset(value.users_material)
obj_materials = frozenset(filter(None, (i.material for i in self.id_data.material_slots)))
return bool(tex_materials & obj_materials)
texture = PointerProperty(name="Texture",
description="The texture to write the localized text on",
type=bpy.types.Texture,
poll=_poll_dyna_text)
font_face = StringProperty(name="Font Face",
default="Arial",
options=set())
font_size = IntProperty(name="Font Size",
default=12,
min=0, soft_max=72,
options=set())
font_color = FloatVectorProperty(name="Font Color",
default=(0.0, 0.0, 0.0, 1.0),
min=0.0, max=1.0,
subtype="COLOR", size=4,
options=set())
# Using individual properties for better UI documentation
margin_top = IntProperty(name="Margin Top",
min=-4096, soft_min=0, max=4096,
options=set())
margin_left = IntProperty(name="Margin Left",
min=-4096, soft_min=0, max=4096,
options=set())
margin_bottom = IntProperty(name="Margin Bottom",
min=-4096, soft_min=0, max=4096,
options=set())
margin_right = IntProperty(name="Margin Right",
min=-4096, soft_min=0, max=4096,
options=set())
justify = EnumProperty(name="Justification",
items=[("left", "Left", ""),
("center", "Center", ""),
("right", "Right", "")],
default="left",
options=set())
line_spacing = IntProperty(name="Line Spacing",
default=0,
soft_min=0, soft_max=10,
options=set())
def pre_export(self, exporter, bo):
yield self.convert_logic(bo, age_name=exporter.age_name, version=exporter.mgr.getVer())
def export(self, exporter, bo, so):
# TODO: This should probably be pulled out into its own export pass for locs
for i in filter(None, self.translations):
exporter.locman.add_string("DynaTexts", self.key_name, i.language, i.text_id, indent=2)
def logicwiz(self, bo, tree, *, age_name, version):
# Rough justice. If the dynamic text map texture doesn't request alpha, then we'll want
# to explicitly clear it to the material's diffuse color. This will allow artists to trivially
# add text surfaces directly to objects, opposed to where Cyan tends to use a separate
# transparent object over the background object.
if not self.texture.use_alpha:
material_filter = lambda slot: slot and slot.material and self.texture in (i.texture for i in slot.material.texture_slots if i)
for slot in filter(material_filter, bo.material_slots):
self._create_nodes(bo, tree, age_name=age_name, version=version,
material=slot.material, clear_color=slot.material.diffuse_color)
else:
self._create_nodes(bo, tree, age_name=age_name, version=version)
def _create_nodes(self, bo, tree, *, age_name, version, material=None, clear_color=None):
pfm_node = self._create_python_file_node(tree, "xDynTextLoc.py", _LOCALIZED_TEXT_PFM)
loc_path = self.key_name if version <= pvPots else "{}.DynaTexts.{}".format(age_name, self.key_name)
self._create_python_attribute(pfm_node, "dynTextMap", "ptAttribDynamicMap",
target_object=bo, material=material, texture=self.texture)
self._create_python_attribute(pfm_node, "locPath", value=loc_path)
self._create_python_attribute(pfm_node, "fontFace", value=self.font_face)
self._create_python_attribute(pfm_node, "fontSize", value=self.font_size)
self._create_python_attribute(pfm_node, "fontColorR", value=self.font_color[0])
self._create_python_attribute(pfm_node, "fontColorG", value=self.font_color[1])
self._create_python_attribute(pfm_node, "fontColorB", value=self.font_color[2])
self._create_python_attribute(pfm_node, "fontColorA", value=self.font_color[3])
self._create_python_attribute(pfm_node, "marginTop", value=self.margin_top)
self._create_python_attribute(pfm_node, "marginLeft", value=self.margin_left)
self._create_python_attribute(pfm_node, "marginBottom", value=self.margin_bottom)
self._create_python_attribute(pfm_node, "marginRight", value=self.margin_right)
self._create_python_attribute(pfm_node, "justify", value=self.justify)
if clear_color is not None:
self._create_python_attribute(pfm_node, "clearColorR", value=clear_color[0])
self._create_python_attribute(pfm_node, "clearColorG", value=clear_color[1])
self._create_python_attribute(pfm_node, "clearColorB", value=clear_color[2])
self._create_python_attribute(pfm_node, "clearColorA", value=1.0)
def sanity_check(self):
if self.texture is None:
raise ExportError("'{}': Localized Text modifier requires a texture", self.id_data.name)
class PlasmaShadowCasterMod(PlasmaModifierProperties): class PlasmaShadowCasterMod(PlasmaModifierProperties):
pl_id = "rtshadow" pl_id = "rtshadow"

45
korman/ui/modifiers/render.py

@ -97,6 +97,51 @@ def decal_receive(modifier, layout, context):
layout.alert = decal_mgr is None layout.alert = decal_mgr is None
layout.prop_search(mgr_ref, "name", scene, "decal_managers", icon="BRUSH_DATA") layout.prop_search(mgr_ref, "name", scene, "decal_managers", icon="BRUSH_DATA")
def dynatext(modifier, layout, context):
col = layout.column()
col.alert = modifier.texture is None
col.prop(modifier, "texture")
if modifier.texture is None:
col.label("You must specify a blank image texture to draw on.", icon="ERROR")
split = layout.split()
col = split.column()
col.label("Content Translations:")
col.prop(modifier, "active_translation", text="")
# This should never fail...
try:
translation = modifier.translations[modifier.active_translation_index]
except Exception as e:
col.label(text="Error (see console)", icon="ERROR")
print(e)
else:
col.prop(translation, "text_id", text="")
col = split.column()
col.label("Font:")
sub = col.row()
sub.alert = not modifier.font_face.strip()
sub.prop(modifier, "font_face", text="", icon="OUTLINER_DATA_FONT")
col.prop(modifier, "font_size", text="Size")
layout.separator()
split = layout.split()
col = split.column(align=True)
if modifier.texture is not None:
col.alert = modifier.margin_top + modifier.margin_bottom >= int(modifier.texture.plasma_layer.dynatext_resolution)
col.prop(modifier, "margin_top")
col.prop(modifier, "margin_bottom")
col = split.column(align=True)
if modifier.texture is not None:
col.alert = modifier.margin_left + modifier.margin_right >= int(modifier.texture.plasma_layer.dynatext_resolution)
col.prop(modifier, "margin_left")
col.prop(modifier, "margin_right")
layout.separator()
flow = layout.column_flow(columns=2)
flow.prop_menu_enum(modifier, "justify")
flow.prop(modifier, "line_spacing")
def fademod(modifier, layout, context): def fademod(modifier, layout, context):
layout.prop(modifier, "fader_type") layout.prop(modifier, "fader_type")

Loading…
Cancel
Save