diff --git a/fast64_internal/f3d/f3d_bleed.py b/fast64_internal/f3d/f3d_bleed.py index 9e6d7da44..82b0f5050 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, @@ -63,7 +64,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([]) @@ -91,7 +92,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 49b8bf80a..c5da043a6 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -1410,7 +1410,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: diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 385bbcf31..c73a6e50d 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -515,7 +515,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"]), @@ -1321,7 +1321,7 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData): fMaterial = fModel.addMaterial(materialName) useDict = all_combiner_uses(f3dMat) - 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 94cb0242e..30b0bff20 100644 --- a/fast64_internal/oot/f3d/panels.py +++ b/fast64_internal/oot/f3d/panels.py @@ -94,7 +94,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 4ccb05256..29db2d6ba 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, @@ -73,6 +72,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] @@ -866,9 +867,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 ad05ab2d05..d59c59276 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, Optional, Tuple, TypeVar, Union -from bpy.types import UILayout +from bpy.types import UILayout, Scene, World CollectionProperty = Any # collection prop as defined by using bpy.props.CollectionProperty @@ -1868,3 +1868,30 @@ 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 + + +WORLD_WARNING_COUNT = 0 + + +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. + """ + global WORLD_WARNING_COUNT + if scene.world: + WORLD_WARNING_COUNT = 0 + return scene.world + elif bpy.data.worlds: + world: World = bpy.data.worlds.values()[0] + 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 + 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")