From 7a201cc81626f2716269bbfb960600230fb633bb Mon Sep 17 00:00:00 2001 From: Lila <87947656+Lilaa3@users.noreply.github.com> Date: Sun, 21 Apr 2024 09:28:26 +0100 Subject: [PATCH 1/6] Automatically find or add world --- fast64_internal/f3d/f3d_bleed.py | 5 +++-- fast64_internal/f3d/f3d_material.py | 5 ++++- fast64_internal/f3d/f3d_writer.py | 4 ++-- fast64_internal/oot/f3d/panels.py | 5 ++++- fast64_internal/oot/oot_model_classes.py | 4 ++-- fast64_internal/sm64/sm64_f3d_writer.py | 12 +++++++----- fast64_internal/utility.py | 22 +++++++++++++++++++++- 7 files changed, 43 insertions(+), 14 deletions(-) diff --git a/fast64_internal/f3d/f3d_bleed.py b/fast64_internal/f3d/f3d_bleed.py index b1dbf2057..6a5b6cddf 100644 --- a/fast64_internal/f3d/f3d_bleed.py +++ b/fast64_internal/f3d/f3d_bleed.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, field +from ..utility import create_or_get_world from .f3d_gbi import ( GfxTag, GfxListTag, @@ -65,7 +66,7 @@ def __init__(self): self.build_default_othermodes() def build_default_geo(self): - defaults = bpy.context.scene.world.rdp_defaults + defaults = create_or_get_world(bpy.context.scene).rdp_defaults setGeo = SPSetGeometryMode([]) clearGeo = SPClearGeometryMode([]) @@ -93,7 +94,7 @@ def place_in_flaglist(flag: bool, enum: str, set_list: SPSetGeometryMode, clear_ self.default_clear_geo = clearGeo def build_default_othermodes(self): - defaults = bpy.context.scene.world.rdp_defaults + defaults = create_or_get_world(bpy.context.scene).rdp_defaults othermode_H = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - self.is_f3d_old, []) # if the render mode is set, it will be consider non-default a priori diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 60afedc8a..94fa3e9cf 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -1381,7 +1381,7 @@ def ui_procAnim(material, layout, useTex0, useTex1, title, useDropdown): def update_node_values(self, context, update_preset): - if hasattr(context.scene, "world") and self == context.scene.world.rdp_defaults: + if hasattr(context.scene, "world") and self == create_or_get_world(context.scene).rdp_defaults: pass with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: @@ -3391,6 +3391,9 @@ def poll(cls, context): def draw(self, context): world = context.scene.world layout = self.layout + if not world: + layout.box().label(text="No world in current scene, fast64 will pick/create one when needed", icon="INFO") + return layout.box().label(text="RDP Default Settings") layout.label(text="If a material setting is a same as a default setting, then it won't be set.") ui_geo_mode(world.rdp_defaults, world, layout, True) diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 34a11d832..e4a96c314 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -514,7 +514,7 @@ def addCullCommand(obj, fMesh, transformMatrix, matWriteMethod): ) if matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert: - defaults = bpy.context.scene.world.rdp_defaults + defaults = create_or_get_world(bpy.context.scene).rdp_defaults if defaults.g_lighting: cullCommands = [ SPClearGeometryMode(["G_LIGHTING"]), @@ -1421,7 +1421,7 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData): else: defaultRM = None - defaults = bpy.context.scene.world.rdp_defaults + defaults = create_or_get_world(bpy.context.scene).rdp_defaults if fModel.f3d.F3DEX_GBI_2: saveGeoModeDefinitionF3DEX2(fMaterial, f3dMat.rdp_settings, defaults, fModel.matWriteMethod) else: diff --git a/fast64_internal/oot/f3d/panels.py b/fast64_internal/oot/f3d/panels.py index 70eff9046..af0658fe6 100644 --- a/fast64_internal/oot/f3d/panels.py +++ b/fast64_internal/oot/f3d/panels.py @@ -89,7 +89,10 @@ def poll(cls, context): return context.scene.gameEditorMode == "OOT" def draw(self, context): - ootDefaultRenderModeProp: OOTDefaultRenderModesProperty = context.scene.world.ootDefaultRenderModes + world = context.scene.world + if not world: + return + ootDefaultRenderModeProp: OOTDefaultRenderModesProperty = world.ootDefaultRenderModes ootDefaultRenderModeProp.draw_props(self.layout) diff --git a/fast64_internal/oot/oot_model_classes.py b/fast64_internal/oot/oot_model_classes.py index 59b5b5253..b90e85762 100644 --- a/fast64_internal/oot/oot_model_classes.py +++ b/fast64_internal/oot/oot_model_classes.py @@ -2,7 +2,7 @@ from typing import Union from ..f3d.f3d_parser import F3DContext, F3DTextureReference, getImportData from ..f3d.f3d_material import TextureProperty, createF3DMat, texFormatOf, texBitSizeF3D -from ..utility import PluginError, CData, hexOrDecInt, getNameFromPath, getTextureSuffixFromFormat, toAlnum +from ..utility import PluginError, hexOrDecInt, create_or_get_world from ..f3d.flipbook import TextureFlipbook, FlipbookProperty, usesFlipbook, ootFlipbookReferenceIsValid from ..f3d.f3d_writer import VertexGroupInfo, TriangleConverterInfo @@ -118,7 +118,7 @@ def getRenderMode(self, drawLayer): drawLayerUsed = self.drawLayerOverride else: drawLayerUsed = drawLayer - defaultRenderModes = bpy.context.scene.world.ootDefaultRenderModes + defaultRenderModes = create_or_get_world(bpy.context.scene).ootDefaultRenderModes cycle1 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle1") cycle2 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle2") return [cycle1, cycle2] diff --git a/fast64_internal/sm64/sm64_f3d_writer.py b/fast64_internal/sm64/sm64_f3d_writer.py index 059e40815..a1aafbaa4 100644 --- a/fast64_internal/sm64/sm64_f3d_writer.py +++ b/fast64_internal/sm64/sm64_f3d_writer.py @@ -47,7 +47,6 @@ ) from ..utility import ( - CData, CScrollData, PluginError, raisePluginError, @@ -74,6 +73,7 @@ makeWriteInfoBox, writeBoxExportType, enumExportHeaderType, + create_or_get_world, ) from .sm64_constants import ( @@ -106,8 +106,9 @@ def getDrawLayerV3(self, obj): return int(obj.draw_layer_static) def getRenderMode(self, drawLayer): - cycle1 = getattr(bpy.context.scene.world, "draw_layer_" + str(drawLayer) + "_cycle_1") - cycle2 = getattr(bpy.context.scene.world, "draw_layer_" + str(drawLayer) + "_cycle_2") + world = create_or_get_world(bpy.context.scene) + cycle1 = getattr(world, "draw_layer_" + str(drawLayer) + "_cycle_1") + cycle2 = getattr(world, "draw_layer_" + str(drawLayer) + "_cycle_2") return [cycle1, cycle2] @@ -871,9 +872,10 @@ def poll(cls, context): def draw(self, context): world = context.scene.world - layout = self.layout + if not world: + return - inputGroup = layout.column() + inputGroup = self.layout.column() inputGroup.prop( world, "menu_layers", text="Draw Layers", icon="TRIA_DOWN" if world.menu_layers else "TRIA_RIGHT" ) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 212e83e2d..371605044 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -3,7 +3,7 @@ from mathutils import * from .utility_anim import * from typing import Callable, Iterable, Any, Tuple, Union -from bpy.types import UILayout +from bpy.types import UILayout, Scene, World CollectionProperty = Any # collection prop as defined by using bpy.props.CollectionProperty @@ -1620,6 +1620,26 @@ def getTextureSuffixFromFormat(texFmt): return texFmt.lower() +def create_or_get_world(scene: Scene) -> World: + """ + Given a scene, this function will: + - If the scene has a selected world, it will return the world selected in the scene. + - If bpy.data.worlds is not empty, it will return the first world in bpy.data.worlds. + - If bpy.data.worlds is empty, it will create a world named "Fast64" and return it. + This function does not assign any world to the scene. + """ + if scene.world: + return scene.world + elif bpy.data.worlds: + world: World = bpy.data.worlds.values()[0] + print(f'No world selected in scene, selected the first one found in this file "{world.name}".') + return bpy.data.worlds.values()[0] + else: + # Almost never reached because the node library has its own world + print(f'No world in this file, creating world named "Fast64".') + return bpy.data.worlds.new("Fast64") + + binOps = { ast.Add: operator.add, ast.Sub: operator.sub, From 6fdf9f485d4e5d236bfd02338b035942e2411600 Mon Sep 17 00:00:00 2001 From: Lila Date: Tue, 21 May 2024 18:48:23 +0100 Subject: [PATCH 2/6] Make docstring sound less like... AI It sounded like AI and had redundancy, I'm ashamed --- fast64_internal/utility.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 371605044..0c9ac0c6d 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1622,22 +1622,21 @@ def getTextureSuffixFromFormat(texFmt): def create_or_get_world(scene: Scene) -> World: """ - Given a scene, this function will: - - If the scene has a selected world, it will return the world selected in the scene. - - If bpy.data.worlds is not empty, it will return the first world in bpy.data.worlds. - - If bpy.data.worlds is empty, it will create a world named "Fast64" and return it. + Given a scene, this function will return: + - The world selected in the scene if the scene has a selected world. + - The first world in bpy.data.worlds if the current file has a world. (Which it almost always does because of the f3d nodes library) + - Create a world named "Fast64" and return it if no world exits. This function does not assign any world to the scene. """ if scene.world: return scene.world - elif bpy.data.worlds: + if bpy.data.worlds: world: World = bpy.data.worlds.values()[0] print(f'No world selected in scene, selected the first one found in this file "{world.name}".') return bpy.data.worlds.values()[0] - else: - # Almost never reached because the node library has its own world - print(f'No world in this file, creating world named "Fast64".') - return bpy.data.worlds.new("Fast64") + # Almost never reached because the node library has its own world + print(f'No world in this file, creating world named "Fast64".') + return bpy.data.worlds.new("Fast64") binOps = { From 7d3511c7e9ff2990894eb459c4d86ebeeb24e631 Mon Sep 17 00:00:00 2001 From: Lila Date: Sun, 18 Aug 2024 13:54:55 +0100 Subject: [PATCH 3/6] remove newline --- fast64_internal/utility.py | 1 - 1 file changed, 1 deletion(-) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 6c02e1f6f..25e26dfec 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -2,7 +2,6 @@ from math import pi, ceil, degrees, radians, copysign from mathutils import * from .utility_anim import * - from typing import Callable, Iterable, Any, Optional, Tuple, TypeVar, Union from bpy.types import UILayout, Scene, World From 29a996f7cef1a8c3df2e7c567fef90813c51a002 Mon Sep 17 00:00:00 2001 From: Lila Date: Mon, 19 Aug 2024 09:52:46 +0100 Subject: [PATCH 4/6] Add back create_or_get_world I guess? --- fast64_internal/utility.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 25e26dfec..10db23f30 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1868,3 +1868,22 @@ def upgrade_old_prop( print(f"Failed to upgrade {new_prop} from old location {old_loc} with props {old_props}") traceback.print_exc() return False + + +def create_or_get_world(scene: Scene) -> World: + """ + Given a scene, this function will return: + - The world selected in the scene if the scene has a selected world. + - The first world in bpy.data.worlds if the current file has a world. (Which it almost always does because of the f3d nodes library) + - Create a world named "Fast64" and return it if no world exits. + This function does not assign any world to the scene. + """ + if scene.world: + return scene.world + if bpy.data.worlds: + world: World = bpy.data.worlds.values()[0] + print(f'No world selected in scene, selected the first one found in this file "{world.name}".') + return bpy.data.worlds.values()[0] + # Almost never reached because the node library has its own world + print(f'No world in this file, creating world named "Fast64".') + return bpy.data.worlds.new("Fast64") From 65f802a0b160cc127dd19dc2a53a660f42ab046e Mon Sep 17 00:00:00 2001 From: Lila Date: Mon, 19 Aug 2024 10:16:03 +0100 Subject: [PATCH 5/6] Update fast64_internal/utility.py Co-authored-by: Dragorn421 --- fast64_internal/utility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 10db23f30..26261016f 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1883,7 +1883,7 @@ def create_or_get_world(scene: Scene) -> World: if bpy.data.worlds: world: World = bpy.data.worlds.values()[0] print(f'No world selected in scene, selected the first one found in this file "{world.name}".') - return bpy.data.worlds.values()[0] + return world # Almost never reached because the node library has its own world print(f'No world in this file, creating world named "Fast64".') return bpy.data.worlds.new("Fast64") From 5a78bebb3dc7b07cd0101eb602396b12c30c486e Mon Sep 17 00:00:00 2001 From: Lila Date: Mon, 19 Aug 2024 10:35:48 +0100 Subject: [PATCH 6/6] WORLD_WARNING_COUNT --- fast64_internal/utility.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 26261016f..d59c59276 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1870,6 +1870,9 @@ def upgrade_old_prop( return False +WORLD_WARNING_COUNT = 0 + + def create_or_get_world(scene: Scene) -> World: """ Given a scene, this function will return: @@ -1878,12 +1881,17 @@ def create_or_get_world(scene: Scene) -> World: - Create a world named "Fast64" and return it if no world exits. This function does not assign any world to the scene. """ + global WORLD_WARNING_COUNT if scene.world: + WORLD_WARNING_COUNT = 0 return scene.world - if bpy.data.worlds: + elif bpy.data.worlds: world: World = bpy.data.worlds.values()[0] - print(f'No world selected in scene, selected the first one found in this file "{world.name}".') + if WORLD_WARNING_COUNT < 10: + print(f'No world selected in scene, selected the first one found in this file "{world.name}".') + WORLD_WARNING_COUNT += 1 return world - # Almost never reached because the node library has its own world - print(f'No world in this file, creating world named "Fast64".') - return bpy.data.worlds.new("Fast64") + else: # Almost never reached because the node library has its own world + WORLD_WARNING_COUNT = 0 + print(f'No world in this file, creating world named "Fast64".') + return bpy.data.worlds.new("Fast64")