diff --git a/README.md b/README.md index 81c3052..5e21ef4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fast64 -This requires Blender 3.2+. +This requires Blender 3.2+. Blender 4.0+ is recommended. Forked from [kurethedead/fast64 on BitBucket](https://bitbucket.org/kurethedead/fast64/src). @@ -22,7 +22,7 @@ Thanks to InTheBeef for LowPolySkinnedMario. ### Discord Server We have a Discord server for support as well as development [here](https://discord.gg/ny7PDcN2x8). -### Table of Contents +### Links to Docs / Guides for Each Game 1. [ Super Mario 64 ](/fast64_internal/sm64/README.md) 2. [ Ocarina Of Time ](/fast64_internal/oot/README.md) @@ -48,6 +48,21 @@ There may occur cases where code is formatted differently based on the code use ### Converting To F3D v5 Materials A new optimized shader graph was introduced to decrease processing times for material creation and exporting. If you have a project that still uses old materials, you may want to convert them to v5. To convert an old project, click the "Recreate F3D Materials As V5" operator near the top of the Fast64 tab in 3D view. This may take a while depending on the number of materials in the project. Then go to the outliner, change the display mode to "Orphan Data" (broken heart icon), then click "Purge" in the top right corner. Purge multiple times until all of the old node groups are gone. +### F3DEX3 Features + +Fast64 supports exporting data for [F3DEX3](https://github.com/HackerN64/F3DEX3), a modded microcode which brings many new features and higher performance. **Preview of these new features in the 3D view is not currently supported**, but they will be exported correctly. To modify the vertex colors of an object with a material which is using packed normals (shading/lighting and vertex colors together), temporarily switch to a vertex colored preset or uncheck `Lighting` in the material geometry mode settings. Once the vertex colors are painted how you want them, re-enable `Lighting` and `Packed Normals`. + +Selecting F3DEX3 as your microcode unlocks a large number of additional presets based on F3DEX3 features. For more information on all these features, see the F3DEX3 readme, GBI, and [these videos](https://www.youtube.com/playlist?list=PLU2OUGtyQi6QswDQOXWIMaYFUcgQ9Psvm). The preset names get very long and are abbreviated as follows: +- `Shaded`: Computes lighting, which normally affects shade color. +- `Vcol`: Vertex colors are enabled in addition to lighting; normally these are multiplied together to become shade color. +- `Ao`: Ambient occlusion. +- `Cel`: Cel shading. If followed by a number, this is the number of cel levels. +- `Ltcol`: Cel shading tints are loaded from light colors. +- `Blend` vs. `Mul` for cel shading: Whether to apply the tint in the blender with linear interpolation, or by multiplication in the CC. The latter sometimes looks better, but does not support vertex colors. +- `Lerp` vs. `Mult` for multitexture (water): Whether the two textures are combined by linear interpolation or multiplication. + +For cel shading, it is recommended to start with one of the cel shading presets, then modify the settings under the `Use Cel Shading` panel. Hover over each UI control for additional information about how that setting works. + ### Updater Fast64 features an updater ([CGCookie/blender-addon-updater](https://github.com/CGCookie/blender-addon-updater)). diff --git a/__init__.py b/__init__.py index f63d4a6..6a4c147 100644 --- a/__init__.py +++ b/__init__.py @@ -161,7 +161,6 @@ class F3D_GlobalSettingsPanel(bpy.types.Panel): col = self.layout.column() col.scale_y = 1.1 # extra padding prop_split(col, context.scene, "f3d_type", "F3D Microcode") - col.prop(context.scene, "isHWv1") col.prop(context.scene, "saveTextures") col.prop(context.scene, "f3d_simple", text="Simple Material UI") col.prop(context.scene, "generateF3DNodeGraph", text="Generate F3D Node Graph For Materials") @@ -427,7 +426,6 @@ def gameEditorUpdate(self, context): # register operators and panels here # append menu layout drawing function to an existing window def register(): - if bpy.app.version < (3, 2, 0): msg = "\n".join( ( @@ -472,8 +470,11 @@ def register(): bpy.types.Scene.saveTextures = bpy.props.BoolProperty(name="Save Textures As PNGs (Breaks CI Textures)") bpy.types.Scene.generateF3DNodeGraph = bpy.props.BoolProperty(name="Generate F3D Node Graph", default=True) bpy.types.Scene.exportHiddenGeometry = bpy.props.BoolProperty(name="Export Hidden Geometry", default=True) - bpy.types.Scene.exportInlineF3D = bpy.props.BoolProperty(name="Bleed and Inline Material Exports", \ - description = "Inlines and bleeds materials in a single mesh. GeoLayout + Armature exports bleed over entire model", default=False) + bpy.types.Scene.exportInlineF3D = bpy.props.BoolProperty( + name="Bleed and Inline Material Exports", + description="Inlines and bleeds materials in a single mesh. GeoLayout + Armature exports bleed over entire model", + default=False, + ) bpy.types.Scene.blenderF3DScale = bpy.props.FloatProperty( name="F3D Blender Scale", default=100, update=on_update_render_settings ) diff --git a/fast64_internal/f3d/f3d_enums.py b/fast64_internal/f3d/f3d_enums.py index d546b49..e17f108 100644 --- a/fast64_internal/f3d/f3d_enums.py +++ b/fast64_internal/f3d/f3d_enums.py @@ -171,7 +171,11 @@ enumCombKey = [ enumTextConv = [ ("G_TC_CONV", "Convert", "Convert YUV to RGB"), - ("G_TC_FILTCONV", "Filter And Convert", "Applies chosen filter on cycle 1 and converts YUB to RGB in the second cycle"), + ( + "G_TC_FILTCONV", + "Filter And Convert", + "Applies chosen filter on cycle 1 and converts YUB to RGB in the second cycle", + ), ("G_TC_FILT", "Filter", "Applies chosen filter on textures with no color conversion"), ] @@ -189,11 +193,19 @@ enumTextLUT = [ enumTextLOD = [ ("G_TL_TILE", "Tile", "Shows selected color combiner tiles"), - ("G_TL_LOD", "LoD", "Enables LoD calculations, LoD tile is base tile + clamp(log2(texel/pixel)), remainder of log2(texel/pixel) ratio gets stored to LoD Fraction in the color combiner"), + ( + "G_TL_LOD", + "LoD", + "Enables LoD calculations, LoD tile is base tile + clamp(log2(texel/pixel)), remainder of log2(texel/pixel) ratio gets stored to LoD Fraction in the color combiner", + ), ] enumTextDetail = [ - ("G_TD_CLAMP", "Clamp", "Shows base tile for texel0 and texel 1 when magnifying (>1 texel/pixel), else shows LoD tiles"), + ( + "G_TD_CLAMP", + "Clamp", + "Shows base tile for texel0 and texel 1 when magnifying (>1 texel/pixel), else shows LoD tiles", + ), ("G_TD_SHARPEN", "Sharpen", "Sharpens pixel colors when magnifying (<1 texel/pixel), always shows LoD tiles"), ("G_TD_DETAIL", "Detail", "Shows base tile when magnifying (<1 texel/pixel), else shows LoD tiles + 1"), ] @@ -213,8 +225,16 @@ enumCycleType = [ enumColorDither = [("G_CD_DISABLE", "Disable", "Disable"), ("G_CD_ENABLE", "Enable", "Enable")] enumPipelineMode = [ - ("G_PM_1PRIMITIVE", "1 Primitive", "Adds in pipe sync after every tri draw. Adds significant amounts of lag. Only use in vanilla SM64 hacking projects"), - ("G_PM_NPRIMITIVE", "N Primitive", "No additional syncs are added after tri draws. Default option for every game but vanilla SM64"), + ( + "G_PM_1PRIMITIVE", + "1 Primitive", + "Adds in pipe sync after every tri draw. Adds significant amounts of lag. Only use in vanilla SM64 hacking projects", + ), + ( + "G_PM_NPRIMITIVE", + "N Primitive", + "No additional syncs are added after tri draws. Default option for every game but vanilla SM64", + ), ] enumAlphaCompare = [ @@ -248,7 +268,7 @@ enumBlendColor = [ "Input (CC/Blender)", "First cycle: Color Combiner RGB, Second cycle: Blender numerator from first cycle", ), - ("G_BL_CLR_MEM", "Framebuffer Color", "Framebuffer Color (Memory)"), + ("G_BL_CLR_MEM", "Framebuffer Color", "Framebuffer (Memory) Color"), ("G_BL_CLR_BL", "Blend Color", "Blend Color Register"), ("G_BL_CLR_FOG", "Fog Color", "Fog Color Register"), ] @@ -256,13 +276,13 @@ enumBlendColor = [ enumBlendAlpha = [ ("G_BL_A_IN", "Color Combiner Alpha", "Color Combiner Alpha"), ("G_BL_A_FOG", "Fog Alpha", "Fog Color Register Alpha"), - ("G_BL_A_SHADE", "Shade Alpha", "Stepped Shade Alpha"), + ("G_BL_A_SHADE", "Shade Alpha", "Stepped Shade Alpha from RSP, often fog"), ("G_BL_0", "0", "0"), ] enumBlendMix = [ ("G_BL_1MA", "1 - A", "1 - A, where A is selected above"), - ("G_BL_A_MEM", "Framebuffer Alpha", "Framebuffer (Memory) Alpha"), + ("G_BL_A_MEM", "Framebuffer Alpha", "Framebuffer (Memory) Alpha (Coverage)"), ("G_BL_1", "1", "1"), ("G_BL_0", "0", "0"), ] @@ -357,15 +377,38 @@ maxTexelCount = { } enumF3D = [ - ("F3D", "F3D", "F3D"), - ("F3DEX/LX", "F3DEX/LX", "F3DEX/LX"), + ("F3D", "F3D", "Original microcode used in SM64"), + ("F3DEX/LX", "F3DEX/LX", "F3DEX version 1"), ("F3DLX.Rej", "F3DLX.Rej", "F3DLX.Rej"), ("F3DLP.Rej", "F3DLP.Rej", "F3DLP.Rej"), - ("F3DEX2/LX2", "F3DEX2/LX2", "F3DEX2/LX2"), - ("F3DEX2.Rej/LX2.Rej", "F3DEX2.Rej/LX2.Rej", "F3DEX2.Rej/LX2.Rej"), + ("F3DEX2/LX2", "F3DEX2/LX2/ZEX", "Family of microcodes used in later N64 games including OoT and MM"), + ("F3DEX2.Rej/LX2.Rej", "F3DEX2.Rej/LX2.Rej", "Variant of F3DEX2 family using vertex rejection instead of clipping"), + ("F3DEX3", "F3DEX3", "Custom microcode by Sauraen"), ] enumLargeEdges = [ ("Clamp", "Clamp", "Clamp outside image bounds"), ("Wrap", "Wrap", "Wrap outside image bounds (more expensive)"), ] + +enumCelThreshMode = [ + ("Lighter", "Lighter", "This cel level is drawn when the lighting level per-pixel is LIGHTER than (>=) the threshold"), + ("Darker", "Darker", "This cel level is drawn when the lighting level per-pixel is DARKER than (<) the threshold"), +] + +enumCelTintPipeline = [ + ("CC", "CC (tint in Prim Color)", "Cel shading puts tint color in Prim Color and tint level in Prim Alpha. Set up CC color to LERP between source color and tint color based on tint level, or multiply source color by tint color. Source may be Tex 0 or Env Color"), + ("Blender", "Blender (tint in Fog Color)", "Cel shading puts tint color in Fog Color and tint level in Fog Alpha. Set up blender to LERP between CC output and tint color based on tint level. Then set CC to Tex 0 * shade color (vertex colors)"), +] + +enumCelCutoutSource = [ + ("TEXEL0", "Texture 0", "Cel shading material has binary alpha cutout from Texture 0 alpha. Does not work with I4 or I8 formats"), + ("TEXEL1", "Texture 1", "Cel shading material has binary alpha cutout from Texture 1 alpha. Does not work with I4 or I8 formats"), + ("ENVIRONMENT", "None / Env Alpha", "Make sure your material writes env color, and set env alpha to opaque (255)"), +] + +enumCelTintType = [ + ("Fixed", "Fixed", "Fixed tint color and level stored directly in DL"), + ("Segment", "Segment", "Call a segmented DL to set the tint, can change at runtime"), + ("Light", "From Light", "Automatically load tint color from selectable light slot. Tint level stored in DL"), +] diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 55301f7..a8af5eb 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -53,16 +53,8 @@ dlTypeEnum = [ ("PROCEDURAL", "Procedural", "Procedural"), ] -lightIndex = { - "LIGHT_1": 1, - "LIGHT_2": 2, - "LIGHT_3": 3, - "LIGHT_4": 4, - "LIGHT_5": 5, - "LIGHT_6": 6, - "LIGHT_7": 7, - "LIGHT_8": 8, -} +# 1-8 for F3DEX2 etc., 1-10 for F3DEX3 +lightIndex = {f"LIGHT_{n}": n for n in range(1, 11)} # tuple of max buffer size, max load count. vertexBufferSize = { @@ -72,6 +64,7 @@ vertexBufferSize = { "F3DLP.Rej": (80, 32), "F3DEX2/LX2": (32, 32), "F3DEX2.Rej/LX2.Rej": (64, 64), + "F3DEX3": (56, 56), } drawLayerRenderMode = { @@ -123,39 +116,41 @@ ACMUXDict = { } +def isUcodeF3DEX1(F3D_VER: str) -> bool: + return F3D_VER in {"F3DLP.Rej", "F3DLX.Rej", "F3DEX/LX"} + + +def isUcodeF3DEX2(F3D_VER: str) -> bool: + return F3D_VER in {"F3DEX2.Rej/LX2.Rej", "F3DEX2/LX2"} + + +def isUcodeF3DEX3(F3D_VER: str) -> bool: + return F3D_VER == "F3DEX3" + + class F3D: """NOTE: do not initialize this class manually! use get_F3D_GBI so that the single instance is cached from the microcode type.""" - def __init__(self, F3D_VER, _HW_VERSION_1): - if F3D_VER == "F3DEX2.Rej/LX2.Rej" or F3D_VER == "F3DEX2/LX2": - self.F3DEX_GBI = False - self.F3DEX_GBI_2 = True - self.F3DLP_GBI = False - elif F3D_VER == "F3DLP.Rej" or F3D_VER == "F3DLX.Rej" or F3D_VER == "F3DEX/LX": - self.F3DEX_GBI = True - self.F3DEX_GBI_2 = False - self.F3DLP_GBI = True - elif F3D_VER == "F3D": - self.F3DEX_GBI = False - self.F3DEX_GBI_2 = False - self.F3DLP_GBI = False - else: - raise PluginError("Invalid F3D version " + F3D_VER + ".") + def __init__(self, F3D_VER): + self.F3D_VER = F3D_VER + F3DEX_GBI = self.F3DEX_GBI = isUcodeF3DEX1(F3D_VER) + F3DEX_GBI_2 = self.F3DEX_GBI_2 = isUcodeF3DEX2(F3D_VER) + F3DEX_GBI_3 = self.F3DEX_GBI_3 = isUcodeF3DEX3(F3D_VER) + F3DLP_GBI = self.F3DLP_GBI = self.F3DEX_GBI + self.F3D_OLD_GBI = not (F3DEX_GBI or F3DEX_GBI_2 or F3DEX_GBI_3) + + # F3DEX2 is F3DEX1 and F3DEX3 is F3DEX2, but F3DEX3 is not F3DEX1 + if F3DEX_GBI_2: + F3DEX_GBI = self.F3DEX_GBI = True + elif F3DEX_GBI_3: + F3DEX_GBI_2 = self.F3DEX_GBI_2 = True self.vert_buffer_size = vertexBufferSize[F3D_VER][0] self.vert_load_size = vertexBufferSize[F3D_VER][1] - - F3DEX_GBI = self.F3DEX_GBI - F3DEX_GBI_2 = self.F3DEX_GBI_2 - F3DLP_GBI = self.F3DLP_GBI - self._HW_VERSION_1 = _HW_VERSION_1 - self.F3D_VER = F3D_VER - # self._LANGUAGE_ASSEMBLY = _LANGUAGE_ASSEMBLY + self.G_MAX_LIGHTS = 9 if F3DEX_GBI_3 else 7 + self.G_INPUT_BUFFER_CMDS = 21 if F3DEX_GBI_2: - self.F3DEX_GBI = True - F3DEX_GBI = True - self.G_NOOP = 0x00 self.G_RDPHALF_2 = 0xF1 self.G_SETOTHERMODE_H = 0xE3 @@ -172,9 +167,6 @@ class F3D: self.G_POPMTX = 0xD8 self.G_TEXTURE = 0xD7 self.G_DMA_IO = 0xD6 - self.G_SPECIAL_1 = 0xD5 - self.G_SPECIAL_2 = 0xD4 - self.G_SPECIAL_3 = 0xD3 self.G_VTX = 0x01 self.G_MODIFYVTX = 0x02 @@ -183,7 +175,16 @@ class F3D: self.G_TRI1 = 0x05 self.G_TRI2 = 0x06 self.G_QUAD = 0x07 - self.G_LINE3D = 0x08 + + if F3DEX_GBI_3: + self.G_TRISTRIP = 0x08 + self.G_TRIFAN = 0x09 + self.G_LIGHTTORDP = 0x0A + else: + self.G_SPECIAL_1 = 0xD5 + self.G_SPECIAL_2 = 0xD4 + self.G_SPECIAL_3 = 0xD3 + self.G_LINE3D = 0x08 else: # DMA commands @@ -274,13 +275,9 @@ class F3D: self.G_RDP_TRI_TXTR_MASK = 0x02 self.G_RDP_TRI_ZBUFF_MASK = 0x01 - self.BOWTIE_VAL = 0 - # gets added to RDP command, in order to test for addres fixup self.G_RDP_ADDR_FIXUP = 3 # |RDP cmds| <= this, do addr fixup - # if _LANGUAGE_ASSEMBLY: self.G_RDP_TEXRECT_CHECK = (-1 * self.G_TEXRECTFLIP) & 0xFF - # endif self.G_DMACMDSIZ = 128 self.G_IMMCMDSIZ = 64 @@ -336,7 +333,45 @@ class F3D: else: self.G_CLIPPING = 0x00000000 - # if _LANGUAGE_ASSEMBLY: + if F3DEX_GBI_3: + self.G_AMBOCCLUSION = 0x00000040 + self.G_ATTROFFSET_Z_ENABLE = 0x00000080 + self.G_ATTROFFSET_ST_ENABLE = 0x00000100 + self.G_PACKED_NORMALS = 0x00000800 + self.G_LIGHTTOALPHA = 0x00001000 + self.G_LIGHTING_SPECULAR = 0x00002000 + self.G_FRESNEL_COLOR = 0x00004000 + self.G_FRESNEL_ALPHA = 0x00008000 + self.G_LIGHTING_POSITIONAL = 0x00400000 # Ignored, always on + + self.allGeomModeFlags = { + "G_ZBUFFER", + "G_TEXTURE_ENABLE", + "G_SHADE", + "G_CULL_FRONT", + "G_CULL_BACK", + "G_CULL_BOTH", + "G_FOG", + "G_LIGHTING", + "G_TEXTURE_GEN", + "G_TEXTURE_GEN_LINEAR", + "G_LOD", + "G_SHADING_SMOOTH", + "G_LIGHTING_POSITIONAL", + "G_CLIPPING", + } + if F3DEX_GBI_3: + self.allGeomModeFlags |= { + "G_AMBOCCLUSION", + "G_ATTROFFSET_Z_ENABLE", + "G_ATTROFFSET_ST_ENABLE", + "G_PACKED_NORMALS", + "G_LIGHTTOALPHA", + "G_LIGHTING_SPECULAR", + "G_FRESNEL_COLOR", + "G_FRESNEL_ALPHA", + } + self.G_FOG_H = self.G_FOG / 0x10000 self.G_LIGHTING_H = self.G_LIGHTING / 0x10000 self.G_TEXTURE_GEN_H = self.G_TEXTURE_GEN / 0x10000 @@ -344,10 +379,8 @@ class F3D: self.G_LOD_H = self.G_LOD / 0x10000 # NOT IMPLEMENTED if F3DEX_GBI or F3DLP_GBI: self.G_CLIPPING_H = self.G_CLIPPING / 0x10000 - # endif # Need these defined for Sprite Microcode - # if _LANGUAGE_ASSEMBLY: self.G_TX_LOADTILE = 7 self.G_TX_RENDERTILE = 0 @@ -357,7 +390,6 @@ class F3D: self.G_TX_CLAMP = 0x2 self.G_TX_NOMASK = 0 self.G_TX_NOLOD = 0 - # endif self.G_TX_VARS = { "G_TX_NOMIRROR": 0, @@ -692,13 +724,8 @@ class F3D: self.G_CD_MAGICSQ = 0 << G_MDSFT_RGBDITHER self.G_CD_BAYER = 1 << G_MDSFT_RGBDITHER self.G_CD_NOISE = 2 << G_MDSFT_RGBDITHER - - if not _HW_VERSION_1: - self.G_CD_DISABLE = 3 << G_MDSFT_RGBDITHER - self.G_CD_ENABLE = self.G_CD_NOISE # HW 1.0 compatibility mode - else: - self.G_CD_ENABLE = 1 << G_MDSFT_COLORDITHER - self.G_CD_DISABLE = 0 << G_MDSFT_COLORDITHER + self.G_CD_DISABLE = 3 << G_MDSFT_RGBDITHER + self.G_CD_ENABLE = self.G_CD_NOISE # G_SETOTHERMODE_H gSetAlphaDither self.G_AD_PATTERN = 0 << G_MDSFT_ALPHADITHER @@ -1396,6 +1423,15 @@ class F3D: self.G_DL_PUSH = 0x00 self.G_DL_NOPUSH = 0x01 + if F3DEX_GBI_3: + self.G_NORMALS_MODE_FAST = 0x00 + self.G_NORMALS_MODE_AUTO = 0x01 + self.G_NORMALS_MODE_MANUAL = 0x02 + + self.G_ALPHA_COMPARE_CULL_DISABLE = 0 + self.G_ALPHA_COMPARE_CULL_BELOW = 1 + self.G_ALPHA_COMPARE_CULL_ABOVE = -1 + # Some structs here self.G_MAXZ = 0x03FF # 10 bits of integer screen-Z precision @@ -1416,18 +1452,19 @@ class F3D: self.G_MV_PMTX = 6 self.G_MV_VIEWPORT = 8 self.G_MV_LIGHT = 10 - self.G_MV_POINT = 12 - self.G_MV_MATRIX = 14 # NOTE: this is in moveword table - self.G_MVO_LOOKATX = 0 * 24 - self.G_MVO_LOOKATY = 1 * 24 - self.G_MVO_L0 = 2 * 24 - self.G_MVO_L1 = 3 * 24 - self.G_MVO_L2 = 4 * 24 - self.G_MVO_L3 = 5 * 24 - self.G_MVO_L4 = 6 * 24 - self.G_MVO_L5 = 7 * 24 - self.G_MVO_L6 = 8 * 24 - self.G_MVO_L7 = 9 * 24 + if not F3DEX_GBI_3: + self.G_MV_POINT = 12 + self.G_MV_MATRIX = 14 # NOTE: this is in moveword table + self.G_MVO_LOOKATX = 0 * 24 + self.G_MVO_LOOKATY = 1 * 24 + self.G_MVO_L0 = 2 * 24 + self.G_MVO_L1 = 3 * 24 + self.G_MVO_L2 = 4 * 24 + self.G_MVO_L3 = 5 * 24 + self.G_MVO_L4 = 6 * 24 + self.G_MVO_L5 = 7 * 24 + self.G_MVO_L6 = 8 * 24 + self.G_MVO_L7 = 9 * 24 else: self.G_MV_VIEWPORT = 0x80 self.G_MV_LOOKATY = 0x82 @@ -1454,17 +1491,25 @@ class F3D: an immediate word will be stored. """ - self.G_MW_MATRIX = 0x00 # NOTE: also used by movemem + if F3DEX_GBI_3: + self.G_MW_FX = 0x00 + else: + self.G_MW_MATRIX = 0x00 # NOTE: also used by movemem self.G_MW_NUMLIGHT = 0x02 - self.G_MW_CLIP = 0x04 + if not F3DEX_GBI_3: + self.G_MW_CLIP = 0x04 self.G_MW_SEGMENT = 0x06 self.G_MW_FOG = 0x08 self.G_MW_LIGHTCOL = 0x0A - if F3DEX_GBI_2: - self.G_MW_FORCEMTX = 0x0C - else: - self.G_MW_POINTS = 0x0C - self.G_MW_PERSPNORM = 0x0E + if not F3DEX_GBI_3: + if F3DEX_GBI_2: + self.G_MW_FORCEMTX = 0x0C + else: + self.G_MW_POINTS = 0x0C + self.G_MW_PERSPNORM = 0x0E + + if F3DEX_GBI_3: + self.G_MW_HALFWORD_FLAG = 0x8000 # These are offsets from the address in the dmem table @@ -1493,7 +1538,26 @@ class F3D: self.G_MWO_aLIGHT_1 = 0x00 self.G_MWO_bLIGHT_1 = 0x04 - if F3DEX_GBI_2: + if F3DEX_GBI_3: + self.G_MWO_aLIGHT_2 = 0x10 + self.G_MWO_bLIGHT_2 = 0x14 + self.G_MWO_aLIGHT_3 = 0x20 + self.G_MWO_bLIGHT_3 = 0x24 + self.G_MWO_aLIGHT_4 = 0x30 + self.G_MWO_bLIGHT_4 = 0x34 + self.G_MWO_aLIGHT_5 = 0x40 + self.G_MWO_bLIGHT_5 = 0x44 + self.G_MWO_aLIGHT_6 = 0x50 + self.G_MWO_bLIGHT_6 = 0x54 + self.G_MWO_aLIGHT_7 = 0x60 + self.G_MWO_bLIGHT_7 = 0x64 + self.G_MWO_aLIGHT_8 = 0x70 + self.G_MWO_bLIGHT_8 = 0x74 + self.G_MWO_aLIGHT_9 = 0x80 + self.G_MWO_bLIGHT_9 = 0x84 + self.G_MWO_aLIGHT_10 = 0x90 + self.G_MWO_bLIGHT_10 = 0x94 + elif F3DEX_GBI_2: self.G_MWO_aLIGHT_2 = 0x18 self.G_MWO_bLIGHT_2 = 0x1C self.G_MWO_aLIGHT_3 = 0x30 @@ -1524,27 +1588,42 @@ class F3D: self.G_MWO_aLIGHT_8 = 0xE0 self.G_MWO_bLIGHT_8 = 0xE4 - self.G_MWO_MATRIX_XX_XY_I = 0x00 - self.G_MWO_MATRIX_XZ_XW_I = 0x04 - self.G_MWO_MATRIX_YX_YY_I = 0x08 - self.G_MWO_MATRIX_YZ_YW_I = 0x0C - self.G_MWO_MATRIX_ZX_ZY_I = 0x10 - self.G_MWO_MATRIX_ZZ_ZW_I = 0x14 - self.G_MWO_MATRIX_WX_WY_I = 0x18 - self.G_MWO_MATRIX_WZ_WW_I = 0x1C - self.G_MWO_MATRIX_XX_XY_F = 0x20 - self.G_MWO_MATRIX_XZ_XW_F = 0x24 - self.G_MWO_MATRIX_YX_YY_F = 0x28 - self.G_MWO_MATRIX_YZ_YW_F = 0x2C - self.G_MWO_MATRIX_ZX_ZY_F = 0x30 - self.G_MWO_MATRIX_ZZ_ZW_F = 0x34 - self.G_MWO_MATRIX_WX_WY_F = 0x38 - self.G_MWO_MATRIX_WZ_WW_F = 0x3C + if not F3DEX_GBI_3: + self.G_MWO_MATRIX_XX_XY_I = 0x00 + self.G_MWO_MATRIX_XZ_XW_I = 0x04 + self.G_MWO_MATRIX_YX_YY_I = 0x08 + self.G_MWO_MATRIX_YZ_YW_I = 0x0C + self.G_MWO_MATRIX_ZX_ZY_I = 0x10 + self.G_MWO_MATRIX_ZZ_ZW_I = 0x14 + self.G_MWO_MATRIX_WX_WY_I = 0x18 + self.G_MWO_MATRIX_WZ_WW_I = 0x1C + self.G_MWO_MATRIX_XX_XY_F = 0x20 + self.G_MWO_MATRIX_XZ_XW_F = 0x24 + self.G_MWO_MATRIX_YX_YY_F = 0x28 + self.G_MWO_MATRIX_YZ_YW_F = 0x2C + self.G_MWO_MATRIX_ZX_ZY_F = 0x30 + self.G_MWO_MATRIX_ZZ_ZW_F = 0x34 + self.G_MWO_MATRIX_WX_WY_F = 0x38 + self.G_MWO_MATRIX_WZ_WW_F = 0x3C + self.G_MWO_POINT_RGBA = 0x10 self.G_MWO_POINT_ST = 0x14 self.G_MWO_POINT_XYSCREEN = 0x18 self.G_MWO_POINT_ZSCREEN = 0x1C + if F3DEX_GBI_3: + self.G_MWO_AO_AMBIENT = 0x00 + self.G_MWO_AO_DIRECTIONAL = 0x02 + self.G_MWO_AO_POINT = 0x04 + self.G_MWO_PERSPNORM = 0x06 + self.G_MWO_FRESNEL_SCALE = 0x0C + self.G_MWO_FRESNEL_OFFSET = 0x0E + self.G_MWO_ATTR_OFFSET_S = 0x10 + self.G_MWO_ATTR_OFFSET_T = 0x12 + self.G_MWO_ATTR_OFFSET_Z = 0x14 + self.G_MWO_ALPHA_COMPARE_CULL = 0x16 + self.G_MWO_NORMALS_MODE = 0x18 + # Texturing macros # These are also defined defined above for Sprite Microcode @@ -1579,39 +1658,31 @@ class F3D: tile manipulation yourself. RJM. """ - if _HW_VERSION_1: - self.G_TX_LDBLK_MAX_TXL = 4095 - else: - self.G_TX_LDBLK_MAX_TXL = 2047 + self.G_TX_LDBLK_MAX_TXL = 2047 - # Clipping Macros - self.FR_NEG_FRUSTRATIO_1 = 0x00000001 - self.FR_POS_FRUSTRATIO_1 = 0x0000FFFF - self.FR_NEG_FRUSTRATIO_2 = 0x00000002 - self.FR_POS_FRUSTRATIO_2 = 0x0000FFFE - self.FR_NEG_FRUSTRATIO_3 = 0x00000003 - self.FR_POS_FRUSTRATIO_3 = 0x0000FFFD - self.FR_NEG_FRUSTRATIO_4 = 0x00000004 - self.FR_POS_FRUSTRATIO_4 = 0x0000FFFC - self.FR_NEG_FRUSTRATIO_5 = 0x00000005 - self.FR_POS_FRUSTRATIO_5 = 0x0000FFFB - self.FR_NEG_FRUSTRATIO_6 = 0x00000006 - self.FR_POS_FRUSTRATIO_6 = 0x0000FFFA + if not F3DEX_GBI_3: + # Clipping Macros + self.FR_NEG_FRUSTRATIO_1 = 0x00000001 + self.FR_POS_FRUSTRATIO_1 = 0x0000FFFF + self.FR_NEG_FRUSTRATIO_2 = 0x00000002 + self.FR_POS_FRUSTRATIO_2 = 0x0000FFFE + self.FR_NEG_FRUSTRATIO_3 = 0x00000003 + self.FR_POS_FRUSTRATIO_3 = 0x0000FFFD + self.FR_NEG_FRUSTRATIO_4 = 0x00000004 + self.FR_POS_FRUSTRATIO_4 = 0x0000FFFC + self.FR_NEG_FRUSTRATIO_5 = 0x00000005 + self.FR_POS_FRUSTRATIO_5 = 0x0000FFFB + self.FR_NEG_FRUSTRATIO_6 = 0x00000006 + self.FR_POS_FRUSTRATIO_6 = 0x0000FFFA self.G_BZ_PERSP = 0 self.G_BZ_ORTHO = 1 # Lighting Macros - self.numLights = { - "NUMLIGHTS_0": 1, - "NUMLIGHTS_1": 1, - "NUMLIGHTS_2": 2, - "NUMLIGHTS_3": 3, - "NUMLIGHTS_4": 4, - "NUMLIGHTS_5": 5, - "NUMLIGHTS_6": 6, - "NUMLIGHTS_7": 7, - } + if F3DEX_GBI_3: + self.numLights = {f"NUMLIGHTS_{n}": n for n in range(10)} + else: + self.numLights = {f"NUMLIGHTS_{n}": (1 if n == 0 else n) for n in range(8)} def GBL_c1(self, m1a, m1b, m2a, m2b): return (m1a) << 30 | (m1b) << 26 | (m2a) << 22 | (m2b) << 18 @@ -1648,65 +1719,47 @@ class F3D: return int(((1 << self.G_TX_DXT_FRAC) + self.TXL2WORDS_4b(width) - 1) / self.TXL2WORDS_4b(width)) def NUML(self, n): + if self.F3DEX_GBI_3: + return n * 0x10 nVal = self.numLights[n] return ((nVal) * 24) if self.F3DEX_GBI_2 else (((nVal) + 1) * 32 + 0x80000000) def getLightMWO_a(self, n): - if n == "G_MWO_aLIGHT_1": - return self.G_MWO_aLIGHT_1 - elif n == "G_MWO_aLIGHT_2": - return self.G_MWO_aLIGHT_2 - elif n == "G_MWO_aLIGHT_3": - return self.G_MWO_aLIGHT_3 - elif n == "G_MWO_aLIGHT_4": - return self.G_MWO_aLIGHT_4 - elif n == "G_MWO_aLIGHT_5": - return self.G_MWO_aLIGHT_5 - elif n == "G_MWO_aLIGHT_6": - return self.G_MWO_aLIGHT_6 - elif n == "G_MWO_aLIGHT_7": - return self.G_MWO_aLIGHT_7 - elif n == "G_MWO_aLIGHT_8": - return self.G_MWO_aLIGHT_8 + if n.startswith("G_MWO_aLIGHT_") and hasattr(self, n): + return getattr(self, n) else: raise PluginError("Invalid G_MWO_a value for lights: " + n) def getLightMWO_b(self, n): - if n == "G_MWO_bLIGHT_1": - return self.G_MWO_bLIGHT_1 - elif n == "G_MWO_bLIGHT_2": - return self.G_MWO_bLIGHT_2 - elif n == "G_MWO_bLIGHT_3": - return self.G_MWO_bLIGHT_3 - elif n == "G_MWO_bLIGHT_4": - return self.G_MWO_bLIGHT_4 - elif n == "G_MWO_bLIGHT_5": - return self.G_MWO_bLIGHT_5 - elif n == "G_MWO_bLIGHT_6": - return self.G_MWO_bLIGHT_6 - elif n == "G_MWO_bLIGHT_7": - return self.G_MWO_bLIGHT_7 - elif n == "G_MWO_bLIGHT_8": - return self.G_MWO_bLIGHT_8 + if n.startswith("G_MWO_bLIGHT_") and hasattr(self, n): + return getattr(self, n) else: raise PluginError("Invalid G_MWO_b value for lights: " + n) - -g_F3D = {"GBI": None, "f3d_type": None, "isHWv1": None} + def _DLHINTVALUE(self, count: int) -> int: + remainderCommands = count % self.G_INPUT_BUFFER_CMDS + if not self.F3DEX_GBI_3 or count == 0 or remainderCommands == 0: + return 0 + return (self.G_INPUT_BUFFER_CMDS - remainderCommands) << 3 -def get_cached_F3D_GBI(f3d_type: str, isHWv1: bool) -> F3D: +g_F3D = { + "GBI": None, + "f3d_type": None, +} + + +def get_cached_F3D_GBI(f3d_type: str) -> F3D: """Get constructed/cached F3D class""" - if g_F3D["GBI"] is None or f3d_type != g_F3D["f3d_type"] or isHWv1 != g_F3D["isHWv1"]: + if g_F3D["GBI"] is None or f3d_type != g_F3D["f3d_type"]: g_F3D["f3d_type"] = f3d_type - g_F3D["isHWv1"] = isHWv1 - g_F3D["GBI"] = F3D(f3d_type, isHWv1) + g_F3D["GBI"] = F3D(f3d_type) return g_F3D["GBI"] def get_F3D_GBI() -> F3D: """Gets cached F3D class and automatically supplies params""" - return get_cached_F3D_GBI(bpy.context.scene.f3d_type, bpy.context.scene.isHWv1) + return get_cached_F3D_GBI(bpy.context.scene.f3d_type) def _SHIFTL(value, amount, mask): @@ -1716,10 +1769,10 @@ def _SHIFTL(value, amount, mask): MTX_SIZE = 64 VTX_SIZE = 16 GFX_SIZE = 8 -VP_SIZE = 8 -LIGHT_SIZE = 16 # 12, but padded to 64bit alignment +VP_SIZE = 16 # it's 16 bytes but vanilla GBI has only one s64 for alignment, not two +LIGHT_SIZE = 16 AMBIENT_SIZE = 8 -HILITE_SIZE = 16 +HILITE_SIZE = 16 # 8 in F3DEX3, but this variable is not used in fast64 class ExportCData: @@ -1995,10 +2048,11 @@ class GfxFormatter: class Vtx: - def __init__(self, position, uv, colorOrNormal): + def __init__(self, position, uv, colorOrNormal, packedNormal=0): self.position = position self.uv = uv self.colorOrNormal = colorOrNormal + self.packedNormal = packedNormal def to_binary(self): signX = 1 if self.uv[0] >= 0 else -1 @@ -2008,7 +2062,7 @@ class Vtx: self.position[0].to_bytes(2, "big", signed=True) + self.position[1].to_bytes(2, "big", signed=True) + self.position[2].to_bytes(2, "big", signed=True) - + bytearray([0x00, 0x00]) + + self.packedNormal.to_bytes(2, "big", signed=True) + uv[0].to_bytes(2, "big", signed=True) + uv[1].to_bytes(2, "big", signed=True) + bytearray(self.colorOrNormal) @@ -2018,7 +2072,8 @@ class Vtx: def spc(x): return "{" + ", ".join([str(a) for a in x]) + "}" - return "{{ " + ", ".join([spc(self.position), "0", spc(self.uv), spc(self.colorOrNormal)]) + " }}" + flag = "0" if self.packedNormal == 0 else f"{self.packedNormal:#06x}" + return "{{ " + ", ".join([spc(self.position), flag, spc(self.uv), spc(self.colorOrNormal)]) + " }}" class VtxList: @@ -2235,8 +2290,6 @@ class FPaletteKey: class FModel: def __init__( self, - f3dType: F3D, - isHWv1: bool, name: str, DLFormat: "DLFormat", matWriteMethod: GfxMatWriteMethod, @@ -2253,7 +2306,7 @@ class FModel: # GfxList self.materialRevert: Union[GfxList, None] = None # F3D library - self.f3d: F3D = F3D(f3dType, isHWv1) + self.f3d: F3D = get_F3D_GBI() # array of FModel self.subModels: list[FModel] = [] self.parentModel: Union[FModel, None] = None @@ -2659,9 +2712,9 @@ class FModel: class FTexRect(FModel): - def __init__(self, f3dType, isHWv1, name, matWriteMethod): + def __init__(self, name, matWriteMethod): self.draw = GfxList(name, GfxListTag.Draw, DLFormat.Dynamic) - FModel.__init__(self, f3dType, isHWv1, name, DLFormat, matWriteMethod) + FModel.__init__(self, name, DLFormat, matWriteMethod) def to_c(self, savePNG, texDir, gfxFormatter): staticData = CData() @@ -2861,6 +2914,13 @@ class FTriGroup: self.fMaterial = fMaterial self.vertexList = VtxList(name + "_vtx_" + str(index)) self.triList = GfxList(name + "_tri_" + str(index), GfxListTag.Geometry, DLFormat.Static) + self.celTriLists = [] + self.celTriListBaseName = f"{name}_tri_{index}_cel" + + def add_cel_tri_list(self): + ret = GfxList(f"{self.celTriListBaseName}{len(self.celTriLists)}", GfxListTag.Geometry, DLFormat.Static) + self.celTriLists.append(ret) + return ret def get_ptr_addresses(self, f3d): return self.triList.get_ptr_addresses(f3d) @@ -2871,12 +2931,16 @@ class FTriGroup: return startAddress, addrRange[1] def save_binary(self, romfile, f3d, segments): + for celTriList in self.celTriLists: + celTriList.save_binary(romfile, f3d, segments); self.triList.save_binary(romfile, f3d, segments) self.vertexList.save_binary(romfile) def to_c(self, f3d, gfxFormatter): data = CData() data.append(self.vertexList.to_c()) + for celTriList in self.celTriLists: + data.append(celTriList.to_c(f3d)) if self.triList: data.append(self.triList.to_c(f3d)) return data @@ -3069,8 +3133,9 @@ class Hilite: class Lights: - def __init__(self, name): + def __init__(self, name, f3d): self.name = name + self.f3d = f3d self.startAddress = 0 self.a = None self.l = [] @@ -3086,21 +3151,36 @@ class Lights: romfile.write(self.to_binary()) def size(self): - return max(len(self.l), 1) * LIGHT_SIZE + AMBIENT_SIZE + if self.f3d.F3DEX_GBI_3: + count = len(self.l) + else: + count = max(len(self.l), 1) + return count * LIGHT_SIZE + AMBIENT_SIZE def getLightPointer(self, i): - return self.startAddress + AMBIENT_SIZE + i * LIGHT_SIZE + if self.f3d.F3DEX_GBI_3: + return self.startAddress + i * LIGHT_SIZE + else: + return self.startAddress + AMBIENT_SIZE + i * LIGHT_SIZE def getAmbientPointer(self): - return self.startAddress + if self.f3d.F3DEX_GBI_3: + return self.startAddress + len(self.l) * LIGHT_SIZE + else: + return self.startAddress def to_binary(self): - data = self.a.to_binary() - if len(self.l) == 0: + ambientData = self.a.to_binary() + data = bytes() + if len(self.l) == 0 and not self.f3d.F3DEX_GBI_3: data += Light([0, 0, 0], [0, 0, 0]).to_binary() else: for i in range(len(self.l)): data += self.l[i].to_binary() + if self.f3d.F3DEX_GBI_3: + data = data + ambientData + else: + data = ambientData + data return data def to_c(self): @@ -3115,8 +3195,10 @@ class Lights: class LookAt: - def __init__(self, name): + # F3DEX3 TODO: update this + def __init__(self, name, f3d): self.name = name + self.f3d = f3d self.startAddress = 0 self.l = [] # 2 lights @@ -3141,7 +3223,7 @@ class LookAt: + spc(self.l[1].color) + "}, 0, " + "{" - + spc(self.l[0].normal) + + spc(self.l[1].normal) + "}, 0}}" + "}}\n" ) @@ -3256,6 +3338,7 @@ def gsSPNoOp(f3d): class GbiMacro: _segptrs = False _ptr_amp = False + _hex = 0 # If nonzero, write int values as hex with specified digits tags = GfxTag(0) """ @@ -3288,6 +3371,9 @@ class GbiMacro: return field.name if hasattr(field, "__iter__") and type(field) is not str: return " | ".join(field) if len(field) else "0" + if self._hex > 0 and isinstance(field, int): + temp = field if field >= 0 else (1 << (self._hex * 4)) + field + return f"{temp:#0{self._hex + 2}x}" # + 2 for the 0x part return str(field) def to_c(self, static=True): @@ -3369,6 +3455,9 @@ class SPViewport(GbiMacro): return gsDma1p(f3d.G_MOVEMEM, vpPtr, VP_SIZE, f3d.G_MV_VIEWPORT) +# F3DEX3 TODO: Encoding of hints (and generation of the hint values) + + @dataclass(unsafe_hash=True) class SPDisplayList(GbiMacro): displayList: GfxList @@ -3400,6 +3489,13 @@ class SPBranchList(GbiMacro): return gsDma1p(f3d.G_DL, dlPtr, 0, f3d.G_DL_NOPUSH) +@dataclass(unsafe_hash=True) +class SPEndDisplayList(GbiMacro): + def to_binary(self, f3d, segments): + words = _SHIFTL(f3d.G_ENDDL, 24, 8), 0 + return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + + # SPSprite2DBase @@ -3431,12 +3527,20 @@ def gsImmp21(c, p0, p1, dat): def gsMoveWd(index, offset, data, f3d): + if f3d.F3DEX_GBI_3: + offset &= 0xFFF if f3d.F3DEX_GBI_2: return gsDma1p(f3d.G_MOVEWORD, data, offset, index) else: return gsImmp21(f3d.G_MOVEWORD, offset, index, data) +def gsMoveHalfwd(index, offset, data, f3d): + if not f3d.F3DEX_GBI_3: + raise PluginError("gsMoveHalfwd requires F3DEX3 microcode") + return gsDma1p(f3d.G_MOVEWORD, data, (offset & 0xFFF) | f3d.G_MW_HALFWORD_FLAG, index) + + # SPSprite2DScaleFlip # SPSprite2DDraw @@ -3519,7 +3623,9 @@ class SPLine3D(GbiMacro): flag: int def to_binary(self, f3d, segments): - if f3d.F3DEX_GBI_2: + if f3d.F3DEX_GBI_3: + raise PluginError("SPLine3D is removed in F3DEX3") + elif f3d.F3DEX_GBI_2: words = _SHIFTL(f3d.G_LINE3D, 24, 8) | _gsSPLine3D_w1f(self.v0, self.v1, 0, self.flag, f3d), 0 else: words = _SHIFTL(f3d.G_LINE3D, 24, 8), _gsSPLine3D_w1f(self.v0, self.v1, 0, self.flag, f3d) @@ -3534,7 +3640,9 @@ class SPLineW3D(GbiMacro): flag: int def to_binary(self, f3d, segments): - if f3d.F3DEX_GBI_2: + if f3d.F3DEX_GBI_3: + raise PluginError("SPLineW3D is removed in F3DEX3") + elif f3d.F3DEX_GBI_2: words = _SHIFTL(f3d.G_LINE3D, 24, 8) | _gsSPLine3D_w1f(self.v0, self.v1, self.wd, self.flag, f3d), 0 else: words = _SHIFTL(f3d.G_LINE3D, 24, 8), _gsSPLine3D_w1f(self.v0, self.v1, self.wd, self.flag, f3d) @@ -3566,6 +3674,10 @@ class SP2Triangles(GbiMacro): return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") +# F3DEX3 TODO: Encoding of _g*SP5Triangles commands (SPTriangleStrip, SPTriangleFan) +# and support for these in export including tri reordering + + @dataclass(unsafe_hash=True) class SPCullDisplayList(GbiMacro): vstart: int @@ -3597,6 +3709,9 @@ class SPClipRatio(GbiMacro): ratio: int def to_binary(self, f3d, segments): + if f3d.F3DEX_GBI_3: + return gsSPNoOp(f3d) + # These values are supposed to be flipped. shortRatioPos = int.from_bytes((-self.ratio).to_bytes(2, "big", signed=True), "big", signed=False) shortRatioNeg = int.from_bytes(self.ratio.to_bytes(2, "big", signed=True), "big", signed=False) @@ -3616,6 +3731,182 @@ class SPClipRatio(GbiMacro): # SPForceMatrix +@dataclass(unsafe_hash=True) +class SPAmbOcclusionAmb(GbiMacro): + amb: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusionAmb requires F3DEX3 microcode") + return gsMoveHalfwd(f3d.G_MW_FX, f3d.G_MWO_AO_AMBIENT, self.amb, f3d) + + +@dataclass(unsafe_hash=True) +class SPAmbOcclusionDir(GbiMacro): + dir: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusionDir requires F3DEX3 microcode") + return gsMoveHalfwd(f3d.G_MW_FX, f3d.G_MWO_AO_DIRECTIONAL, self.dir, f3d) + + +@dataclass(unsafe_hash=True) +class SPAmbOcclusionPoint(GbiMacro): + point: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusionPoint requires F3DEX3 microcode") + return gsMoveHalfwd(f3d.G_MW_FX, f3d.G_MWO_AO_POINT, self.point, f3d) + + +@dataclass(unsafe_hash=True) +class SPAmbOcclusionAmbDir(GbiMacro): + amb: int + dir: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusionAmbDir requires F3DEX3 microcode") + return gsMoveWd( + f3d.G_MW_FX, f3d.G_MWO_AO_AMBIENT, (_SHIFTL(self.amb, 16, 16) | _SHIFTL(self.dir, 0, 16)), f3d + ) + + +@dataclass(unsafe_hash=True) +class SPAmbOcclusionDirPoint(GbiMacro): + dir: int + point: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusionDirPoint requires F3DEX3 microcode") + return gsMoveWd( + f3d.G_MW_FX, f3d.G_MWO_AO_DIRECTIONAL, (_SHIFTL(self.dir, 16, 16) | _SHIFTL(self.point, 0, 16)), f3d + ) + + +@dataclass(unsafe_hash=True) +class SPAmbOcclusion(GbiMacro): + amb: int + dir: int + point: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbOcclusion requires F3DEX3 microcode") + return SPAmbOcclusionAmbDir(self.amb, self.dir).to_binary(f3d, segments) + \ + SPAmbOcclusionPoint(self.point).to_binary(f3d, segments) + + +@dataclass(unsafe_hash=True) +class SPFresnelScale(GbiMacro): + scale: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPFresnelScale requires F3DEX3 microcode") + return gsMoveHalfwd( + f3d.G_MW_FX, f3d.G_MWO_FRESNEL_SCALE, self.scale, f3d + ) + + +@dataclass(unsafe_hash=True) +class SPFresnelOffset(GbiMacro): + offset: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPFresnelOffset requires F3DEX3 microcode") + return gsMoveHalfwd( + f3d.G_MW_FX, f3d.G_MWO_FRESNEL_OFFSET, self.offset, f3d + ) + + +@dataclass(unsafe_hash=True) +class SPFresnel(GbiMacro): + scale: int + offset: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPFresnel requires F3DEX3 microcode") + return gsMoveWd( + f3d.G_MW_FX, f3d.G_MWO_FRESNEL_SCALE, (_SHIFTL(self.scale, 16, 16) | _SHIFTL(self.offset, 0, 16)), f3d + ) + + +@dataclass(unsafe_hash=True) +class SPAttrOffsetST(GbiMacro): + s: int + t: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAttrOffsetST requires F3DEX3 microcode") + return gsMoveWd(f3d.G_MW_FX, f3d.G_MWO_ATTR_OFFSET_S, (_SHIFTL(self.s, 16, 16) | _SHIFTL(self.t, 0, 16)), f3d) + + +@dataclass(unsafe_hash=True) +class SPAttrOffsetZ(GbiMacro): + z: int + _hex = 4 + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAttrOffsetZ requires F3DEX3 microcode") + return gsMoveWd(f3d.G_MW_FX, f3d.G_MWO_ATTR_OFFSET_Z, (_SHIFTL(self.z, 16, 16)), f3d) + + +@dataclass(unsafe_hash=True) +class SPAlphaCompareCull(GbiMacro): + mode: str + thresh: int + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAlphaCompareCull requires F3DEX3 microcode") + if self.mode == "G_ALPHA_COMPARE_CULL_DISABLE": + modeVal = f3d.G_ALPHA_COMPARE_CULL_DISABLE + elif self.mode == "G_ALPHA_COMPARE_CULL_BELOW": + modeVal = f3d.G_ALPHA_COMPARE_CULL_BELOW + elif self.mode == "G_ALPHA_COMPARE_CULL_ABOVE": + modeVal = f3d.G_ALPHA_COMPARE_CULL_ABOVE + return gsMoveHalfwd( + f3d.G_MW_FX, f3d.G_MWO_ALPHA_COMPARE_CULL, (_SHIFTL(modeVal, 8, 8) | _SHIFTL(self.thresh, 0, 8)), f3d + ) + + +@dataclass(unsafe_hash=True) +class SPNormalsMode(GbiMacro): + mode: str + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPNormalsMode requires F3DEX3 microcode") + if self.mode == "G_NORMALS_MODE_FAST": + modeVal = f3d.G_NORMALS_MODE_FAST + elif self.mode == "G_NORMALS_MODE_AUTO": + modeVal = f3d.G_NORMALS_MODE_AUTO + elif self.mode == "G_NORMALS_MODE_MANUAL": + modeVal = f3d.G_NORMALS_MODE_MANUAL + return gsMoveHalfwd(f3d.G_MW_FX, f3d.G_MWO_NORMALS_MODE, modeVal & 0xFF, f3d) + + +# SPMITMatrix (F3DEX3) + + @dataclass(unsafe_hash=True) class SPModifyVertex(GbiMacro): vtx: int @@ -3687,16 +3978,32 @@ class SPLight(GbiMacro): light: int # start address of light n: str _segptrs = True # call segmented_to_virtual in to_c method + _size = LIGHT_SIZE def to_binary(self, f3d, segments): lightPtr = int.from_bytes(encodeSegmentedAddr(self.light, segments), "big") + idx = lightIndex[self.n] if f3d.F3DEX_GBI_2: - data = gsDma2p(f3d.G_MOVEMEM, lightPtr, LIGHT_SIZE, f3d.G_MV_LIGHT, lightIndex[self.n] * 24 + 24) + if f3d.F3DEX_GBI_3: + offset = (idx - 1) * 0x10 + 0x10 + else: + offset = idx * 24 + 24 + data = gsDma2p(f3d.G_MOVEMEM, lightPtr, self._size, f3d.G_MV_LIGHT, offset) else: - data = gsDma1p(f3d.G_MOVEMEM, lightPtr, LIGHT_SIZE, (lightIndex[self.n] - 1) * 2 + f3d.G_MV_L0) + data = gsDma1p(f3d.G_MOVEMEM, lightPtr, self._size, (idx - 1) * 2 + f3d.G_MV_L0) return data +@dataclass(unsafe_hash=True) +class SPAmbient(SPLight): + _size = AMBIENT_SIZE + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPAmbient requires F3DEX3 microcode") + return super().to_binary(f3d, segments) + + @dataclass(unsafe_hash=True) class SPLightColor(GbiMacro): # n is macro name (string) @@ -3704,7 +4011,7 @@ class SPLightColor(GbiMacro): col: int def to_binary(self, f3d, segments): - return gsMoveWd(f3d.G_MW_LIGHTCOL, f3d.getLightMWO_a(self.n), self.col, f3d), +gsMoveWd( + return gsMoveWd(f3d.G_MW_LIGHTCOL, f3d.getLightMWO_a(self.n), self.col, f3d) + gsMoveWd( f3d.G_MW_LIGHTCOL, f3d.getLightMWO_b(self.n), self.col, f3d ) @@ -3718,6 +4025,8 @@ class SPSetLights(GbiMacro): lights: Lights def get_ptr_offsets(self, f3d): + if f3d.F3DEX_GBI_3: + return [12] offsets = [] if len(self.lights.l) == 0: offsets = [12, 20] @@ -3729,8 +4038,13 @@ class SPSetLights(GbiMacro): return offsets def to_binary(self, f3d, segments): - data = SPNumLights("NUMLIGHTS_" + str(len(self.lights.l))).to_binary(f3d, segments) - if len(self.lights.l) == 0: + n = len(self.lights.l) + data = SPNumLights(f"NUMLIGHTS_{n}").to_binary(f3d, segments) + if f3d.F3DEX_GBI_3: + data += gsDma2p( + f3d.G_MOVEMEM, self.lights.startAddress, len(self.lights.l) * 0x10 + 8, f3d.G_MV_LIGHT, 0x10 + ) + elif len(self.lights.l) == 0: # The light does not exist in python, but is added in # when converted to binary, making this address valid. data += SPLight(self.lights.getLightPointer(0), "LIGHT_1").to_binary(f3d, segments) @@ -3738,27 +4052,27 @@ class SPSetLights(GbiMacro): else: for i in range(len(self.lights.l)): data += SPLight(self.lights.getLightPointer(i), "LIGHT_" + str(i + 1)).to_binary(f3d, segments) - data += SPLight(self.lights.getAmbientPointer(), "LIGHT_" + str(len(self.lights.l) + 1)).to_binary( - f3d, segments - ) + data += SPLight(self.lights.getAmbientPointer(), "LIGHT_" + str(n + 1)).to_binary(f3d, segments) return data def to_c(self, static=True): - header = ( - "gsSPSetLights" + str(len(self.lights.l)) + "(" - if static - else "gSPSetLights" + str(len(self.lights.l)) + "(glistp++, " - ) + n = len(self.lights.l) + header = f"gsSPSetLights{n}(" if static else f"gSPSetLights{n}(glistp++, " if not static and bpy.context.scene.decomp_compatible: - header += "(*(Lights" + str(len(self.lights.l)) + "*) segmented_to_virtual(&" + self.lights.name + "))" + header += f"(*(Lights{n}*) segmented_to_virtual(&{self.lights.name}))" else: header += self.lights.name return header + ")" def size(self, f3d): - return GFX_SIZE * (2 + max(len(self.lights.l), 1)) + if f3d.F3DEX_GBI_3: + return GFX_SIZE * 2 + else: + return GFX_SIZE * (2 + max(len(self.lights.l), 1)) +# F3DEX3 TODO: SPCameraWorld + # Reflection/Hiliting Macros @@ -3782,8 +4096,11 @@ class SPLookAt(GbiMacro): _ptr_amp = True # add an ampersand to names def to_binary(self, f3d, segments): - light0Ptr = int.from_bytes(encodeSegmentedAddr(self.la.startAddress, segments), "big") - return gsSPLookAtX(light0Ptr, f3d) + gsSPLookAtY(light0Ptr + 16, f3d) + lookAtPtr = int.from_bytes(encodeSegmentedAddr(self.la.startAddress, segments), "big") + if f3d.F3DEX_GBI_3: + return gsDma2p(f3d.G_MOVEMEM, lookAtPtr, 8, f3d.G_MV_LIGHT, 8) + else: + return gsSPLookAtX(lookAtPtr, f3d) + gsSPLookAtY(lookAtPtr + 16, f3d) @dataclass(unsafe_hash=True) @@ -3864,7 +4181,6 @@ class SPTexture(GbiMacro): if f3d.F3DEX_GBI_2: words = ( _SHIFTL(f3d.G_TEXTURE, 24, 8) - | _SHIFTL(f3d.BOWTIE_VAL, 16, 8) | _SHIFTL((self.level), 11, 3) | _SHIFTL((self.tile), 8, 3) | _SHIFTL((self.on), 1, 7) @@ -3872,7 +4188,6 @@ class SPTexture(GbiMacro): else: words = ( _SHIFTL(f3d.G_TEXTURE, 24, 8) - | _SHIFTL(f3d.BOWTIE_VAL, 16, 8) | _SHIFTL((self.level), 11, 3) | _SHIFTL((self.tile), 8, 3) | _SHIFTL((self.on), 0, 8) @@ -3889,20 +4204,16 @@ class SPPerspNormalize(GbiMacro): s: int def to_binary(self, f3d, segments): - return gsMoveWd(f3d.G_MW_PERSPNORM, 0, (self.s), f3d) + if f3d.F3DEX_GBI_3: + return gsMoveHalfwd(f3d.G_MW_FX, G_MWO_PERSPNORM, (self.s), f3d) + else: + return gsMoveWd(f3d.G_MW_PERSPNORM, 0, (self.s), f3d) # SPPopMatrixN # SPPopMatrix -@dataclass(unsafe_hash=True) -class SPEndDisplayList(GbiMacro): - def to_binary(self, f3d, segments): - words = _SHIFTL(f3d.G_ENDDL, 24, 8), 0 - return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") - - def gsSPGeometryMode_F3DEX_GBI_2(c, s, f3d): words = (_SHIFTL(f3d.G_GEOMETRYMODE, 24, 8) | _SHIFTL(~c, 0, 24)), s return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") @@ -3916,32 +4227,8 @@ def gsSPGeometryMode_Non_F3DEX_GBI_2(word, f3d): def geoFlagListToWord(flagList, f3d): word = 0 for name in flagList: - if name == "G_ZBUFFER": - word += f3d.G_ZBUFFER - elif name == "G_SHADE": - word += f3d.G_SHADE - elif name == "G_TEXTURE_ENABLE": - word += f3d.G_TEXTURE_ENABLE - elif name == "G_SHADING_SMOOTH": - word += f3d.G_SHADING_SMOOTH - elif name == "G_CULL_FRONT": - word += f3d.G_CULL_FRONT - elif name == "G_CULL_BACK": - word += f3d.G_CULL_BACK - elif name == "G_CULL_BOTH": - word += f3d.G_CULL_BOTH - elif name == "G_FOG": - word += f3d.G_FOG - elif name == "G_LIGHTING": - word += f3d.G_LIGHTING - elif name == "G_TEXTURE_GEN": - word += f3d.G_TEXTURE_GEN - elif name == "G_TEXTURE_GEN_LINEAR": - word += f3d.G_TEXTURE_GEN_LINEAR - elif name == "G_LOD": - word += f3d.G_LOD - elif name == "G_CLIPPING": - word += f3d.G_CLIPPING + if name in f3d.allGeomModeFlags: + word += getattr(f3d, name) else: raise PluginError("Invalid geometry mode flag " + name) @@ -4162,24 +4449,17 @@ class DPSetColorDither(GbiMacro): mode: str def to_binary(self, f3d, segments): - if not f3d._HW_VERSION_1: - if self.mode == "G_CD_MAGICSQ": - modeVal = f3d.G_CD_MAGICSQ - elif self.mode == "G_CD_BAYER": - modeVal = f3d.G_CD_BAYER - elif self.mode == "G_CD_NOISE": - modeVal = f3d.G_CD_NOISE - elif self.mode == "G_CD_DISABLE": - modeVal = f3d.G_CD_DISABLE - elif self.mode == "G_CD_ENABLE": - modeVal = f3d.G_CD_ENABLE - return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_RGBDITHER, 2, modeVal, f3d) - else: - if self.mode == "G_CD_ENABLE": - modeVal = f3d.G_CD_ENABLE - elif self.mode == "G_CD_DISABLE": - modeVal = f3d.G_CD_DISABLE - return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_COLORDITHER, 1, modeVal, f3d) + if self.mode == "G_CD_MAGICSQ": + modeVal = f3d.G_CD_MAGICSQ + elif self.mode == "G_CD_BAYER": + modeVal = f3d.G_CD_BAYER + elif self.mode == "G_CD_NOISE": + modeVal = f3d.G_CD_NOISE + elif self.mode == "G_CD_DISABLE": + modeVal = f3d.G_CD_DISABLE + elif self.mode == "G_CD_ENABLE": + modeVal = f3d.G_CD_ENABLE + return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_RGBDITHER, 2, modeVal, f3d) @dataclass(unsafe_hash=True) @@ -4188,18 +4468,15 @@ class DPSetAlphaDither(GbiMacro): mode: str def to_binary(self, f3d, segments): - if not f3d._HW_VERSION_1: - if self.mode == "G_AD_PATTERN": - modeVal = f3d.G_AD_PATTERN - elif self.mode == "G_AD_NOTPATTERN": - modeVal = f3d.G_AD_NOTPATTERN - elif self.mode == "G_AD_NOISE": - modeVal = f3d.G_AD_NOISE - elif self.mode == "G_AD_DISABLE": - modeVal = f3d.G_AD_DISABLE - return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_ALPHADITHER, 2, modeVal, f3d) - else: - raise PluginError("SetAlphaDither not available in HW v1.") + if self.mode == "G_AD_PATTERN": + modeVal = f3d.G_AD_PATTERN + elif self.mode == "G_AD_NOTPATTERN": + modeVal = f3d.G_AD_NOTPATTERN + elif self.mode == "G_AD_NOISE": + modeVal = f3d.G_AD_NOISE + elif self.mode == "G_AD_DISABLE": + modeVal = f3d.G_AD_DISABLE + return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_ALPHADITHER, 2, modeVal, f3d) @dataclass(unsafe_hash=True) @@ -4487,6 +4764,45 @@ class DPSetPrimColor(GbiMacro): return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") +@dataclass(unsafe_hash=True) +class SPLightToRDP(GbiMacro): + light: int + alpha: int + word0: int # word0 of the command to write, which is word1 of this command + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPLightToRDP requires F3DEX3 microcode") + word = _SHIFTL(f3d.G_LIGHTTORDP, 24, 8) | _SHIFTL(self.light * 0x10, 8, 8) | _SHIFTL(self.alpha, 0, 8) + return word.to_bytes(4, "big") + self.word0.to_bytes(4, "big") + + +@dataclass(unsafe_hash=True) +class SPLightToPrimColor(GbiMacro): + light: int + alpha: int + m: int + l: int + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPLightToPrimColor requires F3DEX3 microcode") + word0 = _SHIFTL(f3d.G_SETPRIMCOLOR, 24, 8) | _SHIFTL(self.m, 8, 8) | _SHIFTL(self.l, 0, 8) + return SPLightToRDP(self.light, self.alpha, word0).to_binary(f3d, segments) + + +@dataclass(unsafe_hash=True) +class SPLightToFogColor(GbiMacro): + light: int + alpha: int + + def to_binary(self, f3d, segments): + if not f3d.F3DEX_GBI_3: + raise PluginError("SPLightToFogColor requires F3DEX3 microcode") + word0 = _SHIFTL(f3d.G_SETFOGCOLOR, 24, 8) + return SPLightToRDP(self.light, self.alpha, word0).to_binary(f3d, segments) + + @dataclass(unsafe_hash=True) class DPSetOtherMode(GbiMacro): mode0: list @@ -5059,39 +5375,19 @@ class DPLoadTLUT_pal16(GbiMacro): _ptr_amp = True # adds & to name of image def to_binary(self, f3d, segments): - if not f3d._HW_VERSION_1: - return ( - DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) - + DPTileSync().to_binary(f3d, segments) - + DPSetTile( - "0", "0", 0, (256 + (((self.pal) & 0xF) * 16)), f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0 - ).to_binary(f3d, segments) - + DPLoadSync().to_binary(f3d, segments) - + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, 15).to_binary(f3d, segments) - + DPPipeSync().to_binary(f3d, segments) - ) - else: - return _DPLoadTextureBlock( - self.dram, - (256 + (((self.pal) & 0xF) * 16)), - f3d.G_IM_FMT_VARS["G_IM_FMT_RGBA"], - f3d.G_IM_SIZ_VARS["G_IM_SIZ_16b"], - 4 * 16, - 1, - self.pal, - 0, - 0, - 0, - 0, - 0, - 0, + return ( + DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) + + DPTileSync().to_binary(f3d, segments) + + DPSetTile( + "0", "0", 0, (256 + (((self.pal) & 0xF) * 16)), f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0 ).to_binary(f3d, segments) + + DPLoadSync().to_binary(f3d, segments) + + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, 15).to_binary(f3d, segments) + + DPPipeSync().to_binary(f3d, segments) + ) def size(self, f3d): - if not f3d._HW_VERSION_1: - return GFX_SIZE * 6 - else: - return GFX_SIZE * 7 + return GFX_SIZE * 6 @dataclass(unsafe_hash=True) @@ -5100,37 +5396,17 @@ class DPLoadTLUT_pal256(GbiMacro): _ptr_amp = True # adds & to name of image def to_binary(self, f3d, segments): - if not f3d._HW_VERSION_1: - return ( - DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) - + DPTileSync().to_binary(f3d, segments) - + DPSetTile("0", "0", 0, 256, f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0).to_binary(f3d, segments) - + DPLoadSync().to_binary(f3d, segments) - + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, 255).to_binary(f3d, segments) - + DPPipeSync().to_binary(f3d, segments) - ) - else: - return _DPLoadTextureBlock( - self.dram, - 256, - f3d.G_IM_FMT_VARS["G_IM_FMT_RGBA"], - f3d.G_IM_SIZ_VARS["G_IM_SIZ_16b"], - 4 * 256, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ).to_binary(f3d, segments) + return ( + DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) + + DPTileSync().to_binary(f3d, segments) + + DPSetTile("0", "0", 0, 256, f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0).to_binary(f3d, segments) + + DPLoadSync().to_binary(f3d, segments) + + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, 255).to_binary(f3d, segments) + + DPPipeSync().to_binary(f3d, segments) + ) def size(self, f3d): - if not f3d._HW_VERSION_1: - return GFX_SIZE * 6 - else: - return GFX_SIZE * 7 + return GFX_SIZE * 6 @dataclass(unsafe_hash=True) @@ -5141,37 +5417,17 @@ class DPLoadTLUT(GbiMacro): _ptr_amp = True # adds & to name of image def to_binary(self, f3d, segments): - if not f3d._HW_VERSION_1: - return ( - DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) - + DPTileSync().to_binary(f3d, segments) - + DPSetTile("0", "0", 0, self.tmemaddr, f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0).to_binary(f3d, segments) - + DPLoadSync().to_binary(f3d, segments) - + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, self.count - 1).to_binary(f3d, segments) - + DPPipeSync().to_binary(f3d, segments) - ) - else: - return _DPLoadTextureBlock( - self.dram, - self.tmemaddr, - f3d.G_IM_FMT_VARS["G_IM_FMT_RGBA"], - f3d.G_IM_SIZ_VARS["G_IM_SIZ_16b"], - 4, - self.count, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ).to_binary(f3d, segments) + return ( + DPSetTextureImage("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 1, self.dram).to_binary(f3d, segments) + + DPTileSync().to_binary(f3d, segments) + + DPSetTile("0", "0", 0, self.tmemaddr, f3d.G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0).to_binary(f3d, segments) + + DPLoadSync().to_binary(f3d, segments) + + DPLoadTLUTCmd(f3d.G_TX_LOADTILE, self.count - 1).to_binary(f3d, segments) + + DPPipeSync().to_binary(f3d, segments) + ) def size(self, f3d): - if not f3d._HW_VERSION_1: - return GFX_SIZE * 6 - else: - return GFX_SIZE * 7 + return GFX_SIZE * 6 # gsDPSetScissor diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 7f5e647..4fad597 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -1,18 +1,42 @@ import logging import bpy, math, os -from bpy.types import Operator, Menu +from bpy.types import ( + Attribute, + Context, + Image, + Light, + Material, + Menu, + Mesh, + NodeGroupOutput, + NodeInputs, + NodeLink, + NodeSocket, + NodeTree, + Object, + Operator, + Panel, + Property, + PropertyGroup, + Scene, + ShaderNodeGroup, + TextureNodeImage, + UILayout, + VIEW3D_HT_header, + World, +) from bl_operators.presets import AddPresetBase from bpy.utils import register_class, unregister_class from mathutils import Color from .f3d_enums import * -from .f3d_gbi import get_F3D_GBI, GBL_c1, GBL_c2, enumTexScroll +from .f3d_gbi import get_F3D_GBI, GBL_c1, GBL_c2, enumTexScroll, isUcodeF3DEX1 from .f3d_material_presets import * from ..utility import * from ..render_settings import Fast64RenderSettings_Properties, update_scene_props_from_render_settings from .f3d_material_helpers import F3DMaterial_UpdateLock from bpy.app.handlers import persistent -from typing import Generator, Optional, Tuple, Any +from typing import Generator, Optional, Tuple, Any, Dict, Union F3DMaterialHash = Any # giant tuple @@ -153,6 +177,36 @@ def update_draw_layer(self, context): set_output_node_groups(material) +def rendermodePresetToBits(rdp_settings: "RDPSettings") -> Tuple[int, int]: + f3d = get_F3D_GBI() + r1 = getattr(f3d, rdp_settings.rendermode_preset_cycle_1, f3d.G_RM_AA_ZB_OPA_SURF) + r2 = getattr(f3d, rdp_settings.rendermode_preset_cycle_2, f3d.G_RM_AA_ZB_OPA_SURF) + if rdp_settings.g_mdsft_cycletype == "G_CYC_1CYCLE": + r2 = 0 + return r1, r2 + + +def all_blender_uses(rdp_settings: "RDPSettings") -> Dict[str, bool]: + """ + Returns a dictionary of the external features which the blender may or may + not use, or None if set_rendermode is disabled so we don't know. + """ + if not rdp_settings.set_rendermode: + return None + is_one_cycle = rdp_settings.g_mdsft_cycletype == "G_CYC_1CYCLE" + if rdp_settings.rendermode_advanced_enabled: + useZ = rdp_settings.z_cmp or rdp_settings.z_upd + useShade = rdp_settings.blend_a1 == "G_BL_A_SHADE" + if not is_one_cycle: + useShade = useShade or rdp_settings.blend_a2 == "G_BL_A_SHADE" + else: + r1, r2 = rendermodePresetToBits(rdp_settings) + f3d = get_F3D_GBI() + useZ = bool((r1 | r2) & (f3d.Z_CMP | f3d.Z_UPD)) + useShade = ((r1 >> 26) & 3) == f3d.G_BL_A_SHADE or ((r2 >> 24) & 3) == f3d.G_BL_A_SHADE + return {"Shade Alpha": useShade, "Z Buffer": useZ} + + def get_blend_method(material): f3dMat = material.f3d_mat drawLayer = material.f3d_mat.draw_layer @@ -204,13 +258,26 @@ def get_blend_method(material): return blend_method -def update_blend_method(material: bpy.types.Material, context): +def update_blend_method(material: Material, context): material.blend_method = get_blend_method(material) if material.blend_method == "CLIP": material.alpha_threshold = 0.125 -class DrawLayerProperty(bpy.types.PropertyGroup): +def getZMode(material: Material): + f3dMat = material.f3d_mat + settings = f3dMat.rdp_settings + if not settings.set_rendermode: + return "ZMODE_OPA" + if settings.rendermode_advanced_enabled: + return settings.zmode + r1, r2 = rendermodePresetToBits(settings) + f3d = get_F3D_GBI() + zmode = ((r1 | r2) & f3d.ZMODE_DEC) // f3d.ZMODE_INTER + return enumZMode[zmode][0] + + +class DrawLayerProperty(PropertyGroup): sm64: bpy.props.EnumProperty(items=sm64EnumDrawLayers, default="1", update=update_draw_layer) oot: bpy.props.EnumProperty(items=ootEnumDrawLayers, default="Opaque", update=update_draw_layer) @@ -230,24 +297,9 @@ def getTmemMax(texFormat): # Necessary for UV half pixel offset (see 13.7.5.3) def isTexturePointSampled(material): f3dMat = material.f3d_mat - return f3dMat.rdp_settings.g_mdsft_text_filt == "G_TF_POINT" -def isLightingDisabled(material): - f3dMat = material.f3d_mat - return not f3dMat.rdp_settings.g_lighting - - -# Necessary as G_SHADE_SMOOTH actually does nothing -def checkIfFlatShaded(material): - if material.mat_ver > 3: - f3dMat = material.f3d_mat - else: - f3dMat = material - return not f3dMat.rdp_settings.g_shade_smooth - - def F3DOrganizeLights(self, context): # Flag to prevent infinite recursion on update callback with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: @@ -278,92 +330,41 @@ def F3DOrganizeLights(self, context): self.f3d_light7 = lightList[6] if len(lightList) > 6 else None -def combiner_uses(material, checkList, is2Cycle): - display = False - for value in checkList: - if value[:5] == "TEXEL": - value1 = value - value2 = value.replace("0", "1") if "0" in value else value.replace("1", "0") - else: - value1 = value - value2 = value - - display |= material.combiner1.A == value1 - if is2Cycle: - display |= material.combiner2.A == value2 - - display |= material.combiner1.B == value1 - if is2Cycle: - display |= material.combiner2.B == value2 - - display |= material.combiner1.C == value1 - if is2Cycle: - display |= material.combiner2.C == value2 - - display |= material.combiner1.D == value1 - if is2Cycle: - display |= material.combiner2.D == value2 - - display |= material.combiner1.A_alpha == value1 - if is2Cycle: - display |= material.combiner2.A_alpha == value2 - - display |= material.combiner1.B_alpha == value1 - if is2Cycle: - display |= material.combiner2.B_alpha == value2 - - display |= material.combiner1.C_alpha == value1 - if is2Cycle: - display |= material.combiner2.C_alpha == value2 - - display |= material.combiner1.D_alpha == value1 - if is2Cycle: - display |= material.combiner2.D_alpha == value2 - - return display - - -def combiner_uses_alpha(material, checkList, is2Cycle): - display = False - for value in checkList: - if value[:5] == "TEXEL": - value1 = value - value2 = value.replace("0", "1") if "0" in value else value.replace("1", "0") - else: - value1 = value - value2 = value - - display |= material.combiner1.A_alpha == value1 - if is2Cycle: - display |= material.combiner2.A_alpha == value2 - - display |= material.combiner1.B_alpha == value1 - if is2Cycle: - display |= material.combiner2.B_alpha == value2 - - display |= material.combiner1.C_alpha == value1 - if is2Cycle: - display |= material.combiner2.C_alpha == value2 - - display |= material.combiner1.D_alpha == value1 - if is2Cycle: - display |= material.combiner2.D_alpha == value2 - - return display - - -CombinerUses = dict[str, bool] +def combiner_uses( + f3dMat: "F3DMaterialProperty", + checkList, + checkCycle1 = True, + checkCycle2 = True, + checkColor = True, + checkAlpha = True, + swapTexelsCycle2 = True, +): + is2Cycle = f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" + for i in range(1, 3): + if i == 1 and not checkCycle1 or i == 2 and (not checkCycle2 or not is2Cycle): + continue + combiner = getattr(f3dMat, f"combiner{i}") + for isAlpha in [False, True]: + if not isAlpha and not checkColor or isAlpha and not checkAlpha: + continue + for letter in ["A", "B", "C", "D"]: + value = getattr(combiner, letter + ("_alpha" if isAlpha else "")) + if i == 2 and swapTexelsCycle2 and value.startswith("TEXEL"): + value = "TEXEL" + chr(ord(value[5]) ^ 1) # Swap 0 and 1 + if value in checkList: + return True + return False def combiner_uses_tex0(f3d_mat: "F3DMaterialProperty"): - return combiner_uses(f3d_mat, ["TEXEL0", "TEXEL0_ALPHA"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE") + return combiner_uses(f3d_mat, ["TEXEL0", "TEXEL0_ALPHA"]) def combiner_uses_tex1(f3d_mat: "F3DMaterialProperty"): - return combiner_uses(f3d_mat, ["TEXEL1", "TEXEL1_ALPHA"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE") + return combiner_uses(f3d_mat, ["TEXEL1", "TEXEL1_ALPHA"]) -def all_combiner_uses(f3d_mat: "F3DMaterialProperty") -> CombinerUses: +def all_combiner_uses(f3d_mat: "F3DMaterialProperty") -> dict[str, bool]: use_tex0 = combiner_uses_tex0(f3d_mat) use_tex1 = combiner_uses_tex1(f3d_mat) @@ -374,22 +375,23 @@ def all_combiner_uses(f3d_mat: "F3DMaterialProperty") -> CombinerUses: "Primitive": combiner_uses( f3d_mat, ["PRIMITIVE", "PRIMITIVE_ALPHA", "PRIM_LOD_FRAC"], - f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE", ), "Environment": combiner_uses( - f3d_mat, ["ENVIRONMENT", "ENV_ALPHA"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" + f3d_mat, ["ENVIRONMENT", "ENV_ALPHA"] ), "Shade": combiner_uses( - f3d_mat, ["SHADE", "SHADE_ALPHA"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" + f3d_mat, ["SHADE"], checkAlpha = False ), - "Shade Alpha": combiner_uses_alpha( - f3d_mat, ["SHADE"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" + "Shade Alpha": combiner_uses( + f3d_mat, ["SHADE"], checkColor = False + ) or combiner_uses( + f3d_mat, ["SHADE_ALPHA"], checkAlpha = False ), - "Key": combiner_uses(f3d_mat, ["CENTER", "SCALE"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE"), + "Key": combiner_uses(f3d_mat, ["CENTER", "SCALE"]), "LOD Fraction": combiner_uses( - f3d_mat, ["LOD_FRACTION"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" + f3d_mat, ["LOD_FRACTION"] ), - "Convert": combiner_uses(f3d_mat, ["K4", "K5"], f3d_mat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE"), + "Convert": combiner_uses(f3d_mat, ["K4", "K5"]), } return useDict @@ -404,21 +406,133 @@ def ui_geo_mode(settings, dataHolder, layout, useDropdown): icon="TRIA_DOWN" if dataHolder.menu_geo else "TRIA_RIGHT", ) if not useDropdown or dataHolder.menu_geo: - inputGroup.prop(settings, "g_zbuffer", text="Z Buffer") - inputGroup.prop(settings, "g_shade", text="Shading") - inputGroup.prop(settings, "g_cull_front", text="Cull Front") - inputGroup.prop(settings, "g_cull_back", text="Cull Back") - inputGroup.prop(settings, "g_fog", text="Fog") - inputGroup.prop(settings, "g_lighting", text="Lighting") - inputGroup.prop(settings, "g_tex_gen", text="Texture UV Generate") - inputGroup.prop(settings, "g_tex_gen_linear", text="Texture UV Generate Linear") - inputGroup.prop(settings, "g_shade_smooth", text="Smooth Shading") - if bpy.context.scene.f3d_type == "F3DEX_GBI_2" or bpy.context.scene.f3d_type == "F3DEX_GBI": - inputGroup.prop(settings, "g_clipping", text="Clipping") + + def indentGroup(parent: UILayout, textOrProp: Union[str, "F3DMaterialProperty"], isText: bool) -> UILayout: + c = parent.column(align=True) + if isText: + c.label(text=textOrProp) + else: + c.prop(settings, textOrProp) + if not getattr(settings, textOrProp): + return None + c = c.split(factor=0.1) + c.label(text="") + c = c.column(align=True) + return c + + isF3DEX3 = bpy.context.scene.f3d_type == "F3DEX3" + lightFxPrereq = isF3DEX3 and settings.g_lighting + if isinstance(dataHolder, F3DMaterialProperty): + ccWarnings = blendWarnings = True + ccUse = all_combiner_uses(dataHolder) + shadeInCC = ccUse["Shade"] or ccUse["Shade Alpha"] + blendUse = all_blender_uses(settings) + if blendUse is None: + blendWarnings = shadeInBlender = zInBlender = False + else: + shadeInBlender = blendUse["Shade Alpha"] + zInBlender = blendUse["Z Buffer"] + else: + ccWarnings = shadeInCC = False + blendWarnings = shadeInBlender = zInBlender = False + + inputGroup.prop(settings, "g_shade_smooth") + + c = indentGroup(inputGroup, "g_lighting", False) + if c is not None: + if ccWarnings and not shadeInCC and not settings.g_tex_gen: + c.label(text="Shade not used in CC, can disable lighting.", icon="INFO") + if isF3DEX3: + c.prop(settings, "g_packed_normals") + c.prop(settings, "g_lighting_specular") + c.prop(settings, "g_ambocclusion") + d = indentGroup(c, "g_tex_gen", False) + if d is not None: + d.prop(settings, "g_tex_gen_linear") + + if lightFxPrereq and settings.g_fresnel_color: + shadeColorLabel = "Fresnel" + elif not settings.g_lighting or (lightFxPrereq and settings.g_lighttoalpha): + shadeColorLabel = "Vertex color" + elif lightFxPrereq and settings.g_packed_normals and not settings.g_lighttoalpha: + shadeColorLabel = "Lighting * vertex color" + else: + shadeColorLabel = "Lighting" + if lightFxPrereq: + c = indentGroup(inputGroup, f"Shade color = {shadeColorLabel}:", True) + c.prop(settings, "g_fresnel_color") + else: + inputGroup.column().label(text = f"Shade color = {shadeColorLabel}") + + shadowMapInShadeAlpha = False + if settings.g_fog: + shadeAlphaLabel = "Fog" + elif lightFxPrereq and settings.g_fresnel_alpha: + shadeAlphaLabel = "Fresnel" + elif lightFxPrereq and settings.g_lighttoalpha: + shadeAlphaLabel = "Light intensity" + elif lightFxPrereq and settings.g_ambocclusion: + shadeAlphaLabel = "Shadow map / AO in vtx alpha" + shadowMapInShadeAlpha = True + else: + shadeAlphaLabel = "Vtx alpha" + c = indentGroup(inputGroup, f"Shade alpha = {shadeAlphaLabel}:", True) + if lightFxPrereq: + c.prop(settings, "g_lighttoalpha") + c.prop(settings, "g_fresnel_alpha") + c.prop(settings, "g_fog") + if lightFxPrereq and settings.g_fog and settings.g_fresnel_alpha: + c.label(text="Fog overrides Fresnel Alpha.", icon="ERROR") + if lightFxPrereq and settings.g_fog and settings.g_lighttoalpha: + c.label(text="Fog overrides Light-to-Alpha.", icon="ERROR") + if lightFxPrereq and settings.g_fresnel_alpha and settings.g_lighttoalpha: + c.label(text="Fresnel Alpha overrides Light-to-Alpha.", icon="ERROR") + if shadowMapInShadeAlpha and ccUse["Shade Alpha"]: + c.label(text="Shadow map = shade alpha used in CC, probably wrong.", icon="INFO") + if settings.g_fog and ccUse["Shade Alpha"]: + c.label(text="Fog = shade alpha used in CC, probably wrong.", icon="INFO") + if blendWarnings and shadeInBlender and not settings.g_fog: + c.label(text="Rendermode uses shade alpha, probably fog.", icon="INFO") + elif blendWarnings and not shadeInBlender and settings.g_fog: + c.label(text="Fog not used in rendermode / blender, can disable.", icon="INFO") + + if isF3DEX3: + c = indentGroup(inputGroup, "Attribute offsets:", True) + c.prop(settings, "g_attroffset_st_enable") + c.prop(settings, "g_attroffset_z_enable") + + c = indentGroup(inputGroup, "Face culling:", True) + c.prop(settings, "g_cull_front") + c.prop(settings, "g_cull_back") + if settings.g_cull_front and settings.g_cull_back: + c.label(text="Nothing will be drawn.", icon="ERROR") + + c = indentGroup(inputGroup, "Disable if not using:", True) + c.prop(settings, "g_zbuffer") + if blendWarnings and not settings.g_zbuffer and zInBlender: + c.label(text="Rendermode / blender using Z, must enable.", icon="ERROR") + elif blendWarnings and settings.g_zbuffer and not zInBlender: + c.label(text="Z is not being used, can disable.", icon="INFO") + c.prop(settings, "g_shade") + if ccWarnings and not settings.g_shade and (shadeInCC or shadeInBlender): + if shadeInCC and shadeInBlender: + where = "CC and blender" + elif shadeInCC: + where = "CC" + else: + where = "rendermode / blender" + c.label(text=f"Shade in use in {where}, must enable.", icon="ERROR") + elif ccWarnings and settings.g_shade and not shadeInCC and not shadeInBlender: + c.label(text="Shade is not being used, can disable.", icon="INFO") + + c = indentGroup(inputGroup, "Not useful:", True) + c.prop(settings, "g_lod") + if isUcodeF3DEX1(bpy.context.scene.f3d_type): + c.prop(settings, "g_clipping") -def ui_upper_mode(settings, dataHolder, layout: bpy.types.UILayout, useDropdown): - inputGroup: bpy.types.UILayout = layout.column() +def ui_upper_mode(settings, dataHolder, layout: UILayout, useDropdown): + inputGroup: UILayout = layout.column() if useDropdown: inputGroup.prop( dataHolder, @@ -427,11 +541,8 @@ def ui_upper_mode(settings, dataHolder, layout: bpy.types.UILayout, useDropdown) icon="TRIA_DOWN" if dataHolder.menu_upper else "TRIA_RIGHT", ) if not useDropdown or dataHolder.menu_upper: - if not bpy.context.scene.isHWv1: - prop_split(inputGroup, settings, "g_mdsft_alpha_dither", "Alpha Dither") - prop_split(inputGroup, settings, "g_mdsft_rgb_dither", "RGB Dither") - else: - prop_split(inputGroup, settings, "g_mdsft_color_dither", "Color Dither") + prop_split(inputGroup, settings, "g_mdsft_alpha_dither", "Alpha Dither") + prop_split(inputGroup, settings, "g_mdsft_rgb_dither", "RGB Dither") prop_split(inputGroup, settings, "g_mdsft_combkey", "Chroma Key") prop_split(inputGroup, settings, "g_mdsft_textconv", "Texture Convert") prop_split(inputGroup, settings, "g_mdsft_text_filt", "Texture Filter") @@ -452,8 +563,8 @@ def ui_upper_mode(settings, dataHolder, layout: bpy.types.UILayout, useDropdown) prop_split(inputGroup, settings, "g_mdsft_pipeline", "Pipeline Span Buffer Coherency") -def ui_lower_mode(settings, dataHolder, layout: bpy.types.UILayout, useDropdown): - inputGroup: bpy.types.UILayout = layout.column() +def ui_lower_mode(settings, dataHolder, layout: UILayout, useDropdown): + inputGroup: UILayout = layout.column() if useDropdown: inputGroup.prop( dataHolder, @@ -463,8 +574,6 @@ def ui_lower_mode(settings, dataHolder, layout: bpy.types.UILayout, useDropdown) ) if not useDropdown or dataHolder.menu_lower: prop_split(inputGroup, settings, "g_mdsft_alpha_compare", "Alpha Compare") - if settings.g_mdsft_alpha_compare == "G_AC_THRESHOLD" and settings.g_mdsft_cycletype == "G_CYC_2CYCLE": - inputGroup.label(text="Compares blend alpha to *first cycle* combined (CC) alpha.") prop_split(inputGroup, settings, "g_mdsft_zsrcsel", "Z Source Selection") if settings.g_mdsft_zsrcsel == "G_ZS_PRIM": prim_box = inputGroup.box() @@ -484,7 +593,7 @@ def ui_other(settings, dataHolder, layout, useDropdown): clipRatioGroup = inputGroup.column() prop_split(clipRatioGroup, settings, "clip_ratio", "Clip Ratio") - if isinstance(dataHolder, bpy.types.Material) or isinstance(dataHolder, F3DMaterialProperty): + if isinstance(dataHolder, Material) or isinstance(dataHolder, F3DMaterialProperty): blend_color_group = layout.row() prop_input_name = blend_color_group.column() prop_input = blend_color_group.column() @@ -509,7 +618,7 @@ def tmemUsageUI(layout, textureProp): # shading = 1 # lighting = 1 # cycle type = 1 cycle -class F3DPanel(bpy.types.Panel): +class F3DPanel(Panel): bl_label = "F3D Material" bl_idname = "MATERIAL_PT_F3D_Inspector" bl_space_type = "PROPERTIES" @@ -601,7 +710,7 @@ class F3DPanel(bpy.types.Panel): prop_input.enabled = setProp return inputGroup - def ui_lights(self, f3d_mat: "F3DMaterialProperty", layout: bpy.types.UILayout, name, showCheckBox): + def ui_lights(self, f3d_mat: "F3DMaterialProperty", layout: UILayout, name, showCheckBox): inputGroup = layout.row() prop_input_left = inputGroup.column() prop_input = inputGroup.column() @@ -611,7 +720,7 @@ class F3DPanel(bpy.types.Panel): prop_input_left.label(text=name) prop_input_left.enabled = f3d_mat.rdp_settings.g_lighting and f3d_mat.rdp_settings.g_shade - lightSettings: bpy.types.UILayout = prop_input.column() + lightSettings: UILayout = prop_input.column() if f3d_mat.rdp_settings.g_lighting: prop_input_left.separator(factor=0.25) light_controls = prop_input_left.box() @@ -713,6 +822,22 @@ class F3DPanel(bpy.types.Panel): prop_split(renderGroup, material.rdp_settings, "alpha_cvg_sel", "Use Coverage For Alpha") prop_split(renderGroup, material.rdp_settings, "force_bl", "Force Blending") + if material.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE": + if ( + material.rdp_settings.blend_b1 == "G_BL_A_MEM" or + material.rdp_settings.blend_p1 == "G_BL_CLR_MEM" or + material.rdp_settings.blend_m1 == "G_BL_CLR_MEM" + ): + multilineLabel(renderGroup.box(), + "RDP silicon bug: Framebuffer color / alpha in blender\n" + + "cycle 1 is broken, actually value from PREVIOUS pixel.", + 'ORPHAN_DATA') + if material.rdp_settings.blend_a2 == "G_BL_A_SHADE": + multilineLabel(renderGroup.box(), + "RDP silicon bug: Shade alpha in blender cycle 2\n" + + "is broken, actually shade alpha from NEXT pixel.", + 'ORPHAN_DATA') + # cycle dependent - (P * A + M - B) / (A + B) combinerBox = renderGroup.box() combinerBox.label(text="Blender (Color = (P * A + M * B) / (A + B)") @@ -738,11 +863,7 @@ class F3DPanel(bpy.types.Panel): renderGroup.enabled = material.rdp_settings.set_rendermode def ui_uvCheck(self, layout, context): - if ( - hasattr(context, "object") - and context.object is not None - and isinstance(context.object.data, bpy.types.Mesh) - ): + if hasattr(context, "object") and context.object is not None and isinstance(context.object.data, Mesh): uv_layers = context.object.data.uv_layers if uv_layers.active is None or uv_layers.active.name != "UVMap": uvErrorBox = layout.box() @@ -755,9 +876,45 @@ class F3DPanel(bpy.types.Panel): elif context.scene.gameEditorMode == "OOT": prop_split(layout, material.f3d_mat.draw_layer, "oot", "Draw Layer") - def ui_fog(self, f3dMat, inputCol, showCheckBox): + def ui_misc(self, f3dMat: "F3DMaterialProperty", inputCol: UILayout, showCheckBox: bool) -> None: + if f3dMat.rdp_settings.g_ambocclusion: + if showCheckBox or f3dMat.set_ao: + inputGroup = inputCol.column() + if showCheckBox: + inputGroup.prop(f3dMat, "set_ao", text="Set Ambient Occlusion") + if f3dMat.set_ao: + prop_split(inputGroup.row(), f3dMat, "ao_ambient", "AO Ambient") + prop_split(inputGroup.row(), f3dMat, "ao_directional", "AO Directional") + prop_split(inputGroup.row(), f3dMat, "ao_point", "AO Point") + + if f3dMat.rdp_settings.g_fresnel_color or f3dMat.rdp_settings.g_fresnel_alpha: + if showCheckBox or f3dMat.set_fresnel: + inputGroup = inputCol.column() + if showCheckBox: + inputGroup.prop(f3dMat, "set_fresnel", text="Set Fresnel") + if f3dMat.set_fresnel: + prop_split(inputGroup.row(), f3dMat, "fresnel_lo", "Fresnel Lo") + prop_split(inputGroup.row(), f3dMat, "fresnel_hi", "Fresnel Hi") + + if f3dMat.rdp_settings.g_attroffset_st_enable: + if showCheckBox or f3dMat.set_attroffs_st: + inputGroup = inputCol.column() + if showCheckBox: + inputGroup.prop(f3dMat, "set_attroffs_st", text="Set ST Attr Offset") + if f3dMat.set_attroffs_st: + prop_split(inputGroup.row(), f3dMat, "attroffs_st", "ST Attr Offset") + + if f3dMat.rdp_settings.g_attroffset_z_enable: + if showCheckBox or f3dMat.set_attroffs_z: + inputGroup = inputCol.column() + if showCheckBox: + inputGroup.prop(f3dMat, "set_attroffs_z", text="Set Z Attr Offset") + if f3dMat.set_attroffs_z: + prop_split(inputGroup.row(), f3dMat, "attroffs_z", "Z Attr Offset") + if f3dMat.rdp_settings.g_fog: - inputGroup = inputCol.column() + if showCheckBox or f3dMat.set_fog: + inputGroup = inputCol.column() if showCheckBox: inputGroup.prop(f3dMat, "set_fog", text="Set Fog") if f3dMat.set_fog: @@ -765,20 +922,129 @@ class F3DPanel(bpy.types.Panel): if f3dMat.use_global_fog: inputGroup.label(text="Only applies to levels (area fog settings).", icon="INFO") else: - fogColorGroup = inputGroup.row().split(factor=0.5) - fogColorGroup.label(text="Fog Color") - fogColorGroup.prop(f3dMat, "fog_color", text="") - fogPositionGroup = inputGroup.row().split(factor=0.5) - fogPositionGroup.label(text="Fog Range") - fogPositionGroup.prop(f3dMat, "fog_position", text="") + prop_split(inputGroup.row(), f3dMat, "fog_color", "Fog Color") + prop_split(inputGroup.row(), f3dMat, "fog_position", "Fog Range") - def drawVertexColorNotice(self, layout): + def ui_cel_shading(self, material: Material, layout: UILayout): + inputGroup = layout.box().column() + r = inputGroup.row(align=True) + r.prop( + material.f3d_mat, + "expand_cel_shading_ui", + text="", + icon='TRIA_DOWN' if material.f3d_mat.expand_cel_shading_ui else 'TRIA_RIGHT', + icon_only=True, + emboss=False + ) + r.prop(material.f3d_mat, "use_cel_shading") + if not material.f3d_mat.expand_cel_shading_ui: + return + if not material.f3d_mat.use_cel_shading: + inputGroup = inputGroup.column() + inputGroup.enabled = False + cel = material.f3d_mat.cel_shading + prop_split(inputGroup.row(), cel, "tintPipeline", "Tint pipeline:") + prop_split(inputGroup.row(), cel, "cutoutSource", "Cutout:") + + if getZMode(material) != "ZMODE_OPA": + inputGroup.label(text = "zmode in blender / rendermode must be opaque.", icon = "ERROR") + + if cel.cutoutSource == "ENVIRONMENT": + if not material.f3d_mat.set_env or material.f3d_mat.env_color[3] != 1.0: + inputGroup.label(text = "Enable env color, and set env alpha to 255.", icon = "ERROR") + else: + tex = material.f3d_mat.tex0 if cel.cutoutSource == "TEXEL0" else material.f3d_mat.tex1 + if tex.tex is None or not tex.tex_set: + inputGroup.label(text = f"Texture {cel.cutoutSource[5]} is not set up correctly.", icon = "ERROR") + + if ( + len(cel.levels) >= 3 and + cel.levels[0].threshMode == cel.levels[1].threshMode and + not all([cel.levels[0].threshMode == lvl.threshMode for lvl in cel.levels[1:]]) + ): + multilineLabel(inputGroup.box(), + "If using both lighter and darker cel\n" + + "levels, one of each must be at the beginning", + "ERROR" + ) + + r = inputGroup.row(align=True) + r.label(text="Cel levels:") + op = r.operator(CelLevelAdd.bl_idname, text="", icon='ADD') + op.materialName = material.name + if len(cel.levels) > 0: + op = r.operator(CelLevelRemove.bl_idname, text="", icon='REMOVE') + op.materialName = material.name + + showSegHelp = False + for level in cel.levels: + box = inputGroup.box().column() + r = box.row().split(factor=0.2) + r.label(text="Draw when") + r = r.split(factor=0.3) + r.prop(level, "threshMode", text="") + r = r.split(factor=0.2) + r.label(text="than") + r.prop(level, "threshold") + r = box.row().split(factor=0.08) + r.label(text="Tint:") + r = r.split(factor=0.27) + r.prop(level, "tintType", text="") + r = r.split(factor=0.45) + if level.tintType == "Fixed": + r.prop(level, "tintFixedLevel") + r = r.split(factor=0.3) + r.label(text="Color:") + r.prop(level, "tintFixedColor", text="") + elif level.tintType == "Segment": + r.prop(level, "tintSegmentNum") + r.prop(level, "tintSegmentOffset") + showSegHelp = True + elif level.tintType == "Light": + r.prop(level, "tintFixedLevel") + r.prop(level, "tintLightSlot") + else: + raise PluginError("Invalid tintType") + if showSegHelp: + tintName, tintNameCap = ("prim", "Prim") if cel.tintPipeline == "CC" else ("fog", "Fog") + multilineLabel(inputGroup, + "Segments: In your code, set up DL in segment(s) used with\n" + + f"gsDPSet{tintNameCap}Color then gsSPEndDisplayList at appropriate offset\n" + + f"with {tintName} color = tint color and {tintName} alpha = tint level.", + 'INFO' + ) + + + def checkDrawLayersWarnings(self, f3dMat: "F3DMaterialProperty", useDict: Dict[str, bool], layout: UILayout): + settings = f3dMat.rdp_settings + isF3DEX3 = bpy.context.scene.f3d_type == "F3DEX3" + lightFxPrereq = isF3DEX3 and settings.g_lighting + + blendUse = all_blender_uses(settings) + anyUseShadeAlpha = useDict["Shade Alpha"] or (blendUse is not None and blendUse["Shade Alpha"]) + + g_lighting = settings.g_lighting + g_fog = settings.g_fog + g_packed_normals = lightFxPrereq and settings.g_packed_normals + g_ambocclusion = lightFxPrereq and settings.g_ambocclusion + g_lighttoalpha = lightFxPrereq and settings.g_lighttoalpha + g_fresnel_color = lightFxPrereq and settings.g_fresnel_color + g_fresnel_alpha = lightFxPrereq and settings.g_fresnel_alpha + + usesVertexColor = useDict["Shade"] and (not g_lighting or (g_packed_normals and not g_fresnel_color)) + usesVertexAlpha = anyUseShadeAlpha and (g_ambocclusion or not (g_fog or g_lighttoalpha or g_fresnel_alpha)) + if not usesVertexColor and not usesVertexAlpha: + return noticeBox = layout.box().column() - noticeBox.label(text="There must be two vertex color layers.", icon="LINENUMBERS_ON") - noticeBox.label(text='They should be called "Col" and "Alpha".') - - def drawShadeAlphaNotice(self, layout): - layout.box().column().label(text='There must be a vertex color layer called "Alpha".', icon="IMAGE_ALPHA") + if not usesVertexColor: + noticeBox.label(text='Mesh must have Color Attribute (vtx color) layer called "Alpha".', icon="IMAGE_ALPHA") + elif not usesVertexAlpha: + noticeBox.label( + text='Mesh must have Color Attribute (vtx color) layer called "Col".', icon="IMAGE_RGB_ALPHA" + ) + else: + noticeBox.label(text="Mesh must have two Color Attribute (vtx color) layers.", icon="IMAGE_RGB_ALPHA") + noticeBox.label(text='They must be called "Col" and "Alpha".', icon="IMAGE_ALPHA") def checkDrawMixedCIWarning(self, layout, useDict, f3dMat): useTex0 = useDict["Texture 0"] and f3dMat.tex0.tex_set @@ -798,10 +1064,7 @@ class F3DPanel(bpy.types.Panel): inputCol = layout.column() useDict = all_combiner_uses(f3dMat) - if not f3dMat.rdp_settings.g_lighting: - self.drawVertexColorNotice(layout) - elif useDict["Shade Alpha"]: - self.drawShadeAlphaNotice(layout) + self.checkDrawLayersWarnings(f3dMat, useDict, layout) useMultitexture = useDict["Texture 0"] and useDict["Texture 1"] and f3dMat.tex0.tex_set and f3dMat.tex1.tex_set @@ -836,10 +1099,9 @@ class F3DPanel(bpy.types.Panel): if useDict["Convert"] and f3dMat.set_k0_5: self.ui_convert(f3dMat, inputCol, False) - if f3dMat.set_fog: - self.ui_fog(f3dMat, inputCol, False) + self.ui_misc(f3dMat, inputCol, False) - def draw_full(self, f3dMat, material, layout: bpy.types.UILayout, context): + def draw_full(self, f3dMat, material, layout: UILayout, context): layout.row().prop(material, "menu_tab", expand=True) menuTab = material.menu_tab useDict = all_combiner_uses(f3dMat) @@ -847,58 +1109,41 @@ class F3DPanel(bpy.types.Panel): if menuTab == "Combiner": self.ui_draw_layer(material, layout, context) - if not f3dMat.rdp_settings.g_lighting: - self.drawVertexColorNotice(layout) - elif useDict["Shade Alpha"]: - self.drawShadeAlphaNotice(layout) + self.checkDrawLayersWarnings(f3dMat, useDict, layout) + + def drawCCProps(ui: UILayout, combiner: "CombinerProperty", isAlpha: bool, enabled: bool = True) -> None: + ui = ui.column() + ui.enabled = enabled + for letter in ["A", "B", "C", "D"]: + r = ui.row().split(factor=0.25 if isAlpha else 0.1) + r.label(text = f"{letter}{' Alpha' if isAlpha else ''}:") + r.prop(combiner, f"{letter}{'_alpha' if isAlpha else ''}", text="") + + isTwoCycle = f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" combinerBox = layout.box() combinerBox.prop(f3dMat, "set_combiner", text="Color Combiner (Color = (A - B) * C + D)") - combinerCol = combinerBox.row() + combinerCol = combinerBox.row().split(factor=0.45) combinerCol.enabled = f3dMat.set_combiner - rowColor = combinerCol.column() - rowAlpha = combinerCol.column() + drawCCProps(combinerCol, f3dMat.combiner1, False) + drawCCProps(combinerCol, f3dMat.combiner1, True, not f3dMat.use_cel_shading) + if f3dMat.use_cel_shading: + r = combinerBox.column().label(text = f"CC alpha{' cycle 1' if isTwoCycle else ''} is occupied by cel shading.") - rowColor.prop(f3dMat.combiner1, "A") - rowColor.prop(f3dMat.combiner1, "B") - rowColor.prop(f3dMat.combiner1, "C") - rowColor.prop(f3dMat.combiner1, "D") - rowAlpha.prop(f3dMat.combiner1, "A_alpha") - rowAlpha.prop(f3dMat.combiner1, "B_alpha") - rowAlpha.prop(f3dMat.combiner1, "C_alpha") - rowAlpha.prop(f3dMat.combiner1, "D_alpha") - if ( - f3dMat.rdp_settings.g_mdsft_alpha_compare == "G_AC_THRESHOLD" - and f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" - ): - combinerBox.label(text="First cycle alpha out used for compare threshold.") - - if f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE": + if isTwoCycle: combinerBox2 = layout.box() combinerBox2.label(text="Color Combiner Cycle 2") combinerBox2.enabled = f3dMat.set_combiner - combinerCol2 = combinerBox2.row() - rowColor2 = combinerCol2.column() - rowAlpha2 = combinerCol2.column() + combinerCol2 = combinerBox2.row().split(factor=0.45) + drawCCProps(combinerCol2, f3dMat.combiner2, False) + drawCCProps(combinerCol2, f3dMat.combiner2, True) - rowColor2.prop(f3dMat.combiner2, "A") - rowColor2.prop(f3dMat.combiner2, "B") - rowColor2.prop(f3dMat.combiner2, "C") - rowColor2.prop(f3dMat.combiner2, "D") - rowAlpha2.prop(f3dMat.combiner2, "A_alpha") - rowAlpha2.prop(f3dMat.combiner2, "B_alpha") - rowAlpha2.prop(f3dMat.combiner2, "C_alpha") - rowAlpha2.prop(f3dMat.combiner2, "D_alpha") + if combiner_uses(f3dMat, ["TEXEL0", "TEXEL0_ALPHA"], checkCycle1 = False, swapTexelsCycle2 = False): + combinerBox2.label(text = "'Texture 0' in Cycle 2 is actually Texture 1.", icon = "INFO") + if combiner_uses(f3dMat, ["TEXEL1", "TEXEL1_ALPHA"], checkCycle1 = False, swapTexelsCycle2 = False): + multilineLabel(combinerBox2, "RDP silicon bug: 'Texture 1' in Cycle 2 is actually\n" + + "Texture 0 for the NEXT pixel, causes visual issues.", "ORPHAN_DATA") - if useDict["Texture 0"]: - cc_list = ["A", "B", "C", "D", "A_alpha", "B_alpha", "C_alpha", "D_alpha"] - if len([c for c in cc_list if getattr(f3dMat.combiner2, c) == "TEXEL1"]): - combinerBox2.label( - text="Warning: Using 'Texture 1' in Cycle 2 can cause display issues!", - icon="LIBRARY_DATA_BROKEN", - ) - - combinerBox2.label(text="Note: In second cycle, texture 0 and texture 1 are flipped.") if menuTab == "Sources": self.ui_uvCheck(layout, context) @@ -936,7 +1181,7 @@ class F3DPanel(bpy.types.Panel): if useDict["Convert"]: self.ui_convert(f3dMat, inputCol, True) - self.ui_fog(f3dMat, inputCol, True) + self.ui_misc(f3dMat, inputCol, True) if menuTab == "Geo": ui_geo_mode(f3dMat.rdp_settings, f3dMat, layout, False) @@ -961,6 +1206,7 @@ class F3DPanel(bpy.types.Panel): return f3dMat = material.f3d_mat + settings = f3dMat.rdp_settings layout.prop(context.scene, "f3d_simple", text="Show Simplified UI") layout = layout.box() titleCol = layout.column() @@ -971,14 +1217,27 @@ class F3DPanel(bpy.types.Panel): split.label(text="Preset") row = split.row(align=True) row.menu(MATERIAL_MT_f3d_presets.__name__, text=f3dMat.presetName) - row.operator(AddPresetF3D.bl_idname, text="", icon="ZOOM_IN") - row.operator(AddPresetF3D.bl_idname, text="", icon="ZOOM_OUT").remove_active = True + row.operator(AddPresetF3D.bl_idname, text="", icon="ADD") + row.operator(AddPresetF3D.bl_idname, text="", icon="REMOVE").remove_active = True + + if settings.g_mdsft_alpha_compare == "G_AC_THRESHOLD" and settings.g_mdsft_cycletype == "G_CYC_2CYCLE": + multilineLabel(layout.box(), + "RDP silicon bug: Alpha compare in 2-cycle mode is broken.\n" + + "Compares to FIRST cycle CC alpha output from NEXT pixel.", + 'ORPHAN_DATA') if context.scene.f3d_simple and f3dMat.presetName != "Custom": self.draw_simple(f3dMat, material, layout, context) else: presetCol.prop(context.scene, "f3dUserPresetsOnly") self.draw_full(f3dMat, material, layout, context) + + if context.scene.f3d_type == "F3DEX3": + self.ui_cel_shading(material, layout) + else: + r = layout.row() + r.enabled = False + r.label(text="Use Cel Shading (requires F3DEX3)", icon='TRIA_RIGHT') def ui_tileScroll(tex, name, layout): @@ -1093,6 +1352,24 @@ def update_light_properties(self, context): update_light_colors(material, context) +def update_cel_cutout_source(self, context): + with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: + if not material: + return + if not material.f3d_mat.use_cel_shading: + return + + f3dMat = material.f3d_mat + cel = f3dMat.cel_shading + firstDarker = len(cel.levels) >= 1 and cel.levels[0].threshMode == "Darker" + + f3dMat.combiner1.A_alpha, f3dMat.combiner1.B_alpha = ( + ("1", "SHADE") if firstDarker else ("SHADE", "0") + ) + f3dMat.combiner1.C_alpha = cel.cutoutSource + f3dMat.combiner1.D_alpha = "0" + + def getSocketFromCombinerToNodeDictColor(nodes, combinerInput): nodeName, socketIndex = combinerToNodeDictColor[combinerInput] return nodes[nodeName].outputs[socketIndex] if nodeName is not None else None @@ -1143,14 +1420,14 @@ alpha_combiner_inputs = { } -def remove_first_link_if_exists(material: bpy.types.Material, links: tuple[bpy.types.NodeLink]): +def remove_first_link_if_exists(material: Material, links: tuple[NodeLink]): if len(links) > 0: link = links[0] material.node_tree.links.remove(link) def link_if_none_exist( - material: bpy.types.Material, fromOutput: bpy.types.NodeSocket, toInput: bpy.types.NodeSocket + material: Material, fromOutput: NodeSocket, toInput: NodeSocket ): # TODO: (V5) add output/input type annotations if len(fromOutput.links) == 0: material.node_tree.links.new(fromOutput, toInput) @@ -1207,7 +1484,7 @@ def update_node_combiner(material, combinerInputs, cycleIndex): material.node_tree.links.new(cycle_node.inputs[i], input_value) -def check_fog_settings(material: bpy.types.Material): +def check_fog_settings(material: Material): f3dMat: "F3DMaterialProperty" = material.f3d_mat fog_enabled: bool = f3dMat.rdp_settings.g_fog fog_rendermode_enabled: bool = fog_enabled @@ -1236,7 +1513,7 @@ def check_fog_settings(material: bpy.types.Material): return fog_enabled, fog_rendermode_enabled -def update_fog_nodes(material: bpy.types.Material, context: bpy.types.Context): +def update_fog_nodes(material: Material, context: Context): nodes = material.node_tree.nodes f3dMat: "F3DMaterialProperty" = material.f3d_mat @@ -1244,7 +1521,7 @@ def update_fog_nodes(material: bpy.types.Material, context: bpy.types.Context): nodes["Shade Color"].inputs["Fog"].default_value = int(fog_enabled) - fogBlender: bpy.types.ShaderNodeGroup = nodes["FogBlender"] + fogBlender: ShaderNodeGroup = nodes["FogBlender"] if fog_rendermode_enabled and fog_enabled: fogBlender.node_tree = bpy.data.node_groups["FogBlender_On"] else: @@ -1269,7 +1546,7 @@ def update_fog_nodes(material: bpy.types.Material, context: bpy.types.Context): nodes["CalcFog"].inputs["FogFar"].default_value = f3dMat.fog_position[1] -def update_noise_nodes(material: bpy.types.Material): +def update_noise_nodes(material: Material): f3dMat: "F3DMaterialProperty" = material.f3d_mat uses_noise = f3dMat.combiner1.A == "NOISE" or f3dMat.combiner2.A == "NOISE" noise_group = bpy.data.node_groups["F3DNoise_Animated" if uses_noise else "F3DNoise_NonAnimated"] @@ -1279,9 +1556,7 @@ def update_noise_nodes(material: bpy.types.Material): nodes["F3DNoiseFactor"].node_tree = noise_group -def update_combiner_connections( - material: bpy.types.Material, context: bpy.types.Context, combiner: (int | None) = None -): +def update_combiner_connections(material: Material, context: Context, combiner: (int | None) = None): f3dMat: "F3DMaterialProperty" = material.f3d_mat update_noise_nodes(material) @@ -1314,7 +1589,7 @@ def update_combiner_connections( update_node_combiner(material, combinerInputs2, 2) -def set_output_node_groups(material: bpy.types.Material): +def set_output_node_groups(material: Material): nodes = material.node_tree.nodes f3dMat: "F3DMaterialProperty" = material.f3d_mat is_one_cycle = f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_1CYCLE" @@ -1390,7 +1665,7 @@ def update_color_node(combiner_inputs, color: Color, prefix: str): # prim_color | Prim # env_color | Env def get_color_input_update_callback(attr_name="", prefix=""): - def input_update_callback(self: bpy.types.Material, context: bpy.types.Context): + def input_update_callback(self: Material, context: Context): with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: if not material: return @@ -1402,7 +1677,7 @@ def get_color_input_update_callback(attr_name="", prefix=""): return input_update_callback -def update_node_values_of_material(material: bpy.types.Material, context): +def update_node_values_of_material(material: Material, context): nodes = material.node_tree.nodes update_blend_method(material, context) @@ -1454,9 +1729,9 @@ def update_node_values_of_material(material: bpy.types.Material, context): update_fog_nodes(material, context) -def set_texture_settings_node(material: bpy.types.Material): +def set_texture_settings_node(material: Material): nodes = material.node_tree.nodes - textureSettings: bpy.types.ShaderNodeGroup = nodes["TextureSettings"] + textureSettings: ShaderNodeGroup = nodes["TextureSettings"] desired_group = bpy.data.node_groups["TextureSettings_Lite"] if (material.f3d_mat.tex0.tex and not material.f3d_mat.tex0.autoprop) or ( @@ -1479,7 +1754,7 @@ def setAutoProp(fieldProperty, pixelLength): def set_texture_size(self, tex_size, tex_index): nodes = self.node_tree.nodes - uv_basis: bpy.types.ShaderNodeGroup = nodes["UV Basis"] + uv_basis: ShaderNodeGroup = nodes["UV Basis"] inputs = uv_basis.inputs inputs[f"{tex_index} S TexSize"].default_value = tex_size[0] @@ -1490,11 +1765,9 @@ def trunc_10_2(val: float): return int(val * 4) / 4 -def update_tex_values_field( - self: bpy.types.Material, texProperty: "TextureProperty", tex_size: list[int], tex_index: int -): +def update_tex_values_field(self: Material, texProperty: "TextureProperty", tex_size: list[int], tex_index: int): nodes = self.node_tree.nodes - textureSettings: bpy.types.ShaderNodeGroup = nodes["TextureSettings"] + textureSettings: ShaderNodeGroup = nodes["TextureSettings"] inputs = textureSettings.inputs set_texture_size(self, tex_size, tex_index) @@ -1530,19 +1803,19 @@ def update_tex_values_field( inputs[str_index + " T Shift"].default_value = texProperty.T.shift -def iter_tex_nodes(node_tree: bpy.types.NodeTree, texIndex: int) -> Generator[bpy.types.TextureNodeImage, None, None]: +def iter_tex_nodes(node_tree: NodeTree, texIndex: int) -> Generator[TextureNodeImage, None, None]: for i in range(1, 5): nodeName = f"Tex{texIndex}_{i}" if node_tree.nodes.get(nodeName): yield node_tree.nodes[nodeName] -def toggle_texture_node_muting(material: bpy.types.Material, texIndex: int, isUsed: bool): +def toggle_texture_node_muting(material: Material, texIndex: int, isUsed: bool): node_tree = material.node_tree f3dMat: "F3DMaterialProperty" = material.f3d_mat # Enforce typing from generator - texNode: None | bpy.types.TextureNodeImage = None + texNode: None | TextureNodeImage = None node_3point_key = "3 Point Lerp" if texIndex == 0 else "3 Point Lerp.001" node_3point = node_tree.nodes.get(node_3point_key) @@ -1566,7 +1839,7 @@ def toggle_texture_node_muting(material: bpy.types.Material, texIndex: int, isUs def set_texture_nodes_settings( - material: bpy.types.Material, texProperty: "TextureProperty", texIndex: int, isUsed: bool + material: Material, texProperty: "TextureProperty", texIndex: int, isUsed: bool ) -> list[int] | None: node_tree = material.node_tree f3dMat: "F3DMaterialProperty" = material.f3d_mat @@ -1580,7 +1853,7 @@ def set_texture_nodes_settings( return texSize # Enforce typing from generator - texNode: None | bpy.types.TextureNodeImage = None + texNode: None | TextureNodeImage = None for texNode in iter_tex_nodes(node_tree, texIndex): if texNode.image is not texProperty.tex: texNode.image = texProperty.tex @@ -1597,7 +1870,7 @@ def set_texture_nodes_settings( return texSize -def update_tex_values_index(self: bpy.types.Material, *, texProperty: "TextureProperty", texIndex: int, isUsed: bool): +def update_tex_values_index(self: Material, *, texProperty: "TextureProperty", texIndex: int, isUsed: bool): nodes = self.node_tree.nodes tex_size = set_texture_nodes_settings(self, texProperty, texIndex, isUsed) @@ -1670,11 +1943,11 @@ def get_tex_gen_size(tex_size: list[int | float]): return (tex_size[0] - 1) / 1024, (tex_size[1] - 1) / 1024 -def update_tex_values_manual(material: bpy.types.Material, context, prop_path=None): +def update_tex_values_manual(material: Material, context, prop_path=None): f3dMat: "F3DMaterialProperty" = material.f3d_mat nodes = material.node_tree.nodes texture_settings = nodes["TextureSettings"] - texture_inputs: bpy.types.NodeInputs = texture_settings.inputs + texture_inputs: NodeInputs = texture_settings.inputs useDict = all_combiner_uses(f3dMat) tex0_used = useDict["Texture 0"] and f3dMat.tex0.tex is not None @@ -1707,7 +1980,7 @@ def update_tex_values_manual(material: bpy.types.Material, context, prop_path=No texture_inputs["1 S TexSize"].default_value = f3dMat.tex1.tex.size[0] texture_inputs["1 T TexSize"].default_value = f3dMat.tex1.tex.size[0] - uv_basis: bpy.types.ShaderNodeGroup = nodes["UV Basis"] + uv_basis: ShaderNodeGroup = nodes["UV Basis"] if f3dMat.uv_basis == "TEXEL0": uv_basis.node_tree = bpy.data.node_groups["UV Basis 0"] else: @@ -1787,21 +2060,18 @@ def update_preset_manual(material, context): def update_preset_manual_v4(material, preset): - override = bpy.context.copy() - override["material"] = material if preset == "Shaded Solid": preset = "sm64_shaded_solid" if preset == "Shaded Texture": preset = "sm64_shaded_texture" if preset.lower() != "custom": material.f3d_update_flag = True - bpy.ops.script.execute_preset( - override, filepath=findF3DPresetPath(preset), menu_idname="MATERIAL_MT_f3d_presets" - ) + with bpy.context.temp_override(material=material): + bpy.ops.script.execute_preset(filepath=findF3DPresetPath(preset), menu_idname="MATERIAL_MT_f3d_presets") material.f3d_update_flag = False -def has_f3d_nodes(material: bpy.types.Material): +def has_f3d_nodes(material: Material): return "Material Output F3D" in material.node_tree.nodes @@ -1845,8 +2115,13 @@ def createOrUpdateSceneProperties(): if upgrade_group and group: # Need to upgrade; remove old outputs - for out in group.outputs: - group.outputs.remove(out) + if bpy.app.version >= (4,0,0): + for item in group.interface.items_tree: + if item.item_type == 'SOCKET' and item.in_out == 'OUTPUT': + group.interface.remove(item) + else: + for out in group.outputs: + group.outputs.remove(out) new_group = group else: logger.info("Creating Scene Properties") @@ -1858,27 +2133,58 @@ def createOrUpdateSceneProperties(): new_group["version"] = SCENE_PROPERTIES_VERSION # Create outputs - _nodeFogEnable: bpy.types.NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogEnable") - _nodeFogColor: bpy.types.NodeSocketColor = new_group.outputs.new("NodeSocketColor", "FogColor") - _nodeF3D_NearClip: bpy.types.NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_NearClip") - _nodeF3D_FarClip: bpy.types.NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_FarClip") - _nodeBlender_Game_Scale: bpy.types.NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "Blender_Game_Scale") - _nodeFogNear: bpy.types.NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogNear") - _nodeFogFar: bpy.types.NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogFar") - _nodeShadeColor: bpy.types.NodeSocketColor = new_group.outputs.new("NodeSocketColor", "ShadeColor") - _nodeAmbientColor: bpy.types.NodeSocketColor = new_group.outputs.new("NodeSocketColor", "AmbientColor") - _nodeLightDirection: bpy.types.NodeSocketVectorDirection = new_group.outputs.new( - "NodeSocketVectorDirection", "LightDirection" - ) + if bpy.app.version >= (4, 0, 0): + tree_interface = new_group.interface + + _nodeFogEnable: NodeSocketFloat = tree_interface.new_socket( + "FogEnable", socket_type="NodeSocketFloat", in_out="OUTPUT" + ) + _nodeFogColor: NodeSocketColor = tree_interface.new_socket( + "FogColor", socket_type="NodeSocketColor", in_out="OUTPUT" + ) + _nodeF3D_NearClip: NodeSocketFloat = tree_interface.new_socket( + "F3D_NearClip", socket_type="NodeSocketFloat", in_out="OUTPUT" + ) + _nodeF3D_FarClip: NodeSocketFloat = tree_interface.new_socket( + "F3D_FarClip", socket_type="NodeSocketFloat", in_out="OUTPUT" + ) + _nodeBlender_Game_Scale: NodeSocketFloat = tree_interface.new_socket( + "Blender_Game_Scale", socket_type="NodeSocketFloat", in_out="OUTPUT" + ) + _nodeFogNear: NodeSocketFloat = tree_interface.new_socket("FogNear", socket_type="NodeSocketFloat", in_out="OUTPUT") + _nodeFogFar: NodeSocketFloat = tree_interface.new_socket("FogFar", socket_type="NodeSocketFloat", in_out="OUTPUT") + _nodeShadeColor: NodeSocketColor = tree_interface.new_socket( + "ShadeColor", socket_type="NodeSocketColor", in_out="OUTPUT" + ) + _nodeAmbientColor: NodeSocketColor = tree_interface.new_socket( + "AmbientColor", socket_type="NodeSocketColor", in_out="OUTPUT" + ) + _nodeLightDirection: NodeSocketVector = tree_interface.new_socket( + "LightDirection", socket_type="NodeSocketVector", in_out="OUTPUT" + ) + + else: + _nodeFogEnable: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogEnable") + _nodeFogColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "FogColor") + _nodeF3D_NearClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_NearClip") + _nodeF3D_FarClip: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "F3D_FarClip") + _nodeBlender_Game_Scale: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "Blender_Game_Scale") + _nodeFogNear: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogNear") + _nodeFogFar: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogFar") + _nodeShadeColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "ShadeColor") + _nodeAmbientColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "AmbientColor") + _nodeLightDirection: NodeSocketVectorDirection = new_group.outputs.new( + "NodeSocketVectorDirection", "LightDirection" + ) # Set outputs from render settings - sceneOutputs: bpy.types.NodeGroupOutput = new_group.nodes["Group Output"] + sceneOutputs: NodeGroupOutput = new_group.nodes["Group Output"] renderSettings: "Fast64RenderSettings_Properties" = bpy.context.scene.fast64.renderSettings update_scene_props_from_render_settings(bpy.context, sceneOutputs, renderSettings) -def createScenePropertiesForMaterial(material: bpy.types.Material): +def createScenePropertiesForMaterial(material: Material): node_tree = material.node_tree # Either create or update SceneProperties if needed @@ -1933,16 +2239,16 @@ def link_f3d_material_library(): bpy.ops.object.mode_set(mode=get_mode_set_from_context_mode(prevMode)) -def shouldConvOrCreateColorAttribute(mesh: bpy.types.Mesh, attr_name="Col"): +def shouldConvOrCreateColorAttribute(mesh: Mesh, attr_name="Col"): has_attr, conv_attr = False, False if attr_name in mesh.attributes: - attribute: bpy.types.Attribute = mesh.attributes[attr_name] + attribute: Attribute = mesh.attributes[attr_name] has_attr = True conv_attr = attribute.data_type != "FLOAT_COLOR" or attribute.domain != "CORNER" return has_attr, conv_attr -def convertColorAttribute(mesh: bpy.types.Mesh, attr_name="Col"): +def convertColorAttribute(mesh: Mesh, attr_name="Col"): prev_index = mesh.attributes.active_index attr_index = mesh.attributes.find(attr_name) if attr_index < 0: @@ -1953,7 +2259,7 @@ def convertColorAttribute(mesh: bpy.types.Mesh, attr_name="Col"): mesh.attributes.active_index = prev_index -def addColorAttributesToModel(obj: bpy.types.Object): +def addColorAttributesToModel(obj: Object): if obj.type != "MESH": return @@ -1963,7 +2269,7 @@ def addColorAttributesToModel(obj: bpy.types.Object): selectSingleObject(obj) - mesh: bpy.types.Mesh = obj.data + mesh: Mesh = obj.data conv_col, has_col = shouldConvOrCreateColorAttribute(mesh, attr_name="Col") if conv_col: @@ -1981,7 +2287,7 @@ def addColorAttributesToModel(obj: bpy.types.Object): bpy.ops.object.mode_set(mode=get_mode_set_from_context_mode(prevMode)) -def createF3DMat(obj: bpy.types.Object | None, preset="Shaded Solid", index=None): +def createF3DMat(obj: Object | None, preset="Shaded Solid", index=None): # link all node_groups + material from addon's data .blend link_f3d_material_library() @@ -2025,7 +2331,7 @@ def reloadDefaultF3DPresets(): update_preset_manual_v4(material, presetNameToFilename[material.f3d_mat.presetName]) -class CreateFast3DMaterial(bpy.types.Operator): +class CreateFast3DMaterial(Operator): bl_idname = "object.create_f3d_mat" bl_label = "Create Fast3D Material" bl_options = {"REGISTER", "UNDO", "PRESET"} @@ -2041,7 +2347,7 @@ class CreateFast3DMaterial(bpy.types.Operator): return {"FINISHED"} -class ReloadDefaultF3DPresets(bpy.types.Operator): +class ReloadDefaultF3DPresets(Operator): bl_idname = "object.reload_f3d_presets" bl_label = "Reload Default Fast3D Presets" bl_options = {"REGISTER", "UNDO", "PRESET"} @@ -2052,18 +2358,18 @@ class ReloadDefaultF3DPresets(bpy.types.Operator): return {"FINISHED"} -def get_tex_prop_from_path(material: bpy.types.Material, path: str) -> Tuple["TextureProperty", int]: +def get_tex_prop_from_path(material: Material, path: str) -> Tuple["TextureProperty", int]: if "tex0" in path: return material.f3d_mat.tex0, 0 return material.f3d_mat.tex1, 1 -def already_updating_material(material: bpy.types.Material | None): +def already_updating_material(material: Material | None): """Check if material is updating already""" return getattr(material, "f3d_update_flag", False) -def update_tex_field_prop(self: bpy.types.Property, context: bpy.types.Context): +def update_tex_field_prop(self: Property, context: Context): with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: if not material: return @@ -2077,7 +2383,7 @@ def update_tex_field_prop(self: bpy.types.Property, context: bpy.types.Context): set_texture_settings_node(material) -def toggle_auto_prop(self, context: bpy.types.Context): +def toggle_auto_prop(self, context: Context): with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: if not material: return @@ -2094,7 +2400,7 @@ def toggle_auto_prop(self, context: bpy.types.Context): set_texture_settings_node(material) -class TextureFieldProperty(bpy.types.PropertyGroup): +class TextureFieldProperty(PropertyGroup): clamp: bpy.props.BoolProperty( name="Clamp", update=update_tex_field_prop, @@ -2133,7 +2439,7 @@ class TextureFieldProperty(bpy.types.PropertyGroup): return (self.clamp, self.mirror, round(self.low * 4), round(self.high * 4), self.mask, self.shift) -class SetTileSizeScrollProperty(bpy.types.PropertyGroup): +class SetTileSizeScrollProperty(PropertyGroup): s: bpy.props.IntProperty(min=-4095, max=4095, default=0) t: bpy.props.IntProperty(min=-4095, max=4095, default=0) interval: bpy.props.IntProperty(min=1, soft_max=1000, default=1) @@ -2142,9 +2448,9 @@ class SetTileSizeScrollProperty(bpy.types.PropertyGroup): return (self.s, self.t, self.interval) -class TextureProperty(bpy.types.PropertyGroup): +class TextureProperty(PropertyGroup): tex: bpy.props.PointerProperty( - type=bpy.types.Image, + type=Image, name="Texture", update=update_tex_values_and_formats, ) @@ -2239,7 +2545,7 @@ def on_tex_autoprop(texProperty, context): setAutoProp(texProperty.T, tex_size[1]) -def update_combiner_connections_and_preset(self, context: bpy.types.Context): +def update_combiner_connections_and_preset(self, context: Context): with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: if not material: return @@ -2258,8 +2564,8 @@ def update_combiner_connections_and_preset(self, context: bpy.types.Context): def ui_image( canUseLargeTextures: bool, - layout: bpy.types.UILayout, - material: bpy.types.Material, + layout: UILayout, + material: Material, textureProp: TextureProperty, name: str, showCheckBox: bool, @@ -2379,7 +2685,7 @@ def ui_image( high.prop(textureProp.T, "high", text="T High") -class CombinerProperty(bpy.types.PropertyGroup): +class CombinerProperty(PropertyGroup): A: bpy.props.EnumProperty( name="A", description="A", @@ -2457,7 +2763,7 @@ class CombinerProperty(bpy.types.PropertyGroup): ) -class ProceduralAnimProperty(bpy.types.PropertyGroup): +class ProceduralAnimProperty(PropertyGroup): speed: bpy.props.FloatProperty(name="Speed", default=1) amplitude: bpy.props.FloatProperty(name="Amplitude", default=1) frequency: bpy.props.FloatProperty(name="Frequency", default=1) @@ -2481,7 +2787,7 @@ class ProceduralAnimProperty(bpy.types.PropertyGroup): ) -class ProcAnimVectorProperty(bpy.types.PropertyGroup): +class ProcAnimVectorProperty(PropertyGroup): x: bpy.props.PointerProperty(type=ProceduralAnimProperty) y: bpy.props.PointerProperty(type=ProceduralAnimProperty) z: bpy.props.PointerProperty(type=ProceduralAnimProperty) @@ -2500,7 +2806,7 @@ class ProcAnimVectorProperty(bpy.types.PropertyGroup): ) -class PrimDepthSettings(bpy.types.PropertyGroup): +class PrimDepthSettings(PropertyGroup): z: bpy.props.IntProperty( name="Prim Depth: Z", default=0, @@ -2531,64 +2837,122 @@ class PrimDepthSettings(bpy.types.PropertyGroup): return (self.z, self.dz) -class RDPSettings(bpy.types.PropertyGroup): +class RDPSettings(PropertyGroup): g_zbuffer: bpy.props.BoolProperty( name="Z Buffer", default=True, update=update_node_values_with_preset, - description="Enables calculation of Z value for primitives. Disable if not reading or writing Z-Buffer in the blender" + description="Enables calculation of Z value for primitives. Disable if not reading or writing Z-Buffer in the blender", ) g_shade: bpy.props.BoolProperty( name="Shading", default=True, update=update_node_values_with_preset, - description="Computes shade coordinates for primitives. Disable if not using lighting, vertex colors or fog" + description="Computes shade coordinates for primitives. Disable if not using lighting, vertex colors or fog", + ) + g_ambocclusion: bpy.props.BoolProperty( + name="Ambient Occlusion", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Scales each type light intensity differently with vertex alpha. Bake scene shadows / AO into vertex alpha, not vertex color", + ) + g_attroffset_z_enable: bpy.props.BoolProperty( + name="Z Offset (for decal fix)", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Enables offset to vertex Z. To fix decals, set the Z mode to opaque and enable this", + ) + g_attroffset_st_enable: bpy.props.BoolProperty( + name="ST Offset (for UV scroll)", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Enables offsets to vertex ST values, usually for UV scrolling", ) # v1/2 difference g_cull_front: bpy.props.BoolProperty( name="Cull Front", + default=False, update=update_node_values_with_preset, - description="Disables drawing of front faces" + description="Disables drawing of front faces", ) # v1/2 difference g_cull_back: bpy.props.BoolProperty( name="Cull Back", default=True, update=update_node_values_with_preset, - description="Disables drawing of back faces" + description="Disables drawing of back faces", + ) + g_packed_normals: bpy.props.BoolProperty( + name="Packed Normals (Vtx Colors + Lighting)", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Packs vertex normals in unused 16 bits of each vertex, enabling simultaneous vertex colors and lighting", + ) + g_lighttoalpha: bpy.props.BoolProperty( + name="Light to Alpha (for cel shading)", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Moves light intensity to shade alpha, used for cel shading and other effects", + ) + g_lighting_specular: bpy.props.BoolProperty( + name="Specular Lighting", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Microcode lighting computes specular instead of diffuse component. If using, must set size field of every light in code", + ) + g_fresnel_color: bpy.props.BoolProperty( + name="Fresnel to Color", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Shade color derived from how much each vertex normal faces the camera. For bump mapping", + ) + g_fresnel_alpha: bpy.props.BoolProperty( + name="Fresnel to Alpha", + default=False, + update=update_node_values_with_preset, + description="F3DEX3: Shade alpha derived from how much each vertex normal faces the camera. For water, glass, ghosts, etc., or toon outlines", ) g_fog: bpy.props.BoolProperty( name="Fog", + default=False, update=update_node_values_with_preset, - description="Turns on/off fog calculation. Fog variable gets stored into shade alpha" + description="Turns on/off fog calculation. Fog variable gets stored into shade alpha", ) g_lighting: bpy.props.BoolProperty( name="Lighting", default=True, update=update_node_values_with_preset, - description="Enables calculation shade color using lights. Turn off for vertex colors as shade color" + description="Enables calculating shade color using lights. Turn off for vertex colors as shade color", ) g_tex_gen: bpy.props.BoolProperty( name="Texture UV Generate", + default=False, update=update_node_values_with_preset, - description="Generates texture coordinates for reflection mapping based on vertex normals and lookat direction. On a skybox texture, maps the sky to the center of the texture and the ground to a circle inscribed in the border. Requires lighting enabled to use" + description="Generates texture coordinates for reflection mapping based on vertex normals and lookat direction. On a skybox texture, maps the sky to the center of the texture and the ground to a circle inscribed in the border. Requires lighting enabled to use", ) g_tex_gen_linear: bpy.props.BoolProperty( name="Texture UV Generate Linear", + default=False, update=update_node_values_with_preset, - description="Modifies the texgen mapping; enable with texgen. Use a normal panorama image for the texture, with the sky at the top and the ground at the bottom. Requires lighting enabled to use" + description="Modifies the texgen mapping; enable with texgen. Use a normal panorama image for the texture, with the sky at the top and the ground at the bottom. Requires lighting enabled to use", + ) + g_lod: bpy.props.BoolProperty( + name="LoD (does nothing)", + default=False, + update=update_node_values_with_preset, + description="Not implemented in any known microcodes. No effect whether enabled or disabled", ) - # v1/2 difference g_shade_smooth: bpy.props.BoolProperty( name="Smooth Shading", default=True, update=update_node_values_with_preset, - description="Shades primitive smoothly using interpolation between shade values for each vertex (Gouraud shading)" + description="Shades primitive smoothly using interpolation between shade values for each vertex (Gouraud shading)", ) - # f3dlx2 only g_clipping: bpy.props.BoolProperty( name="Clipping", + default=False, update=update_node_values_with_preset, + description="F3DEX1/LX only, exact function unknown", ) # upper half mode @@ -2598,7 +2962,7 @@ class RDPSettings(bpy.types.PropertyGroup): items=enumAlphaDither, default="G_AD_NOISE", update=update_node_values_with_preset, - description="Applies your choice dithering type to output framebuffer alpha. Dithering is used to convert high precision source colors into lower precision framebuffer values" + description="Applies your choice dithering type to output framebuffer alpha. Dithering is used to convert high precision source colors into lower precision framebuffer values", ) # v2 only g_mdsft_rgb_dither: bpy.props.EnumProperty( @@ -2606,41 +2970,41 @@ class RDPSettings(bpy.types.PropertyGroup): items=enumRGBDither, default="G_CD_MAGICSQ", update=update_node_values_with_preset, - description="Applies your choice dithering type to output framebuffer color. Dithering is used to convert high precision source colors into lower precision framebuffer values" + description="Applies your choice dithering type to output framebuffer color. Dithering is used to convert high precision source colors into lower precision framebuffer values", ) g_mdsft_combkey: bpy.props.EnumProperty( name="Chroma Key", items=enumCombKey, default="G_CK_NONE", update=update_node_values_with_preset, - description="Turns on/off the chroma key. Chroma key requires a special setup to work properly" + description="Turns on/off the chroma key. Chroma key requires a special setup to work properly", ) g_mdsft_textconv: bpy.props.EnumProperty( name="Texture Convert", items=enumTextConv, default="G_TC_FILT", update=update_node_values_with_preset, - description="Sets the function of the texture convert unit, to do texture filtering, YUV to RGB conversion, or both" + description="Sets the function of the texture convert unit, to do texture filtering, YUV to RGB conversion, or both", ) g_mdsft_text_filt: bpy.props.EnumProperty( name="Texture Filter", items=enumTextFilt, default="G_TF_BILERP", update=update_node_values_without_preset, - description="Applies your choice of filtering to texels" + description="Applies your choice of filtering to texels", ) g_mdsft_textlut: bpy.props.EnumProperty( name="Texture LUT", items=enumTextLUT, default="G_TT_NONE", - description="Changes texture look up table (LUT) behavior. This property is auto set if you choose a CI texture" + description="Changes texture look up table (LUT) behavior. This property is auto set if you choose a CI texture", ) g_mdsft_textlod: bpy.props.EnumProperty( name="Texture LOD", items=enumTextLOD, default="G_TL_TILE", update=update_node_values_with_preset, - description="Turns on/off the use of LoD on textures. LoD textures change the used tile based on the texel/pixel ratio" + description="Turns on/off the use of LoD on textures. LoD textures change the used tile based on the texel/pixel ratio", ) num_textures_mipmapped: bpy.props.IntProperty( name="Number of Mipmaps", @@ -2654,21 +3018,21 @@ class RDPSettings(bpy.types.PropertyGroup): items=enumTextDetail, default="G_TD_CLAMP", update=update_node_values_with_preset, - description="Changes type of LoD usage. Affects how tiles are selected based on texel magnification. Only works when G_TL_LOD is selected" + description="Changes type of LoD usage. Affects how tiles are selected based on texel magnification. Only works when G_TL_LOD is selected", ) g_mdsft_textpersp: bpy.props.EnumProperty( name="Texture Perspective Correction", items=enumTextPersp, default="G_TP_PERSP", update=update_node_values_with_preset, - description="Turns on/off texture perspective correction" + description="Turns on/off texture perspective correction", ) g_mdsft_cycletype: bpy.props.EnumProperty( name="Cycle Type", items=enumCycleType, default="G_CYC_1CYCLE", update=update_node_values_with_preset, - description="Changes RDP pipeline configuration. For normal textured triangles use one or two cycle mode" + description="Changes RDP pipeline configuration. For normal textured triangles use one or two cycle mode", ) # v1 only g_mdsft_color_dither: bpy.props.EnumProperty( @@ -2676,14 +3040,14 @@ class RDPSettings(bpy.types.PropertyGroup): items=enumColorDither, default="G_CD_ENABLE", update=update_node_values_with_preset, - description="Applies your choice dithering type to output frambuffer" + description="Applies your choice dithering type to output frambuffer", ) g_mdsft_pipeline: bpy.props.EnumProperty( name="Pipeline Span Buffer Coherency", items=enumPipelineMode, default="G_PM_1PRIMITIVE", update=update_node_values_with_preset, - description="Changes primitive rasterization timing by adding syncs after tri draws. Vanilla SM64 has synchronization issues which could cause a crash if not using 1 prim. For any modern SM64 hacking project or other game N-prim should always be used" + description="Changes primitive rasterization timing by adding syncs after tri draws. Vanilla SM64 has synchronization issues which could cause a crash if not using 1 prim. For any modern SM64 hacking project or other game N-prim should always be used", ) # lower half mode @@ -2692,14 +3056,14 @@ class RDPSettings(bpy.types.PropertyGroup): items=enumAlphaCompare, default="G_AC_NONE", update=update_node_values_with_preset, - description="Uses alpha comparisons to decide if a pixel should be written. Applies before blending" + description="Uses alpha comparisons to decide if a pixel should be written. Applies before blending", ) g_mdsft_zsrcsel: bpy.props.EnumProperty( name="Z Source Selection", items=enumDepthSource, default="G_ZS_PIXEL", update=update_node_values_with_preset, - description="Changes screen-space Z value source used for Z-Buffer calculations" + description="Changes screen-space Z value source used for Z-Buffer calculations", ) prim_depth: bpy.props.PointerProperty( @@ -2738,47 +3102,45 @@ class RDPSettings(bpy.types.PropertyGroup): ) aa_en: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Enables anti-aliasing to rasterized primitive edges. Uses coverage to determine edges" + description="Enables anti-aliasing to rasterized primitive edges. Uses coverage to determine edges", ) z_cmp: bpy.props.BoolProperty( - update=update_node_values_with_preset, - description="Checks pixel Z value against Z-Buffer to test writing" + update=update_node_values_with_preset, description="Checks pixel Z value against Z-Buffer to test writing" ) z_upd: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Updates the Z-Buffer with the most recently written pixel Z value" + description="Updates the Z-Buffer with the most recently written pixel Z value", ) im_rd: bpy.props.BoolProperty( - update=update_node_values_with_preset, - description="Enables reading from framebuffer for blending calculations" + update=update_node_values_with_preset, description="Enables reading from framebuffer for blending calculations" ) clr_on_cvg: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Only draw on coverage (amount primitive covers target pixel) overflow" + description="Only draw on coverage (amount primitive covers target pixel) overflow", ) cvg_dst: bpy.props.EnumProperty( name="Coverage Destination", items=enumCoverage, update=update_node_values_with_preset, - description="Changes how coverage (amount primitive covers target pixel) gets retrieved/stored" + description="Changes how coverage (amount primitive covers target pixel) gets retrieved/stored", ) zmode: bpy.props.EnumProperty( name="Z Mode", items=enumZMode, update=update_node_values_with_preset, - description="Changes Z calculation for different types of primitives" + description="Changes Z calculation for different types of primitives", ) cvg_x_alpha: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Multiply coverage (amount primitive covers target pixel) with alpha and store result as coverage" + description="Multiply coverage (amount primitive covers target pixel) with alpha and store result as coverage", ) alpha_cvg_sel: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Use coverage (amount primitive covers target pixel) as alpha instead of color combiner alpha" + description="Use coverage (amount primitive covers target pixel) as alpha instead of color combiner alpha", ) force_bl: bpy.props.BoolProperty( update=update_node_values_with_preset, - description="Always uses blending on. Default blending is conditionally only applied during partial coverage. Forcing blending will disable division step of the blender, so B input must be 1-A or there may be rendering issues. Always use this option when Z Buffering is off" + description="Always uses blending on. Default blending is conditionally only applied during partial coverage. Forcing blending will disable division step of the blender, so B input must be 1-A or there may be rendering issues. Always use this option when Z Buffering is off", ) # cycle dependent - (P * A + M - B) / (A + B) @@ -2832,10 +3194,16 @@ class RDPSettings(bpy.types.PropertyGroup): self.g_shade, self.g_cull_front, self.g_cull_back, + self.g_attroffset_st_enable, + self.g_attroffset_z_enable, + self.g_packed_normals, + self.g_lighttoalpha, + self.g_ambocclusion, self.g_fog, self.g_lighting, self.g_tex_gen, self.g_tex_gen_linear, + self.g_lod, self.g_shade_smooth, self.g_clipping, self.g_mdsft_alpha_dither, @@ -2877,7 +3245,7 @@ class RDPSettings(bpy.types.PropertyGroup): ) -class DefaultRDPSettingsPanel(bpy.types.Panel): +class DefaultRDPSettingsPanel(Panel): bl_label = "RDP Default Settings" bl_idname = "WORLD_PT_RDP_Default_Inspector" bl_space_type = "PROPERTIES" @@ -2900,6 +3268,102 @@ class DefaultRDPSettingsPanel(bpy.types.Panel): ui_other(world.rdp_defaults, world, layout, True) +class CelLevelProperty(PropertyGroup): + threshMode : bpy.props.EnumProperty( + items = enumCelThreshMode, + name = "Draw when", + default = "Lighter", + update = update_cel_cutout_source + ) + threshold : bpy.props.IntProperty( + name = "Threshold", + description = "Light level at which the boundary between cel levels occurs. One level is >= this value, the other is < it", + min = 2, + max = 255, + default = 128 + ) + tintType : bpy.props.EnumProperty(items = enumCelTintType, name = "Tint type", default = "Fixed") + tintFixedLevel : bpy.props.IntProperty( + name = "Level", + description = "0: original color <=> 255: fully tint color", + min = 0, + max = 255, + default = 50, + ) + tintFixedColor : bpy.props.FloatVectorProperty( + name = "Tint color", + size = 3, + min = 0.0, + max = 1.0, + subtype = "COLOR", + ) + tintSegmentNum : bpy.props.IntProperty( + name = "Segment", + description = "Segment number to store tint DL in", + min = 8, + max = 0xD, + default = 8, + ) + tintSegmentOffset : bpy.props.IntProperty( + name = "Offset (instr)", + description = "Number of instructions (8 bytes) within this DL to jump to", + min = 0, + max = 1000, + default = 0, + ) + tintLightSlot : bpy.props.IntProperty( + name = "Light (/end)", + description = "Which light to load RGB color from, counting from the end. 0 = ambient, 1 = last directional / point light, 2 = second-to-last, etc.", + min = 0, + max = 9, + default = 1, + ) + + +class CelShadingProperty(PropertyGroup): + tintPipeline : bpy.props.EnumProperty(items = enumCelTintPipeline, name = "Tint pipeline", default = "CC") + cutoutSource : bpy.props.EnumProperty( + items = enumCelCutoutSource, + name = "Cutout", + default = "ENVIRONMENT", + update = update_cel_cutout_source, + ) + levels : bpy.props.CollectionProperty(type = CelLevelProperty, name = "Cel levels") + + +def celGetMaterialLevels(materialName): + material = bpy.data.materials.get(materialName) + if material is None: + raise PluginError(f"Could not find material {materialName}") + return material.f3d_mat.cel_shading.levels + + +class CelLevelAdd(bpy.types.Operator): + bl_idname = "material.f3d_cel_level_add" + bl_label = "Add Cel Level" + bl_options = {'REGISTER', 'UNDO'} + + materialName : bpy.props.StringProperty() + + def execute(self, context): + levels = celGetMaterialLevels(self.materialName) + levels.add() + return {'FINISHED'} + + +class CelLevelRemove(bpy.types.Operator): + bl_idname = "material.f3d_cel_level_remove" + bl_label = "Remove Last Level" + bl_options = {'REGISTER', 'UNDO'} + + materialName : bpy.props.StringProperty() + + def execute(self, context): + levels = celGetMaterialLevels(self.materialName) + levels.remove(len(levels) - 1) + return {'FINISHED'} + + def getOptimalFormat(tex, curFormat, isMultitexture): texFormat = "RGBA16" if isMultitexture: @@ -2975,8 +3439,11 @@ class MATERIAL_MT_f3d_presets(Menu): props_default = getattr(self, "preset_operator_defaults", None) add_operator = getattr(self, "preset_add_operator", None) presetDir = getCurrentPresetDir() - paths = bpy.utils.preset_paths(presetDir) if not bpy.context.scene.f3dUserPresetsOnly else [] - paths += bpy.utils.preset_paths("f3d/user") + paths = bpy.utils.preset_paths("f3d/user") + if not bpy.context.scene.f3dUserPresetsOnly: + paths += bpy.utils.preset_paths(presetDir) + if bpy.context.scene.f3d_type == "F3DEX3": + paths += bpy.utils.preset_paths(f"{presetDir}_f3dex3") self.path_menu( paths, self.preset_operator, @@ -3123,7 +3590,7 @@ class AddPresetF3D(AddPresetBase, Operator): def rna_recursive_attr_expand(value, rna_path_step, level): if rna_path_step in self.ignore_props: return - if isinstance(value, bpy.types.PropertyGroup): + if isinstance(value, PropertyGroup): for sub_value_attr in value.bl_rna.properties.keys(): if sub_value_attr == "rna_type": continue @@ -3293,7 +3760,7 @@ def convertToNewMat(material, oldMat): old_light = oldMat.get(f"f3d_light{str(i)}") # can be a broken property with V1 materials (IDPropertyGroup), thankfully this isnt typical to see when upgrading but # this method is safer - if type(old_light) is bpy.types.Light: + if type(old_light) is Light: setattr(material.f3d_mat, f"f3d_light{str(i)}", old_light) # Fog Properties @@ -3312,7 +3779,7 @@ def convertToNewMat(material, oldMat): recursiveCopyOldPropertyGroup(oldMat["rdp_settings"], material.f3d_mat.rdp_settings) -class F3DMaterialProperty(bpy.types.PropertyGroup): +class F3DMaterialProperty(PropertyGroup): presetName: bpy.props.StringProperty( name="Preset Name", default="Custom", @@ -3520,13 +3987,80 @@ class F3DMaterialProperty(bpy.types.PropertyGroup): default=(0.5, 0.5, 0.5, 1), update=update_light_properties, ) - f3d_light1: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light2: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light3: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light4: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light5: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light6: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) - f3d_light7: bpy.props.PointerProperty(type=bpy.types.Light, update=F3DOrganizeLights) + f3d_light1: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light2: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light3: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light4: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light5: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light6: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + f3d_light7: bpy.props.PointerProperty(type=Light, update=F3DOrganizeLights) + + # Ambient Occlusion + ao_ambient: bpy.props.FloatProperty( + name="AO Ambient", + min=0.0, + max=1.0, + default=1.0, + description="How much ambient occlusion (vertex alpha) affects ambient light intensity", + update=update_node_values_without_preset, + ) + ao_directional: bpy.props.FloatProperty( + name="AO Directional", + min=0.0, + max=1.0, + default=0.625, + description="How much ambient occlusion (vertex alpha) affects directional light intensity", + update=update_node_values_without_preset, + ) + ao_point: bpy.props.FloatProperty( + name="AO Point", + min=0.0, + max=1.0, + default=0.0, + description="How much ambient occlusion (vertex alpha) affects point light intensity", + update=update_node_values_without_preset, + ) + set_ao: bpy.props.BoolProperty(update=update_node_values_without_preset) + + # Fresnel + fresnel_lo: bpy.props.FloatProperty( + name="Fresnel lo", + min=-1000.0, + max=1000.0, + default=0.7, + description="Dot product value which gives shade alpha = 0. The dot product ranges from 1 when the normal points directly at the camera, to 0 when it points sideways", + update=update_node_values_without_preset, + ) + fresnel_hi: bpy.props.FloatProperty( + name="Fresnel hi", + min=-1000.0, + max=1000.0, + default=0.4, + description="Dot product value which gives shade alpha = FF. The dot product ranges from 1 when the normal points directly at the camera, to 0 when it points sideways", + update=update_node_values_without_preset, + ) + set_fresnel: bpy.props.BoolProperty(update=update_node_values_without_preset) + + # Attribute Offsets + attroffs_st: bpy.props.FloatVectorProperty( + name="ST Attr Offset", + size=2, + min=-1024.0, + max=1024.0, + default=(0.0, 0.0), + description="Offset applied to ST (UV) coordinates, after texture scale. Units are texels. Usually for UV scrolling", + update=update_node_values_without_preset, + ) + attroffs_z: bpy.props.IntProperty( + name="Z Attr Offset", + min=-0x8000, + max=0x7FFF, + default=-2, + description="Offset applied to Z coordinate. To fix decals, set Z mode to opaque and set Z attr offset to something like -2", + update=update_node_values_without_preset, + ) + set_attroffs_st: bpy.props.BoolProperty(update=update_node_values_without_preset) + set_attroffs_z: bpy.props.BoolProperty(update=update_node_values_without_preset) # Fog Properties fog_color: bpy.props.FloatVectorProperty( @@ -3562,6 +4096,10 @@ class F3DMaterialProperty(bpy.types.PropertyGroup): use_large_textures: bpy.props.BoolProperty(name="Large Texture Mode") large_edges: bpy.props.EnumProperty(items=enumLargeEdges, default="Clamp") + expand_cel_shading_ui : bpy.props.BoolProperty(name = "Expand Cel Shading UI") + use_cel_shading : bpy.props.BoolProperty(name = "Use Cel Shading", update = update_cel_cutout_source) + cel_shading : bpy.props.PointerProperty(type = CelShadingProperty) + def key(self) -> F3DMaterialHash: useDefaultLighting = self.set_lights and self.use_default_lighting return ( @@ -3575,6 +4113,18 @@ class F3DMaterialProperty(bpy.types.PropertyGroup): self.rdp_settings.key(), self.draw_layer.key(), self.use_large_textures, + self.use_cel_shading, + self.cel_shading.tintPipeline if self.use_cel_shading else None, + tuple([( + c.threshMode, + c.threshold, + c.tintType, + c.tintFixedLevel, + c.tintFixedColor, + c.tintSegmentNum, + c.tintSegmentOffset, + c.tintLightSlot + ) for c in self.cel_shading.levels]) if self.use_cel_shading else None, self.use_default_lighting, self.set_blend, self.set_prim, @@ -3600,6 +4150,10 @@ class F3DMaterialProperty(bpy.types.PropertyGroup): round(self.k5, 4) if self.set_k0_5 else None, self.combiner1.key() if self.set_combiner else None, self.combiner2.key() if self.set_combiner else None, + tuple([round(value, 4) for value in (self.ao_ambient, self.ao_directional, self.ao_point)]) if self.set_ao else None, + tuple([round(value, 4) for value in (self.fresnel_lo, self.fresnel_hi)]) if self.set_fresnel else None, + tuple([round(value, 4) for value in self.attroffs_st]) if self.set_attroffs_st else None, + self.attroffs_z if self.set_attroffs_z else None, tuple([round(value, 4) for value in self.fog_color]) if self.set_fog else None, tuple([round(value, 4) for value in self.fog_position]) if self.set_fog else None, tuple([round(value, 4) for value in self.default_light_color]) if useDefaultLighting else None, @@ -3617,7 +4171,7 @@ class F3DMaterialProperty(bpy.types.PropertyGroup): ) -class UnlinkF3DImage0(bpy.types.Operator): +class UnlinkF3DImage0(Operator): bl_idname = "image.tex0_unlink" bl_label = "Unlink F3D Image" bl_options = {"REGISTER", "UNDO", "PRESET"} @@ -3629,7 +4183,7 @@ class UnlinkF3DImage0(bpy.types.Operator): return {"FINISHED"} # must return a set -class UnlinkF3DImage1(bpy.types.Operator): +class UnlinkF3DImage1(Operator): bl_idname = "image.tex1_unlink" bl_label = "Unlink F3D Image" bl_options = {"REGISTER", "UNDO", "PRESET"} @@ -3641,7 +4195,7 @@ class UnlinkF3DImage1(bpy.types.Operator): return {"FINISHED"} # must return a set -class UpdateF3DNodes(bpy.types.Operator): +class UpdateF3DNodes(Operator): bl_idname = "material.update_f3d_nodes" bl_label = "Update F3D Nodes" bl_options = {"REGISTER", "UNDO", "PRESET"} @@ -3668,7 +4222,7 @@ class UpdateF3DNodes(bpy.types.Operator): return {"FINISHED"} # must return a set -class F3DRenderSettingsPanel(bpy.types.Panel): +class F3DRenderSettingsPanel(Panel): bl_label = "F3D Render Settings" bl_idname = "OBJECT_PT_F3D_RENDER_SETTINGS_PANEL" bl_space_type = "VIEW_3D" @@ -3750,7 +4304,7 @@ class F3DRenderSettingsPanel(bpy.types.Panel): def draw_f3d_render_settings(self, context): - layout: bpy.types.UILayout = self.layout + layout: UILayout = self.layout layout.popover(F3DRenderSettingsPanel.bl_idname) @@ -3771,6 +4325,10 @@ mat_classes = ( PrimDepthSettings, RDPSettings, DefaultRDPSettingsPanel, + CelLevelProperty, + CelShadingProperty, + CelLevelAdd, + CelLevelRemove, F3DMaterialProperty, ReloadDefaultF3DPresets, UpdateF3DNodes, @@ -3815,61 +4373,60 @@ def mat_register(): savePresets() - bpy.types.Scene.f3d_type = bpy.props.EnumProperty( + Scene.f3d_type = bpy.props.EnumProperty( name="F3D Microcode", items=enumF3D, default="F3D", ) - bpy.types.Scene.isHWv1 = bpy.props.BoolProperty(name="Is Hardware v1?") # RDP Defaults - bpy.types.World.rdp_defaults = bpy.props.PointerProperty(type=RDPSettings) - bpy.types.World.menu_geo = bpy.props.BoolProperty() - bpy.types.World.menu_upper = bpy.props.BoolProperty() - bpy.types.World.menu_lower = bpy.props.BoolProperty() - bpy.types.World.menu_other = bpy.props.BoolProperty() - bpy.types.World.menu_layers = bpy.props.BoolProperty() + World.rdp_defaults = bpy.props.PointerProperty(type=RDPSettings) + World.menu_geo = bpy.props.BoolProperty() + World.menu_upper = bpy.props.BoolProperty() + World.menu_lower = bpy.props.BoolProperty() + World.menu_other = bpy.props.BoolProperty() + World.menu_layers = bpy.props.BoolProperty() - bpy.types.Material.is_f3d = bpy.props.BoolProperty() - bpy.types.Material.mat_ver = bpy.props.IntProperty(default=1) - bpy.types.Material.f3d_update_flag = bpy.props.BoolProperty() - bpy.types.Material.f3d_mat = bpy.props.PointerProperty(type=F3DMaterialProperty) - bpy.types.Material.menu_tab = bpy.props.EnumProperty(items=enumF3DMenu) + Material.is_f3d = bpy.props.BoolProperty() + Material.mat_ver = bpy.props.IntProperty(default=1) + Material.f3d_update_flag = bpy.props.BoolProperty() + Material.f3d_mat = bpy.props.PointerProperty(type=F3DMaterialProperty) + Material.menu_tab = bpy.props.EnumProperty(items=enumF3DMenu) - bpy.types.Scene.f3dUserPresetsOnly = bpy.props.BoolProperty(name="User Presets Only") - bpy.types.Scene.f3d_simple = bpy.props.BoolProperty(name="Display Simple", default=True) + Scene.f3dUserPresetsOnly = bpy.props.BoolProperty(name="User Presets Only") + Scene.f3d_simple = bpy.props.BoolProperty(name="Display Simple", default=True) - bpy.types.Object.use_f3d_culling = bpy.props.BoolProperty( + Object.use_f3d_culling = bpy.props.BoolProperty( name="Enable Culling (Applies to F3DEX and up)", default=True, ) - bpy.types.Object.ignore_render = bpy.props.BoolProperty(name="Ignore Render") - bpy.types.Object.ignore_collision = bpy.props.BoolProperty(name="Ignore Collision") - bpy.types.Object.f3d_lod_z = bpy.props.IntProperty( + Object.ignore_render = bpy.props.BoolProperty(name="Ignore Render") + Object.ignore_collision = bpy.props.BoolProperty(name="Ignore Collision") + Object.f3d_lod_z = bpy.props.IntProperty( name="F3D LOD Z", min=1, default=10, ) - bpy.types.Object.f3d_lod_always_render_farthest = bpy.props.BoolProperty(name="Always Render Farthest LOD") + Object.f3d_lod_always_render_farthest = bpy.props.BoolProperty(name="Always Render Farthest LOD") - bpy.types.VIEW3D_HT_header.append(draw_f3d_render_settings) + VIEW3D_HT_header.append(draw_f3d_render_settings) def mat_unregister(): - bpy.types.VIEW3D_HT_header.remove(draw_f3d_render_settings) + VIEW3D_HT_header.remove(draw_f3d_render_settings) - del bpy.types.Material.menu_tab - del bpy.types.Material.f3d_mat - del bpy.types.Material.is_f3d - del bpy.types.Material.mat_ver - del bpy.types.Material.f3d_update_flag - del bpy.types.Scene.f3d_simple - del bpy.types.Object.ignore_render - del bpy.types.Object.ignore_collision - del bpy.types.Object.use_f3d_culling - del bpy.types.Scene.f3dUserPresetsOnly - del bpy.types.Object.f3d_lod_z - del bpy.types.Object.f3d_lod_always_render_farthest + del Material.menu_tab + del Material.f3d_mat + del Material.is_f3d + del Material.mat_ver + del Material.f3d_update_flag + del Scene.f3d_simple + del Object.ignore_render + del Object.ignore_collision + del Object.use_f3d_culling + del Scene.f3dUserPresetsOnly + del Object.f3d_lod_z + del Object.f3d_lod_always_render_farthest for cls in reversed(mat_classes): unregister_class(cls) diff --git a/fast64_internal/f3d/f3d_material_presets.py b/fast64_internal/f3d/f3d_material_presets.py index aa3e269..bcdfa07 100644 --- a/fast64_internal/f3d/f3d_material_presets.py +++ b/fast64_internal/f3d/f3d_material_presets.py @@ -57,6 +57,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -64,12 +68,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = True f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -175,6 +188,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -182,12 +199,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = True f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -293,6 +319,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -300,12 +330,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -411,6 +450,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -418,12 +461,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -529,6 +581,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -536,12 +592,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -647,6 +712,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -654,12 +723,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -765,6 +843,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -772,12 +854,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -883,6 +974,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -890,12 +985,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1001,6 +1105,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1008,12 +1116,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1119,6 +1236,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1126,12 +1247,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1237,6 +1367,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1244,12 +1378,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1355,6 +1498,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1362,12 +1509,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1473,6 +1629,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1480,12 +1640,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = False f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1591,6 +1760,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1598,12 +1771,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = False f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1709,6 +1891,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1716,12 +1902,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = False f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1827,6 +2022,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1834,12 +2033,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -1944,6 +2152,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -1951,12 +2163,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = True f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2061,6 +2282,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = True @@ -2068,12 +2293,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2178,6 +2412,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = True @@ -2185,12 +2423,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2295,6 +2542,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = True @@ -2302,12 +2553,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = True f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2412,6 +2672,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -2419,12 +2683,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2529,6 +2802,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -2536,12 +2813,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2646,6 +2932,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -2653,12 +2943,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2763,6 +3062,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -2770,12 +3073,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2880,6 +3192,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -2887,12 +3203,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -2997,6 +3322,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3004,12 +3333,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3114,6 +3452,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3121,12 +3463,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3231,6 +3582,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3238,12 +3593,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = False f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3348,6 +3712,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3355,12 +3723,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = False f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3466,6 +3843,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3473,12 +3854,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3585,6 +3975,10 @@ f3d_mat.f3d_light4 = None f3d_mat.f3d_light5 = None f3d_mat.f3d_light6 = None f3d_mat.f3d_light7 = None +f3d_mat.set_ao = False +f3d_mat.set_fresnel = False +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) f3d_mat.fog_position = (985, 1000) f3d_mat.set_fog = False @@ -3592,12 +3986,21 @@ f3d_mat.use_global_fog = True f3d_mat.rdp_settings.name = '' f3d_mat.rdp_settings.g_zbuffer = True f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False f3d_mat.rdp_settings.g_cull_front = False f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False f3d_mat.rdp_settings.g_fog = False f3d_mat.rdp_settings.g_lighting = True f3d_mat.rdp_settings.g_tex_gen = False f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False f3d_mat.rdp_settings.g_shade_smooth = True f3d_mat.rdp_settings.g_clipping = False f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' @@ -3644,6 +4047,3271 @@ bpy.context.material.f3d_update_flag = False f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update """ +oot_cel_4_blend_tex_vcol_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'OoT Cel Shaded 4 Blend Tex VCol AO' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'SHADE' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'TEXEL0' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = '0' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = '0' +f3d_mat.combiner2.D = 'SHADE' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = '1' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = False +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = True +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 0.5) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.25979697704315186, 0.09201788902282715, 0.022466713562607765, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = True +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_DISABLE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_1CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_THRESHOLD' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_AA_ZB_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = True +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_FOG' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_FOG' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.use_cel_shading = True +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'Blender' +f3d_mat.cel_shading.cutoutSource = 'TEXEL0' +f3d_mat.cel_shading.levels.clear() +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 60 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 140 +item_sub_1.tintFixedColor = (0.012216208502650261, 0.012216208502650261, 0.012216208502650261) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 240 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 150 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 48 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 150 +item_sub_1.tintFixedColor = (0.0, 0.0, 0.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_cel_blend_tex_vcol_ltcol = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'OoT Cel Shaded Blend Tex VCol LtCol' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'SHADE' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'TEXEL0' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = '0' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = '0' +f3d_mat.combiner2.D = 'SHADE' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = '1' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = False +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = True +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 0.5) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.25979697704315186, 0.09201788902282715, 0.022466713562607765, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = True +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_DISABLE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_1CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_THRESHOLD' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_AA_ZB_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = True +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_FOG' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_FOG' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.use_cel_shading = True +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'Blender' +f3d_mat.cel_shading.cutoutSource = 'TEXEL0' +f3d_mat.cel_shading.levels.clear() +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Light' +item_sub_1.tintFixedLevel = 60 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Light' +item_sub_1.tintFixedLevel = 140 +item_sub_1.tintFixedColor = (0.012216208502650261, 0.012216208502650261, 0.012216208502650261) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 0 +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_cel_blend_tex_vcol_ltcol_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'OoT Cel Shaded Blend Tex VCol LtCol AO' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'SHADE' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'TEXEL0' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = '0' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = '0' +f3d_mat.combiner2.D = 'SHADE' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = '1' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = False +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 0.5) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.25979697704315186, 0.09201788902282715, 0.022466713562607765, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = True +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_DISABLE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_1CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_THRESHOLD' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_AA_ZB_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = True +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_FOG' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_FOG' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.use_cel_shading = True +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'Blender' +f3d_mat.cel_shading.cutoutSource = 'TEXEL0' +f3d_mat.cel_shading.levels.clear() +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Light' +item_sub_1.tintFixedLevel = 60 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Light' +item_sub_1.tintFixedLevel = 140 +item_sub_1.tintFixedColor = (0.012216208502650261, 0.012216208502650261, 0.012216208502650261) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 0 +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_cel_blend_vcol = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'OoT Cel Shaded Blend VCol' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = 'SHADE' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'ENVIRONMENT' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = '0' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = '0' +f3d_mat.combiner2.D = 'SHADE' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = '1' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = False +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = True +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 0.5) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.25979697704315186, 0.09201788902282715, 0.022466713562607765, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = True +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_DISABLE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_1CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_THRESHOLD' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_AA_ZB_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = True +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_FOG' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_FOG' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.use_cel_shading = True +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'Blender' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 60 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 140 +item_sub_1.tintFixedColor = (0.012216208502650261, 0.012216208502650261, 0.012216208502650261) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_cel_mul_tex = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'OoT Cel Shaded Mul Tex' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'PRIMITIVE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'SHADE' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'TEXEL0' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = '0' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = '0' +f3d_mat.combiner2.D = 'SHADE' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = '1' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = False +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = True +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 0.5) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.25979697704315186, 0.09201788902282715, 0.022466713562607765, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = True +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_DISABLE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_1CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_THRESHOLD' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_AA_ZB_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = True +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_0' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.use_cel_shading = True +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'TEXEL0' +f3d_mat.cel_shading.levels.clear() +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Lighter' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 50 +item_sub_1.tintFixedColor = (1.0, 1.0, 1.0) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +item_sub_1 = f3d_mat.cel_shading.levels.add() +item_sub_1.name = '' +item_sub_1.threshMode = 'Darker' +item_sub_1.threshold = 128 +item_sub_1.tintType = 'Fixed' +item_sub_1.tintFixedLevel = 50 +item_sub_1.tintFixedColor = (0.04445325583219528, 0.04445325583219528, 0.04445325583219528) +item_sub_1.tintSegmentNum = 8 +item_sub_1.tintSegmentOffset = 0 +item_sub_1.tintLightSlot = 1 +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_ghost_fresnel = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Ghost Fresnel' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = 'SHADE' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.20000000298023224 +f3d_mat.set_fresnel = True +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = True +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_PASS' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_ghost_texture_fresnel = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Ghost Texture Fresnel' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = 'SHADE' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.20000000298023224 +f3d_mat.set_fresnel = True +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = True +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_PASS' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_glass_specular_fresnel = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Glass Specular Fresnel' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = 'SHADE' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.20000000298023224 +f3d_mat.set_fresnel = True +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = True +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = True +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_PASS' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_solid_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Solid Ao' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = 'COMBINED' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_solid_ao_transparent = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Solid Ao Transparent' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_specular_vcol_texture = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Specular Vcol Texture' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = 'COMBINED' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = False +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = True +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_multitexture_lerp_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Multitexture Lerp Ao' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL1' +f3d_mat.combiner1.B = 'TEXEL0' +f3d_mat.combiner1.C = 'ENV_ALPHA' +f3d_mat.combiner1.D = 'TEXEL0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'SHADE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = 'COMBINED' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_multitexture_lerp_ao_transparent = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Multitexture Lerp Ao Transparent' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL1' +f3d_mat.combiner1.B = 'TEXEL0' +f3d_mat.combiner1.C = 'ENV_ALPHA' +f3d_mat.combiner1.D = 'TEXEL0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'SHADE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.env_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_multitexture_lerp_transparent_vertex_alpha = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Multitexture Lerp Transparent Vertex Alpha' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL1' +f3d_mat.combiner1.B = 'TEXEL0' +f3d_mat.combiner1.C = 'ENV_ALPHA' +f3d_mat.combiner1.D = 'TEXEL0' +f3d_mat.combiner1.A_alpha = 'TEXEL1' +f3d_mat.combiner1.B_alpha = 'TEXEL0' +f3d_mat.combiner1.C_alpha = 'ENVIRONMENT' +f3d_mat.combiner1.D_alpha = 'TEXEL0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'SHADE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'SHADE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_solid_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Solid Ao' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = 'COMBINED' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_solid_ao_transparent = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Solid Ao Transparent' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = '0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = '0' +f3d_mat.combiner1.D = 'SHADE' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_texture_ao = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Texture Ao' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = '1' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = '0' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = '0' +f3d_mat.combiner2.D_alpha = 'COMBINED' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = False +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = True +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_OPA_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '1' +f3d_mat.draw_layer.oot = 'Opaque' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_texture_ao_transparent = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Texture Ao Transparent' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'SHADE' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = '0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = '0' +f3d_mat.combiner1.D_alpha = 'TEXEL0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'PRIMITIVE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = False +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.env_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = True +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = True +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_FOG_SHADE_A' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_shaded_vcol_texture_transparent_vertex_alpha = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Shaded Vcol Texture Transparent Vertex Alpha' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'ENVIRONMENT' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'TEXEL0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'ENVIRONMENT' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'COMBINED' +f3d_mat.combiner2.B = '0' +f3d_mat.combiner2.C = 'SHADE' +f3d_mat.combiner2.D = '0' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'SHADE' +f3d_mat.combiner2.D_alpha = '0' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.4000000059604645 +f3d_mat.set_fresnel = False +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = True +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = False +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = False +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = False +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_OPA_SURF' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = False +f3d_mat.rdp_settings.z_cmp = False +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = False +f3d_mat.rdp_settings.clr_on_cvg = False +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_CLAMP' +f3d_mat.rdp_settings.zmode = 'ZMODE_OPA' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = False +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_water_lerp_specular_fresnel = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Water Lerp Specular Fresnel' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL1' +f3d_mat.combiner1.B = 'TEXEL0' +f3d_mat.combiner1.C = 'ENV_ALPHA' +f3d_mat.combiner1.D = 'TEXEL0' +f3d_mat.combiner1.A_alpha = 'TEXEL1' +f3d_mat.combiner1.B_alpha = 'TEXEL0' +f3d_mat.combiner1.C_alpha = 'ENVIRONMENT' +f3d_mat.combiner1.D_alpha = 'TEXEL0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'SHADE' +f3d_mat.combiner2.B = 'PRIMITIVE' +f3d_mat.combiner2.C = 'COMBINED' +f3d_mat.combiner2.D = 'PRIMITIVE' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = 'SHADE' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (0.10837142914533615, 0.24593788385391235, 0.5403782725334167, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 0.5) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.20000000298023224 +f3d_mat.set_fresnel = True +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = True +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = True +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_NOOP' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = True +f3d_mat.rdp_settings.clr_on_cvg = True +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_WRAP' +f3d_mat.rdp_settings.zmode = 'ZMODE_INTER' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_MEM' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + +oot_water_mult_specular_fresnel = """ +import bpy +f3d_mat = bpy.context.material.f3d_mat + +bpy.context.material.f3d_update_flag = True +f3d_mat.presetName = 'Oot Water Mult Specular Fresnel' +f3d_mat.combiner1.name = '' +f3d_mat.combiner1.A = 'TEXEL0' +f3d_mat.combiner1.B = '0' +f3d_mat.combiner1.C = 'TEXEL1' +f3d_mat.combiner1.D = '0' +f3d_mat.combiner1.A_alpha = 'TEXEL0' +f3d_mat.combiner1.B_alpha = '0' +f3d_mat.combiner1.C_alpha = 'TEXEL1' +f3d_mat.combiner1.D_alpha = '0' +f3d_mat.combiner2.name = '' +f3d_mat.combiner2.A = 'SHADE' +f3d_mat.combiner2.B = 'PRIMITIVE' +f3d_mat.combiner2.C = 'COMBINED' +f3d_mat.combiner2.D = 'PRIMITIVE' +f3d_mat.combiner2.A_alpha = 'COMBINED' +f3d_mat.combiner2.B_alpha = '0' +f3d_mat.combiner2.C_alpha = 'PRIMITIVE' +f3d_mat.combiner2.D_alpha = 'SHADE' +f3d_mat.tex0.name = '' +f3d_mat.tex0.tex_set = True +f3d_mat.tex1.name = '' +f3d_mat.tex1.tex_set = True +f3d_mat.set_prim = True +f3d_mat.set_lights = False +f3d_mat.set_env = True +f3d_mat.set_blend = False +f3d_mat.set_key = True +f3d_mat.set_k0_5 = True +f3d_mat.set_combiner = True +f3d_mat.use_default_lighting = True +f3d_mat.blend_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.prim_color = (0.10837142914533615, 0.24593788385391235, 0.5403782725334167, 1.0) +f3d_mat.env_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.key_center = (0.5, 0.5, 0.5, 1.0) +f3d_mat.key_scale = (0.0, 0.0, 0.0) +f3d_mat.key_width = (0.0, 0.0, 0.0) +f3d_mat.k0 = 0.686274528503418 +f3d_mat.k1 = -0.16862745583057404 +f3d_mat.k2 = -0.3490196168422699 +f3d_mat.k3 = 0.8705882430076599 +f3d_mat.k4 = 0.4470588266849518 +f3d_mat.k5 = 0.16470588743686676 +f3d_mat.prim_lod_frac = 0.0 +f3d_mat.prim_lod_min = 0.0 +f3d_mat.default_light_color = (1.0, 1.0, 1.0, 1.0) +f3d_mat.set_ambient_from_light = True +f3d_mat.ambient_light_color = (0.2140410989522934, 0.2140410989522934, 0.2140410989522934, 1.0) +f3d_mat.f3d_light1 = None +f3d_mat.f3d_light2 = None +f3d_mat.f3d_light3 = None +f3d_mat.f3d_light4 = None +f3d_mat.f3d_light5 = None +f3d_mat.f3d_light6 = None +f3d_mat.f3d_light7 = None +f3d_mat.ao_ambient = 1.0 +f3d_mat.ao_directional = 0.625 +f3d_mat.ao_point = 0.0 +f3d_mat.set_ao = False +f3d_mat.fresnel_lo = 0.699999988079071 +f3d_mat.fresnel_hi = 0.20000000298023224 +f3d_mat.set_fresnel = True +f3d_mat.attroffs_st = (0.0, 0.0) +f3d_mat.attroffs_z = -2 +f3d_mat.set_attroffs_st = False +f3d_mat.set_attroffs_z = False +f3d_mat.fog_color = (0.0, 0.0, 0.0, 1.0) +f3d_mat.fog_position = (985, 1000) +f3d_mat.set_fog = False +f3d_mat.use_global_fog = True +f3d_mat.rdp_settings.name = '' +f3d_mat.rdp_settings.g_zbuffer = True +f3d_mat.rdp_settings.g_shade = True +f3d_mat.rdp_settings.g_ambocclusion = False +f3d_mat.rdp_settings.g_attroffset_z_enable = False +f3d_mat.rdp_settings.g_attroffset_st_enable = False +f3d_mat.rdp_settings.g_cull_front = False +f3d_mat.rdp_settings.g_cull_back = False +f3d_mat.rdp_settings.g_packed_normals = False +f3d_mat.rdp_settings.g_lighttoalpha = False +f3d_mat.rdp_settings.g_lighting_specular = True +f3d_mat.rdp_settings.g_fresnel_color = False +f3d_mat.rdp_settings.g_fresnel_alpha = True +f3d_mat.rdp_settings.g_fog = False +f3d_mat.rdp_settings.g_lighting = True +f3d_mat.rdp_settings.g_tex_gen = False +f3d_mat.rdp_settings.g_tex_gen_linear = False +f3d_mat.rdp_settings.g_lod = False +f3d_mat.rdp_settings.g_shade_smooth = True +f3d_mat.rdp_settings.g_clipping = False +f3d_mat.rdp_settings.g_mdsft_alpha_dither = 'G_AD_NOISE' +f3d_mat.rdp_settings.g_mdsft_rgb_dither = 'G_CD_MAGICSQ' +f3d_mat.rdp_settings.g_mdsft_combkey = 'G_CK_NONE' +f3d_mat.rdp_settings.g_mdsft_textconv = 'G_TC_FILT' +f3d_mat.rdp_settings.g_mdsft_text_filt = 'G_TF_BILERP' +f3d_mat.rdp_settings.g_mdsft_textlut = 'G_TT_NONE' +f3d_mat.rdp_settings.g_mdsft_textlod = 'G_TL_TILE' +f3d_mat.rdp_settings.num_textures_mipmapped = 2 +f3d_mat.rdp_settings.g_mdsft_textdetail = 'G_TD_CLAMP' +f3d_mat.rdp_settings.g_mdsft_textpersp = 'G_TP_PERSP' +f3d_mat.rdp_settings.g_mdsft_cycletype = 'G_CYC_2CYCLE' +f3d_mat.rdp_settings.g_mdsft_color_dither = 'G_CD_ENABLE' +f3d_mat.rdp_settings.g_mdsft_pipeline = 'G_PM_NPRIMITIVE' +f3d_mat.rdp_settings.g_mdsft_alpha_compare = 'G_AC_NONE' +f3d_mat.rdp_settings.g_mdsft_zsrcsel = 'G_ZS_PIXEL' +f3d_mat.rdp_settings.prim_depth.name = '' +f3d_mat.rdp_settings.prim_depth.z = 0 +f3d_mat.rdp_settings.prim_depth.dz = 0 +f3d_mat.rdp_settings.clip_ratio = 1 +f3d_mat.rdp_settings.set_rendermode = True +f3d_mat.rdp_settings.rendermode_advanced_enabled = True +f3d_mat.rdp_settings.rendermode_preset_cycle_1 = 'G_RM_NOOP' +f3d_mat.rdp_settings.rendermode_preset_cycle_2 = 'G_RM_AA_ZB_XLU_SURF2' +f3d_mat.rdp_settings.aa_en = True +f3d_mat.rdp_settings.z_cmp = True +f3d_mat.rdp_settings.z_upd = False +f3d_mat.rdp_settings.im_rd = True +f3d_mat.rdp_settings.clr_on_cvg = True +f3d_mat.rdp_settings.cvg_dst = 'CVG_DST_WRAP' +f3d_mat.rdp_settings.zmode = 'ZMODE_INTER' +f3d_mat.rdp_settings.cvg_x_alpha = False +f3d_mat.rdp_settings.alpha_cvg_sel = False +f3d_mat.rdp_settings.force_bl = True +f3d_mat.rdp_settings.blend_p1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_p2 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m1 = 'G_BL_CLR_IN' +f3d_mat.rdp_settings.blend_m2 = 'G_BL_CLR_MEM' +f3d_mat.rdp_settings.blend_a1 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_a2 = 'G_BL_A_IN' +f3d_mat.rdp_settings.blend_b1 = 'G_BL_1MA' +f3d_mat.rdp_settings.blend_b2 = 'G_BL_1MA' +f3d_mat.draw_layer.name = '' +f3d_mat.draw_layer.sm64 = '5' +f3d_mat.draw_layer.oot = 'Transparent' +f3d_mat.large_edges = 'Clamp' +f3d_mat.expand_cel_shading_ui = False +f3d_mat.use_cel_shading = False +f3d_mat.cel_shading.name = '' +f3d_mat.cel_shading.tintPipeline = 'CC' +f3d_mat.cel_shading.cutoutSource = 'ENVIRONMENT' +f3d_mat.cel_shading.levels.clear() +bpy.context.material.f3d_update_flag = False +f3d_mat.use_default_lighting = f3d_mat.use_default_lighting # Force nodes update +""" + material_presets = { "oot": { "oot_shaded_environment_mapped": oot_shaded_environment_mapped, @@ -3664,6 +7332,29 @@ material_presets = { "oot_vertex_colored_texture_cutout": oot_vertex_colored_texture_cutout, "oot_vertex_colored_texture_transparent": oot_vertex_colored_texture_transparent, }, + "oot_f3dex3": { + "oot_cel_4_blend_tex_vcol_ao": oot_cel_4_blend_tex_vcol_ao, + "oot_cel_blend_tex_vcol_ltcol": oot_cel_blend_tex_vcol_ltcol, + "oot_cel_blend_tex_vcol_ltcol_ao": oot_cel_blend_tex_vcol_ltcol_ao, + "oot_cel_blend_vcol": oot_cel_blend_vcol, + "oot_cel_mul_tex": oot_cel_mul_tex, + "oot_ghost_fresnel": oot_ghost_fresnel, + "oot_ghost_texture_fresnel": oot_ghost_texture_fresnel, + "oot_glass_specular_fresnel": oot_glass_specular_fresnel, + "oot_shaded_solid_ao": oot_shaded_solid_ao, + "oot_shaded_solid_ao_transparent": oot_shaded_solid_ao_transparent, + "oot_shaded_specular_vcol_texture": oot_shaded_specular_vcol_texture, + "oot_shaded_vcol_multitexture_lerp_ao": oot_shaded_vcol_multitexture_lerp_ao, + "oot_shaded_vcol_multitexture_lerp_ao_transparent": oot_shaded_vcol_multitexture_lerp_ao_transparent, + "oot_shaded_vcol_multitexture_lerp_transparent_vertex_alpha": oot_shaded_vcol_multitexture_lerp_transparent_vertex_alpha, + "oot_shaded_vcol_solid_ao": oot_shaded_vcol_solid_ao, + "oot_shaded_vcol_solid_ao_transparent": oot_shaded_vcol_solid_ao_transparent, + "oot_shaded_vcol_texture_ao": oot_shaded_vcol_texture_ao, + "oot_shaded_vcol_texture_ao_transparent": oot_shaded_vcol_texture_ao_transparent, + "oot_shaded_vcol_texture_transparent_vertex_alpha": oot_shaded_vcol_texture_transparent_vertex_alpha, + "oot_water_lerp_specular_fresnel": oot_water_lerp_specular_fresnel, + "oot_water_mult_specular_fresnel": oot_water_mult_specular_fresnel, + }, "sm64": { "sm64_decal": sm64_decal, "sm64_environment_map": sm64_environment_map, diff --git a/fast64_internal/f3d/f3d_parser.py b/fast64_internal/f3d/f3d_parser.py index 8212a6a..cb3f973 100644 --- a/fast64_internal/f3d/f3d_parser.py +++ b/fast64_internal/f3d/f3d_parser.py @@ -1,5 +1,6 @@ from typing import Union import bmesh, bpy, mathutils, re, math, traceback +from mathutils import Vector from bpy.utils import register_class, unregister_class from .f3d_gbi import * from .f3d_material import ( @@ -257,7 +258,7 @@ def interpretLoadVertices(romfile, vertexBuffer, transformMatrix, command, segme data = romfile.read(dataLength) for i in range(numVerts): - vert = mathutils.Vector(readVectorFromShorts(data, i * 16)) + vert = Vector(readVectorFromShorts(data, i * 16)) vert = transformMatrix @ vert transformedVert = bytearray(6) writeVectorToShorts(transformedVert, 0, vert) @@ -276,9 +277,9 @@ def interpretDrawTriangle(command, vertexBuffer, faceSeq, vertSeq, uv_layer, def index1 = int(command[6] / 0x0A) index2 = int(command[7] / 0x0A) - vert0 = mathutils.Vector(getPosition(vertexBuffer, index0)) - vert1 = mathutils.Vector(getPosition(vertexBuffer, index1)) - vert2 = mathutils.Vector(getPosition(vertexBuffer, index2)) + vert0 = Vector(getPosition(vertexBuffer, index0)) + vert1 = Vector(getPosition(vertexBuffer, index1)) + vert2 = Vector(getPosition(vertexBuffer, index2)) verts[0] = vertSeq.new(vert0) verts[1] = vertSeq.new(vert1) @@ -292,7 +293,7 @@ def interpretDrawTriangle(command, vertexBuffer, faceSeq, vertSeq, uv_layer, def loopIndex = 0 for loop in tri.loops: - loop[uv_layer].uv = mathutils.Vector(getUV(vertexBuffer, int(command[5 + loopIndex] / 0x0A))) + loop[uv_layer].uv = Vector(getUV(vertexBuffer, int(command[5 + loopIndex] / 0x0A))) loopIndex += 1 return verts @@ -506,19 +507,19 @@ class F3DContext: # data for Mesh.from_pydata, list of BufferVertex tuples # use BufferVertex to also form uvs / normals / colors self.verts: list[F3DVert] = [] - self.limbGroups: dict[str : list[int]] = {} # dict of groupName : vertex indices + self.limbGroups: dict[str, list[int]] = {} # dict of groupName : vertex indices - self.lights: Lights = Lights("lights_context") + self.lights: Lights = Lights("lights_context", self.f3d) # Here these are ints, but when parsing the values will be normalized. self.lights.l = [ - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), ] self.lights.a = Ambient([0, 0, 0]) self.numLights: int = 0 @@ -576,15 +577,15 @@ class F3DContext: self.tileSizes = [DPSetTileSize(i, 0, 0, 32, 32) for i in range(8)] - self.lights = Lights("lights_context") + self.lights = Lights("lights_context", self.f3d) self.lights.l = [ - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), - Light([0, 0, 0], [0x28, 0x28, 0x28]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), + Light([0, 0, 0], [0x49, 0x49, 0x49]), ] self.lights.a = Ambient([0, 0, 0]) self.numLights = 0 @@ -600,14 +601,14 @@ class F3DContext: # decomp format "\{\s*\{\s*" + "\{([^,\}]*),([^,\}]*),([^,\}]*)\}\s*," - + "[^,\}]*,\s*" + + "([^,\}]*),\s*" + "\{([^,\}]*),([^,\}]*)\}\s*,\s*" + "\{([^,\}]*),([^,\}]*),([^,\}]*),([^,\}]*)\}\s*" + "\}\s*\}", # nusys format "\{\s*" + "([^,\}]*),([^,\}]*),([^,\}]*)," - + "[^,\}]*," + + "([^,\}]*)," + "([^,\}]*),([^,\}]*)," + "([^,\}]*),([^,\}]*),([^,\}]*),([^,\}]*)\s*" + "\}", @@ -641,7 +642,7 @@ class F3DContext: mat = self.mat() f3dVert = bufferVert.f3dVert - position = transform @ mathutils.Vector(f3dVert.position) + position = transform @ Vector(f3dVert.position) if mat.tex0.tex is not None: texDimensions = mat.tex0.tex.size elif mat.tex0.use_tex_reference: @@ -656,18 +657,19 @@ class F3DContext: uv = [convertF3DUV(f3dVert.uv[i], texDimensions[i]) for i in range(2)] uv[1] = 1 - uv[1] - color = [ - value / 256 - if value > 0 - else int.from_bytes(round(value).to_bytes(1, "big", signed=True), "big", signed=False) / 256 - for value in f3dVert.getColorOrNormal() - ] - - normal = bytesToNormal(f3dVert.getColorOrNormal()[:3]) + [0] - normal = (transform.inverted().transposed() @ mathutils.Vector(normal)).normalized()[:3] + has_rgb, has_normal, has_packed_normals = getRgbNormalSettings(self.mat()) + rgb = Vector([v / 0xFF for v in f3dVert.rgb]) if has_rgb else Vector([1.0, 1.0, 1.0]) + alpha = f3dVert.alpha / 0xFF + normal = Vector([0.0, 0.0, 0.0]) # Zero normal makes normals_split_custom_set use auto + if has_normal: + if has_packed_normals: + normal = f3dVert.normal + else: + normal = Vector([v - 0x100 if v >= 0x80 else v for v in f3dVert.rgb]).normalized() + normal = (transform.inverted().transposed() @ normal).normalized() # NOTE: The groupIndex here does NOT correspond to a vertex group, but to the name of the limb (c variable) - return BufferVertex(F3DVert(position, uv, color, normal), bufferVert.groupIndex, bufferVert.materialIndex) + return BufferVertex(F3DVert(position, uv, rgb, normal, alpha), bufferVert.groupIndex, bufferVert.materialIndex) def addVertices(self, num, start, vertexDataName, vertexDataOffset): vertexData = self.vertexData[vertexDataName] @@ -879,6 +881,23 @@ class F3DContext: mat.rdp_settings.g_cull_front = value if bitFlags & self.f3d.G_CULL_BACK: mat.rdp_settings.g_cull_back = value + if self.f3d.F3DEX_GBI_3: + if bitFlags & self.f3d.G_AMBOCCLUSION: + mat.rdp_settings.g_ambocclusion = value + if bitFlags & self.f3d.G_ATTROFFSET_Z_ENABLE: + mat.rdp_settings.g_attroffset_z_enable = value + if bitFlags & self.f3d.G_ATTROFFSET_ST_ENABLE: + mat.rdp_settings.g_attroffset_st_enable = value + if bitFlags & self.f3d.G_PACKED_NORMALS: + mat.rdp_settings.g_packed_normals = value + if bitFlags & self.f3d.G_LIGHTTOALPHA: + mat.rdp_settings.g_lighttoalpha = value + if bitFlags & self.f3d.G_LIGHTING_SPECULAR: + mat.rdp_settings.g_lighting_specular = value + if bitFlags & self.f3d.G_FRESNEL_COLOR: + mat.rdp_settings.g_fresnel_color = value + if bitFlags & self.f3d.G_FRESNEL_ALPHA: + mat.rdp_settings.g_fresnel_alpha = value if bitFlags & self.f3d.G_FOG: mat.rdp_settings.g_fog = value if bitFlags & self.f3d.G_LIGHTING: @@ -887,6 +906,8 @@ class F3DContext: mat.rdp_settings.g_tex_gen = value if bitFlags & self.f3d.G_TEXTURE_GEN_LINEAR: mat.rdp_settings.g_tex_gen_linear = value + if bitFlags & self.f3d.G_LOD: + mat.rdp_settings.g_lod = value if bitFlags & self.f3d.G_SHADING_SMOOTH: mat.rdp_settings.g_shade_smooth = value if bitFlags & self.f3d.G_CLIPPING: @@ -901,10 +922,29 @@ class F3DContext: mat.rdp_settings.g_shade = bitFlags & self.f3d.G_SHADE != 0 mat.rdp_settings.g_cull_front = bitFlags & self.f3d.G_CULL_FRONT != 0 mat.rdp_settings.g_cull_back = bitFlags & self.f3d.G_CULL_BACK != 0 + if self.f3d.F3DEX_GBI_3: + mat.rdp_settings.g_ambocclusion = bitFlags & self.f3d.G_AMBOCCLUSION != 0 + mat.rdp_settings.g_attroffset_z_enable = bitFlags & self.f3d.G_ATTROFFSET_Z_ENABLE != 0 + mat.rdp_settings.g_attroffset_st_enable = bitFlags & self.f3d.G_ATTROFFSET_ST_ENABLE != 0 + mat.rdp_settings.g_packed_normals = bitFlags & self.f3d.G_PACKED_NORMALS != 0 + mat.rdp_settings.g_lighttoalpha = bitFlags & self.f3d.G_LIGHTTOALPHA != 0 + mat.rdp_settings.g_lighting_specular = bitFlags & self.f3d.G_LIGHTING_SPECULAR != 0 + mat.rdp_settings.g_fresnel_color = bitFlags & self.f3d.G_FRESNEL_COLOR != 0 + mat.rdp_settings.g_fresnel_alpha = bitFlags & self.f3d.G_FRESNEL_ALPHA != 0 + else: + mat.rdp_settings.g_ambocclusion = False + mat.rdp_settings.g_attroffset_z_enable = False + mat.rdp_settings.g_attroffset_st_enable = False + mat.rdp_settings.g_packed_normals = False + mat.rdp_settings.g_lighttoalpha = False + mat.rdp_settings.g_lighting_specular = False + mat.rdp_settings.g_fresnel_color = False + mat.rdp_settings.g_fresnel_alpha = False mat.rdp_settings.g_fog = bitFlags & self.f3d.G_FOG != 0 mat.rdp_settings.g_lighting = bitFlags & self.f3d.G_LIGHTING != 0 mat.rdp_settings.g_tex_gen = bitFlags & self.f3d.G_TEXTURE_GEN != 0 mat.rdp_settings.g_tex_gen_linear = bitFlags & self.f3d.G_TEXTURE_GEN_LINEAR != 0 + mat.rdp_settings.g_lod = bitFlags & self.f3d.G_LOD != 0 mat.rdp_settings.g_shade_smooth = bitFlags & self.f3d.G_SHADING_SMOOTH != 0 mat.rdp_settings.g_clipping = bitFlags & self.f3d.G_CLIPPING != 0 @@ -1147,9 +1187,9 @@ class F3DContext: lightObj.rotation_euler = ( mathutils.Euler((0, 0, math.pi)).to_quaternion() - @ ( - mathutils.Euler((math.pi / 2, 0, 0)).to_quaternion() @ mathutils.Vector(light.normal) - ).rotation_difference(mathutils.Vector((0, 0, 1))) + @ (mathutils.Euler((math.pi / 2, 0, 0)).to_quaternion() @ Vector(light.normal)).rotation_difference( + Vector((0, 0, 1)) + ) ).to_euler() # lightObj.rotation_euler[0] *= 1 bLight.color = light.color @@ -1173,7 +1213,7 @@ class F3DContext: self.mat().set_lights = True lightIndex = self.getLightIndex(command.params[0]) colorData = math_eval(command.params[1], self.f3d) - color = mathutils.Vector( + color = Vector( [((colorData >> 24) & 0xFF) / 0xFF, ((colorData >> 16) & 0xFF) / 0xFF, ((colorData >> 8) & 0xFF) / 0xFF] ) @@ -1231,21 +1271,21 @@ class F3DContext: def createLights(self, data, lightsName): numLights, lightValues = parseLightsData(data, lightsName, self) - ambientColor = mathutils.Vector(gammaInverse([value / 255 for value in lightValues[0:3]])) + ambientColor = Vector(gammaInverse([value / 255 for value in lightValues[0:3]])) lightList = [] for i in range(numLights): - color = mathutils.Vector(gammaInverse([value / 255 for value in lightValues[3 + 6 * i : 3 + 6 * i + 3]])) - direction = mathutils.Vector(bytesToNormal(lightValues[3 + 6 * i + 3 : 3 + 6 * i + 6])) + color = Vector(gammaInverse([value / 255 for value in lightValues[3 + 6 * i : 3 + 6 * i + 3]])) + direction = Vector(bytesToNormal(lightValues[3 + 6 * i + 3 : 3 + 6 * i + 6])) lightList.append(Light(color, direction)) while len(lightList) < 7: - lightList.append(Light(mathutils.Vector([0, 0, 0]), mathutils.Vector([0x28, 0x28, 0x28]))) + lightList.append(Light(Vector([0, 0, 0]), Vector([0x49, 0x49, 0x49]))) # normally a and l are Ambient and Light objects, # but here they will be a color and blender light object array. - lights = Lights(lightsName) + lights = Lights(lightsName, self.f3d) lights.a = Ambient(ambientColor) lights.l = lightList @@ -1547,6 +1587,45 @@ class F3DContext: self.setLightColor(dlData, command) elif command.name[:13] == "gsSPSetLights": self.setLights(dlData, command) + elif command.name == "gsSPAmbOcclusionAmb": + mat.ao_ambient = float_from_u16_str(command.params[0]) + mat.set_ao = True + elif command.name == "gsSPAmbOcclusionDir": + mat.ao_directional = float_from_u16_str(command.params[0]) + mat.set_ao = True + elif command.name == "gsSPAmbOcclusionPoint": + mat.ao_point = float_from_u16_str(command.params[0]) + mat.set_ao = True + elif command.name == "gsSPAmbOcclusionAmbDir": + mat.ao_ambient = float_from_u16_str(command.params[0]) + mat.ao_directional = float_from_u16_str(command.params[1]) + mat.set_ao = True + elif command.name == "gsSPAmbOcclusionDirPoint": + mat.ao_directional = float_from_u16_str(command.params[0]) + mat.ao_point = float_from_u16_str(command.params[1]) + mat.set_ao = True + elif command.name == "gsSPAmbOcclusion": + mat.ao_ambient = float_from_u16_str(command.params[0]) + mat.ao_directional = float_from_u16_str(command.params[1]) + mat.ao_point = float_from_u16_str(command.params[2]) + mat.set_ao = True + elif command.name == "gsSPFresnel": + scale = int_from_s16_str(command.params[0]) + offset = int_from_s16_str(command.params[1]) + dotMax = ((0x7F - offset) << 15) // scale + dotMin = ((0x00 - offset) << 15) // scale + mat.fresnel_hi = dotMax / float(0x7FFF) + mat.fresnel_lo = dotMin / float(0x7FFF) + mat.set_fresnel = True + elif command.name == "gsSPAttrOffsetST": + mat.attroffs_st = [ + int_from_s16_str(command.params[0]) / 32, + int_from_s16_str(command.params[1]) / 32, + ] + mat.set_attroffs_st = True + elif command.name == "gsSPAttrOffsetZ": + mat.attroffs_z = int_from_s16_str(command.params[0]) + mat.set_attroffs_z = True elif command.name == "gsSPFogFactor": pass elif command.name == "gsSPFogPosition": @@ -1776,12 +1855,11 @@ class F3DContext: color_layer = mesh.vertex_colors.new(name="Col").data for i in range(len(mesh.loops)): - # if self.materialContext.f3d_mat.rdp_settings.g_lighting: - color_layer[i].color = self.verts[i].color + color_layer[i].color = self.verts[i].rgb.to_4d() alpha_layer = mesh.vertex_colors.new(name="Alpha").data for i in range(len(mesh.loops)): - alpha_layer[i].color = [self.verts[i].color[3]] * 3 + [1] + alpha_layer[i].color = [self.verts[i].alpha] * 3 + [1] if bpy.context.mode != "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") @@ -1803,7 +1881,7 @@ class F3DContext: i = 0 for key, lightObj in self.lightData.items(): - lightObj.location = bpy.context.scene.cursor.location + mathutils.Vector((i, 0, 0)) + lightObj.location = bpy.context.scene.cursor.location + Vector((i, 0, 0)) i += 1 self.clearGeometry() @@ -1903,29 +1981,21 @@ def parseVertexData(dlData: str, vertexDataName: str, f3dContext: F3DContext): patterns = f3dContext.vertexFormatPatterns(data) vertexData = [] for pattern in patterns: - # Note that color is None here, as we are just parsing vertex data. - # The same values should be used for color and normal, but getColorOrNormal() will be used later - # which returns whichever value is not None between the two. - - # When loaded into the vertex buffer and transformed, the actual normal/color will be calculated. - vertexData = [ - F3DVert( - mathutils.Vector( - [math_eval(match.group(1), f3d), math_eval(match.group(2), f3d), math_eval(match.group(3), f3d)] - ), - mathutils.Vector([math_eval(match.group(4), f3d), math_eval(match.group(5), f3d)]), - None, - mathutils.Vector( - [ - math_eval(match.group(6), f3d), - math_eval(match.group(7), f3d), - math_eval(match.group(8), f3d), - math_eval(match.group(9), f3d), - ] - ), + # For this step, store rgb/normal as rgb and packed normal as normal. + for match in re.finditer(pattern, data, re.DOTALL): + values = [math_eval(g, f3d) for g in match.groups()] + if len(values) == 9: + # A format without the flag / packed normal + values = values[0:3] + [0] + values[3:9] + vertexData.append( + F3DVert( + Vector(values[0:3]), + Vector(values[4:6]), + Vector(values[6:9]), + unpackNormal(values[3]), + values[9], + ) ) - for match in re.finditer(pattern, data, re.DOTALL) - ] if len(vertexData) > 0: break f3dContext.vertexData[vertexDataName] = vertexData @@ -2233,8 +2303,6 @@ class F3D_ImportDL(bpy.types.Operator): removeDoubles = context.scene.DLRemoveDoubles importNormals = context.scene.DLImportNormals drawLayer = context.scene.DLImportDrawLayer - f3dType = context.scene.f3d_type - isHWv1 = context.scene.isHWv1 data = getImportData([importPath]) @@ -2245,7 +2313,7 @@ class F3D_ImportDL(bpy.types.Operator): removeDoubles, importNormals, drawLayer, - F3DContext(F3D(f3dType, isHWv1), basePath, createF3DMat(None)), + F3DContext(get_F3D_GBI(), basePath, createF3DMat(None)), ) self.report({"INFO"}, "Success!") diff --git a/fast64_internal/f3d/f3d_texture_writer.py b/fast64_internal/f3d/f3d_texture_writer.py index 9c93c64..1671f7a 100644 --- a/fast64_internal/f3d/f3d_texture_writer.py +++ b/fast64_internal/f3d/f3d_texture_writer.py @@ -1061,35 +1061,13 @@ def savePaletteLoad( assert 0 <= palAddr < 256 and (palAddr & 0xF) == 0 palFmt = texFormatOf[palFormat] nocm = ["G_TX_WRAP", "G_TX_NOMIRROR"] - - if not f3d._HW_VERSION_1: - gfxOut.commands.extend( - [ - DPSetTextureImage(palFmt, "G_IM_SIZ_16b", 1, fPalette), - DPSetTile("0", "0", 0, 256 + palAddr, loadtile, 0, nocm, 0, 0, nocm, 0, 0), - DPLoadTLUTCmd(loadtile, palLen - 1), - ] - ) - else: - gfxOut.commands.extend( - [ - _DPLoadTextureBlock( - fPalette, - 256 + palAddr, - palFmt, - "G_IM_SIZ_16b", - 4 * palLen, - 1, - 0, - nocm, - nocm, - 0, - 0, - 0, - 0, - ) - ] - ) + gfxOut.commands.extend( + [ + DPSetTextureImage(palFmt, "G_IM_SIZ_16b", 1, fPalette), + DPSetTile("0", "0", 0, 256 + palAddr, loadtile, 0, nocm, 0, 0, nocm, 0, 0), + DPLoadTLUTCmd(loadtile, palLen - 1), + ] + ) # Functions for converting and writing texture and palette data diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 9f923a0..bc8906b 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -1,6 +1,8 @@ -from typing import Union +from typing import Union, Optional, Callable, Any, List +from dataclasses import dataclass import functools import bpy, mathutils, os, re, copy, math +from mathutils import Vector from math import ceil from bpy.utils import register_class, unregister_class @@ -9,9 +11,9 @@ from .f3d_constants import * from .f3d_material import ( all_combiner_uses, getMaterialScrollDimensions, + getZMode, isTexturePointSampled, - isLightingDisabled, - checkIfFlatShaded, + RDPSettings, ) from .f3d_texture_writer import MultitexManager, TileLoad, maybeSaveSingleLargeTextureSetup from .f3d_gbi import * @@ -117,9 +119,7 @@ def getInfoDict(obj): edgeDict[edgeKey].append(face) for loopIndex in face.loops: - convertInfo = LoopConvertInfo( - uv_data, obj, isLightingDisabled(obj.material_slots[face.material_index].material) - ) + convertInfo = LoopConvertInfo(uv_data, obj, obj.material_slots[face.material_index].material) f3dVertDict[loopIndex] = getF3DVert(mesh.loops[loopIndex], face, convertInfo, mesh) for face in mesh.loop_triangles: for edgeKey in face.edge_keys: @@ -184,7 +184,6 @@ def fixLargeUVs(obj): uvOffset = [0, 0] for i in range(2): - # Move any UVs close to or straddling edge minDiff = (-cellSize[i] + 2) - minUV[i] if minDiff > 0: @@ -306,8 +305,7 @@ def saveMeshWithLargeTexturesByFaces( texDimensions, material, currentGroupIndex, - triGroup.triList, - triGroup.vertexList, + triGroup, copy.deepcopy(existingVertData), copy.deepcopy(matRegionDict), ) @@ -320,7 +318,7 @@ def saveMeshWithLargeTexturesByFaces( firstFace = False triGroup.triList.commands.append(SPEndDisplayList()) - + if fMaterial.revert is not None: fMesh.draw.commands.append(SPDisplayList(fMaterial.revert)) @@ -409,18 +407,18 @@ def addCullCommand(obj, fMesh, transformMatrix, matWriteMethod): fMesh.add_cull_vtx() # if the object has a specifically set culling bounds, use that instead for vertexPos in obj.get("culling_bounds", obj.bound_box): - # Most other fields of convertVertexData are unnecessary for bounding box verts fMesh.cullVertexList.vertices.append( - convertVertexData( - obj.data, - mathutils.Vector(vertexPos), + F3DVert( + Vector(vertexPos), [0, 0], + Vector([0, 0, 0]), None, - mathutils.Vector([0, 0, 0, 0]), + 0.0, + ).toVtx( + obj.data, [32, 32], transformMatrix, - False, - False, + True, ) ) @@ -632,8 +630,7 @@ def saveMeshByFaces( texDimensions, material, currentGroupIndex, - triGroup.triList, - triGroup.vertexList, + triGroup, copy.deepcopy(existingVertData), copy.deepcopy(matRegionDict), ) @@ -646,28 +643,11 @@ def saveMeshByFaces( return currentGroupIndex -def get8bitRoundedNormal(loop: bpy.types.MeshLoop, mesh): - alpha_layer = getColorLayer(mesh, "Alpha") - - if alpha_layer is not None: - normalizedAColor = alpha_layer[loop.index].color - if is3_2_or_above(): - normalizedAColor = gammaCorrect(normalizedAColor) - normalizedA = colorToLuminance(normalizedAColor[0:3]) - else: - normalizedA = 1 - - # Don't round, as this may move UV toward UV bounds. - return mathutils.Vector( - (int(loop.normal[0] * 128) / 128, int(loop.normal[1] * 128) / 128, int(loop.normal[2] * 128) / 128, normalizedA) - ) - - +@dataclass class LoopConvertInfo: - def __init__(self, uv_data: bpy.types.bpy_prop_collection | list[bpy.types.MeshUVLoop], obj, exportVertexColors): - self.uv_data: bpy.types.bpy_prop_collection | list[bpy.types.MeshUVLoop] = uv_data - self.obj = obj - self.exportVertexColors = exportVertexColors + uv_data: bpy.types.bpy_prop_collection | list[bpy.types.MeshUVLoop] + obj: bpy.types.Object + material: bpy.types.Material def getNewIndices(existingIndices, bufferStart): @@ -682,21 +662,21 @@ def getNewIndices(existingIndices, bufferStart): return newIndices -# Color and normal are separate, since for parsing, the normal must be transformed into -# bone/object space while the color should just be a regular conversion. class F3DVert: def __init__( self, - position: mathutils.Vector, - uv: mathutils.Vector, - color: mathutils.Vector | None, # 4 components - normal: mathutils.Vector | None, # 4 components + position: Vector, + uv: Vector, + rgb: Optional[Vector], + normal: Optional[Vector], + alpha: float, ): - self.position: mathutils.Vector = position - self.uv: mathutils.Vector = uv - self.stOffset: tuple(int, int) | None = None - self.color: mathutils.Vector | None = color - self.normal: mathutils.Vector | None = normal + self.position: Vector = position + self.uv: Vector = uv + self.stOffset: Optional[tuple(int, int)] = None + self.rgb: Optional[Vector] = rgb + self.normal: Optional[Vector] = normal + self.alpha: float = alpha def __eq__(self, other): if not isinstance(other, F3DVert): @@ -705,17 +685,50 @@ class F3DVert: self.position == other.position and self.uv == other.uv and self.stOffset == other.stOffset - and self.color == other.color + and self.rgb == other.rgb and self.normal == other.normal + and self.alpha == other.alpha ) - def getColorOrNormal(self): - if self.color is None and self.normal is None: - raise PluginError("An F3D vert has neither a color or a normal.") - elif self.color is not None: - return self.color + def toVtx(self, mesh, texDimensions, transformMatrix, isPointSampled: bool, tex_scale=(1, 1)) -> Vtx: + # Position (8 bytes) + position = [int(round(floatValue)) for floatValue in (transformMatrix @ self.position)] + + # UV (4 bytes) + # For F3D, Bilinear samples the point from the center of the pixel. + # However, Point samples from the corner. + # Thus we add 0.5 to the UV only if bilinear filtering. + # see section 13.7.5.3 in programming manual. + pixelOffset = ( + (0, 0) + if (isPointSampled or tex_scale[0] == 0 or tex_scale[1] == 0) + else (0.5 / tex_scale[0], 0.5 / tex_scale[1]) + ) + pixelOffset = self.stOffset if self.stOffset is not None else pixelOffset + + uv = [ + convertFloatToFixed16(self.uv[0] * texDimensions[0] - pixelOffset[0]), + convertFloatToFixed16(self.uv[1] * texDimensions[1] - pixelOffset[1]), + ] + + packedNormal = 0 + if self.normal is not None: + # normal transformed correctly. + normal = (transformMatrix.inverted().transposed() @ self.normal).normalized() + if self.rgb is not None: + packedNormal = packNormal(normal) + + if self.rgb is not None: + colorOrNormal = [scaleToU8(c).to_bytes(1, "big")[0] for c in self.rgb] else: - return self.normal + colorOrNormal = [ + int(round(normal[0] * 127)).to_bytes(1, "big", signed=True)[0], + int(round(normal[1] * 127)).to_bytes(1, "big", signed=True)[0], + int(round(normal[2] * 127)).to_bytes(1, "big", signed=True)[0], + ] + colorOrNormal.append(scaleToU8(self.alpha).to_bytes(1, "big")[0]) + + return Vtx(position, uv, colorOrNormal, packedNormal) # groupIndex is either a vertex group (writing), or name of c variable identifying a transform group, like a limb (parsing) @@ -777,8 +790,7 @@ class TriangleConverter: texDimensions: tuple[int, int], material: bpy.types.Material, currentGroupIndex, - triList, - vtxList, + triGroup: FTriGroup, existingVertexData: list[BufferVertex], existingVertexMaterialRegions, ): @@ -794,15 +806,15 @@ class TriangleConverter: self.bufferStart = len(self.vertBuffer) self.vertexBufferTriangles = [] # [(index0, index1, index2)] - self.triList = triList - self.vtxList = vtxList + self.triGroup = triGroup + self.triList = triGroup.triList + self.vtxList = triGroup.vertexList - exportVertexColors = isLightingDisabled(material) + self.material = material uv_data = triConverterInfo.obj.data.uv_layers["UVMap"].data - self.convertInfo = LoopConvertInfo(uv_data, triConverterInfo.obj, exportVertexColors) + self.convertInfo = LoopConvertInfo(uv_data, triConverterInfo.obj, material) self.texDimensions = texDimensions self.isPointSampled = isTexturePointSampled(material) - self.exportVertexColors = exportVertexColors self.tex_scale = material.f3d_mat.tex_scale def vertInBuffer(self, bufferVert, material_index): @@ -843,16 +855,11 @@ class TriangleConverter: # Save vertices for bufferVert in self.vertBuffer[bufferStart:bufferEnd]: self.vtxList.vertices.append( - convertVertexData( + bufferVert.f3dVert.toVtx( self.triConverterInfo.mesh, - bufferVert.f3dVert.position, - bufferVert.f3dVert.uv, - bufferVert.f3dVert.stOffset, - bufferVert.f3dVert.getColorOrNormal(), self.texDimensions, self.triConverterInfo.getTransformMatrix(bufferVert.groupIndex), self.isPointSampled, - self.exportVertexColors, tex_scale=self.tex_scale, ) ) @@ -878,16 +885,11 @@ class TriangleConverter: # Save vertices for bufferVert in self.vertBuffer[bufferStart:bufferEnd]: self.vtxList.vertices.append( - convertVertexData( + bufferVert.f3dVert.toVtx( self.triConverterInfo.mesh, - bufferVert.f3dVert.position, - bufferVert.f3dVert.uv, - bufferVert.f3dVert.stOffset, - bufferVert.f3dVert.getColorOrNormal(), self.texDimensions, self.triConverterInfo.getTransformMatrix(bufferVert.groupIndex), self.isPointSampled, - self.exportVertexColors, tex_scale=self.tex_scale, ) ) @@ -895,9 +897,129 @@ class TriangleConverter: bufferStart = bufferEnd # Load triangles - self.triList.commands.extend( - createTriangleCommands(self.vertexBufferTriangles, self.vertBuffer, self.triConverterInfo.f3d.F3DEX_GBI) + triCmds = createTriangleCommands( + self.vertexBufferTriangles, self.vertBuffer, not self.triConverterInfo.f3d.F3D_OLD_GBI ) + if not self.material.f3d_mat.use_cel_shading: + self.triList.commands.extend(triCmds) + else: + if len(triCmds) <= 2: + self.writeCelLevels(triCmds = triCmds) + else: + celTriList = self.triGroup.add_cel_tri_list() + celTriList.commands.extend(triCmds) + celTriList.commands.append(SPEndDisplayList()) + self.writeCelLevels(celTriList = celTriList) + + def writeCelLevels(self, celTriList: Optional[GfxList] = None, triCmds: Optional[List[GbiMacro]] = None) -> None: + assert (celTriList == None) != (triCmds == None) + f3dMat = self.material.f3d_mat + cel = f3dMat.cel_shading + f3d = get_F3D_GBI() + + # Don't want to have to change back and forth arbitrarily between decal and + # opaque mode. So if you're using both lighter and darker, need to do those + # first before switching to decal. + if getZMode(self.material) != "ZMODE_OPA": + raise PluginError(f"Material {self.material.name} with cel shading: zmode in blender / rendermode must be opaque.", icon = "ERROR") + wroteLighter = wroteDarker = usesDecal = False + if len(cel.levels) == 0: + raise PluginError(f"Material {self.material.name} with cel shading has no cel levels") + for level in cel.levels: + if level.threshMode == "Darker": + if wroteDarker: + usesDecal = True + elif usesDecal: + raise PluginError(f"Material {self.material.name}: must use Lighter and Darker cel levels before duplicating either of them") + wroteDarker = True + else: + if wroteLighter: + usesDecal = True + elif usesDecal: + raise PluginError(f"Material {self.material.name}: must use Lighter and Darker cel levels before duplicating either of them") + wroteLighter = True + + # Because this might not be the first tri list in the object with this + # material, we have to set things even if they were set up already in + # the material. + wroteLighter = wroteDarker = wroteOpaque = wroteDecal = False + lastDarker = None + for level in cel.levels: + darker = level.threshMode == "Darker" + self.triList.commands.append(DPPipeSync()) + if usesDecal: + if not wroteOpaque: + wroteOpaque = True + self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", + 10, 2, ["ZMODE_OPA"])) + if not wroteDecal and (darker and wroteDarker or not darker and wroteLighter): + wroteDecal = True + self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", + 10, 2, ["ZMODE_DEC"])) + if darker: + wroteDarker = True + else: + wroteLighter = True + + if lastDarker != darker: + lastDarker = darker + # Set up CC. + ccSettings = [] + for prop in ["A", "B", "C", "D"]: + ccSettings.append(getattr(f3dMat.combiner1, prop)) + ccSettings.extend(["1", "SHADE"] if darker else ["SHADE", "0"]) + ccSettings.extend([cel.cutoutSource, "0"]) + if f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE": + for prop in ["A", "B", "C", "D", "A_alpha", "B_alpha", "C_alpha", "D_alpha"]: + ccSettings.append(getattr(f3dMat.combiner2, prop)) + else: + ccSettings *= 2 + self.triList.commands.append(DPSetCombineMode(*ccSettings)) + + # Set up tint color and level + if level.tintType == "Fixed": + color = exportColor(level.tintFixedColor) + if cel.tintPipeline == "CC": + self.triList.commands.append(DPSetPrimColor(0, 0, color[0], + color[1], color[2], level.tintFixedLevel)) + else: + self.triList.commands.append(DPSetFogColor(color[0], + color[1], color[2], level.tintFixedLevel)) + elif level.tintType == "Segment": + self.triList.commands.append(SPDisplayList(GfxList( + f"{level.tintSegmentNum:#04x}{level.tintSegmentOffset * 8:06x}", + GfxListTag.Material, + DLFormat.Static + ))) + elif level.tintType == "Light": + if cel.tintPipeline == "CC": + self.triList.commands.append(SPLightToPrimColor( + level.tintLightSlot, level.tintFixedLevel, 0, 0 + )) + else: + self.triList.commands.append(SPLightToFogColor( + level.tintLightSlot, level.tintFixedLevel + )) + else: + raise PluginError("Unknown tint type") + + # Set up threshold + self.triList.commands.append(DPSetBlendColor(255, 255, 255, + 0x100 - level.threshold if darker else level.threshold)) + self.triList.commands.append(SPAlphaCompareCull( + "G_ALPHA_COMPARE_CULL_ABOVE" if darker else + "G_ALPHA_COMPARE_CULL_BELOW", + level.threshold)) + + # Draw tris, inline or by call + if triCmds is not None: + self.triList.commands.extend(triCmds) + else: + self.triList.commands.append(SPDisplayList(celTriList)) + + # Disable alpha compare culling for future DLs + self.triList.commands.append(SPAlphaCompareCull( + "G_ALPHA_COMPARE_CULL_DISABLE", 0)) def addFace(self, face, stOffset): triIndices = [] @@ -943,179 +1065,35 @@ class TriangleConverter: def getF3DVert(loop: bpy.types.MeshLoop, face, convertInfo: LoopConvertInfo, mesh: bpy.types.Mesh): - position: mathutils.Vector = mesh.vertices[loop.vertex_index].co.copy().freeze() + position: Vector = mesh.vertices[loop.vertex_index].co.copy().freeze() # N64 is -Y, Blender is +Y - uv: mathutils.Vector = convertInfo.uv_data[loop.index].uv.copy() + uv: Vector = convertInfo.uv_data[loop.index].uv.copy() uv[:] = [field if not math.isnan(field) else 0 for field in uv] uv[1] = 1 - uv[1] uv = uv.freeze() - color, normal = getLoopColorOrNormal( - loop, face, convertInfo.obj.data, convertInfo.obj, convertInfo.exportVertexColors - ) - return F3DVert(position, uv, color, normal) + has_rgb, has_normal, _ = getRgbNormalSettings(convertInfo.material.f3d_mat) + mesh = convertInfo.obj.data + color = getLoopColor(loop, mesh) + rgb = color[:3] if has_rgb else None + normal = getLoopNormal(loop) if has_normal else None + alpha = color[3] + + return F3DVert(position, uv, rgb, normal, alpha) -def getLoopNormal(loop: bpy.types.MeshLoop, face, mesh, isFlatShaded): - # This is a workaround for flat shading not working well. - # Since we support custom blender normals we can now ignore this. - # if isFlatShaded: - # normal = -face.normal #??? - # else: - # normal = -loop.normal #??? - # return get8bitRoundedNormal(normal).freeze() - return get8bitRoundedNormal(loop, mesh).freeze() - - -""" -def getLoopNormalCreased(bLoop, obj): - edges = obj.data.edges - centerVert = bLoop.vert - - availableFaces = [] - visitedFaces = [bLoop.face] - connectedFaces = getConnectedFaces(bLoop.face, bLoop.vert) - if len(connectedFaces) == 0: - return bLoop.calc_normal() - - for face in connectedFaces: - availableFaces.append(FaceWeight(face, bLoop.face, 1)) - - curNormal = bLoop.calc_normal() * bLoop.calc_angle() - while len(availableFaces) > 0: - nextFaceWeight = getHighestFaceWeight(availableFaces) - curNormal += getWeightedNormalAndMoveToNextFace( - nextFaceWeight, visitedFaces, availableFaces, centerVert, edges) - - return curNormal.normalized() - -def getConnectedFaces(bFace, bVert): - connectedFaces = [] - for face in bVert.link_faces: - if face == bFace: - continue - for edge in face.edges: - if bFace in edge.link_faces: - connectedFaces.append(face) - return connectedFaces - -# returns false if not enough faces to check for creasing -def getNextFace(faceWeight, bVert, visitedFaces, availableFaces): - connectedFaces = getConnectedFaces(faceWeight.face, bVert) - visitedFaces.append(faceWeight.face) - - newFaceFound = False - nextPrevFace = faceWeight.face - for face in connectedFaces: - if face in visitedFaces: - continue - elif not newFaceFound: - newFaceFound = True - faceWeight.prevFace = faceWeight.face - faceWeight.face = face - else: - availableFaces.append(FaceWeight(face, nextPrevFace, - faceWeight.weight)) - - if not newFaceFound: - availableFaces.remove(faceWeight) - - removedFaceWeights = [] - for otherFaceWeight in availableFaces: - if otherFaceWeight.face in visitedFaces: - removedFaceWeights.append(otherFaceWeight) - for removedFaceWeight in removedFaceWeights: - availableFaces.remove(removedFaceWeight) - -def getLoopFromFaceVert(bFace, bVert): - for loop in bFace.loops: - if loop.vert == bVert: - return loop - return None - -def getEdgeBetweenFaces(faceWeight): - face1 = faceWeight.face - face2 = faceWeight.prevFace - for edge1 in face1.edges: - for edge2 in face2.edges: - if edge1 == edge2: - return edge1 - return None - -class FaceWeight: - def __init__(self, face, prevFace, weight): - self.face = face - self.prevFace = prevFace - self.weight = weight - -def getWeightedNormalAndMoveToNextFace(selectFaceWeight, visitedFaces, - availableFaces, centerVert, edges): - selectLoop = getLoopFromFaceVert(selectFaceWeight.face, centerVert) - edgeIndex = getEdgeBetweenFaces(selectFaceWeight).index - - # Ignore triangulated faces - if edgeIndex < len(edges): - selectFaceWeight.weight *= 1 - edges[edgeIndex].crease - - getNextFace(selectFaceWeight, centerVert, visitedFaces, availableFaces) - return selectLoop.calc_normal() * selectLoop.calc_angle() * \ - selectFaceWeight.weight - -def getHighestFaceWeight(faceWeights): - highestFaceWeight = faceWeights[0] - for faceWeight in faceWeights[1:]: - if faceWeight.weight > highestFaceWeight.weight: - highestFaceWeight = faceWeight - return highestFaceWeight -""" - - -def convertVertexData( - mesh, - loopPos, - loopUV, - stOffset, - loopColorOrNormal, - texDimensions, - transformMatrix, - isPointSampled, - exportVertexColors, - tex_scale=(1, 1), -): - # Position (8 bytes) - position = [int(round(floatValue)) for floatValue in (transformMatrix @ loopPos)] - - # UV (4 bytes) - # For F3D, Bilinear samples the point from the center of the pixel. - # However, Point samples from the corner. - # Thus we add 0.5 to the UV only if bilinear filtering. - # see section 13.7.5.3 in programming manual. - pixelOffset = ( - (0, 0) - if (isPointSampled or tex_scale[0] == 0 or tex_scale[1] == 0) - else (0.5 / tex_scale[0], 0.5 / tex_scale[1]) - ) - pixelOffset = stOffset if stOffset is not None else pixelOffset - - uv = [ - convertFloatToFixed16(loopUV[0] * texDimensions[0] - pixelOffset[0]), - convertFloatToFixed16(loopUV[1] * texDimensions[1] - pixelOffset[1]), - ] - - # Color/Normal (4 bytes) - if exportVertexColors: - colorOrNormal = [scaleToU8(c).to_bytes(1, "big")[0] for c in loopColorOrNormal] - else: - # normal transformed correctly. - normal = (transformMatrix.inverted().transposed() @ loopColorOrNormal).normalized() - colorOrNormal = [ - int(round(normal[0] * 127)).to_bytes(1, "big", signed=True)[0], - int(round(normal[1] * 127)).to_bytes(1, "big", signed=True)[0], - int(round(normal[2] * 127)).to_bytes(1, "big", signed=True)[0], - scaleToU8(loopColorOrNormal[3]).to_bytes(1, "big")[0], - ] - - return Vtx(position, uv, colorOrNormal) +def getLoopNormal(loop: bpy.types.MeshLoop) -> Vector: + # Have to quantize to something because F3DVerts will be compared, and we + # don't want floating-point inaccuracy causing "same" vertices not to be + # merged. But, it hasn't been transformed yet, so quantizing to s8 here will + # lose some accuracy. + return Vector( + ( + round(loop.normal[0] * 2**16) / 2**16, + round(loop.normal[1] * 2**16) / 2**16, + round(loop.normal[2] * 2**16) / 2**16, + ) + ).freeze() @functools.lru_cache(0) @@ -1123,8 +1101,7 @@ def is3_2_or_above(): return bpy.app.version[0] >= 3 and bpy.app.version[1] >= 2 -def getLoopColor(loop: bpy.types.MeshLoop, mesh, mat_ver): - +def getLoopColor(loop: bpy.types.MeshLoop, mesh: bpy.types.Mesh) -> Vector: color_layer = getColorLayer(mesh, layer="Col") alpha_layer = getColorLayer(mesh, layer="Alpha") @@ -1146,57 +1123,22 @@ def getLoopColor(loop: bpy.types.MeshLoop, mesh, mat_ver): return mathutils.Vector((normalizedRGB[0], normalizedRGB[1], normalizedRGB[2], normalizedA)) -def getLoopColorOrNormal( - loop: bpy.types.MeshLoop, face, mesh: bpy.types.Mesh, obj: bpy.types.Object, exportVertexColors: bool -) -> tuple[mathutils.Vector, None] | tuple[None, mathutils.Vector]: - material = obj.material_slots[face.material_index].material - isFlatShaded = checkIfFlatShaded(material) - if exportVertexColors: - return getLoopColor(loop, mesh, material.mat_ver), None - else: - return None, getLoopNormal(loop, face, mesh, isFlatShaded) - - def createTriangleCommands(triangles, vertexBuffer, useSP2Triangle): triangles = copy.deepcopy(triangles) commands = [] - if useSP2Triangle: - while len(triangles) > 0: - if len(triangles) >= 2: - commands.append( - SP2Triangles( - vertexBuffer.index(triangles[0][0]), - vertexBuffer.index(triangles[0][1]), - vertexBuffer.index(triangles[0][2]), - 0, - vertexBuffer.index(triangles[1][0]), - vertexBuffer.index(triangles[1][1]), - vertexBuffer.index(triangles[1][2]), - 0, - ) - ) - triangles = triangles[2:] - else: - commands.append( - SP1Triangle( - vertexBuffer.index(triangles[0][0]), - vertexBuffer.index(triangles[0][1]), - vertexBuffer.index(triangles[0][2]), - 0, - ) - ) - triangles = [] - else: - while len(triangles) > 0: - commands.append( - SP1Triangle( - vertexBuffer.index(triangles[0][0]), - vertexBuffer.index(triangles[0][1]), - vertexBuffer.index(triangles[0][2]), - 0, - ) - ) - triangles = triangles[1:] + + def getIndices(tri): + return [vertexBuffer.index(v) for v in tri] + + t = 0 + while t < len(triangles): + firstTriIndices = getIndices(triangles[t]) + t += 1 + if useSP2Triangle and t < len(triangles): + commands.append(SP2Triangles(*firstTriIndices, 0, *getIndices(triangles[t]), 0)) + t += 1 + else: + commands.append(SP1Triangle(*firstTriIndices, 0)) return commands @@ -1321,6 +1263,33 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData): ) ) + if f3dMat.set_ao: + fMaterial.mat_only_DL.commands.append( + SPAmbOcclusion( + min(round(f3dMat.ao_ambient * 2**16), 0xFFFF), + min(round(f3dMat.ao_directional * 2**16), 0xFFFF), + min(round(f3dMat.ao_point * 2**16), 0xFFFF), + ) + ) + + if f3dMat.set_fresnel: + dotMin = round(f3dMat.fresnel_lo * 0x7FFF) + dotMax = round(f3dMat.fresnel_hi * 0x7FFF) + scale = max(min(0x3F8000 // (dotMax - dotMin), 0x7FFF), -0x8000) + offset = max(min(-(0x7F * dotMin) // (dotMax - dotMin), 0x7FFF), -0x8000) + fMaterial.mat_only_DL.commands.append(SPFresnel(scale, offset)) + + if f3dMat.set_attroffs_st: + fMaterial.mat_only_DL.commands.append( + SPAttrOffsetST( + to_s16(f3dMat.attroffs_st[0] * 32), + to_s16(f3dMat.attroffs_st[1] * 32), + ) + ) + + if f3dMat.set_attroffs_z: + fMaterial.mat_only_DL.commands.append(SPAttrOffsetZ(f3dMat.attroffs_z)) + if f3dMat.set_fog: if f3dMat.use_global_fog and fModel.global_data.getCurrentAreaData() is not None: fogData = fModel.global_data.getCurrentAreaData().fog_data @@ -1357,9 +1326,8 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData): f3dMat.rdp_settings, multitexManager.getTT(), defaults, - fModel.f3d._HW_VERSION_1, fModel.matWriteMethod, - fModel.f3d + fModel.f3d, ) saveOtherModeLDefinition(fMaterial, f3dMat.rdp_settings, defaults, defaultRM, fModel.matWriteMethod, fModel.f3d) saveOtherDefinition(fMaterial, f3dMat, defaults) @@ -1466,11 +1434,11 @@ def saveLightsDefinition(fModel, fMaterial, material, lightsName): if lights is not None: return lights - lights = Lights(toAlnum(lightsName)) + lights = Lights(toAlnum(lightsName), fModel.f3d) if material.use_default_lighting: lights.a = Ambient(exportColor(material.ambient_light_color)) - lights.l.append(Light(exportColor(material.default_light_color), [0x28, 0x28, 0x28])) + lights.l.append(Light(exportColor(material.default_light_color), [0x49, 0x49, 0x49])) else: lights.a = Ambient(exportColor(material.ambient_light_color)) @@ -1513,21 +1481,41 @@ def saveBitGeoF3DEX2(value, defaultValue, flagName, geo, matWriteMethod): geo.clearFlagList.append(flagName) +def saveBitGeo(value, defaultValue, flagName, setGeo, clearGeo, matWriteMethod): + if value != defaultValue or matWriteMethod == GfxMatWriteMethod.WriteAll: + if value: + setGeo.flagList.append(flagName) + else: + clearGeo.flagList.append(flagName) + + +def saveGeoModeCommon(saveFunc: Callable, settings: RDPSettings, defaults: RDPSettings, args: Any): + saveFunc(settings.g_zbuffer, defaults.g_zbuffer, "G_ZBUFFER", *args) + saveFunc(settings.g_shade, defaults.g_shade, "G_SHADE", *args) + saveFunc(settings.g_cull_front, defaults.g_cull_front, "G_CULL_FRONT", *args) + saveFunc(settings.g_cull_back, defaults.g_cull_back, "G_CULL_BACK", *args) + if bpy.context.scene.f3d_type == "F3DEX3": + saveFunc(settings.g_ambocclusion, defaults.g_ambocclusion, "G_AMBOCCLUSION", *args) + saveFunc(settings.g_attroffset_z_enable, defaults.g_attroffset_z_enable, "G_ATTROFFSET_Z_ENABLE", *args) + saveFunc(settings.g_attroffset_st_enable, defaults.g_attroffset_st_enable, "G_ATTROFFSET_ST_ENABLE", *args) + saveFunc(settings.g_packed_normals, defaults.g_packed_normals, "G_PACKED_NORMALS", *args) + saveFunc(settings.g_lighttoalpha, defaults.g_lighttoalpha, "G_LIGHTTOALPHA", *args) + saveFunc(settings.g_lighting_specular, defaults.g_lighting_specular, "G_LIGHTING_SPECULAR", *args) + saveFunc(settings.g_fresnel_color, defaults.g_fresnel_color, "G_FRESNEL_COLOR", *args) + saveFunc(settings.g_fresnel_alpha, defaults.g_fresnel_alpha, "G_FRESNEL_ALPHA", *args) + saveFunc(settings.g_fog, defaults.g_fog, "G_FOG", *args) + saveFunc(settings.g_lighting, defaults.g_lighting, "G_LIGHTING", *args) + saveFunc(settings.g_tex_gen, defaults.g_tex_gen, "G_TEXTURE_GEN", *args) + saveFunc(settings.g_tex_gen_linear, defaults.g_tex_gen_linear, "G_TEXTURE_GEN_LINEAR", *args) + saveFunc(settings.g_lod, defaults.g_lod, "G_LOD", *args) + saveFunc(settings.g_shade_smooth, defaults.g_shade_smooth, "G_SHADING_SMOOTH", *args) + if isUcodeF3DEX1(bpy.context.scene.f3d_type): + saveFunc(settings.g_clipping, defaults.g_clipping, "G_CLIPPING", *args) + + def saveGeoModeDefinitionF3DEX2(fMaterial, settings, defaults, matWriteMethod): geo = SPGeometryMode([], []) - - saveBitGeoF3DEX2(settings.g_zbuffer, defaults.g_zbuffer, "G_ZBUFFER", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_shade, defaults.g_shade, "G_SHADE", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_cull_front, defaults.g_cull_front, "G_CULL_FRONT", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_cull_back, defaults.g_cull_back, "G_CULL_BACK", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_fog, defaults.g_fog, "G_FOG", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_lighting, defaults.g_lighting, "G_LIGHTING", geo, matWriteMethod) - - # make sure normals are saved correctly. - saveBitGeoF3DEX2(settings.g_tex_gen, defaults.g_tex_gen, "G_TEXTURE_GEN", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_tex_gen_linear, defaults.g_tex_gen_linear, "G_TEXTURE_GEN_LINEAR", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_shade_smooth, defaults.g_shade_smooth, "G_SHADING_SMOOTH", geo, matWriteMethod) - saveBitGeoF3DEX2(settings.g_clipping, defaults.g_clipping, "G_CLIPPING", geo, matWriteMethod) + saveGeoModeCommon(saveBitGeoF3DEX2, settings, defaults, (geo, matWriteMethod)) if len(geo.clearFlagList) != 0 or len(geo.setFlagList) != 0: if len(geo.clearFlagList) == 0: @@ -1542,33 +1530,11 @@ def saveGeoModeDefinitionF3DEX2(fMaterial, settings, defaults, matWriteMethod): fMaterial.revert.commands.append(SPGeometryMode(geo.setFlagList, geo.clearFlagList)) -def saveBitGeo(value, defaultValue, flagName, setGeo, clearGeo, matWriteMethod): - if value != defaultValue or matWriteMethod == GfxMatWriteMethod.WriteAll: - if value: - setGeo.flagList.append(flagName) - else: - clearGeo.flagList.append(flagName) - - def saveGeoModeDefinition(fMaterial, settings, defaults, matWriteMethod): setGeo = SPSetGeometryMode([]) clearGeo = SPClearGeometryMode([]) - saveBitGeo(settings.g_zbuffer, defaults.g_zbuffer, "G_ZBUFFER", setGeo, clearGeo, matWriteMethod) - saveBitGeo(settings.g_shade, defaults.g_shade, "G_SHADE", setGeo, clearGeo, matWriteMethod) - saveBitGeo(settings.g_cull_front, defaults.g_cull_front, "G_CULL_FRONT", setGeo, clearGeo, matWriteMethod) - saveBitGeo(settings.g_cull_back, defaults.g_cull_back, "G_CULL_BACK", setGeo, clearGeo, matWriteMethod) - saveBitGeo(settings.g_fog, defaults.g_fog, "G_FOG", setGeo, clearGeo, matWriteMethod) - saveBitGeo(settings.g_lighting, defaults.g_lighting, "G_LIGHTING", setGeo, clearGeo, matWriteMethod) - - # make sure normals are saved correctly. - saveBitGeo(settings.g_tex_gen, defaults.g_tex_gen, "G_TEXTURE_GEN", setGeo, clearGeo, matWriteMethod) - saveBitGeo( - settings.g_tex_gen_linear, defaults.g_tex_gen_linear, "G_TEXTURE_GEN_LINEAR", setGeo, clearGeo, matWriteMethod - ) - saveBitGeo(settings.g_shade_smooth, defaults.g_shade_smooth, "G_SHADING_SMOOTH", setGeo, clearGeo, matWriteMethod) - if bpy.context.scene.f3d_type == "F3DEX_GBI_2" or bpy.context.scene.f3d_type == "F3DEX_GBI": - saveBitGeo(settings.g_clipping, defaults.g_clipping, "G_CLIPPING", setGeo, clearGeo, matWriteMethod) + saveGeoModeCommon(saveBitGeo, settings, defaults, (setGeo, clearGeo, matWriteMethod)) if len(setGeo.flagList) > 0: fMaterial.mat_only_DL.commands.append(setGeo) @@ -1586,22 +1552,20 @@ def saveModeSetting(fMaterial, value, defaultValue, cmdClass): fMaterial.revert.commands.append(cmdClass(defaultValue)) -def saveOtherModeHDefinition(fMaterial, settings, tlut, defaults, isHWv1, matWriteMethod, f3d): +def saveOtherModeHDefinition(fMaterial, settings, tlut, defaults, matWriteMethod, f3d): if matWriteMethod == GfxMatWriteMethod.WriteAll: - saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, isHWv1, f3d) + saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, f3d) elif matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert: - saveOtherModeHDefinitionIndividual(fMaterial, settings, tlut, defaults, isHWv1) + saveOtherModeHDefinitionIndividual(fMaterial, settings, tlut, defaults) else: raise PluginError("Unhandled material write method: " + str(matWriteMethod)) -def saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, isHWv1, f3d): - is_f3d_old = all((not f3d.F3DEX_GBI, not f3d.F3DEX_GBI_2, not f3d.F3DLP_GBI)) - cmd = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - is_f3d_old, []) +def saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, f3d): + cmd = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - f3d.F3D_OLD_GBI, []) cmd.flagList.append(settings.g_mdsft_alpha_dither) - if not isHWv1: - cmd.flagList.append(settings.g_mdsft_rgb_dither) - cmd.flagList.append(settings.g_mdsft_combkey) + cmd.flagList.append(settings.g_mdsft_rgb_dither) + cmd.flagList.append(settings.g_mdsft_combkey) cmd.flagList.append(settings.g_mdsft_textconv) cmd.flagList.append(settings.g_mdsft_text_filt) cmd.flagList.append(tlut) @@ -1609,96 +1573,55 @@ def saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, isHWv1, f3d cmd.flagList.append(settings.g_mdsft_textdetail) cmd.flagList.append(settings.g_mdsft_textpersp) cmd.flagList.append(settings.g_mdsft_cycletype) - if isHWv1: - cmd.flagList.append(settings.g_mdsft_color_dither) cmd.flagList.append(settings.g_mdsft_pipeline) fMaterial.mat_only_DL.commands.append(cmd) -def saveOtherModeHDefinitionIndividual(fMaterial, settings, tlut, defaults, isHWv1): +def saveOtherModeHDefinitionIndividual(fMaterial, settings, tlut, defaults): saveModeSetting(fMaterial, settings.g_mdsft_alpha_dither, defaults.g_mdsft_alpha_dither, DPSetAlphaDither) - - if not isHWv1: - saveModeSetting(fMaterial, settings.g_mdsft_rgb_dither, defaults.g_mdsft_rgb_dither, DPSetColorDither) - - saveModeSetting(fMaterial, settings.g_mdsft_combkey, defaults.g_mdsft_combkey, DPSetCombineKey) - + saveModeSetting(fMaterial, settings.g_mdsft_rgb_dither, defaults.g_mdsft_rgb_dither, DPSetColorDither) + saveModeSetting(fMaterial, settings.g_mdsft_combkey, defaults.g_mdsft_combkey, DPSetCombineKey) saveModeSetting(fMaterial, settings.g_mdsft_textconv, defaults.g_mdsft_textconv, DPSetTextureConvert) - saveModeSetting(fMaterial, settings.g_mdsft_text_filt, defaults.g_mdsft_text_filt, DPSetTextureFilter) - saveModeSetting(fMaterial, tlut, "G_TT_NONE", DPSetTextureLUT) - saveModeSetting(fMaterial, settings.g_mdsft_textlod, defaults.g_mdsft_textlod, DPSetTextureLOD) - saveModeSetting(fMaterial, settings.g_mdsft_textdetail, defaults.g_mdsft_textdetail, DPSetTextureDetail) - saveModeSetting(fMaterial, settings.g_mdsft_textpersp, defaults.g_mdsft_textpersp, DPSetTexturePersp) - saveModeSetting(fMaterial, settings.g_mdsft_cycletype, defaults.g_mdsft_cycletype, DPSetCycleType) - - if isHWv1: - saveModeSetting(fMaterial, settings.g_mdsft_color_dither, defaults.g_mdsft_color_dither, DPSetColorDither) - saveModeSetting(fMaterial, settings.g_mdsft_pipeline, defaults.g_mdsft_pipeline, DPPipelineMode) def saveOtherModeLDefinition(fMaterial, settings, defaults, defaultRenderMode, matWriteMethod, f3d): if matWriteMethod == GfxMatWriteMethod.WriteAll: - saveOtherModeLDefinitionAll(fMaterial, settings, defaults, defaultRenderMode, f3d) + saveOtherModeLDefinitionAll(fMaterial, settings, defaults, f3d) elif matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert: saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRenderMode) else: raise PluginError("Unhandled material write method: " + str(matWriteMethod)) -def saveOtherModeLDefinitionAll(fMaterial: FMaterial, settings, defaults, defaultRenderMode, f3d): - is_f3d_old = all((not f3d.F3DEX_GBI, not f3d.F3DEX_GBI_2, not f3d.F3DLP_GBI)) - if not settings.set_rendermode: - cmd = SPSetOtherMode("G_SETOTHERMODE_L", 0, 3 - is_f3d_old, []) - cmd.flagList.append(settings.g_mdsft_alpha_compare) - cmd.flagList.append(settings.g_mdsft_zsrcsel) +def saveOtherModeLDefinitionAll(fMaterial: FMaterial, settings, defaults, f3d): + baseLength = 3 if not settings.set_rendermode else 32 + cmd = SPSetOtherMode("G_SETOTHERMODE_L", 0, baseLength - f3d.F3D_OLD_GBI, []) + cmd.flagList.append(settings.g_mdsft_alpha_compare) + cmd.flagList.append(settings.g_mdsft_zsrcsel) - else: - cmd = SPSetOtherMode("G_SETOTHERMODE_L", 0, 32 - is_f3d_old, []) - cmd.flagList.append(settings.g_mdsft_alpha_compare) - cmd.flagList.append(settings.g_mdsft_zsrcsel) - - if settings.set_rendermode: - flagList, blendList = getRenderModeFlagList(settings, fMaterial) - cmd.flagList.extend(flagList) - if blendList is not None: - cmd.flagList.extend( - [ - "GBL_c1(" - + blendList[0] - + ", " - + blendList[1] - + ", " - + blendList[2] - + ", " - + blendList[3] - + ")", - "GBL_c2(" - + blendList[4] - + ", " - + blendList[5] - + ", " - + blendList[6] - + ", " - + blendList[7] - + ")", - ] - ) - else: - cmd.flagList.extend(defaultRenderMode) + if settings.set_rendermode: + flagList, blendList = getRenderModeFlagList(settings, fMaterial) + cmd.flagList.extend(flagList) + if blendList is not None: + cmd.flagList.extend( + [ + "GBL_c1(" + blendList[0] + ", " + blendList[1] + ", " + blendList[2] + ", " + blendList[3] + ")", + "GBL_c2(" + blendList[4] + ", " + blendList[5] + ", " + blendList[6] + ", " + blendList[7] + ")", + ] + ) fMaterial.mat_only_DL.commands.append(cmd) if settings.g_mdsft_zsrcsel == "G_ZS_PRIM": fMaterial.mat_only_DL.commands.append(DPSetPrimDepth(z=settings.prim_depth.z, dz=settings.prim_depth.dz)) - fMaterial.revert.commands.append(DPSetPrimDepth()) def saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRenderMode): @@ -1817,12 +1740,9 @@ def getWriteMethodFromEnum(enumVal): return matWriteMethodEnumDict[enumVal] -def exportF3DtoC( - dirPath, obj, DLFormat, transformMatrix, f3dType, isHWv1, texDir, savePNG, texSeparate, name, matWriteMethod -): - +def exportF3DtoC(dirPath, obj, DLFormat, transformMatrix, texDir, savePNG, texSeparate, name, matWriteMethod): inline = bpy.context.scene.exportInlineF3D - fModel = FModel(f3dType, isHWv1, name, DLFormat, matWriteMethod if not inline else GfxMatWriteMethod.WriteAll) + fModel = FModel(name, DLFormat, matWriteMethod if not inline else GfxMatWriteMethod.WriteAll) fMeshes = exportF3DCommon(obj, fModel, transformMatrix, True, name, DLFormat, not savePNG) if inline: @@ -1917,8 +1837,6 @@ class F3D_ExportDL(bpy.types.Operator): exportPath = bpy.path.abspath(context.scene.DLExportPath) dlFormat = DLFormat.Static if context.scene.DLExportisStatic else DLFormat.Dynamic - f3dType = context.scene.f3d_type - isHWv1 = context.scene.isHWv1 texDir = bpy.context.scene.DLTexDir savePNG = bpy.context.scene.saveTextures separateTexDef = bpy.context.scene.DLSeparateTextureDef @@ -1930,8 +1848,6 @@ class F3D_ExportDL(bpy.types.Operator): obj, dlFormat, finalTransform, - f3dType, - isHWv1, texDir, savePNG, separateTexDef, diff --git a/fast64_internal/mm/data/xml/ActorList.xml b/fast64_internal/mm/data/xml/ActorList.xml new file mode 100644 index 0000000..11e2868 --- /dev/null +++ b/fast64_internal/mm/data/xml/ActorList.xml @@ -0,0 +1,928 @@ + + + +
@@ -171,3 +179,9 @@ Note that you can have the actual address of your cutscene if you use ``sym_info
- Result: ``Symbol gHyruleFieldIntroCs (RAM: 0x02013AA0, ROM: 0x27E9AA0, build/assets/scenes/overworld/spot00/spot00_scene.o)``
If you have a softlock in-game then you probably did something wrong when creating the cutscene. Make sure you set up the bones properly. The softlock means the game is playing a cutscene but it's probably reading wrong data. Make sure the cutscene is exported, if it's not export it again.
+
+If the camera preview in Blender isn't following where you have the bones or if the cutscene sort of works in-game but the positions are all wrong:
+
+1. Make sure your scene empty object, room empty object, and cutscene empty object are all at the Blender origin. You can usually do this with a combination of Object > Clear > Origin and Alt+G. Maybe Object > Apply > All Transforms if that doesn't work. If your room empty object is 1 meter below your scene empty object, as fast64 does by default, that offset will be applied to everything in game and then the zcamedit stuff will not be at the correct relative position.
+
+2. If you moved / rotated / etc. one of the camera shots / armatures in object mode, this transformation will be ignored. You can fix this by selecting the shot / armature in object mode and clicking Object > Apply > All Transforms. That will convert the transform to actual changed positions for each bone.
diff --git a/fast64_internal/oot/__init__.py b/fast64_internal/oot/__init__.py
index d9f19d7..ecd7b5f 100644
--- a/fast64_internal/oot/__init__.py
+++ b/fast64_internal/oot/__init__.py
@@ -37,6 +37,12 @@ from .animation.properties import (
from .cutscene.operators import cutscene_ops_register, cutscene_ops_unregister
from .cutscene.properties import cutscene_props_register, cutscene_props_unregister
from .cutscene.panels import cutscene_panels_register, cutscene_panels_unregister
+from .cutscene.preview import cutscene_preview_register, cutscene_preview_unregister
+
+from .cutscene.motion.operators import csMotion_ops_register, csMotion_ops_unregister
+from .cutscene.motion.properties import csMotion_props_register, csMotion_props_unregister
+from .cutscene.motion.panels import csMotion_panels_register, csMotion_panels_unregister
+from .cutscene.motion.preview import csMotion_preview_register, csMotion_preview_unregister
from .skeleton.operators import skeleton_ops_register, skeleton_ops_unregister
from .skeleton.properties import skeleton_props_register, skeleton_props_unregister
@@ -121,6 +127,12 @@ def oot_register(registerPanels):
file_register()
anim_props_register()
+ csMotion_ops_register()
+ csMotion_props_register()
+ csMotion_panels_register()
+ csMotion_preview_register()
+ cutscene_preview_register()
+
for cls in oot_classes:
register_class(cls)
@@ -154,5 +166,11 @@ def oot_unregister(unregisterPanels):
file_unregister()
anim_props_unregister()
+ cutscene_preview_unregister()
+ csMotion_preview_unregister()
+ csMotion_panels_unregister()
+ csMotion_props_unregister()
+ csMotion_ops_unregister()
+
if unregisterPanels:
oot_panel_unregister()
diff --git a/fast64_internal/oot/actor/properties.py b/fast64_internal/oot/actor/properties.py
index 65a6508..50f0a70 100644
--- a/fast64_internal/oot/actor/properties.py
+++ b/fast64_internal/oot/actor/properties.py
@@ -2,7 +2,8 @@ from bpy.types import Object, PropertyGroup, UILayout
from bpy.utils import register_class, unregister_class
from bpy.props import EnumProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty, PointerProperty
from ...utility import prop_split, label_split
-from ..oot_constants import ootData, ootEnumSceneSetupPreset, ootEnumCamTransition
+from ..oot_constants import ootData, ootEnumCamTransition
+from ..oot_upgrade import upgradeActors
from ..scene.properties import OOTAlternateSceneHeaderProperty
from ..room.properties import OOTAlternateRoomHeaderProperty
from .operators import OOT_SearchActorIDEnumOperator
@@ -15,6 +16,12 @@ from ..oot_utility import (
drawEnumWithCustom,
)
+ootEnumSceneSetupPreset = [
+ ("Custom", "Custom", "Custom"),
+ ("All Scene Setups", "All Scene Setups", "All Scene Setups"),
+ ("All Non-Cutscene Scene Setups", "All Non-Cutscene Scene Setups", "All Non-Cutscene Scene Setups"),
+]
+
class OOTActorHeaderItemProperty(PropertyGroup):
headerIndex: IntProperty(name="Scene Setup", min=4, default=4)
@@ -110,6 +117,11 @@ class OOTActorProperty(PropertyGroup):
rotOverrideZ: StringProperty(name="Rot Z", default="0")
headerSettings: PointerProperty(type=OOTActorHeaderProperty)
+ @staticmethod
+ def upgrade_object(obj: Object):
+ print(f"Processing '{obj.name}'...")
+ upgradeActors(obj)
+
def draw_props(self, layout: UILayout, altRoomProp: OOTAlternateRoomHeaderProperty, objName: str):
# prop_split(layout, actorProp, 'actorID', 'Actor')
actorIDBox = layout.column()
@@ -145,15 +157,19 @@ class OOTActorProperty(PropertyGroup):
class OOTTransitionActorProperty(PropertyGroup):
- roomIndex: IntProperty(min=0)
+ fromRoom: PointerProperty(type=Object, poll=lambda self, object: self.isRoomEmptyObject(object))
+ toRoom: PointerProperty(type=Object, poll=lambda self, object: self.isRoomEmptyObject(object))
cameraTransitionFront: EnumProperty(items=ootEnumCamTransition, default="0x00")
cameraTransitionFrontCustom: StringProperty(default="0x00")
cameraTransitionBack: EnumProperty(items=ootEnumCamTransition, default="0x00")
cameraTransitionBackCustom: StringProperty(default="0x00")
- dontTransition: BoolProperty(default=False)
+ isRoomTransition: BoolProperty(name="Is Room Transition", default=True)
actor: PointerProperty(type=OOTActorProperty)
+ def isRoomEmptyObject(self, obj: Object):
+ return obj.type == "EMPTY" and obj.ootEmptyType == "Room"
+
def draw_props(
self, layout: UILayout, altSceneProp: OOTAlternateSceneHeaderProperty, roomObj: Object, objName: str
):
@@ -175,10 +191,12 @@ class OOTTransitionActorProperty(PropertyGroup):
if roomObj is None:
actorIDBox.label(text="This must be part of a Room empty's hierarchy.", icon="OUTLINER")
else:
- actorIDBox.prop(self, "dontTransition")
- if not self.dontTransition:
- label_split(actorIDBox, "Room To Transition From", str(roomObj.ootRoomHeader.roomIndex))
- prop_split(actorIDBox, self, "roomIndex", "Room To Transition To")
+ actorIDBox.prop(self, "isRoomTransition")
+ if self.isRoomTransition:
+ prop_split(actorIDBox, self, "fromRoom", "Room To Transition From")
+ prop_split(actorIDBox, self, "toRoom", "Room To Transition To")
+ if self.fromRoom == self.toRoom:
+ actorIDBox.label(text="Warning: You selected the same room!", icon="ERROR")
actorIDBox.label(text='Y+ side of door faces toward the "from" room.', icon="ORIENTATION_NORMAL")
drawEnumWithCustom(actorIDBox, self, "cameraTransitionFront", "Camera Transition Front", "")
drawEnumWithCustom(actorIDBox, self, "cameraTransitionBack", "Camera Transition Back", "")
@@ -188,23 +206,29 @@ class OOTTransitionActorProperty(PropertyGroup):
class OOTEntranceProperty(PropertyGroup):
- # This is also used in entrance list, and roomIndex is obtained from the room this empty is parented to.
+ # This is also used in entrance list.
spawnIndex: IntProperty(min=0)
customActor: BoolProperty(name="Use Custom Actor")
actor: PointerProperty(type=OOTActorProperty)
+ tiedRoom: PointerProperty(
+ type=Object,
+ poll=lambda self, object: self.isRoomEmptyObject(object),
+ description="Used to set the room index",
+ )
+
+ def isRoomEmptyObject(self, obj: Object):
+ return obj.type == "EMPTY" and obj.ootEmptyType == "Room"
+
def draw_props(self, layout: UILayout, obj: Object, altSceneProp: OOTAlternateSceneHeaderProperty, objName: str):
box = layout.column()
# box.box().label(text = "Properties")
roomObj = getRoomObj(obj)
- if roomObj is not None:
- split = box.split(factor=0.5)
- split.label(text="Room Index")
- split.label(text=str(roomObj.ootRoomHeader.roomIndex))
- else:
+ if roomObj is None:
box.label(text="This must be part of a Room empty's hierarchy.", icon="OUTLINER")
entranceProp = obj.ootEntranceProperty
+ prop_split(box, entranceProp, "tiedRoom", "Room")
prop_split(box, entranceProp, "spawnIndex", "Spawn Index")
prop_split(box, entranceProp.actor, "actorParam", "Actor Param")
box.prop(entranceProp, "customActor")
diff --git a/fast64_internal/oot/cutscene/motion/constants.py b/fast64_internal/oot/cutscene/motion/constants.py
new file mode 100644
index 0000000..236da19
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/constants.py
@@ -0,0 +1,92 @@
+from ...oot_constants import ootData
+
+ootEnumCSMotionCamMode = [
+ ("splineEyeOrAT", "Eye/AT Spline", "Eye/AT Spline"),
+ ("splineEyeOrATRelPlayer", "Spline Rel. Player", "Relative to Player's location/yaw"),
+ ("eyeOrAT", "Eye/AT Point", "Single Eye/AT point (not recommended)"),
+]
+
+ootEnumCSActorCueListCommandType = [
+ item for item in ootData.enumData.ootEnumCsCmd if "actor_cue" in item[0] or "player_cue" in item[0]
+]
+ootEnumCSActorCueListCommandType.sort()
+ootEnumCSActorCueListCommandType.insert(0, ("Custom", "Custom", "Custom"))
+
+ootCSMotionLegacyToNewCmdNames = {
+ "CS_CAM_POS_LIST": "CS_CAM_EYE_SPLINE",
+ "CS_CAM_FOCUS_POINT_LIST": "CS_CAM_AT_SPLINE",
+ "CS_CAM_POS_PLAYER_LIST": "CS_CAM_EYE_SPLINE_REL_TO_PLAYER",
+ "CS_CAM_FOCUS_POINT_PLAYER_LIST": "CS_CAM_AT_SPLINE_REL_TO_PLAYER",
+ "CS_NPC_ACTION_LIST": "CS_ACTOR_CUE_LIST",
+ "CS_PLAYER_ACTION_LIST": "CS_PLAYER_CUE_LIST",
+ "CS_CMD_07": "CS_CAM_EYE",
+ "CS_CMD_08": "CS_CAM_AT",
+ "CS_CAM_POS": "CS_CAM_POINT",
+ "CS_CAM_FOCUS_POINT": "CS_CAM_POINT",
+ "CS_CAM_POS_PLAYER": "CS_CAM_POINT",
+ "CS_CAM_FOCUS_POINT_PLAYER": "CS_CAM_POINT",
+ "CS_NPC_ACTION": "CS_ACTOR_CUE",
+ "CS_PLAYER_ACTION": "CS_PLAYER_CUE",
+ "CS_CMD_09_LIST": "CS_RUMBLE_CONTROLLER_LIST",
+ "CS_CMD_09": "CS_RUMBLE_CONTROLLER",
+ "CS_TEXT_DISPLAY_TEXTBOX": "CS_TEXT",
+ "CS_TEXT_LEARN_SONG": "CS_TEXT_OCARINA_ACTION",
+ "CS_SCENE_TRANS_FX": "CS_TRANSITION",
+ "CS_FADE_BGM_LIST": "CS_FADE_OUT_SEQ_LIST",
+ "CS_FADE_BGM": "CS_FADE_OUT_SEQ",
+ "CS_TERMINATOR": "CS_DESTINATION",
+}
+
+ootCSMotionListCommands = [
+ "CS_ACTOR_CUE_LIST",
+ "CS_PLAYER_CUE_LIST",
+ "CS_CAM_EYE_SPLINE",
+ "CS_CAM_AT_SPLINE",
+ "CS_CAM_EYE_SPLINE_REL_TO_PLAYER",
+ "CS_CAM_AT_SPLINE_REL_TO_PLAYER",
+ "CS_CAM_EYE",
+ "CS_CAM_AT",
+ "CS_MISC_LIST",
+ "CS_LIGHT_SETTING_LIST",
+ "CS_RUMBLE_CONTROLLER_LIST",
+ "CS_TEXT_LIST",
+ "CS_START_SEQ_LIST",
+ "CS_STOP_SEQ_LIST",
+ "CS_FADE_OUT_SEQ_LIST",
+ "CS_TIME_LIST",
+ "CS_UNK_DATA_LIST",
+ "CS_PLAY_BGM_LIST",
+ "CS_STOP_BGM_LIST",
+ "CS_LIGHTING_LIST",
+]
+
+ootCSMotionListEntryCommands = [
+ "CS_ACTOR_CUE",
+ "CS_PLAYER_CUE",
+ "CS_CAM_POINT",
+ "CS_MISC",
+ "CS_LIGHT_SETTING",
+ "CS_RUMBLE_CONTROLLER",
+ "CS_TEXT",
+ "CS_TEXT_NONE",
+ "CS_TEXT_OCARINA_ACTION",
+ "CS_START_SEQ",
+ "CS_STOP_SEQ",
+ "CS_FADE_OUT_SEQ",
+ "CS_TIME",
+ "CS_UNK_DATA",
+ "CS_PLAY_BGM",
+ "CS_STOP_BGM",
+ "CS_LIGHTING",
+]
+
+ootCSMotionSingleCommands = [
+ "CS_BEGIN_CUTSCENE",
+ "CS_END",
+ "CS_TRANSITION",
+ "CS_DESTINATION",
+]
+
+ootCSMotionListAndSingleCommands = ootCSMotionSingleCommands + ootCSMotionListCommands
+ootCSMotionListAndSingleCommands.remove("CS_BEGIN_CUTSCENE")
+ootCSMotionCSCommands = ootCSMotionSingleCommands + ootCSMotionListCommands + ootCSMotionListEntryCommands
diff --git a/fast64_internal/oot/cutscene/motion/exporter/__init__.py b/fast64_internal/oot/cutscene/motion/exporter/__init__.py
new file mode 100644
index 0000000..b4ff461
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/exporter/__init__.py
@@ -0,0 +1 @@
+from .functions import getCutsceneMotionData
diff --git a/fast64_internal/oot/cutscene/motion/exporter/classes.py b/fast64_internal/oot/cutscene/motion/exporter/classes.py
new file mode 100644
index 0000000..67a3fea
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/exporter/classes.py
@@ -0,0 +1,272 @@
+import math
+import bpy
+
+from dataclasses import dataclass
+from bpy.types import Object
+from .....utility import PluginError, indent
+from ....oot_constants import ootData
+
+from ..io_classes import (
+ OOTCSMotionActorCueList,
+ OOTCSMotionActorCue,
+ OOTCSMotionCamEyeSpline,
+ OOTCSMotionCamATSpline,
+ OOTCSMotionCamEyeSplineRelToPlayer,
+ OOTCSMotionCamATSplineRelToPlayer,
+ OOTCSMotionCamEye,
+ OOTCSMotionCamAT,
+ OOTCSMotionCamPoint,
+)
+
+
+class OOTCSMotionExportCommands:
+ """This class contains functions to create the cutscene commands"""
+
+ def getActorCueListCmd(self, actorCueList: OOTCSMotionActorCueList, isPlayerActor: bool):
+ return indent + (
+ f"CS_{'PLAYER' if isPlayerActor else 'ACTOR'}_CUE_LIST("
+ + f"{actorCueList.commandType + ', ' if not isPlayerActor else ''}"
+ + f"{actorCueList.entryTotal}),\n"
+ )
+
+ def getActorCueCmd(self, actorCue: OOTCSMotionActorCue, isPlayerActor: bool):
+ return indent * 2 + (
+ f"CS_{'PLAYER' if isPlayerActor else 'ACTOR'}_CUE("
+ + f"{actorCue.actionID}, {actorCue.startFrame}, {actorCue.endFrame}, "
+ + "".join(f"{rot}, " for rot in actorCue.rot)
+ + "".join(f"{pos}, " for pos in actorCue.startPos)
+ + "".join(f"{pos}, " for pos in actorCue.endPos)
+ + "0.0f, 0.0f, 0.0f),\n"
+ )
+
+ def getCamListCmd(self, cmdName: str, startFrame: int, endFrame: int):
+ return indent + f"{cmdName}({startFrame}, {endFrame}),\n"
+
+ def getCamEyeSplineCmd(self, camEyeSpline: OOTCSMotionCamEyeSpline):
+ return self.getCamListCmd("CS_CAM_EYE_SPLINE", camEyeSpline.startFrame, camEyeSpline.endFrame)
+
+ def getCamATSplineCmd(self, camATSpline: OOTCSMotionCamATSpline):
+ return self.getCamListCmd("CS_CAM_AT_SPLINE", camATSpline.startFrame, camATSpline.endFrame)
+
+ def getCamEyeSplineRelToPlayerCmd(self, camEyeSplinePlayer: OOTCSMotionCamEyeSplineRelToPlayer):
+ return self.getCamListCmd(
+ "CS_CAM_EYE_SPLINE_REL_TO_PLAYER", camEyeSplinePlayer.startFrame, camEyeSplinePlayer.endFrame
+ )
+
+ def getCamATSplineRelToPlayerCmd(self, camATSplinePlayer: OOTCSMotionCamATSplineRelToPlayer):
+ return self.getCamListCmd(
+ "CS_CAM_AT_SPLINE_REL_TO_PLAYER", camATSplinePlayer.startFrame, camATSplinePlayer.endFrame
+ )
+
+ def getCamEyeCmd(self, camEye: OOTCSMotionCamEye):
+ return self.getCamListCmd("CS_CAM_EYE", camEye.startFrame, camEye.endFrame)
+
+ def getCamATCmd(self, camAT: OOTCSMotionCamAT):
+ return self.getCamListCmd("CS_CAM_AT", camAT.startFrame, camAT.endFrame)
+
+ def getCamPointCmd(self, camPoint: OOTCSMotionCamPoint):
+ return indent * 2 + (
+ f"CS_CAM_POINT("
+ + f"{camPoint.continueFlag}, {camPoint.camRoll}, {camPoint.frame}, {camPoint.viewAngle}f, "
+ + "".join(f"{pos}, " for pos in camPoint.pos)
+ + "0),\n"
+ )
+
+
+@dataclass
+class OOTCSMotionExport(OOTCSMotionExportCommands):
+ """This class contains functions to create the new cutscene data"""
+
+ csMotionObjects: dict[str, list[Object]]
+ useDecomp: bool
+ addBeginEndCmds: bool
+ entryTotal: int = 0
+ frameCount: int = 0
+ camEndFrame: int = 0
+
+ def getOoTRotation(self, obj: Object):
+ """Returns the converted Blender rotation"""
+
+ def conv(r):
+ r /= 2.0 * math.pi
+ r -= math.floor(r)
+ r = round(r * 0x10000)
+
+ if r >= 0x8000:
+ r += 0xFFFF0000
+
+ assert r >= 0 and r <= 0xFFFFFFFF and (r <= 0x7FFF or r >= 0xFFFF8000)
+
+ return hex(r & 0xFFFF)
+
+ rotXYZ = [conv(obj.rotation_euler[0]), conv(obj.rotation_euler[2]), conv(obj.rotation_euler[1])]
+ return [f"DEG_TO_BINANG({(int(rot, base=16) * (180 / 0x8000)):.3f})" for rot in rotXYZ]
+
+ def getOoTPosition(self, pos):
+ """Returns the converted Blender position"""
+
+ scale = bpy.context.scene.ootBlenderScale
+
+ x = round(pos[0] * scale)
+ y = round(pos[2] * scale)
+ z = round(-pos[1] * scale)
+
+ if any(v < -0x8000 or v >= 0x8000 for v in (x, y, z)):
+ raise RuntimeError(f"Position(s) too large, out of range: {x}, {y}, {z}")
+
+ return [x, y, z]
+
+ def getActorCueListData(self, isPlayer: bool):
+ """Returns the Actor Cue List commands from the corresponding objects"""
+
+ playerOrActor = f"{'Player' if isPlayer else 'Actor'}"
+ actorCueListObjects = self.csMotionObjects[f"CS {playerOrActor} Cue List"]
+ actorCueListObjects.sort(key=lambda o: o.ootCSMotionProperty.actorCueProp.cueStartFrame)
+ actorCueData = ""
+
+ self.entryTotal += len(actorCueListObjects)
+ for obj in actorCueListObjects:
+ entryTotal = len(obj.children)
+
+ if entryTotal == 0:
+ raise PluginError("ERROR: The Actor Cue List does not contain any child Actor Cue objects")
+
+ if obj.children[-1].ootEmptyType != "CS Dummy Cue":
+ # we need an extra point that won't be exported to get the real last cue's
+ # end frame and end position
+ raise PluginError("ERROR: The Actor Cue List is missing the extra dummy point!")
+
+ commandType = obj.ootCSMotionProperty.actorCueListProp.commandType
+
+ if commandType == "Custom":
+ commandType = obj.ootCSMotionProperty.actorCueListProp.commandTypeCustom
+ elif self.useDecomp:
+ commandType = ootData.enumData.enumByKey["csCmd"].itemByKey[commandType].id
+
+ actorCueList = OOTCSMotionActorCueList(commandType, entryTotal - 1) # ignoring dummy cue
+ actorCueData += self.getActorCueListCmd(actorCueList, isPlayer)
+
+ for i, childObj in enumerate(obj.children, 1):
+ startFrame = childObj.ootCSMotionProperty.actorCueProp.cueStartFrame
+ if i < len(obj.children) and childObj.ootEmptyType != "CS Dummy Cue":
+ endFrame = obj.children[i].ootCSMotionProperty.actorCueProp.cueStartFrame
+ actionID = None
+
+ if isPlayer:
+ cueID = childObj.ootCSMotionProperty.actorCueProp.playerCueID
+ if cueID != "Custom":
+ actionID = ootData.enumData.enumByKey["csPlayerCueId"].itemByKey[cueID].id
+
+ if actionID is None:
+ actionID = childObj.ootCSMotionProperty.actorCueProp.cueActionID
+
+ actorCue = OOTCSMotionActorCue(
+ startFrame,
+ endFrame,
+ actionID,
+ self.getOoTRotation(childObj),
+ self.getOoTPosition(childObj.location),
+ self.getOoTPosition(obj.children[i].location),
+ )
+ actorCueData += self.getActorCueCmd(actorCue, isPlayer)
+
+ return actorCueData
+
+ def getCameraShotPointData(self, bones, useAT: bool):
+ """Returns the Camera Point data from the bone data"""
+
+ shotPoints: list[OOTCSMotionCamPoint] = []
+
+ if len(bones) < 4:
+ raise RuntimeError("Camera Armature needs at least 4 bones!")
+
+ for bone in bones:
+ if bone.parent is not None:
+ raise RuntimeError("Camera Armature bones are not allowed to have parent bones!")
+
+ shotPoints.append(
+ OOTCSMotionCamPoint(
+ ("CS_CAM_CONTINUE" if self.useDecomp else "0"),
+ bone.ootCamShotPointProp.shotPointRoll if useAT else 0,
+ bone.ootCamShotPointProp.shotPointFrame,
+ bone.ootCamShotPointProp.shotPointViewAngle,
+ self.getOoTPosition(bone.head if not useAT else bone.tail),
+ )
+ )
+
+ # NOTE: because of the game's bug explained in the importer we need to add an extra dummy point when exporting
+ shotPoints.append(OOTCSMotionCamPoint("CS_CAM_STOP" if self.useDecomp else "-1", 0, 0, 0.0, [0, 0, 0]))
+ return shotPoints
+
+ def getCamCmdFunc(self, camMode: str, useAT: bool):
+ """Returns the camera get function depending on the camera mode"""
+
+ camCmdFuncMap = {
+ "splineEyeOrAT": self.getCamATSplineCmd if useAT else self.getCamEyeSplineCmd,
+ "splineEyeOrATRelPlayer": self.getCamATSplineRelToPlayerCmd
+ if useAT
+ else self.getCamEyeSplineRelToPlayerCmd,
+ "eyeOrAT": self.getCamATCmd if useAT else self.getCamEyeCmd,
+ }
+
+ return camCmdFuncMap[camMode]
+
+ def getCamClass(self, camMode: str, useAT: bool):
+ """Returns the camera dataclass depending on the camera mode"""
+
+ camCmdClassMap = {
+ "splineEyeOrAT": OOTCSMotionCamATSpline if useAT else OOTCSMotionCamEyeSpline,
+ "splineEyeOrATRelPlayer": OOTCSMotionCamATSplineRelToPlayer
+ if useAT
+ else OOTCSMotionCamEyeSplineRelToPlayer,
+ "eyeOrAT": OOTCSMotionCamAT if useAT else OOTCSMotionCamEye,
+ }
+
+ return camCmdClassMap[camMode]
+
+ def getCamListData(self, shotObj: Object, useAT: bool):
+ """Returns the Camera Shot data from the corresponding Armatures"""
+
+ camPointList = self.getCameraShotPointData(shotObj.data.bones, useAT)
+ startFrame = shotObj.data.ootCamShotProp.shotStartFrame
+
+ # "fake" end frame
+ endFrame = (
+ startFrame + max(2, sum(point.frame for point in camPointList)) + (camPointList[-2].frame if useAT else 1)
+ )
+
+ if not useAT:
+ for pointData in camPointList:
+ pointData.frame = 0
+ self.camEndFrame = endFrame
+
+ camData = self.getCamClass(shotObj.data.ootCamShotProp.shotCamMode, useAT)(startFrame, endFrame)
+ return self.getCamCmdFunc(shotObj.data.ootCamShotProp.shotCamMode, useAT)(camData) + "".join(
+ self.getCamPointCmd(pointData) for pointData in camPointList
+ )
+
+ def getCameraShotData(self):
+ """Returns every Camera Shot commands"""
+
+ shotObjects = self.csMotionObjects["camShot"]
+ cameraShotData = ""
+
+ if len(shotObjects) > 0:
+ frameCount = -1
+ for shotObj in shotObjects:
+ cameraShotData += self.getCamListData(shotObj, False) + self.getCamListData(shotObj, True)
+ frameCount = max(frameCount, self.camEndFrame + 1)
+ self.frameCount += frameCount
+ self.entryTotal += len(shotObjects) * 2
+
+ return cameraShotData
+
+ def getExportData(self):
+ """Returns the cutscene data"""
+
+ data = self.getActorCueListData(False) + self.getActorCueListData(True) + self.getCameraShotData()
+ if self.addBeginEndCmds:
+ data = (
+ indent + f"CS_BEGIN_CUTSCENE({self.entryTotal}, {self.frameCount}),\n" + data + indent + "CS_END(),\n"
+ )
+ return data
diff --git a/fast64_internal/oot/cutscene/motion/exporter/functions.py b/fast64_internal/oot/cutscene/motion/exporter/functions.py
new file mode 100644
index 0000000..8996254
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/exporter/functions.py
@@ -0,0 +1,46 @@
+import bpy
+
+from bpy.types import Object
+from .....utility import PluginError
+from .classes import OOTCSMotionExport
+
+
+def getCSMotionObjects(csName: str):
+ """Returns the object list containing every object from the cutscene to export"""
+
+ csMotionObjects: dict[str, list[Object]] = {
+ "Cutscene": [],
+ "CS Actor Cue List": [],
+ "CS Player Cue List": [],
+ "camShot": [],
+ }
+
+ if csName is None:
+ raise PluginError("ERROR: The cutscene name is None!")
+
+ for obj in bpy.data.objects:
+ isEmptyObj = obj.type == "EMPTY"
+
+ # look for the cutscene object based on the cutscene name
+ parentCheck = obj.parent is not None and obj.parent.name == f"Cutscene.{csName}"
+ csObjCheck = isEmptyObj and obj.ootEmptyType == "Cutscene" and obj.name == f"Cutscene.{csName}"
+ if parentCheck or csObjCheck:
+ # add the relevant objects based on the empty type or if it's an armature
+ if isEmptyObj and obj.ootEmptyType in csMotionObjects.keys():
+ csMotionObjects[obj.ootEmptyType].append(obj)
+
+ if obj.type == "ARMATURE" and obj.parent.ootEmptyType == "Cutscene":
+ csMotionObjects["camShot"].append(obj)
+
+ return csMotionObjects
+
+
+def getCutsceneMotionData(csName: str, addBeginEndCmds: bool):
+ """Returns the initialised cutscene exporter"""
+
+ # this allows us to change the exporter's variables to get what we need
+ return OOTCSMotionExport(
+ getCSMotionObjects(csName),
+ bpy.context.scene.fast64.oot.hackerFeaturesEnabled or bpy.context.scene.useDecompFeatures,
+ addBeginEndCmds,
+ )
diff --git a/fast64_internal/oot/cutscene/motion/importer/__init__.py b/fast64_internal/oot/cutscene/motion/importer/__init__.py
new file mode 100644
index 0000000..2bca31f
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/importer/__init__.py
@@ -0,0 +1 @@
+from .functions import importCutsceneData
diff --git a/fast64_internal/oot/cutscene/motion/importer/classes.py b/fast64_internal/oot/cutscene/motion/importer/classes.py
new file mode 100644
index 0000000..4d04af3
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/importer/classes.py
@@ -0,0 +1,559 @@
+import bpy
+
+from dataclasses import dataclass
+from struct import unpack
+from bpy.types import Object, Armature
+from .....utility import PluginError
+from ....oot_constants import ootData
+from ..utility import setupCutscene, getBlenderPosition, getRotation
+
+from ..constants import (
+ ootCSMotionLegacyToNewCmdNames,
+ ootCSMotionListCommands,
+ ootCSMotionCSCommands,
+ ootCSMotionListEntryCommands,
+ ootCSMotionSingleCommands,
+ ootCSMotionListAndSingleCommands,
+)
+
+from ..io_classes import (
+ OOTCSMotionActorCueList,
+ OOTCSMotionActorCue,
+ OOTCSMotionCamEyeSpline,
+ OOTCSMotionCamATSpline,
+ OOTCSMotionCamEyeSplineRelToPlayer,
+ OOTCSMotionCamATSplineRelToPlayer,
+ OOTCSMotionCamEye,
+ OOTCSMotionCamAT,
+ OOTCSMotionCamPoint,
+ OOTCSMotionCutscene,
+ OOTCSMotionObjectFactory,
+ OOTCSMotionMisc,
+ OOTCSMotionMiscList,
+ OOTCSMotionTransition,
+)
+
+
+@dataclass
+class ParsedCutscene:
+ """Local class used to order the parsed cutscene properly"""
+
+ csName: str
+ csData: list[str] # contains every command lists or standalone ones like ``CS_TRANSITION()``
+
+
+class OOTCSMotionImportCommands:
+ """This class contains functions to create the cutscene dataclasses"""
+
+ def getCmdParams(self, data: str, cmdName: str, paramNumber: int):
+ """Returns the list of every parameter of the given command"""
+
+ params = data.strip().removeprefix(f"{cmdName}(").replace(" ", "").removesuffix(")").split(",")
+ if len(params) != paramNumber:
+ raise PluginError(
+ f"ERROR: The number of expected parameters for `{cmdName}` "
+ + "and the number of found ones is not the same!"
+ )
+ return params
+
+ def getInteger(self, number: str):
+ """Returns an int number (handles properly negative hex numbers)"""
+
+ if number.startswith("0x"):
+ number = number.removeprefix("0x")
+
+ # ``"0" * (8 - len(number)`` adds the missing zeroes (if necessary) to have a 8 digit hex number
+ return unpack("!i", bytes.fromhex("0" * (8 - len(number)) + number))[0]
+ else:
+ return int(number)
+
+ def getNewCutscene(self, csData: str, name: str):
+ params = self.getCmdParams(csData, "CS_BEGIN_CUTSCENE", OOTCSMotionCutscene.paramNumber)
+ return OOTCSMotionCutscene(name, self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewActorCueList(self, cmdData: str, isPlayer: bool):
+ paramNumber = OOTCSMotionActorCueList.paramNumber
+ paramNumber = paramNumber - 1 if isPlayer else paramNumber
+ params = self.getCmdParams(cmdData, f"CS_{'PLAYER' if isPlayer else 'ACTOR'}_CUE_LIST", paramNumber)
+
+ if isPlayer:
+ actorCueList = OOTCSMotionActorCueList("Player", params[0])
+ else:
+ commandType = params[0]
+ if commandType.startswith("0x"):
+ # make it a 4 digit hex
+ commandType = commandType.removeprefix("0x")
+ commandType = "0x" + "0" * (4 - len(commandType)) + commandType
+ actorCueList = OOTCSMotionActorCueList(commandType, self.getInteger(params[1].strip()))
+
+ return actorCueList
+
+ def getNewCamEyeSpline(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_CAM_EYE_SPLINE", OOTCSMotionCamEyeSpline.paramNumber)
+ return OOTCSMotionCamEyeSpline(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewCamATSpline(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_CAM_AT_SPLINE", OOTCSMotionCamATSpline.paramNumber)
+ return OOTCSMotionCamATSpline(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewCamEyeSplineRelToPlayer(self, cmdData: str):
+ params = self.getCmdParams(
+ cmdData, "CS_CAM_EYE_SPLINE_REL_TO_PLAYER", OOTCSMotionCamEyeSplineRelToPlayer.paramNumber
+ )
+ return OOTCSMotionCamEyeSplineRelToPlayer(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewCamATSplineRelToPlayer(self, cmdData: str):
+ params = self.getCmdParams(
+ cmdData, "CS_CAM_AT_SPLINE_REL_TO_PLAYER", OOTCSMotionCamATSplineRelToPlayer.paramNumber
+ )
+ return OOTCSMotionCamATSplineRelToPlayer(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewCamEye(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_CAM_EYE", OOTCSMotionCamEye.paramNumber)
+ return OOTCSMotionCamEye(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewCamAT(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_CAM_AT", OOTCSMotionCamAT.paramNumber)
+ return OOTCSMotionCamAT(self.getInteger(params[0]), self.getInteger(params[1]))
+
+ def getNewActorCue(self, cmdData: str, isPlayer: bool):
+ params = self.getCmdParams(
+ cmdData, f"CS_{'PLAYER' if isPlayer else 'ACTOR'}_CUE", OOTCSMotionActorCue.paramNumber
+ )
+
+ return OOTCSMotionActorCue(
+ self.getInteger(params[1]),
+ self.getInteger(params[2]),
+ self.getInteger(params[0]),
+ [getRotation(params[3]), getRotation(params[4]), getRotation(params[5])],
+ [self.getInteger(params[6]), self.getInteger(params[7]), self.getInteger(params[8])],
+ [self.getInteger(params[9]), self.getInteger(params[10]), self.getInteger(params[11])],
+ )
+
+ def getNewCamPoint(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_CAM_POINT", OOTCSMotionCamPoint.paramNumber)
+
+ return OOTCSMotionCamPoint(
+ params[0],
+ self.getInteger(params[1]),
+ self.getInteger(params[2]),
+ float(params[3][:-1]),
+ [self.getInteger(params[4]), self.getInteger(params[5]), self.getInteger(params[6])],
+ )
+
+ def getNewMisc(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_MISC", OOTCSMotionMisc.paramNumber)
+ miscEnum = ootData.enumData.enumByKey["csMiscType"]
+ item = miscEnum.itemById.get(params[0])
+ if item is None:
+ item = miscEnum.itemByIndex[self.getInteger(params[0])]
+ miscType = item.id
+ return OOTCSMotionMisc(self.getInteger(params[1]), self.getInteger(params[2]), miscType)
+
+ def getNewMiscList(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_MISC_LIST", OOTCSMotionMiscList.paramNumber)
+ return OOTCSMotionMiscList(params[0])
+
+ def getNewTransition(self, cmdData: str):
+ params = self.getCmdParams(cmdData, "CS_TRANSITION", OOTCSMotionTransition.paramNumber)
+ transitionEnum = ootData.enumData.enumByKey["csTransitionType"]
+ item = transitionEnum.itemById.get(params[0])
+ if item is None:
+ item = transitionEnum.itemByIndex[self.getInteger(params[0])]
+ transType = item.id
+ return OOTCSMotionTransition(self.getInteger(params[1]), self.getInteger(params[2]), transType)
+
+
+@dataclass
+class OOTCSMotionImport(OOTCSMotionImportCommands, OOTCSMotionObjectFactory):
+ """This class contains functions to create the new cutscene Blender data"""
+
+ filePath: str # used when importing from the panel
+ fileData: str # used when importing the cutscenes when importing a scene
+
+ def getParsedCutscenes(self):
+ """Returns the parsed commands read from every cutscene we can find"""
+
+ fileData = ""
+
+ if self.fileData is not None:
+ fileData = self.fileData
+ elif self.filePath is not None:
+ with open(self.filePath, "r") as inputFile:
+ fileData = inputFile.read()
+ else:
+ raise PluginError("ERROR: File data can't be found!")
+
+ # replace old names
+ oldNames = list(ootCSMotionLegacyToNewCmdNames.keys())
+ fileData = fileData.replace("CS_CMD_CONTINUE", "CS_CAM_CONTINUE")
+ fileData = fileData.replace("CS_CMD_STOP", "CS_CAM_STOP")
+ for oldName in oldNames:
+ fileData = fileData.replace(f"{oldName}(", f"{ootCSMotionLegacyToNewCmdNames[oldName]}(")
+
+ # parse cutscenes
+ fileLines = fileData.split("\n")
+ csData = []
+ cutsceneList: list[list[str]] = []
+ foundCutscene = False
+ for line in fileLines:
+ if not line.startswith("//") and not line.startswith("/*"):
+ if "CutsceneData " in line:
+ foundCutscene = True
+
+ if foundCutscene:
+ sLine = line.strip()
+ if not sLine.endswith("),") and sLine.endswith(","):
+ line += fileLines[fileLines.index(line) + 1].strip()
+
+ if len(csData) == 0 or "CS_" in line:
+ csData.append(line)
+
+ if "};" in line:
+ foundCutscene = False
+ cutsceneList.append(csData)
+ csData = []
+
+ if len(cutsceneList) == 0:
+ print("INFO: Found no cutscenes in this file!")
+ return None
+
+ # parse the commands from every cutscene we found
+ parsedCutscenes: list[ParsedCutscene] = []
+ for cutscene in cutsceneList:
+ cmdListFound = False
+ curCmdPrefix = None
+ parsedCS = []
+ parsedData = ""
+ csName = None
+
+ for line in cutscene:
+ curCmd = line.strip().split("(")[0]
+ index = cutscene.index(line) + 1
+ nextCmd = cutscene[index].strip().split("(")[0] if index < len(cutscene) else None
+ line = line.strip()
+ if "CutsceneData" in line:
+ csName = line.split(" ")[1][:-2]
+
+ # NOTE: ``CS_UNK_DATA()`` are commands that are completely useless, so we're ignoring those
+ if csName is not None and not "CS_UNK_DATA" in curCmd:
+ if curCmd in ootCSMotionCSCommands:
+ line = line.removesuffix(",") + "\n"
+
+ if curCmd in ootCSMotionSingleCommands and curCmd != "CS_END":
+ parsedData += line
+
+ if not cmdListFound and curCmd in ootCSMotionListCommands:
+ cmdListFound = True
+ parsedData = ""
+
+ # camera and lighting have "non-standard" list names
+ if curCmd.startswith("CS_CAM"):
+ curCmdPrefix = "CS_CAM"
+ elif curCmd.startswith("CS_LIGHT"):
+ curCmdPrefix = "CS_LIGHT"
+ else:
+ curCmdPrefix = curCmd[:-5]
+
+ if curCmdPrefix is not None:
+ if curCmdPrefix in curCmd:
+ parsedData += line
+ elif not cmdListFound and curCmd in ootCSMotionListEntryCommands:
+ print(f"{csName}, command:\n{line}")
+ raise PluginError(f"ERROR: Found a list entry outside a list inside ``{csName}``!")
+
+ if cmdListFound and nextCmd == "CS_END" or nextCmd in ootCSMotionListAndSingleCommands:
+ cmdListFound = False
+ parsedCS.append(parsedData)
+ parsedData = ""
+ elif not "CutsceneData" in curCmd and not "};" in curCmd:
+ print(f"WARNING: Unknown command found: ``{curCmd}``")
+ cmdListFound = False
+ parsedCutscenes.append(ParsedCutscene(csName, parsedCS))
+
+ return parsedCutscenes
+
+ def getCutsceneList(self):
+ """Returns the list of cutscenes with the data processed"""
+
+ parsedCutscenes = self.getParsedCutscenes()
+
+ if parsedCutscenes is None:
+ # if it's none then there's no cutscene in the file
+ return None
+
+ cutsceneList: list[OOTCSMotionCutscene] = []
+ cmdDataList = [
+ ("ACTOR_CUE_LIST", self.getNewActorCueList, self.getNewActorCue, "actorCue"),
+ ("PLAYER_CUE_LIST", self.getNewActorCueList, self.getNewActorCue, "playerCue"),
+ ("CAM_EYE_SPLINE", self.getNewCamEyeSpline, self.getNewCamPoint, "camEyeSpline"),
+ ("CAM_AT_SPLINE", self.getNewCamATSpline, self.getNewCamPoint, "camATSpline"),
+ (
+ "CAM_EYE_SPLINE_REL_TO_PLAYER",
+ self.getNewCamEyeSplineRelToPlayer,
+ self.getNewCamPoint,
+ "camEyeSplineRelPlayer",
+ ),
+ (
+ "CAM_AT_SPLINE_REL_TO_PLAYER",
+ self.getNewCamATSplineRelToPlayer,
+ self.getNewCamPoint,
+ "camATSplineRelPlayer",
+ ),
+ ("CAM_EYE", self.getNewCamEye, self.getNewCamPoint, "camEye"),
+ ("CAM_AT", self.getNewCamAT, self.getNewCamPoint, "camAT"),
+ ("MISC_LIST", self.getNewMiscList, self.getNewMisc, "misc"),
+ ("TRANSITION", self.getNewTransition, None, "transition"),
+ ]
+
+ # for each cutscene from the list returned by getParsedCutscenes(),
+ # create classes containing the cutscene's informations
+ # that will be used later when creating Blender objects to complete the import
+ for parsedCS in parsedCutscenes:
+ cutscene = None
+ for data in parsedCS.csData:
+ # create a new cutscene data
+ if "CS_BEGIN_CUTSCENE(" in data:
+ cutscene = self.getNewCutscene(data, parsedCS.csName)
+
+ # if we have a cutscene, create and add the commands data in it
+ if cutscene is not None:
+ cmdData = data.removesuffix("\n").split("\n")
+ cmdListData = cmdData.pop(0)
+ for cmd, getListFunc, getFunc, listName in cmdDataList:
+ isPlayer = cmd == "PLAYER_CUE_LIST"
+
+ if f"CS_{cmd}(" in data:
+ cmdList = getattr(cutscene, f"{listName}List")
+
+ if getListFunc is not None:
+ if not isPlayer and not cmd == "ACTOR_CUE_LIST":
+ commandData = getListFunc(cmdListData)
+ else:
+ commandData = getListFunc(cmdListData, isPlayer)
+
+ if getFunc is not None:
+ foundEndCmd = False
+ for d in cmdData:
+ if not isPlayer and not cmd == "ACTOR_CUE_LIST":
+ listEntry = getFunc(d)
+ if "CAM" in cmd:
+ flag = d.removeprefix("CS_CAM_POINT(").split(",")[0]
+ if foundEndCmd:
+ raise PluginError("ERROR: More camera commands after last one!")
+ foundEndCmd = "CS_CAM_STOP" in flag or "-1" in flag
+ else:
+ listEntry = getFunc(d, isPlayer)
+ commandData.entries.append(listEntry)
+
+ cmdList.append(commandData)
+
+ # after processing the commands we can add the cutscene to the cutscene list
+ if cutscene is not None:
+ cutsceneList.append(cutscene)
+ return cutsceneList
+
+ def setActorCueData(self, csObj: Object, actorCueList: list[OOTCSMotionActorCueList], cueName: str, csNbr: int):
+ """Creates the objects from the Actor Cue List data"""
+
+ cueObjList = []
+ cueEndFrames = []
+ for i, entry in enumerate(actorCueList, 1):
+ if len(entry.entries) == 0:
+ raise PluginError("ERROR: Actor Cue List does not have any Actor Cue!")
+
+ lastFrame = lastPos = None
+ actorCueListObj = self.getNewActorCueListObject(
+ f"CS_{csNbr:02}.{cueName} Cue List {i:02}", entry.commandType, csObj
+ )
+
+ for j, actorCue in enumerate(entry.entries, 1):
+ if lastFrame is not None and lastFrame != actorCue.startFrame:
+ raise PluginError("ERROR: Actor Cues are not temporally continuous!")
+
+ if lastPos is not None and lastPos != actorCue.startPos:
+ raise PluginError("ERROR: Actor Cues are not spatially continuous!")
+
+ cueObjList.append(
+ self.getNewActorCueObject(
+ f"CS_{csNbr:02}.{cueName} Cue {i}.{j:02}",
+ actorCue.startFrame,
+ actorCue.actionID,
+ actorCue.startPos,
+ actorCue.rot,
+ actorCueListObj,
+ )
+ )
+ lastFrame = actorCue.endFrame
+ lastPos = actorCue.endPos
+ cueEndFrames.append(lastFrame)
+
+ # we need a dummy actor cue to get the end position of the last real one
+ if lastFrame is not None:
+ cueObjList.append(
+ self.getNewActorCueObject(
+ f"CS_{csNbr:02}.{cueName} Cue {i}.999 (D)",
+ lastFrame,
+ "DUMMY",
+ lastPos,
+ actorCue.rot,
+ actorCueListObj,
+ )
+ )
+ cueEndFrames.append(lastFrame + 1)
+
+ # updating the end frames
+ if len(cueEndFrames) != len(cueObjList):
+ raise PluginError("ERROR: Lists lengths do not match!")
+
+ for obj, endFrame in zip(cueObjList, cueEndFrames):
+ # reading this value will trigger the "get" function
+ getEndFrame = obj.ootCSMotionProperty.actorCueProp.cueEndFrame
+
+ if endFrame != getEndFrame and obj.ootEmptyType != "CS Dummy Cue":
+ print(f"WARNING: `{obj.name}`'s end frame do not match the one from the script!")
+
+ def validateCameraData(self, cutscene: OOTCSMotionCutscene):
+ """Safety checks to make sure the camera data is correct"""
+
+ camLists: list[tuple[str, list, list]] = [
+ ("Eye and AT Spline", cutscene.camEyeSplineList, cutscene.camATSplineList),
+ ("Eye and AT Spline Rel to Player", cutscene.camEyeSplineRelPlayerList, cutscene.camATSplineRelPlayerList),
+ ("Eye and AT", cutscene.camEyeList, cutscene.camATList),
+ ]
+
+ for camType, eyeList, atList in camLists:
+ for eyeListEntry, atListEntry in zip(eyeList, atList):
+ eyeTotal = len(eyeListEntry.entries)
+ atTotal = len(atListEntry.entries)
+
+ # Eye -> bone's head, AT -> bone's tail, that's why both lists requires the same length
+ if eyeTotal != atTotal:
+ raise PluginError(f"ERROR: Found {eyeTotal} Eye lists but {atTotal} AT lists in {camType}!")
+
+ if eyeTotal < 4:
+ raise PluginError(f"ERROR: Only {eyeTotal} cam point in this command!")
+
+ if eyeTotal > 4:
+ # NOTE: There is a bug in the game where when incrementing to the next set of key points,
+ # the key point which checked for whether it's the last point or not is the last point
+ # of the next set, not the last point of the old set. This means we need to remove
+ # the extra point at the end that will only tell the game that this camera shot stops.
+ del eyeListEntry.entries[-1]
+ del atListEntry.entries[-1]
+
+ def setBoneData(
+ self, cameraShotObj: Object, boneData: list[tuple[OOTCSMotionCamPoint, OOTCSMotionCamPoint]], csNbr: int
+ ):
+ """Creates the bones from the Camera Point data"""
+
+ scale = bpy.context.scene.ootBlenderScale
+ for i, (eyePoint, atPoint) in enumerate(boneData, 1):
+ # we need the edit mode to be able to change the bone's location
+ bpy.ops.object.mode_set(mode="EDIT")
+ armatureData: Armature = cameraShotObj.data
+ boneName = f"CS_{csNbr:02}.Camera Point {i:02}"
+ newEditBone = armatureData.edit_bones.new(boneName)
+ newEditBone.head = getBlenderPosition(eyePoint.pos, scale)
+ newEditBone.tail = getBlenderPosition(atPoint.pos, scale)
+ bpy.ops.object.mode_set(mode="OBJECT")
+ newBone = armatureData.bones[boneName]
+
+ if eyePoint.frame != 0:
+ print("WARNING: Frames must be 0!")
+
+ # using the "AT" (look-at) data since this is what determines where the camera is looking
+ # the "Eye" only sets the location of the camera
+ newBone.ootCamShotPointProp.shotPointFrame = atPoint.frame
+ newBone.ootCamShotPointProp.shotPointViewAngle = atPoint.viewAngle
+ newBone.ootCamShotPointProp.shotPointRoll = atPoint.camRoll
+
+ def setCameraShotData(
+ self, csObj: Object, eyePoints: list, atPoints: list, camMode: str, startIndex: int, csNbr: int
+ ):
+ """Creates the armatures from the Camera Shot data"""
+
+ endIndex = 0
+
+ # this is required to be able to change the object mode
+ if bpy.context.mode != "OBJECT":
+ bpy.ops.object.mode_set(mode="OBJECT")
+
+ for i, (camEyeSpline, camATSpline) in enumerate(zip(eyePoints, atPoints), startIndex):
+ cameraShotObj = self.getNewArmatureObject(f"CS_{csNbr:02}.Camera Shot {i:02}", True, csObj)
+
+ if camEyeSpline.endFrame < camEyeSpline.startFrame + 2 or camATSpline.endFrame < camATSpline.startFrame + 2:
+ print("WARNING: Non-standard end frame")
+
+ cameraShotObj.data.ootCamShotProp.shotStartFrame = camEyeSpline.startFrame
+ cameraShotObj.data.ootCamShotProp.shotCamMode = camMode
+ boneData = [(eyePoint, atPoint) for eyePoint, atPoint in zip(camEyeSpline.entries, camATSpline.entries)]
+ self.setBoneData(cameraShotObj, boneData, csNbr)
+ endIndex = i
+
+ return endIndex + 1
+
+ def setCutsceneData(self, csNumber):
+ """Creates the cutscene empty objects from the file data"""
+
+ cutsceneList = self.getCutsceneList()
+
+ if cutsceneList is None:
+ # if it's none then there's no cutscene in the file
+ return csNumber
+
+ for i, cutscene in enumerate(cutsceneList, csNumber):
+ print(f'Found Cutscene "{cutscene.name}"!')
+ self.validateCameraData(cutscene)
+ csName = f"Cutscene.{cutscene.name}"
+ csObj = self.getNewCutsceneObject(csName, cutscene.frameCount, None)
+ csNumber = i
+
+ print("Importing Actor Cues...")
+ self.setActorCueData(csObj, cutscene.actorCueList, "Actor", i)
+ self.setActorCueData(csObj, cutscene.playerCueList, "Player", i)
+ print("Done!")
+
+ print("Importing Camera Shots...")
+ if len(cutscene.camEyeSplineList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj, cutscene.camEyeSplineList, cutscene.camATSplineList, "splineEyeOrAT", 1, i
+ )
+
+ if len(cutscene.camEyeSplineRelPlayerList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj,
+ cutscene.camEyeSplineRelPlayerList,
+ cutscene.camATSplineRelPlayerList,
+ "splineEyeOrATRelPlayer",
+ lastIndex,
+ i,
+ )
+
+ if len(cutscene.camEyeList) > 0:
+ lastIndex = self.setCameraShotData(
+ csObj, cutscene.camEyeList, cutscene.camATList, "eyeOrAT", lastIndex, i
+ )
+
+ if len(cutscene.miscList) > 0:
+ for miscList in cutscene.miscList:
+ for miscCmd in miscList.entries:
+ miscProp = csObj.ootCutsceneProperty.preview.miscList.add()
+ miscProp.startFrame = miscCmd.startFrame
+ miscProp.endFrame = miscCmd.endFrame
+ miscProp.type = miscCmd.type
+
+ if len(cutscene.transitionList) > 0:
+ for transition in cutscene.transitionList:
+ transitionProp = csObj.ootCutsceneProperty.preview.transitionList.add()
+ transitionProp.startFrame = transition.startFrame
+ transitionProp.endFrame = transition.endFrame
+ transitionProp.type = transition.type
+
+ # Init camera + preview objects and setup the scene
+ setupCutscene(csObj)
+ print("Done!")
+ bpy.ops.object.select_all(action="DESELECT")
+
+ # ``csNumber`` makes sure there's no duplicates
+ return csNumber + 1
diff --git a/fast64_internal/oot/cutscene/motion/importer/functions.py b/fast64_internal/oot/cutscene/motion/importer/functions.py
new file mode 100644
index 0000000..16a5fbc
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/importer/functions.py
@@ -0,0 +1,10 @@
+import bpy
+
+from .classes import OOTCSMotionImport
+
+
+def importCutsceneData(filePath: str, sceneData: str):
+ """Initialises and imports the cutscene data from either a file or the scene data"""
+ # NOTE: ``sceneData`` is the data read when importing a scene
+ csMotionImport = OOTCSMotionImport(filePath, sceneData)
+ return csMotionImport.setCutsceneData(bpy.context.scene.ootCSNumber)
diff --git a/fast64_internal/oot/cutscene/motion/io_classes.py b/fast64_internal/oot/cutscene/motion/io_classes.py
new file mode 100644
index 0000000..462eeb2
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/io_classes.py
@@ -0,0 +1,258 @@
+import bpy
+
+from dataclasses import dataclass, field
+from bpy.types import Object
+from ...oot_constants import ootData
+from .utility import getBlenderPosition, getBlenderRotation
+
+
+# NOTE: ``paramNumber`` is the expected number of parameters inside the parsed commands,
+# this account for the unused parameters. Every classes are based on the commands arguments from ``z64cutscene_commands.h``
+
+
+@dataclass
+class OOTCSMotionBase:
+ """This class contains common Cutscene data"""
+
+ startFrame: int
+ endFrame: int
+
+
+@dataclass
+class OOTCSMotionCamPoint:
+ """This class contains a single Camera Point command data"""
+
+ continueFlag: str
+ camRoll: int
+ frame: int
+ viewAngle: float
+ pos: list[int, int, int]
+ paramNumber: int = 8
+
+
+@dataclass
+class OOTCSMotionActorCue(OOTCSMotionBase):
+ """This class contains a single Actor Cue command data"""
+
+ actionID: int
+ rot: list[str, str, str]
+ startPos: list[int, int, int]
+ endPos: list[int, int, int]
+ paramNumber: int = 15
+
+
+@dataclass
+class OOTCSMotionActorCueList:
+ """This class contains the Actor Cue List command data"""
+
+ commandType: str
+ entryTotal: int
+ entries: list[OOTCSMotionActorCue] = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamEyeSpline(OOTCSMotionBase):
+ """This class contains the Camera Eye Spline data"""
+
+ entries: list[OOTCSMotionCamPoint] = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamATSpline(OOTCSMotionBase):
+ """This class contains the Camera AT (look-at) Spline data"""
+
+ entries: list[OOTCSMotionCamPoint] = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamEyeSplineRelToPlayer(OOTCSMotionBase):
+ """This class contains the Camera Eye Spline Relative to the Player data"""
+
+ entries: list[OOTCSMotionCamPoint] = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamATSplineRelToPlayer(OOTCSMotionBase):
+ """This class contains the Camera AT Spline Relative to the Player data"""
+
+ entries: list[OOTCSMotionCamPoint] = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamEye(OOTCSMotionBase):
+ """This class contains a single Camera Eye point"""
+
+ # This feature is not used in the final game and lacks polish, it is recommended to use splines in all cases.
+ entries: list = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionCamAT(OOTCSMotionBase):
+ """This class contains a single Camera AT point"""
+
+ # This feature is not used in the final game and lacks polish, it is recommended to use splines in all cases.
+ entries: list = field(default_factory=list)
+ paramNumber: int = 2
+
+
+@dataclass
+class OOTCSMotionMisc(OOTCSMotionBase):
+ """This class contains a single misc command entry"""
+
+ type: str # see ``CutsceneMiscType`` in decomp
+ paramNumber: int = 14
+
+
+@dataclass
+class OOTCSMotionMiscList:
+ """This class contains Misc command data"""
+
+ entryTotal: int
+ entries: list[OOTCSMotionMisc] = field(default_factory=list)
+ paramNumber: int = 1
+
+
+@dataclass
+class OOTCSMotionTransition(OOTCSMotionBase):
+ """This class contains Transition command data"""
+
+ type: str
+ paramNumber: int = 3
+
+
+@dataclass
+class OOTCSMotionCutscene:
+ """This class contains a Cutscene's data, including every commands' data"""
+
+ name: str
+ totalEntries: int
+ frameCount: int
+ paramNumber: int = 2
+
+ actorCueList: list[OOTCSMotionActorCueList] = field(default_factory=list)
+ playerCueList: list[OOTCSMotionActorCueList] = field(default_factory=list)
+ camEyeSplineList: list[OOTCSMotionCamEyeSpline] = field(default_factory=list)
+ camATSplineList: list[OOTCSMotionCamATSpline] = field(default_factory=list)
+ camEyeSplineRelPlayerList: list[OOTCSMotionCamEyeSplineRelToPlayer] = field(default_factory=list)
+ camATSplineRelPlayerList: list[OOTCSMotionCamATSplineRelToPlayer] = field(default_factory=list)
+ camEyeList: list[OOTCSMotionCamEye] = field(default_factory=list)
+ camATList: list[OOTCSMotionCamAT] = field(default_factory=list)
+ miscList: list[OOTCSMotionMiscList] = field(default_factory=list)
+ transitionList: list[OOTCSMotionTransition] = field(default_factory=list)
+
+
+class OOTCSMotionObjectFactory:
+ """This class contains functions to create new Blender objects"""
+
+ def getNewObject(self, name: str, data, selectObject: bool, parentObj: Object) -> Object:
+ newObj = bpy.data.objects.new(name=name, object_data=data)
+ bpy.context.view_layer.active_layer_collection.collection.objects.link(newObj)
+ if selectObject:
+ newObj.select_set(True)
+ bpy.context.view_layer.objects.active = newObj
+ newObj.parent = parentObj
+ newObj.location = [0.0, 0.0, 0.0]
+ newObj.rotation_euler = [0.0, 0.0, 0.0]
+ newObj.scale = [1.0, 1.0, 1.0]
+ return newObj
+
+ def getNewEmptyObject(self, name: str, selectObject: bool, parentObj: Object):
+ return self.getNewObject(name, None, selectObject, parentObj)
+
+ def getNewArmatureObject(self, name: str, selectObject: bool, parentObj: Object):
+ newArmatureData = bpy.data.armatures.new(name)
+ newArmatureData.display_type = "STICK"
+ newArmatureData.show_names = True
+ newArmatureObject = self.getNewObject(name, newArmatureData, selectObject, parentObj)
+ return newArmatureObject
+
+ def getNewCutsceneObject(self, name: str, frameCount: int, parentObj: Object):
+ newCSObj = self.getNewEmptyObject(name, True, parentObj)
+ newCSObj.ootEmptyType = "Cutscene"
+ newCSObj.ootCutsceneProperty.csEndFrame = frameCount
+ return newCSObj
+
+ def getNewActorCueListObject(self, name: str, commandType: str, parentObj: Object):
+ newActorCueListObj = self.getNewEmptyObject(name, False, parentObj)
+ newActorCueListObj.ootEmptyType = f"CS {'Player' if 'Player' in name else 'Actor'} Cue List"
+ cmdEnum = ootData.enumData.enumByKey["csCmd"]
+
+ if commandType == "Player":
+ commandType = "player_cue"
+
+ index = cmdEnum.itemByKey[commandType].index if commandType in cmdEnum.itemByKey else int(commandType, base=16)
+ item = cmdEnum.itemByIndex.get(index)
+
+ if item is not None:
+ newActorCueListObj.ootCSMotionProperty.actorCueListProp.commandType = item.key
+ else:
+ newActorCueListObj.ootCSMotionProperty.actorCueListProp.commandType = "Custom"
+ newActorCueListObj.ootCSMotionProperty.actorCueListProp.commandTypeCustom = commandType
+
+ return newActorCueListObj
+
+ def getNewActorCueObject(
+ self,
+ name: str,
+ startFrame: int,
+ actionID: int | str,
+ location: list[int],
+ rot: list[str],
+ parentObj: Object,
+ ):
+ isDummy = "(D)" in name
+ isPlayer = not isDummy and not "Actor" in name
+
+ newActorCueObj = self.getNewEmptyObject(name, False, parentObj)
+ newActorCueObj.location = getBlenderPosition(location, bpy.context.scene.ootBlenderScale)
+ newActorCueObj.empty_display_type = "ARROWS"
+ newActorCueObj.rotation_mode = "XZY"
+ newActorCueObj.rotation_euler = getBlenderRotation(rot)
+ emptyType = "Dummy" if isDummy else "Player" if isPlayer else "Actor"
+ newActorCueObj.ootEmptyType = f"CS {emptyType} Cue"
+ newActorCueObj.ootCSMotionProperty.actorCueProp.cueStartFrame = startFrame
+
+ item = None
+ if isPlayer:
+ playerEnum = ootData.enumData.enumByKey["csPlayerCueId"]
+ if isinstance(actionID, int):
+ item = playerEnum.itemByIndex.get(actionID)
+ else:
+ item = playerEnum.itemByKey.get(actionID)
+
+ if item is not None:
+ newActorCueObj.ootCSMotionProperty.actorCueProp.playerCueID = item.key
+ elif not isDummy:
+ if isPlayer:
+ newActorCueObj.ootCSMotionProperty.actorCueProp.playerCueID = "Custom"
+
+ if isinstance(actionID, int):
+ cueActionID = f"0x{actionID:04X}"
+ else:
+ cueActionID = actionID
+
+ newActorCueObj.ootCSMotionProperty.actorCueProp.cueActionID = cueActionID
+
+ return newActorCueObj
+
+ def getNewCameraObject(
+ self, name: str, displaySize: float, clipStart: float, clipEnd: float, alpha: float, parentObj: Object
+ ):
+ newCamera = bpy.data.cameras.new(name)
+ newCameraObj = self.getNewObject(name, newCamera, False, parentObj)
+ newCameraObj.data.display_size = displaySize
+ newCameraObj.data.clip_start = clipStart
+ newCameraObj.data.clip_end = clipEnd
+ newCameraObj.data.passepartout_alpha = alpha
+ return newCameraObj
+
+ def getNewActorCuePreviewObject(self, name: str, selectObject, parentObj: Object):
+ newPreviewObj = self.getNewEmptyObject(name, selectObject, parentObj)
+ newPreviewObj.ootEmptyType = f"CS {'Actor' if 'Actor' in name else 'Player'} Cue Preview"
+ return newPreviewObj
diff --git a/fast64_internal/oot/cutscene/motion/operators.py b/fast64_internal/oot/cutscene/motion/operators.py
new file mode 100644
index 0000000..e028b69
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/operators.py
@@ -0,0 +1,371 @@
+import bpy
+
+from bpy.types import Object, Operator, Context, Armature
+from bpy.utils import register_class, unregister_class
+from bpy.props import StringProperty, EnumProperty, BoolProperty
+from ....utility import PluginError
+from ...oot_constants import ootData
+from .io_classes import OOTCSMotionObjectFactory
+from .constants import ootEnumCSActorCueListCommandType
+from ..preview import initFirstFrame, setupCompositorNodes
+from .utility import (
+ setupActorCuePreview,
+ metersToBlend,
+ getCSMotionValidateObj,
+ getNameInformations,
+ createNewBone,
+ createNewCameraShot,
+ getCutsceneEndFrame,
+)
+
+
+def getActorCueList(operator: Operator, context: Context) -> Object | None:
+ cueListObj = activeObj = context.view_layer.objects.active
+
+ if activeObj.ootEmptyType == "CS Actor Cue" and activeObj.parent.ootEmptyType == "CS Actor Cue List":
+ cueListObj = activeObj.parent
+
+ if not cueListObj.ootEmptyType == "CS Actor Cue List" and cueListObj.parent.ootEmptyType == "Cutscene":
+ operator.report({"WARNING"}, "Select an action list or action point.")
+ return None
+
+ return cueListObj
+
+
+def createNewActorCueList(csObj: Object, isPlayer: bool):
+ """Creates a new Actor or Player Cue List and adds one basic cue and the dummy one"""
+ objFactory = OOTCSMotionObjectFactory()
+ playerOrActor = "Player" if isPlayer else "Actor"
+ newActorCueListObj = objFactory.getNewActorCueListObject(f"New {playerOrActor} Cue List", "actor_cue_0_0", None)
+ index, csPrefix = getNameInformations(csObj, f"{playerOrActor} Cue List", None)
+ newActorCueListObj.name = f"{csPrefix}.{playerOrActor} Cue List {index:02}"
+
+ # add a basic actor cue and the dummy one
+ for i in range(2):
+ nameSuffix = f"{i + 1:02}" if i == 0 else "999 (D)"
+ newActorCueObj = objFactory.getNewActorCueObject(
+ f"{csPrefix}.{playerOrActor} Cue {index:02}.{nameSuffix}",
+ i,
+ "cueid_none" if isPlayer else "0x0000",
+ [0, 0, 0],
+ ["0x0", "0x0", "0x0"],
+ newActorCueListObj,
+ )
+
+ # finally, parenting the object to the cutscene
+ newActorCueListObj.parent = csObj
+
+
+class OOTCSMotionPlayPreview(Operator):
+ """Camera Preview Playback"""
+
+ bl_idname = "object.play_preview"
+ bl_label = "Preview Playback"
+
+ def execute(self, context):
+ try:
+ csObj = getCSMotionValidateObj(None, None, None)
+
+ if csObj is not None:
+ # get and set the camera
+ cameraObj = None
+ for childObj in csObj.children:
+ if childObj.type == "CAMERA":
+ cameraObj = childObj
+ break
+
+ # from https://blender.stackexchange.com/a/259103
+ space = None
+ for area in context.screen.areas:
+ if (area != context.area) and (area.type == "VIEW_3D"):
+ for space in area.spaces:
+ if space.type == "VIEW_3D":
+ break
+ if space is not None:
+ space.region_3d.view_perspective = "CAMERA"
+
+ # setup frame data and play the animation
+ endFrame = getCutsceneEndFrame(csObj)
+ context.scene.frame_end = endFrame if endFrame > -1 else context.scene.frame_end
+ context.scene.frame_set(context.scene.frame_start)
+ bpy.context.scene.ootCSPreviewCSObj = csObj
+ bpy.context.scene.ootCSPreviewNodesReady = False
+ setupCompositorNodes()
+ initFirstFrame(csObj, bpy.context.scene.ootCSPreviewNodesReady, cameraObj)
+ bpy.ops.screen.animation_cancel()
+ bpy.ops.screen.animation_play()
+ return {"FINISHED"}
+ except:
+ return {"CANCELLED"}
+
+
+class OOTCSMotionAddBone(Operator):
+ """Add a bone to an armature"""
+
+ bl_idname = "object.add_bone"
+ bl_label = "Add Bone"
+
+ def execute(self, context):
+ try:
+ cameraShotObj = getCSMotionValidateObj(None, None, "Camera Shot")
+
+ if cameraShotObj is not None:
+ armatureData: Armature = cameraShotObj.data
+ cameraShotObj.select_set(True)
+ lastMode = "EDIT" if "EDIT" in context.mode else context.mode
+
+ # create the new bone with standard informations
+ blendOne = metersToBlend(context, float(1))
+ boneName = f"CS_??.Camera Point ??"
+ createNewBone(cameraShotObj, boneName, [blendOne, blendOne, 0.0], [blendOne, 0.0, 0.0])
+
+ # update the name of the new bone and move it to the position of the previous last one
+ bpy.ops.object.mode_set(mode="EDIT")
+ if len(armatureData.edit_bones) > 0 and "CS_" in armatureData.edit_bones[-2].name:
+ secondToLastBone = armatureData.edit_bones[-2]
+ newBone = armatureData.edit_bones[-1]
+ splitName = secondToLastBone.name.split(" ")
+ try:
+ newBone.name = f"{splitName[0]} Point {int(splitName[2]) + 1:02}"
+ except ValueError:
+ newBone.name = f"Camera Point 500"
+ newBone.head = secondToLastBone.head
+ newBone.tail = secondToLastBone.tail
+ bpy.ops.object.mode_set(mode="OBJECT") # going back to object mode to update the bones properly
+
+ if armatureData.bones[-1].name == boneName:
+ raise PluginError("ERROR: Something went wrong...")
+
+ bpy.ops.object.mode_set(mode=lastMode)
+ self.report({"INFO"}, "Success!")
+ return {"FINISHED"}
+ else:
+ raise PluginError("You must select an armature object parented to a cutscene empty object!")
+ except:
+ return {"CANCELLED"}
+
+
+class OOTCSMotionAddActorCue(Operator):
+ """Add an entry to a player or actor cue list"""
+
+ bl_idname = "object.add_actor_cue_point"
+ bl_label = "Add Actor Cue"
+
+ isPlayer: BoolProperty(default=False)
+
+ def execute(self, context):
+ actorCueListObj = getCSMotionValidateObj(None, None, None)
+
+ try:
+ if actorCueListObj is not None:
+ if actorCueListObj.parent is not None and actorCueListObj.parent.ootEmptyType != "Cutscene":
+ actorCueListObj = actorCueListObj.parent
+
+ # start by creating the new object with basic values
+ objFactory = OOTCSMotionObjectFactory()
+ newActorCueObj = objFactory.getNewActorCueObject(
+ f"New {'Player' if self.isPlayer else 'Actor'} Cue",
+ 0,
+ "cueid_none" if self.isPlayer else "0x0000",
+ [0, 0, 0],
+ ["0x0", "0x0", "0x0"],
+ None,
+ )
+
+ # if there's other actor cues, take the information from the last non-dummy one
+ if len(actorCueListObj.children) > 0:
+ dummyCue = None
+ for i, obj in enumerate(actorCueListObj.children):
+ if obj.ootEmptyType == "CS Dummy Cue":
+ dummyCue = obj
+
+ if dummyCue is not None:
+ lastCueObj = actorCueListObj.children[i - 1]
+ nameSplit = lastCueObj.name.split(".")
+ try:
+ index = int(nameSplit[-1]) + 1
+ except ValueError:
+ index = i + 500 # huge number to make sure it's going at the end of the list
+ startFrame = dummyCue.ootCSMotionProperty.actorCueProp.cueStartFrame
+ newActorCueObj.name = f"{nameSplit[0]}.{nameSplit[1]}.{index:02}"
+ newActorCueObj.ootCSMotionProperty.actorCueProp.cueStartFrame = startFrame
+ dummyCue.ootCSMotionProperty.actorCueProp.cueStartFrame += 1
+ newActorCueObj.location = lastCueObj.location
+ newActorCueObj.rotation_euler = lastCueObj.rotation_euler
+ else:
+ # else create the name from the actor cue list object
+ nameSplit = actorCueListObj.name.split(".")
+ try:
+ index = int(nameSplit[1].split(" ")[3])
+ except ValueError:
+ index = 500
+ newActorCueObj.name = f"{nameSplit[0]}.{nameSplit[1][:-8]} {index}.01"
+
+ # add the dummy cue since the list is empty
+ nameSplit = newActorCueObj.name.split(".")
+ newDummyCueObj = objFactory.getNewActorCueObject(
+ f"{nameSplit[0]}.{nameSplit[1]}.999 (D)",
+ 1,
+ "0x0000",
+ [0, 0, 0],
+ ["0x0", "0x0", "0x0"],
+ actorCueListObj,
+ )
+
+ # update the end frame of the real cue
+ getEndFrame = newActorCueObj.ootCSMotionProperty.actorCueProp.cueEndFrame
+ newActorCueObj.parent = actorCueListObj
+ else:
+ raise PluginError("ERROR: Select the Actor or Player Cue List!")
+
+ self.report({"INFO"}, "Success!")
+ return {"FINISHED"}
+ except Exception as e:
+ # print(e)
+ raise PluginError("ERROR: Something went wrong.")
+ return {"CANCELLED"}
+
+
+class OOTCSMotionCreateActorCuePreview(Operator):
+ """Create a preview empty object for a player or an actor cue list"""
+
+ bl_idname = "object.create_actor_cue_preview"
+ bl_label = "Create Preview Object"
+
+ def execute(self, context):
+ cueList = getCSMotionValidateObj(None, None, None)
+
+ if cueList is not None and cueList.ootEmptyType in ["CS Actor Cue List", "CS Player Cue List"]:
+ isCueMoving = setupActorCuePreview(
+ cueList.parent, "Actor" if "Actor" in cueList.ootEmptyType else "Player", True, cueList
+ )
+ self.report({"INFO"}, "Success!" if isCueMoving else "Actor Cue don't move, ignored preview.")
+ return {"FINISHED"}
+ else:
+ return {"CANCELLED"}
+
+
+class OOTCSMotionCreateCameraShot(Operator):
+ """Create and initialize a camera shot armature"""
+
+ bl_idname = "object.create_camera_shot"
+ bl_label = "Create Camera Shot"
+
+ def execute(self, context):
+ csObj = getCSMotionValidateObj(None, None, None)
+
+ if csObj is not None:
+ createNewCameraShot(csObj)
+ self.report({"INFO"}, "Success!")
+ return {"FINISHED"}
+ else:
+ return {"CANCELLED"}
+
+
+class OOTCSMotionCreatePlayerCueList(Operator):
+ """Create a cutscene player cue list"""
+
+ bl_idname = "object.create_player_cue_list"
+ bl_label = "Create Player Cue List"
+
+ def execute(self, context):
+ csObj = getCSMotionValidateObj(None, None, None)
+
+ try:
+ if csObj is not None:
+ createNewActorCueList(csObj, True)
+ else:
+ raise PluginError("ERROR: You must select the cutscene object!")
+
+ self.report({"INFO"}, "Success!")
+ return {"FINISHED"}
+ except:
+ return {"CANCELLED"}
+
+
+class OOTCSMotionCreateActorCueList(Operator):
+ """Create a cutscene actor cue list"""
+
+ bl_idname = "object.create_actor_cue_list"
+ bl_label = "Create Actor Cue list"
+
+ def execute(self, context):
+ csObj = getCSMotionValidateObj(None, None, None)
+
+ try:
+ if csObj is not None:
+ createNewActorCueList(csObj, False)
+ else:
+ raise PluginError("ERROR: You must select the cutscene object!")
+
+ self.report({"INFO"}, "Success!")
+ return {"FINISHED"}
+ except:
+ return {"CANCELLED"}
+
+
+class OOT_SearchActorCueCmdTypeEnumOperator(Operator):
+ bl_idname = "object.oot_search_actorcue_cmdtype_enum_operator"
+ bl_label = "Select Command Type"
+ bl_property = "commandType"
+ bl_options = {"REGISTER", "UNDO"}
+
+ commandType: EnumProperty(items=ootEnumCSActorCueListCommandType, default="actor_cue_0_0")
+ objName: StringProperty()
+
+ def execute(self, context):
+ obj = bpy.data.objects[self.objName]
+ obj.ootCSMotionProperty.actorCueListProp.commandType = self.commandType
+
+ context.region.tag_redraw()
+ self.report({"INFO"}, "Selected: " + self.commandType)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ context.window_manager.invoke_search_popup(self)
+ return {"RUNNING_MODAL"}
+
+
+class OOT_SearchPlayerCueIdEnumOperator(Operator):
+ bl_idname = "object.oot_search_playercueid_enum_operator"
+ bl_label = "Select Player Cue ID"
+ bl_property = "playerCueID"
+ bl_options = {"REGISTER", "UNDO"}
+
+ playerCueID: EnumProperty(items=ootData.enumData.ootEnumCsPlayerCueId, default="cueid_none")
+ objName: StringProperty()
+
+ def execute(self, context):
+ obj = bpy.data.objects[self.objName]
+ obj.ootCSMotionProperty.actorCueProp.playerCueID = self.playerCueID
+
+ context.region.tag_redraw()
+ self.report({"INFO"}, "Selected: " + self.playerCueID)
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ context.window_manager.invoke_search_popup(self)
+ return {"RUNNING_MODAL"}
+
+
+classes = (
+ OOTCSMotionPlayPreview,
+ OOTCSMotionAddBone,
+ OOTCSMotionAddActorCue,
+ OOTCSMotionCreateActorCuePreview,
+ OOTCSMotionCreateCameraShot,
+ OOTCSMotionCreatePlayerCueList,
+ OOTCSMotionCreateActorCueList,
+ OOT_SearchActorCueCmdTypeEnumOperator,
+ OOT_SearchPlayerCueIdEnumOperator,
+)
+
+
+def csMotion_ops_register():
+ for cls in classes:
+ register_class(cls)
+
+
+def csMotion_ops_unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/fast64_internal/oot/cutscene/motion/panels.py b/fast64_internal/oot/cutscene/motion/panels.py
new file mode 100644
index 0000000..8b5a975
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/panels.py
@@ -0,0 +1,50 @@
+from bpy.types import Panel
+from bpy.utils import register_class, unregister_class
+from .properties import OOTCSMotionCameraShotProperty, OOTCSMotionCameraShotPointProperty
+
+
+class OOT_CSMotionCameraShotPanel(Panel):
+ bl_label = "Cutscene Motion Camera Shot Controls"
+ bl_idname = "OOT_PT_camera_shot_panel"
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "object"
+ bl_options = {"HIDE_HEADER"}
+
+ def draw(self, context):
+ obj = context.view_layer.objects.active
+ layout = self.layout
+
+ if obj.type == "ARMATURE":
+ camShotProp: OOTCSMotionCameraShotProperty = obj.data.ootCamShotProp
+ camShotPointProp: OOTCSMotionCameraShotPointProperty = None
+ activeBone = editBone = None
+
+ box = layout.box()
+ camShotProp.draw_props(box, self.bl_label)
+
+ if obj.mode == "POSE":
+ box.label(text="Warning: You can't be in 'Pose' mode to edit camera bones!")
+ elif obj.mode == "OBJECT":
+ activeBone = obj.data.bones.active
+ if activeBone is not None:
+ camShotPointProp = activeBone.ootCamShotPointProp
+ camShotPointProp.draw_props(box)
+ elif obj.mode == "EDIT":
+ editBone = obj.data.edit_bones.active
+ if editBone is not None:
+ camShotPointProp = editBone.ootCamShotPointProp
+ camShotPointProp.draw_props(box)
+
+
+classes = (OOT_CSMotionCameraShotPanel,)
+
+
+def csMotion_panels_register():
+ for cls in classes:
+ register_class(cls)
+
+
+def csMotion_panels_unregister():
+ for cls in reversed(classes):
+ unregister_class(cls)
diff --git a/fast64_internal/oot/cutscene/motion/preview.py b/fast64_internal/oot/cutscene/motion/preview.py
new file mode 100644
index 0000000..9c9007d
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/preview.py
@@ -0,0 +1,220 @@
+import bpy
+import math
+
+from mathutils import Vector, Quaternion
+from bpy.app.handlers import persistent
+from bpy.types import Object, Scene
+from ....utility import PluginError
+from .utility import (
+ BoneData,
+ getCameraShotBoneData,
+ getCSMotionValidateObj,
+)
+
+
+# Eye -> Position, AT -> look-at, where the camera is looking
+
+
+def getUndefinedCamPosEye():
+ return (Vector((0.0, 0.0, 0.0)), Quaternion(), 45.0)
+
+
+def getUndefinedCamPosAT():
+ return (Vector((0.0, 0.0, 0.0)), Vector((0.0, 0.0, -1.0)), 0.0, 45.0)
+
+
+def getSplineCoeffs(t: float):
+ # Reverse engineered from func_800BB0A0 in Debug ROM
+
+ t = min(t, 1.0) # no check for t < 0
+ oneminust = 1.0 - t
+ tsq = t * t
+ oneminustcube6 = (oneminust * oneminust * oneminust) / 6.0
+ tcube = tsq * t
+ spline2 = ((tcube * 0.5) - tsq) + 0.6666667
+ spline3 = ((tsq + t - tcube) * 0.5) + 0.16666667
+ tcube6 = tcube / 6.0
+
+ return oneminustcube6, spline2, spline3, tcube6
+
+
+def getZ64SplineInterpolate(bones: list[BoneData], frame: int):
+ # Reverse engineered from func_800BB2B4 in Debug ROM
+
+ p = 0 # keyframe
+ t = 0.0 # ratioBetweenPoints
+
+ # Simulate cutscene for all frames up to present
+ for _ in range(frame):
+ if p + 2 >= len(bones) - 1:
+ # Camera position is uninitialized
+ return getUndefinedCamPosAT()
+
+ framesPoint1 = bones[p + 1].frame
+ denomPoint1 = 1.0 / framesPoint1 if framesPoint1 != 0 else 0.0
+ framesPoint2 = bones[p + 2].frame
+ denomPoint2 = 1.0 / framesPoint2 if framesPoint2 != 0 else 0.0
+ dt = max(t * (denomPoint2 - denomPoint1) + denomPoint1, 0.0)
+
+ # Different from in game; we remove the extra dummy point at import
+ # and add it at export.
+ if t + dt >= 1.0:
+ if p + 3 == len(bones) - 1:
+ break
+
+ t -= 1.0
+ p += 1
+
+ t += dt
+
+ # Spline interpolate for current situation
+ if p + 3 > len(bones) - 1:
+ if frame > 0:
+ print("Internal error in spline algorithm")
+
+ return getUndefinedCamPosAT()
+
+ s1, s2, s3, s4 = getSplineCoeffs(t)
+ eye = s1 * bones[p].head + s2 * bones[p + 1].head + s3 * bones[p + 2].head + s4 * bones[p + 3].head
+ at = s1 * bones[p].tail + s2 * bones[p + 1].tail + s3 * bones[p + 2].tail + s4 * bones[p + 3].tail
+ roll = s1 * bones[p].roll + s2 * bones[p + 1].roll + s3 * bones[p + 2].roll + s4 * bones[p + 3].roll
+ viewAngle = (
+ s1 * bones[p].viewAngle
+ + s2 * bones[p + 1].viewAngle
+ + s3 * bones[p + 2].viewAngle
+ + s4 * bones[p + 3].viewAngle
+ )
+
+ return (eye, at, roll, viewAngle)
+
+
+def getCmdCamState(shotObj: Object, frame: int):
+ frame -= shotObj.data.ootCamShotProp.shotStartFrame + 1
+
+ if frame < 0:
+ print(f"Warning, camera command evaluated for frame {frame}")
+ return getUndefinedCamPosEye()
+
+ bones = getCameraShotBoneData(shotObj, False)
+
+ if bones is None:
+ return getUndefinedCamPosEye()
+
+ eye, at, roll, viewAngle = getZ64SplineInterpolate(bones, frame)
+ # TODO handle cam_mode (relativeToLink)
+ lookvec = at - eye
+
+ if lookvec.length < 1e-6:
+ return getUndefinedCamPosEye()
+
+ lookvec.normalize()
+ ux = Vector((1.0, 0.0, 0.0))
+ uy = Vector((0.0, 1.0, 0.0))
+ uz = Vector((0.0, 0.0, 1.0))
+ qroll = Quaternion(uz, roll * math.pi / 128.0)
+ qpitch = Quaternion(-ux, math.pi + math.acos(lookvec.dot(uz)))
+ qyaw = Quaternion(-uz, math.atan2(lookvec.dot(ux), lookvec.dot(uy)))
+
+ return (eye, qyaw @ qpitch @ qroll, viewAngle)
+
+
+def getCutsceneCamState(csObj: Object, frame: int):
+ """Returns (pos, rot_quat, viewAngle)"""
+
+ shotObjects: list[Object] = []
+ for childObj in csObj.children:
+ obj = getCSMotionValidateObj(csObj, childObj, "Camera Shot")
+ if obj is not None:
+ shotObjects.append(obj)
+ shotObjects.sort(key=lambda obj: obj.name)
+ shotObj = None
+
+ if len(shotObjects) > 0:
+ startFrame = -1
+
+ for obj in shotObjects:
+ if obj.data.ootCamShotProp.shotStartFrame < frame and obj.data.ootCamShotProp.shotStartFrame > startFrame:
+ shotObj = obj
+ startFrame = obj.data.ootCamShotProp.shotStartFrame
+
+ if shotObj is None or len(shotObjects) == 0:
+ return getUndefinedCamPosEye()
+
+ return getCmdCamState(shotObj, frame)
+
+
+def getActorCueState(cueListObj: Object, frame: int):
+ pos = Vector((0.0, 0.0, 0.0))
+ rot = Vector((0.0, 0.0, 0.0))
+
+ cueList: list[Object] = []
+ for cueObj in cueListObj.children:
+ obj = getCSMotionValidateObj(None, cueObj, None)
+ if obj is not None:
+ cueList.append(obj)
+ cueList.sort(key=lambda o: o.ootCSMotionProperty.actorCueProp.cueStartFrame)
+
+ if len(cueList) >= 2:
+ for i in range(len(cueList) - 1):
+ startFrame = cueList[i].ootCSMotionProperty.actorCueProp.cueStartFrame
+ endFrame = cueList[i + 1].ootCSMotionProperty.actorCueProp.cueStartFrame
+
+ if endFrame > startFrame and frame > startFrame:
+ if frame <= endFrame:
+ pos = cueList[i].location * (endFrame - frame) + cueList[i + 1].location * (frame - startFrame)
+ pos /= endFrame - startFrame
+ rot = cueList[i].rotation_euler
+ return pos, rot
+ elif i == len(cueList) - 2:
+ # If went off the end, use last position
+ pos = cueList[i + 1].location
+ rot = cueList[i].rotation_euler
+ return pos, rot
+
+
+@persistent
+def previewFrameHandler(scene: Scene):
+ for obj in bpy.data.objects:
+ parentObj = obj.parent
+ if (
+ parentObj is not None
+ and parentObj.type == "EMPTY"
+ and parentObj.name.startswith("Cutscene.")
+ and parentObj.ootEmptyType == "Cutscene"
+ ):
+ if obj.type == "CAMERA":
+ pos, rot_quat, viewAngle = getCutsceneCamState(parentObj, scene.frame_current)
+
+ if parentObj.ootCutsceneProperty.preview.useWidescreen:
+ viewAngle *= 4 / 3
+
+ if pos is not None:
+ obj.location = pos
+ obj.rotation_mode = "QUATERNION"
+ obj.rotation_quaternion = rot_quat
+ obj.data.angle = math.pi * viewAngle / 180.0
+ elif obj.ootEmptyType in ["CS Actor Cue Preview", "CS Player Cue Preview"]:
+ cueListToPreview = None
+ if "Actor" in obj.ootEmptyType:
+ cueListToPreview = obj.ootCSMotionProperty.actorCueListProp.actorCueListToPreview
+ elif "Player" in obj.ootEmptyType:
+ cueListToPreview = obj.ootCSMotionProperty.actorCueListProp.playerCueListToPreview
+ else:
+ raise PluginError("Unknown Empty Type!")
+
+ if cueListToPreview is not None:
+ pos, rot = getActorCueState(cueListToPreview, scene.frame_current)
+
+ if pos is not None:
+ obj.location = pos
+ obj.rotation_mode = "XYZ"
+ obj.rotation_euler = rot
+
+
+def csMotion_preview_register():
+ bpy.app.handlers.frame_change_pre.append(previewFrameHandler)
+
+
+def csMotion_preview_unregister():
+ if previewFrameHandler in bpy.app.handlers.frame_change_pre:
+ bpy.app.handlers.frame_change_pre.remove(previewFrameHandler)
diff --git a/fast64_internal/oot/cutscene/motion/properties.py b/fast64_internal/oot/cutscene/motion/properties.py
new file mode 100644
index 0000000..0138cda
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/properties.py
@@ -0,0 +1,277 @@
+import bpy
+
+from bpy.types import PropertyGroup, Object, UILayout, Armature, Bone, Scene, EditBone
+from bpy.props import IntProperty, StringProperty, PointerProperty, EnumProperty, FloatProperty
+from bpy.utils import register_class, unregister_class
+from ...oot_upgrade import upgradeCutsceneMotion
+from ...oot_utility import getEnumName
+from ...oot_constants import ootData
+from .constants import ootEnumCSMotionCamMode, ootEnumCSActorCueListCommandType
+
+from .operators import (
+ OOTCSMotionAddActorCue,
+ OOTCSMotionCreateActorCuePreview,
+ OOT_SearchActorCueCmdTypeEnumOperator,
+ OOTCSMotionAddBone,
+ OOT_SearchPlayerCueIdEnumOperator,
+)
+
+
+def getNextCuesStartFrame(self):
+ curCueObj = bpy.context.view_layer.objects.active
+ parentObj = curCueObj.parent
+
+ if parentObj is not None and parentObj.ootEmptyType in ["CS Actor Cue List", "CS Player Cue List"]:
+ if curCueObj.ootEmptyType in ["CS Actor Cue", "CS Player Cue"]:
+ actorCueObjList = parentObj.children
+ for i, obj in enumerate(actorCueObjList):
+ if obj == curCueObj:
+ return actorCueObjList[i + 1].ootCSMotionProperty.actorCueProp.cueStartFrame
+ return -1
+
+
+class OOTCSMotionActorCueListProperty(PropertyGroup):
+ commandType: EnumProperty(
+ items=ootEnumCSActorCueListCommandType, name="CS Actor Cue Command Type", default="actor_cue_0_0"
+ )
+ commandTypeCustom: StringProperty(name="CS Actor Cue Command Type Custom")
+ actorCueListToPreview: PointerProperty(
+ type=Object, name="", poll=lambda self, object: self.isActorCueListObj(object, "CS Actor Cue List")
+ )
+ playerCueListToPreview: PointerProperty(
+ type=Object, name="", poll=lambda self, object: self.isActorCueListObj(object, "CS Player Cue List")
+ )
+
+ def isActorCueListObj(self, obj: Object, emptyType: str):
+ return obj.type == "EMPTY" and obj.ootEmptyType == emptyType
+
+ def draw_props(self, layout: UILayout, isPreview: bool, labelPrefix: str, objName: str):
+ box = layout.box()
+ box.box().label(text=f"{labelPrefix} Cue List")
+ isPlayer = labelPrefix == "Player"
+
+ # Player Cue has only one command type
+ if not isPlayer and not isPreview:
+ searchBox = box.row()
+ searchOp = searchBox.operator(
+ OOT_SearchActorCueCmdTypeEnumOperator.bl_idname, icon="VIEWZOOM", text="Command Type:"
+ )
+ searchOp.objName = objName
+ searchBox.label(text=getEnumName(ootEnumCSActorCueListCommandType, self.commandType))
+
+ if self.commandType == "Custom":
+ split = box.split(factor=0.5)
+ split.label(text="Command Type Custom:")
+ split.prop(self, "commandTypeCustom", text="")
+
+ if not isPreview:
+ split = box.split(factor=0.5)
+ addCueOp = split.operator(OOTCSMotionAddActorCue.bl_idname, text=f"Add {labelPrefix} Cue")
+ addCueOp.isPlayer = isPlayer
+ split.operator(OOTCSMotionCreateActorCuePreview.bl_idname)
+ else:
+ split = box.split(factor=0.5)
+ split.label(text=f"{labelPrefix} Cue List to Preview")
+ split.prop(self, f"{labelPrefix.lower()}CueListToPreview")
+
+
+class OOTCSMotionActorCueProperty(PropertyGroup):
+ cueStartFrame: IntProperty(name="Start Frame", description="Start frame of the Actor Cue", default=0, min=0)
+
+ cueEndFrame: IntProperty(
+ name="End Frame",
+ description="End Frame of the Actor Cue, corresponds to the Start Frame of the next Actor Cue",
+ default=0,
+ min=-1,
+ get=lambda self: getNextCuesStartFrame(self),
+ )
+
+ playerCueID: EnumProperty(items=ootData.enumData.ootEnumCsPlayerCueId, default="cueid_none")
+ cueActionID: StringProperty(
+ name="Action ID", default="0x0001", description="Actor action. Meaning is unique for each different actor."
+ )
+
+ def draw_props(self, layout: UILayout, labelPrefix: str, isDummy: bool, objName: str):
+ box = layout.box()
+ dummyExtra = "(Sets previous Actor Cue's end frame/pos.)" if isDummy else ""
+ boxBox = box.box()
+ boxBox.label(text=f"{labelPrefix if not isDummy else 'Dummy'} Cue")
+ if len(dummyExtra) > 0:
+ boxBox.label(text=dummyExtra)
+
+ if not isDummy and self.cueStartFrame > self.cueEndFrame:
+ box.label(
+ text="Warning: Start Frame > End Frame!",
+ icon="ERROR",
+ )
+
+ split = box.split(factor=0.5)
+ split.prop(self, "cueStartFrame")
+ if not isDummy:
+ label = f"{labelPrefix} Cue (Action) ID:"
+ isPlayer = labelPrefix == "Player"
+ split.prop(self, "cueEndFrame")
+
+ if isPlayer:
+ split = box.split(factor=0.5)
+ searchOp = split.operator(OOT_SearchPlayerCueIdEnumOperator.bl_idname, icon="VIEWZOOM", text=label)
+ searchOp.objName = objName
+ split.label(text=getEnumName(ootData.enumData.ootEnumCsPlayerCueId, self.playerCueID))
+
+ if not isPlayer or self.playerCueID == "Custom":
+ split = box.split(factor=0.5)
+ split.label(text=label)
+ split.prop(self, "cueActionID", text="")
+
+ addCueOp = box.operator(OOTCSMotionAddActorCue.bl_idname, text=f"Add {labelPrefix} Cue")
+ addCueOp.isPlayer = isPlayer
+
+
+class OOTCSMotionCameraShotProperty(PropertyGroup):
+ shotStartFrame: IntProperty(name="Start Frame", description="Shot start frame", default=0, min=0)
+
+ # only used as a hint for users
+ shotEndFrame: IntProperty(
+ name="End Frame",
+ description="Shot end frame",
+ default=0,
+ min=-1,
+ get=lambda self: self.getEndFrame(),
+ )
+
+ shotCamMode: EnumProperty(
+ items=ootEnumCSMotionCamMode,
+ name="Mode",
+ description="Camera command mode",
+ default="splineEyeOrAT",
+ )
+
+ def getEndFrame(self, camShotObj: Object = None):
+ if camShotObj is None:
+ camShotObj = bpy.context.view_layer.objects.active
+
+ if camShotObj.type == "ARMATURE":
+ boneFrameList: list[int] = [bone.ootCamShotPointProp.shotPointFrame for bone in camShotObj.data.bones]
+ # "fake" eye end frame
+ return self.shotStartFrame + max(2, sum(frame for frame in boneFrameList)) + 1
+ return -1
+
+ def draw_props(self, layout: UILayout, label: str):
+ box = layout.box()
+ box.label(text=label)
+ split = box.split(factor=0.5)
+ split.prop(self, "shotStartFrame")
+ split.prop(self, "shotEndFrame")
+ box.row().prop(self, "shotCamMode", expand=True)
+ box.operator(OOTCSMotionAddBone.bl_idname)
+
+
+class OOTCSMotionCameraShotPointProperty(PropertyGroup):
+ shotPointFrame: IntProperty(
+ name="Frame",
+ description="Key point frames value",
+ default=30,
+ min=0,
+ get=lambda self: self.getValue("frame"),
+ set=lambda self, value: self.setValue(value, "frame"),
+ )
+
+ shotPointViewAngle: FloatProperty(
+ name="FoV",
+ description="Field of view (degrees)",
+ default=60.0,
+ min=0.01,
+ max=179.99,
+ get=lambda self: self.getValue("viewAngle"),
+ set=lambda self, value: self.setValue(value, "viewAngle"),
+ )
+
+ shotPointRoll: IntProperty(
+ name="Roll",
+ description="Camera roll (degrees), positive turns image clockwise",
+ default=0,
+ min=-128,
+ max=127,
+ get=lambda self: self.getValue("roll"),
+ set=lambda self, value: self.setValue(value, "roll"),
+ )
+
+ # internal usage only
+ frame: IntProperty(default=30, min=0)
+ viewAngle: FloatProperty(default=60.0, min=0.01, max=179.99)
+ roll: IntProperty(min=-128, max=127, default=0)
+
+ def getBoneFromEditBone(self, shotObject: Object, editBone: EditBone) -> Bone | None:
+ for bone in shotObject.data.bones:
+ if bone.name == editBone.name:
+ return bone
+ else:
+ print("Could not find corresponding bone")
+ return None
+
+ def getValue(self, propName: str):
+ return getattr(self, propName)
+
+ def setValue(self, value, propName: str):
+ setattr(self, propName, value)
+
+ activeObj = bpy.context.view_layer.objects.active
+ if activeObj.mode == "EDIT":
+ bone = self.getBoneFromEditBone(activeObj, activeObj.data.edit_bones.active)
+ if bone is not None:
+ setattr(bone.ootCamShotPointProp, propName, value)
+ activeObj.data.bones.active = bone
+
+ def draw_props(self, layout: UILayout):
+ box = layout.box()
+ box.label(text="Bone / Key point:")
+ row = box.row()
+ for propName in ["shotPointFrame", "shotPointViewAngle", "shotPointRoll"]:
+ row.prop(self, propName)
+
+
+class OOTCutsceneMotionProperty(PropertyGroup):
+ actorCueListProp: PointerProperty(type=OOTCSMotionActorCueListProperty)
+ actorCueProp: PointerProperty(type=OOTCSMotionActorCueProperty)
+
+ @staticmethod
+ def upgrade_object(csObj: Object):
+ print(f"Processing '{csObj.name}'...")
+ upgradeCutsceneMotion(csObj)
+ print("Done!")
+
+
+classes = (
+ OOTCSMotionActorCueListProperty,
+ OOTCSMotionActorCueProperty,
+ OOTCSMotionCameraShotProperty,
+ OOTCSMotionCameraShotPointProperty,
+ OOTCutsceneMotionProperty,
+)
+
+
+def csMotion_props_register():
+ for cls in classes:
+ register_class(cls)
+
+ Object.ootCSMotionProperty = PointerProperty(type=OOTCutsceneMotionProperty)
+ Armature.ootCamShotProp = PointerProperty(type=OOTCSMotionCameraShotProperty)
+ Bone.ootCamShotPointProp = PointerProperty(type=OOTCSMotionCameraShotPointProperty)
+ EditBone.ootCamShotPointProp = PointerProperty(type=OOTCSMotionCameraShotPointProperty)
+ Scene.previewPlayerAge = EnumProperty(
+ items=[("link_adult", "Adult", "Adult Link (170 cm)", 0), ("link_child", "Child", "Child Link (130 cm)", 1)],
+ name="Player Age for Preview",
+ description="For setting Link's height for preview",
+ default="link_adult",
+ )
+
+
+def csMotion_props_unregister():
+ del Scene.previewPlayerAge
+ del EditBone.ootCamShotPointProp
+ del Bone.ootCamShotPointProp
+ del Armature.ootCamShotProp
+ del Object.ootCSMotionProperty
+
+ for cls in classes:
+ unregister_class(cls)
diff --git a/fast64_internal/oot/cutscene/motion/utility.py b/fast64_internal/oot/cutscene/motion/utility.py
new file mode 100644
index 0000000..a7c4c89
--- /dev/null
+++ b/fast64_internal/oot/cutscene/motion/utility.py
@@ -0,0 +1,275 @@
+import bpy
+
+from bpy.types import Object, Bone, Context, EditBone, Armature
+from mathutils import Vector
+from ....utility import yUpToZUp
+from ...oot_utility import ootParseRotation
+
+
+class BoneData:
+ def __init__(self, shotObj: Object, bone: Bone):
+ editBone = self.getEditBoneFromBone(shotObj, bone) if shotObj.mode == "EDIT" else None
+ self.name = bone.name
+ self.head = editBone.head if editBone is not None else bone.head
+ self.tail = editBone.tail if editBone is not None else bone.tail
+
+ self.frame = (
+ editBone["shotPointFrame"]
+ if editBone is not None and "shotPointFrame" in editBone
+ else bone.ootCamShotPointProp.shotPointFrame
+ )
+
+ self.viewAngle = (
+ editBone["shotPointViewAngle"]
+ if editBone is not None and "shotPointViewAngle" in editBone
+ else bone.ootCamShotPointProp.shotPointViewAngle
+ )
+
+ self.roll = (
+ editBone["shotPointRoll"]
+ if editBone is not None and "shotPointRoll" in editBone
+ else bone.ootCamShotPointProp.shotPointRoll
+ )
+
+ def getEditBoneFromBone(self, shotObj: Object, bone: Bone) -> EditBone | Bone:
+ for editBone in shotObj.data.edit_bones:
+ if editBone.name == bone.name:
+ return editBone
+ else:
+ print("Could not find corresponding bone")
+ return bone
+
+
+def createNewBone(cameraShotObj: Object, name: str, headPos: list[float], tailPos: list[float]):
+ if bpy.context.mode != "OBJECT":
+ bpy.ops.object.mode_set(mode="OBJECT")
+ bpy.ops.object.mode_set(mode="EDIT")
+ armatureData: Armature = cameraShotObj.data
+ newEditBone = armatureData.edit_bones.new(name)
+ newEditBone.head = headPos
+ newEditBone.tail = tailPos
+ bpy.ops.object.mode_set(mode="OBJECT")
+ newBone = armatureData.bones[name]
+ newBone.ootCamShotPointProp.shotPointFrame = 30
+ newBone.ootCamShotPointProp.shotPointViewAngle = 60.0
+ newBone.ootCamShotPointProp.shotPointRoll = 0
+
+
+def createNewCameraShot(csObj: Object):
+ from .io_classes import OOTCSMotionObjectFactory # circular import fix
+
+ index, csPrefix = getNameInformations(csObj, "Camera Shot", None)
+
+ # create a basic armature
+ name = f"{csPrefix}.Camera Shot {index:02}"
+ newCameraShotObj = OOTCSMotionObjectFactory().getNewArmatureObject(name, True, csObj)
+
+ # add 4 bones since it's the minimum required
+ for i in range(1, 5):
+ posX = metersToBlend(bpy.context, float(i))
+ createNewBone(
+ newCameraShotObj,
+ f"{csPrefix}.Camera Point {i:02}",
+ [posX, 0.0, 0.0],
+ [posX, metersToBlend(bpy.context, 1.0), 0.0],
+ )
+
+
+def getBlenderPosition(pos: list[int], scale: int):
+ """Returns the converted OoT position"""
+
+ # OoT: +X right, +Y up, -Z forward
+ # Blender: +X right, +Z up, +Y forward
+ return [float(pos[0]) / scale, -float(pos[2]) / scale, float(pos[1]) / scale]
+
+
+def getRotation(data: str):
+ """Returns the rotation converted to hexadecimal"""
+
+ if "DEG_TO_BINANG" in data or not "0x" in data:
+ angle = float(data.split("(")[1].removesuffix(")") if "DEG_TO_BINANG" in data else data)
+ binang = int(angle * (0x8000 / 180.0)) # from ``DEG_TO_BINANG()`` in decomp
+
+ # if the angle value is higher than 0xFFFF it means we're at 360 degrees
+ return f"0x{0xFFFF if binang > 0xFFFF else binang:04X}"
+ else:
+ return data
+
+
+def getBlenderRotation(rotation: list[str]):
+ """Returns the converted OoT rotation"""
+
+ rot = [int(getRotation(r), base=16) for r in rotation]
+ return yUpToZUp @ Vector(ootParseRotation(rot))
+
+
+def getCSMotionValidateObj(csObj: Object, obj: Object, target: str):
+ """Checks if the given object belong to CS Motion and return it if true"""
+
+ if csObj is not None and obj.parent != csObj:
+ return None
+
+ if obj is None:
+ obj = bpy.context.view_layer.objects.active
+
+ if target is None or target != "Camera Shot":
+ nonListTypes = ["CS Player Cue", "CS Actor Cue", "CS Dummy Cue", "Cutscene"]
+ listTypes = ["CS Player Cue List", "CS Actor Cue List"]
+
+ if obj.type == "EMPTY" and obj.ootEmptyType in nonListTypes or obj.ootEmptyType in listTypes:
+ return obj
+ elif target is not None and target == "Camera Shot":
+ # get the camera shot
+ parentObj = obj.parent
+ if obj.type == "ARMATURE" and parentObj is not None and parentObj.type == "EMPTY":
+ return obj
+
+ return None
+
+
+def getNameInformations(csObj: Object, target: str, index: int):
+ idx = csPrefix = None
+ csNbr = 0
+
+ # get the last target objects names
+ if csObj.children is not None:
+ for i, obj in enumerate(csObj.children):
+ if obj.type == "EMPTY" and "Cue List" in obj.name or "Camera Shot" in obj.name or target in obj.name:
+ csPrefix = obj.name.split(".")[0]
+ if target in obj.name:
+ try:
+ idx = int(obj.name.split(" ")[-1]) + 1
+ except ValueError:
+ pass # idx will stay None
+
+ # saving the cutscene number if the target objects can't be found
+ if csPrefix is None:
+ for obj in bpy.data.objects:
+ if obj.type == "EMPTY" and obj.ootEmptyType == "Cutscene":
+ csNbr += 1
+
+ if obj.name == csObj.name:
+ break
+
+ csPrefix = f"CS_{csNbr:02}"
+
+ if idx is None:
+ idx = 1
+
+ if index is not None:
+ idx = index
+
+ return idx, csPrefix
+
+
+def metersToBlend(context: Context, value: float):
+ return value * 56.0 / context.scene.ootBlenderScale
+
+
+def setupActorCuePreview(csObj: Object, actorOrPlayer: str, selectObject: bool, cueList: Object):
+ from .io_classes import OOTCSMotionObjectFactory # circular import fix
+
+ # check if the cue actually moves, if not it's not necessary to create a preview object
+ isCueMoving = False
+ for i in range(len(cueList.children) - 1):
+ actorCue = cueList.children[i]
+ nextCue = cueList.children[i + 1]
+ curPos = [round(pos) for pos in actorCue.location]
+ nextPos = [round(pos) for pos in nextCue.location]
+ if curPos != nextPos:
+ isCueMoving = True
+ break
+
+ if isCueMoving:
+ try:
+ index, csPrefix = getNameInformations(csObj, "Preview", int(cueList.name.split(" ")[-1]))
+ name = f"{csPrefix}.{actorOrPlayer} Cue Preview {index:02}"
+ except ValueError:
+ name = f"{cueList.name} - Preview"
+
+ for obj in csObj.children:
+ if obj.name == name:
+ previewObj = obj
+ break
+ else:
+ previewObj = OOTCSMotionObjectFactory().getNewActorCuePreviewObject(name, selectObject, csObj)
+
+ actorHeight = 1.5
+ if actorOrPlayer == "Player":
+ actorHeight = 1.7 if bpy.context.scene.previewPlayerAge == "link_adult" else 1.3
+
+ previewObj.empty_display_type = "SINGLE_ARROW"
+ previewObj.empty_display_size = metersToBlend(bpy.context, actorHeight)
+ setattr(previewObj.ootCSMotionProperty.actorCueListProp, f"{actorOrPlayer.lower()}CueListToPreview", cueList)
+
+ return isCueMoving
+
+
+def getCameraShotBoneData(shotObj: Object, runChecks: bool):
+ boneDataList: list[BoneData] = []
+ for bone in shotObj.data.bones:
+ if bone.parent is not None:
+ print("Camera armature bones are not allowed to have parent bones")
+ return None
+ boneDataList.append(BoneData(shotObj, bone))
+ boneDataList.sort(key=lambda b: b.name)
+
+ if runChecks:
+ if boneDataList is None:
+ raise RuntimeError("Error in bone properties")
+
+ if len(boneDataList) < 4:
+ raise RuntimeError(f"Only {len(boneDataList)} bones in `{shotObj.name}`")
+
+ return boneDataList
+
+
+def getCutsceneEndFrame(csObj: Object):
+ shotObjects: list[Object] = []
+ for childObj in csObj.children:
+ obj = getCSMotionValidateObj(csObj, childObj, "Camera Shot")
+ if obj is not None:
+ shotObjects.append(obj)
+ shotObjects.sort(key=lambda obj: obj.name)
+
+ csEndFrame = -1
+ for shotObj in shotObjects:
+ # Seems to be the algorithm which was used in the canon tool: the at list
+ # counts the extra point (same frames as the last real point), and the pos
+ # list doesn't count the extra point but adds 1. Of course, neither of these
+ # values is actually the number of frames the camera motion lasts for.
+ boneDataList = getCameraShotBoneData(shotObj, True)
+ endFrame = shotObj.data.ootCamShotProp.shotStartFrame + max(2, sum(b.frame for b in boneDataList)) + 2
+ csEndFrame = max(csEndFrame, endFrame)
+ return csEndFrame
+
+
+def setupCutscene(csObj: Object):
+ from .io_classes import OOTCSMotionObjectFactory # circular import fix
+
+ objFactory = OOTCSMotionObjectFactory()
+ context = bpy.context
+ bpy.context.scene.ootCSPreviewCSObj = csObj
+ camObj = objFactory.getNewCameraObject(
+ f"{csObj.name}.Camera",
+ metersToBlend(context, 0.25),
+ metersToBlend(context, 1e-3),
+ metersToBlend(context, 200.0),
+ 0.95,
+ csObj,
+ )
+ print("Created New Camera!")
+
+ # Preview setup, used when importing cutscenes
+ for obj in csObj.children:
+ if obj.ootEmptyType in ["CS Actor Cue List", "CS Player Cue List"]:
+ setupActorCuePreview(csObj, "Actor" if "Actor" in obj.ootEmptyType else "Player", False, obj)
+
+ # Other setup
+ context.scene.frame_start = 0
+ context.scene.frame_end = max(getCutsceneEndFrame(csObj), context.scene.frame_end)
+ context.scene.render.fps = 20
+ context.scene.render.resolution_x = 320
+ context.scene.render.resolution_y = 240
+ context.scene.frame_set(context.scene.frame_start)
+ context.scene.camera = camObj
diff --git a/fast64_internal/oot/cutscene/operators.py b/fast64_internal/oot/cutscene/operators.py
index 5d3fd53..97426e9 100644
--- a/fast64_internal/oot/cutscene/operators.py
+++ b/fast64_internal/oot/cutscene/operators.py
@@ -1,4 +1,6 @@
import os
+import re
+
from bpy.path import abspath
from bpy.ops import object
from bpy.props import StringProperty, EnumProperty, IntProperty
@@ -9,6 +11,8 @@ from ..oot_utility import getCollection
from ..scene.exporter.to_c import ootCutsceneDataToC
from .exporter import convertCutsceneObject
from .constants import ootEnumCSTextboxType, ootEnumCSListType, ootEnumCSListTypeIcons
+from .motion.importer import importCutsceneData
+from .motion.exporter import getCutsceneMotionData
def checkGetFilePaths(context: Context):
@@ -56,7 +60,69 @@ def drawCSListAddOp(layout: UILayout, objName: str, collectionType):
row = box.row(align=True)
for _ in range(3):
addButton(row)
- box.label(text="Install zcamedit for camera/actor motion.")
+
+
+def insertCutsceneData(filePath: str, csName: str):
+ """Inserts the motion data in the cutscene and returns the new data"""
+ fileLines = []
+ includes = ootCutsceneIncludes("").source.split("\n")
+
+ # if the file is not found then it's likely a new file that needs to be created
+ try:
+ with open(filePath, "r") as inputFile:
+ fileLines = inputFile.readlines()
+ fileLines = fileLines[len(includes) - 1 :]
+ except FileNotFoundError:
+ fileLines = []
+
+ foundCutscene = False
+ motionExporter = getCutsceneMotionData(csName, False)
+ beginIndex = 0
+
+ for i, line in enumerate(fileLines):
+ # skip commented lines
+ if not line.startswith("//") and not line.startswith("/*"):
+ if f"CutsceneData {csName}" in line:
+ foundCutscene = True
+
+ if foundCutscene:
+ if "CS_BEGIN_CUTSCENE" in line:
+ # save the index of the line that contains the entry total and the framecount for later use
+ beginIndex = i
+
+ # looking at next line to see if we reached the end of the cs script
+ index = i + 1
+ if index < len(fileLines) and "CS_END" in fileLines[index]:
+ # exporting first to get the new framecount and the total of entries values
+ fileLines.insert(index, motionExporter.getExportData())
+
+ # update framecount and entry total values
+ beginLine = fileLines[beginIndex]
+ reMatch = re.search(r"\b\(([0-9a-fA-F, ]*)\b", beginLine)
+ if reMatch is not None:
+ params = reMatch[1].split(", ")
+ entryTotal = int(params[0], base=0)
+ frameCount = int(params[1], base=0)
+ entries = re.sub(
+ r"\b\(([0-9a-fA-F]*)\b", f"({entryTotal + motionExporter.entryTotal}", beginLine
+ )
+ frames = re.sub(r"\b([0-9a-fA-F]*)\)", f"{frameCount + motionExporter.frameCount})", beginLine)
+ fileLines[beginIndex] = f"{entries.split(', ')[0]}, {frames.split(', ')[1]}"
+ else:
+ raise PluginError("ERROR: Can't find `CS_BEGIN_CUTSCENE()` parameters!")
+ break
+
+ fileData = CData()
+
+ if not foundCutscene:
+ print(f"WARNING: Can't find Cutscene ``{csName}``, inserting data at the end of the file.")
+ motionExporter.addBeginEndCmds = True
+ csArrayName = f"CutsceneData {csName}[]"
+ fileLines.append("\n" + csArrayName + " = {\n" + motionExporter.getExportData() + "};\n")
+ fileData.header = f"{csArrayName};\n"
+
+ fileData.source = "".join(line for line in fileLines)
+ return fileData
class OOTCSTextboxAdd(Operator):
@@ -92,6 +158,26 @@ class OOTCSListAdd(Operator):
return {"FINISHED"}
+class OOT_ImportCutscene(Operator):
+ bl_idname = "object.oot_import_cutscenes"
+ bl_label = "Import All Cutscenes"
+ bl_options = {"REGISTER", "UNDO", "PRESET"}
+
+ def execute(self, context):
+ try:
+ if context.mode != "OBJECT":
+ object.mode_set(mode="OBJECT")
+
+ path = abspath(context.scene.ootCutsceneImportPath)
+ context.scene.ootCSNumber = importCutsceneData(path, None)
+
+ self.report({"INFO"}, "Successfully imported cutscenes")
+ return {"FINISHED"}
+ except Exception as e:
+ raisePluginError(self, e)
+ return {"CANCELLED"}
+
+
class OOT_ExportCutscene(Operator):
bl_idname = "object.oot_export_cutscene"
bl_label = "Export Cutscene"
@@ -113,7 +199,11 @@ class OOT_ExportCutscene(Operator):
cpath, hpath, headerfilename = checkGetFilePaths(context)
csdata = ootCutsceneIncludes(headerfilename)
converted = convertCutsceneObject(activeObj)
- csdata.append(ootCutsceneDataToC(converted, converted.name))
+
+ if context.scene.exportMotionOnly:
+ csdata.append(insertCutsceneData(cpath, activeObj.name.removeprefix("Cutscene.")))
+ else:
+ csdata.append(ootCutsceneDataToC(converted, converted.name))
writeCData(csdata, hpath, cpath)
self.report({"INFO"}, "Successfully exported cutscene")
@@ -132,19 +222,27 @@ class OOT_ExportAllCutscenes(Operator):
try:
if context.mode != "OBJECT":
object.mode_set(mode="OBJECT")
+
cpath, hpath, headerfilename = checkGetFilePaths(context)
csdata = ootCutsceneIncludes(headerfilename)
count = 0
+
for obj in context.view_layer.objects:
- if obj.type != "EMPTY" or obj.ootEmptyType != "Cutscene":
- continue
- if obj.parent is not None:
- raise PluginError("Cutscene object must not be parented to anything")
- converted = convertCutsceneObject(obj)
- csdata.append(ootCutsceneDataToC(converted, converted.name))
- count += 1
+ if obj.type == "EMPTY" and obj.ootEmptyType == "Cutscene":
+ if obj.parent is not None:
+ print(f"Parent: {obj.parent.name}, Object: {obj.name}")
+ raise PluginError("Cutscene object must not be parented to anything")
+
+ converted = convertCutsceneObject(obj)
+ if context.scene.exportMotionOnly:
+ raise PluginError("ERROR: Not implemented yet.")
+ else:
+ csdata.append(ootCutsceneDataToC(converted, converted.name))
+ count += 1
+
if count == 0:
raise PluginError("Could not find any cutscenes to export")
+
writeCData(csdata, hpath, cpath)
self.report({"INFO"}, "Successfully exported " + str(count) + " cutscenes")
return {"FINISHED"}
@@ -156,6 +254,7 @@ class OOT_ExportAllCutscenes(Operator):
oot_cutscene_classes = (
OOTCSTextboxAdd,
OOTCSListAdd,
+ OOT_ImportCutscene,
OOT_ExportCutscene,
OOT_ExportAllCutscenes,
)
@@ -166,10 +265,14 @@ def cutscene_ops_register():
register_class(cls)
Scene.ootCutsceneExportPath = StringProperty(name="File", subtype="FILE_PATH")
+ Scene.ootCutsceneImportPath = StringProperty(name="File", subtype="FILE_PATH")
+ Scene.ootCSNumber = IntProperty(default=1, min=0)
def cutscene_ops_unregister():
for cls in reversed(oot_cutscene_classes):
unregister_class(cls)
+ del Scene.ootCSNumber
+ del Scene.ootCutsceneImportPath
del Scene.ootCutsceneExportPath
diff --git a/fast64_internal/oot/cutscene/panels.py b/fast64_internal/oot/cutscene/panels.py
index f7a8323..4a0dddb 100644
--- a/fast64_internal/oot/cutscene/panels.py
+++ b/fast64_internal/oot/cutscene/panels.py
@@ -1,7 +1,9 @@
from bpy.utils import register_class, unregister_class
+from bpy.types import Scene
+from bpy.props import BoolProperty
from ...utility import prop_split
from ...panels import OOT_Panel
-from .operators import OOT_ExportCutscene, OOT_ExportAllCutscenes
+from .operators import OOT_ExportCutscene, OOT_ExportAllCutscenes, OOT_ImportCutscene
class OOT_CutscenePanel(OOT_Panel):
@@ -12,20 +14,65 @@ class OOT_CutscenePanel(OOT_Panel):
bl_category = "OOT"
def draw(self, context):
- col = self.layout.column()
- col.operator(OOT_ExportCutscene.bl_idname)
+ layout = self.layout
+
+ exportBox = layout.box()
+ exportBox.label(text="Cutscene Exporter")
+
+ col = exportBox.column()
+ if not context.scene.fast64.oot.hackerFeaturesEnabled:
+ col.prop(context.scene, "useDecompFeatures")
+ col.prop(context.scene, "exportMotionOnly")
+
+ prop_split(exportBox, context.scene, "ootCutsceneExportPath", "Export To")
+
+ activeObj = context.view_layer.objects.active
+ label = None
+ col = exportBox.column()
+ colcol = col.column()
+ if activeObj is None or activeObj.type != "EMPTY" or activeObj.ootEmptyType != "Cutscene":
+ label = "Select a cutscene object"
+
+ if activeObj is not None and activeObj.parent is not None:
+ label = "Cutscene object must not be parented to anything"
+
+ if label is not None:
+ col.label(text=label)
+ colcol.enabled = False
+
+ colcol.operator(OOT_ExportCutscene.bl_idname)
col.operator(OOT_ExportAllCutscenes.bl_idname)
- prop_split(col, context.scene, "ootCutsceneExportPath", "File")
+
+ importBox = layout.box()
+ importBox.label(text="Cutscene Importer")
+ prop_split(importBox, context.scene, "ootCutsceneImportPath", "Import From")
+
+ col = importBox.column()
+ col.operator(OOT_ImportCutscene.bl_idname)
oot_cutscene_panel_classes = (OOT_CutscenePanel,)
def cutscene_panels_register():
+ Scene.useDecompFeatures = BoolProperty(
+ name="Use Decomp for Export", description="Use names and macros from decomp when exporting", default=True
+ )
+ Scene.exportMotionOnly = BoolProperty(
+ name="Export Motion Data Only",
+ description=(
+ "Export everything or only the camera and actor motion data.\n"
+ + "This will insert the data into the cutscene."
+ ),
+ )
+
for cls in oot_cutscene_panel_classes:
register_class(cls)
def cutscene_panels_unregister():
+ del Scene.exportMotionOnly
+ del Scene.useDecompFeatures
+
for cls in oot_cutscene_panel_classes:
unregister_class(cls)
diff --git a/fast64_internal/oot/cutscene/preview.py b/fast64_internal/oot/cutscene/preview.py
new file mode 100644
index 0000000..7693099
--- /dev/null
+++ b/fast64_internal/oot/cutscene/preview.py
@@ -0,0 +1,278 @@
+import bpy
+
+from math import isclose
+from bpy.types import Scene, Object, Node
+from bpy.app.handlers import persistent
+from ...utility import gammaInverse, hexOrDecInt
+
+
+def getLerp(max: float, min: float, val: float):
+ # from ``Environment_LerpWeight()`` in decomp
+ diff = max - min
+ ret = None
+
+ if diff != 0.0:
+ ret = 1.0 - (max - val) / diff
+
+ if not ret >= 1.0:
+ return ret
+
+ return 1.0
+
+
+def getColor(value: float) -> float:
+ """Returns the value converted in the linear color space"""
+ return gammaInverse([value / 0xFF, 0.0, 0.0])[0]
+
+
+def getNode(node: Node, type: str, name: str, location: tuple[float, float]):
+ """Returns a compositor node"""
+ if node is None:
+ node = bpy.context.scene.node_tree.nodes.new(type)
+ node.select = False
+ node.name = node.label = name
+ node.location = location
+ return node
+
+
+def setupCompositorNodes():
+ """Creates or re-setups compositor nodes"""
+ if bpy.app.version < (3, 6, 0):
+ # Blender 3.6+ is required in order to use Compositor Nodes
+ return
+
+ if not bpy.context.scene.use_nodes:
+ bpy.context.scene.use_nodes = True
+
+ # sets the compositor render mode to "Camera"
+ space = None
+ for area in bpy.context.screen.areas:
+ if (area != bpy.context.area) and (area.type == "VIEW_3D"):
+ for space in area.spaces:
+ if space.type == "VIEW_3D":
+ break
+ if space is not None and space.shading.use_compositor != "CAMERA":
+ space.shading.use_compositor = "CAMERA"
+
+ # if everything's fine and nodes are already ready to use stop there
+ if bpy.context.scene.ootCSPreviewNodesReady:
+ return
+
+ # get the existing nodes
+ nodeTree = bpy.context.scene.node_tree
+ nodeRenderLayer = nodeComposite = nodeRGBTrans = nodeAlphaOver = nodeRGBMisc = nodeMixRGBMisc = None
+ for node in nodeTree.nodes.values():
+ if node.type == "R_LAYERS":
+ nodeRenderLayer = node
+ if node.type == "COMPOSITE":
+ nodeComposite = node
+ if node.label == "CSTrans_RGB":
+ nodeRGBTrans = node
+ if node.label == "CSMisc_RGB":
+ nodeRGBMisc = node
+ if node.label == "CSPreview_AlphaOver":
+ nodeAlphaOver = node
+ if node.label == "CSMisc_MixRGB":
+ nodeMixRGBMisc = node
+
+ # create or set the data of each nodes
+ nodeRenderLayer = getNode(nodeRenderLayer, "CompositorNodeRLayers", "CSPreview_RenderLayer", (-500, 0))
+ nodeRGBMisc = getNode(nodeRGBMisc, "CompositorNodeRGB", "CSMisc_RGB", (-200, -200))
+ nodeRGBTrans = getNode(nodeRGBTrans, "CompositorNodeRGB", "CSTrans_RGB", (0, -200))
+ nodeMixRGBMisc = getNode(nodeMixRGBMisc, "CompositorNodeMixRGB", "CSMisc_MixRGB", (0, 0))
+ nodeAlphaOver = getNode(nodeAlphaOver, "CompositorNodeAlphaOver", "CSPreview_AlphaOver", (200, 0))
+ nodeComposite = getNode(nodeComposite, "CompositorNodeComposite", "CSPreview_Composite", (400, 0))
+
+ # link the nodes together
+ nodeTree.links.new(nodeMixRGBMisc.inputs[1], nodeRenderLayer.outputs[0])
+ nodeTree.links.new(nodeMixRGBMisc.inputs[2], nodeRGBMisc.outputs[0])
+ nodeTree.links.new(nodeAlphaOver.inputs[1], nodeMixRGBMisc.outputs[0])
+ nodeTree.links.new(nodeAlphaOver.inputs[2], nodeRGBTrans.outputs[0])
+ nodeTree.links.new(nodeComposite.inputs[0], nodeAlphaOver.outputs[0])
+
+ # misc settings
+ nodeMixRGBMisc.use_alpha = True
+ nodeMixRGBMisc.blend_type = "COLOR"
+ bpy.context.scene.ootCSPreviewNodesReady = True
+
+
+def initFirstFrame(csObj: Object, useNodeFeatures: bool, defaultCam: Object):
+ # set default values for frame 0
+ if useNodeFeatures:
+ color = [0.0, 0.0, 0.0, 0.0]
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
+ bpy.context.scene.node_tree.nodes["CSMisc_RGB"].outputs[0].default_value = color
+ csObj.ootCutsceneProperty.preview.trigger = False
+ csObj.ootCutsceneProperty.preview.isFixedCamSet = False
+ if defaultCam is not None:
+ bpy.context.scene.camera = defaultCam
+
+
+def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, cameraObjects: Object):
+ """Execute the actions of each command to create the preview for the current frame"""
+ # this function was partially adapted from ``z_demo.c``
+
+ if curFrame == 0:
+ initFirstFrame(csObj, useNodeFeatures, cameraObjects[1])
+
+ if useNodeFeatures:
+ previewProp = csObj.ootCutsceneProperty.preview
+ for transitionCmd in csObj.ootCutsceneProperty.preview.transitionList:
+ startFrame = transitionCmd.startFrame
+ endFrame = transitionCmd.endFrame
+ frameCur = curFrame
+ isTriggerInstance = transitionCmd.type == "CS_TRANS_TRIGGER_INSTANCE"
+ linear160 = getColor(160.0)
+
+ if transitionCmd.type == "Unknown":
+ print("ERROR: Unknown command!")
+
+ if curFrame == 0:
+ # makes transitions appear a frame earlier if frame 0
+ frameCur += 1
+
+ if isTriggerInstance and not previewProp.trigger:
+ color = [linear160, linear160, linear160, 1.0]
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
+
+ if frameCur >= startFrame and frameCur <= endFrame:
+ color = [0.0, 0.0, 0.0, 0.0]
+ lerp = getLerp(endFrame, startFrame, frameCur)
+ linear255 = getColor(255.0)
+ linear155 = getColor(155.0)
+
+ if isTriggerInstance:
+ previewProp.trigger = True
+
+ if transitionCmd.type.endswith("IN"):
+ alpha = linear255 * lerp
+ else:
+ alpha = (1.0 - lerp) * linear255
+
+ if "HALF" in transitionCmd.type:
+ if "_IN_" in transitionCmd.type:
+ alpha = linear255 - ((1.0 - lerp) * linear155)
+ else:
+ alpha = linear255 - (linear155 * lerp)
+
+ if "_GRAY_" in transitionCmd.type or previewProp.trigger:
+ color[0] = color[1] = color[2] = linear160 * alpha
+ elif "_RED_" in transitionCmd.type:
+ color[0] = linear255 * alpha
+ elif "_GREEN_" in transitionCmd.type:
+ color[1] = linear255 * alpha
+ elif "_BLUE_" in transitionCmd.type:
+ color[2] = linear255 * alpha
+
+ color[3] = alpha
+ bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color
+
+ for miscCmd in csObj.ootCutsceneProperty.preview.miscList:
+ startFrame = miscCmd.startFrame
+ endFrame = miscCmd.endFrame
+
+ if miscCmd.type == "Unknown":
+ print("ERROR: Unknown command!")
+
+ if curFrame == startFrame:
+ if miscCmd.type == "CS_MISC_SET_LOCKED_VIEWPOINT" and not None in cameraObjects:
+ bpy.context.scene.camera = cameraObjects[int(csObj.ootCutsceneProperty.preview.isFixedCamSet)]
+ csObj.ootCutsceneProperty.preview.isFixedCamSet ^= True
+
+ if miscCmd.type == "CS_MISC_STOP_CUTSCENE":
+ # stop the playback and set the frame to 0
+ bpy.ops.screen.animation_cancel()
+ bpy.context.scene.frame_set(bpy.context.scene.frame_start)
+
+ if curFrame >= startFrame and (curFrame < endFrame or endFrame == startFrame):
+ if useNodeFeatures:
+ color = [0.0, 0.0, 0.0, 0.0]
+ lerp = getLerp(endFrame - 1, startFrame, curFrame)
+
+ if miscCmd.type in ["CS_MISC_VISMONO_SEPIA", "CS_MISC_VISMONO_BLACK_AND_WHITE"]:
+ if miscCmd.type == "CS_MISC_VISMONO_SEPIA":
+ col = [255.0, 180.0, 100.0]
+ else:
+ col = [255.0, 255.0, 254.0]
+
+ for i in range(3):
+ color[i] = getColor(col[i])
+
+ color[3] = getColor(255.0) * lerp
+ bpy.context.scene.node_tree.nodes["CSMisc_RGB"].outputs[0].default_value = color
+
+ if miscCmd.type == "CS_MISC_RED_PULSATING_LIGHTS":
+ color = bpy.context.scene.node_tree.nodes["CSMisc_RGB"].outputs[0].default_value
+ color[0] = getColor(255.0)
+ color[1] = color[2] = 0.0
+ step = 0.05
+ if curFrame & 8:
+ if color[3] < 0.20:
+ color[3] += step
+ else:
+ if color[3] > 0.05:
+ color[3] -= step
+ bpy.context.scene.node_tree.nodes["CSMisc_RGB"].outputs[0].default_value = color
+
+
+@persistent
+def cutscenePreviewFrameHandler(scene: Scene):
+ """Preview frame handler, executes each frame when the cutscene is played"""
+ csObj: Object = bpy.context.scene.ootCSPreviewCSObj
+
+ if csObj is None or not csObj.type == "EMPTY" and not csObj.ootEmptyType == "Cutscene":
+ return
+
+ # populate ``cameraObjects`` with the cutscene camera and the first found prerend fixed camera
+ cameraObjects = [None, None]
+ for obj in csObj.children:
+ if obj.type == "CAMERA":
+ cameraObjects[1] = obj
+ break
+
+ foundObj = None
+ for obj in bpy.data.objects:
+ if obj.type == "CAMERA" and obj.parent is not None and obj.parent.ootEmptyType in ["Scene", "Room"]:
+ camPosProp = obj.ootCameraPositionProperty
+ camTypes = ["CAM_SET_PREREND0", "CAM_SET_PREREND_FIXED"]
+ if camPosProp.camSType != "Custom" and camPosProp.camSType in camTypes:
+ foundObj = obj
+ break
+ elif camPosProp.camSType == "Custom":
+ if camPosProp.camSTypeCustom.startswith("0x"):
+ if hexOrDecInt(camPosProp.camSTypeCustom) == 25:
+ foundObj = obj
+ break
+ elif camPosProp.camSTypeCustom in camTypes:
+ foundObj = obj
+ break
+
+ if foundObj is not None:
+ cameraObjects[0] = foundObj
+
+ # setup nodes
+ bpy.context.scene.ootCSPreviewNodesReady = False
+ setupCompositorNodes()
+
+ # execute the main preview logic
+ previewProp = csObj.ootCutsceneProperty.preview
+ curFrame = bpy.context.scene.frame_current
+ if isclose(curFrame, previewProp.prevFrame, abs_tol=1) and isclose(curFrame, previewProp.nextFrame, abs_tol=1):
+ processCurrentFrame(csObj, curFrame, bpy.context.scene.ootCSPreviewNodesReady, cameraObjects)
+ else:
+ # Simulate cutscene for all frames up to present
+ for i in range(bpy.context.scene.frame_current):
+ processCurrentFrame(csObj, i, bpy.context.scene.ootCSPreviewNodesReady, cameraObjects)
+
+ # since we reached the end of the function, the current frame becomes the previous one
+ previewProp.nextFrame = curFrame + 2 if curFrame > previewProp.prevFrame else curFrame - 2
+ previewProp.prevFrame = curFrame
+
+
+def cutscene_preview_register():
+ bpy.app.handlers.frame_change_pre.append(cutscenePreviewFrameHandler)
+
+
+def cutscene_preview_unregister():
+ if cutscenePreviewFrameHandler in bpy.app.handlers.frame_change_pre:
+ bpy.app.handlers.frame_change_pre.remove(cutscenePreviewFrameHandler)
diff --git a/fast64_internal/oot/cutscene/properties.py b/fast64_internal/oot/cutscene/properties.py
index ae6d25a..f6f5625 100644
--- a/fast64_internal/oot/cutscene/properties.py
+++ b/fast64_internal/oot/cutscene/properties.py
@@ -1,10 +1,19 @@
-from bpy.types import PropertyGroup, Object, UILayout
+import bpy
+from bpy.types import PropertyGroup, Object, UILayout, Scene, Context
from bpy.props import StringProperty, EnumProperty, IntProperty, BoolProperty, CollectionProperty, PointerProperty
from bpy.utils import register_class, unregister_class
from ...utility import PluginError, prop_split
from ..oot_utility import OOTCollectionAdd, drawCollectionOps
from .operators import OOTCSTextboxAdd, drawCSListAddOp
from .constants import ootEnumCSTextboxType, ootEnumCSListType, ootEnumCSTransitionType, ootEnumCSTextboxTypeIcons
+from .motion.preview import previewFrameHandler
+
+from .motion.operators import (
+ OOTCSMotionPlayPreview,
+ OOTCSMotionCreateCameraShot,
+ OOTCSMotionCreatePlayerCueList,
+ OOTCSMotionCreateActorCueList,
+)
# Perhaps this should have been called something like OOTCSParentPropertyType,
@@ -219,6 +228,43 @@ class OOTCSListProperty(PropertyGroup):
addOp.objName = objName
+class OOTCutsceneCommandBase:
+ startFrame: IntProperty(min=0)
+ endFrame: IntProperty(min=0)
+
+
+class OOTCutsceneTransitionProperty(OOTCutsceneCommandBase, PropertyGroup):
+ type: StringProperty(default="Unknown")
+
+
+class OOTCutsceneMiscProperty(OOTCutsceneCommandBase, PropertyGroup):
+ type: StringProperty(default="Unknown")
+
+
+class OOTCutscenePreviewProperty(PropertyGroup):
+ useWidescreen: BoolProperty(
+ name="Use Widescreen Camera", default=False, update=lambda self, context: self.updateWidescreen(context)
+ )
+
+ transitionList: CollectionProperty(type=OOTCutsceneTransitionProperty)
+ miscList: CollectionProperty(type=OOTCutsceneMiscProperty)
+
+ trigger: BoolProperty(default=False) # for ``CS_TRANS_TRIGGER_INSTANCE``
+ isFixedCamSet: BoolProperty(default=False)
+ prevFrame: IntProperty(default=-1)
+ nextFrame: IntProperty(default=1)
+
+ def updateWidescreen(self, context: Context):
+ if self.useWidescreen:
+ context.scene.render.resolution_x = 426
+ else:
+ context.scene.render.resolution_x = 320
+ context.scene.render.resolution_y = 240
+
+ # force a refresh of the current frame
+ previewFrameHandler(context.scene)
+
+
class OOTCutsceneProperty(PropertyGroup):
csEndFrame: IntProperty(name="End Frame", min=0, default=100)
csWriteTerminator: BoolProperty(name="Write Terminator (Code Execution)")
@@ -227,7 +273,23 @@ class OOTCutsceneProperty(PropertyGroup):
csTermEnd: IntProperty(name="End Frm", min=0, default=100)
csLists: CollectionProperty(type=OOTCSListProperty, name="Cutscene Lists")
+ preview: PointerProperty(type=OOTCutscenePreviewProperty)
+
def draw_props(self, layout: UILayout, obj: Object):
+ split = layout.split(factor=0.5)
+ split.label(text="Player Age for Preview")
+ split.prop(bpy.context.scene, "previewPlayerAge", text="")
+
+ split = layout.split(factor=0.5)
+ split.operator(OOTCSMotionCreateCameraShot.bl_idname, icon="VIEW_CAMERA")
+ split.operator(OOTCSMotionPlayPreview.bl_idname, icon="RESTRICT_VIEW_OFF")
+
+ split = layout.split(factor=0.5)
+ split.operator(OOTCSMotionCreatePlayerCueList.bl_idname)
+ split.operator(OOTCSMotionCreateActorCueList.bl_idname)
+
+ layout.prop(self.preview, "useWidescreen")
+
layout.prop(self, "csEndFrame")
layout.prop(self, "csWriteTerminator")
if self.csWriteTerminator:
@@ -250,6 +312,9 @@ classes = (
OOTCS0x09Property,
OOTCSUnkProperty,
OOTCSListProperty,
+ OOTCutsceneTransitionProperty,
+ OOTCutsceneMiscProperty,
+ OOTCutscenePreviewProperty,
OOTCutsceneProperty,
)
@@ -259,9 +324,13 @@ def cutscene_props_register():
register_class(cls)
Object.ootCutsceneProperty = PointerProperty(type=OOTCutsceneProperty)
+ Scene.ootCSPreviewNodesReady = BoolProperty(default=False)
+ Scene.ootCSPreviewCSObj = PointerProperty(type=Object)
def cutscene_props_unregister():
+ del Scene.ootCSPreviewCSObj
+ del Scene.ootCSPreviewNodesReady
del Object.ootCutsceneProperty
for cls in reversed(classes):
diff --git a/fast64_internal/oot/cutscene_docs.md b/fast64_internal/oot/cutscene_docs.md
new file mode 100644
index 0000000..e22acb5
--- /dev/null
+++ b/fast64_internal/oot/cutscene_docs.md
@@ -0,0 +1,106 @@
+# Cutscene Documentation
+
+### Dictionnary
+- Cutscene "Command/Commands": Cutscene "Commands" are macros that will tell the game what to do in your cutscene. It can be anything from stopping the cutscene at a certain frame, doing scene transitions or even manipulating Actors during the cutscene. The array containing the commands is called a cutscene "script". You can find more informations about each commands below.
+- Camera "AT/Eye": AT means "look-at", it defines where the camera is looking, "Eye" is the position of the camera. For a person, "Eye" would be where they are and "AT" the object they are currently looking at. On Blender, the AT is a bone's tail and the Eye is a bone's head.
+- "Spline": it's the movement the camera will follow.
+- Actor "Cue": This is a system that triggers an Actor's code to make some action. Every Actor that has an implementation for this listens to the channel (defined by the "Actor Cue List" command) and will execute the action depending on the cue's action ID. This system is far from perfect, for instance two actors using the same channel will execute their actions at the same time.
+- "Seq": Sequence, it refers to the audio (most of the time it's the background music)
+
+### Commands
+More detailed informations and commands' parameters can be found [here](https://github.com/zeldaret/oot/blob/master/include/z64cutscene_commands.h)
+
+- ``CS_BEGIN_CUTSCENE``: defines the beginning of a cutscene script
+- ``CS_END``: defines the end of a cutscene script
+- ``CS_CAM_POINT``: defines a single camera point, it can be used with any of the "eye" or "at" camera commands
+- ``CS_CAM_EYE``: defines a single eye point, this feature is not used in the final game and lacks polish
+- ``CS_CAM_EYE_SPLINE``: declares a list of "eye" camera points that forms a spline
+- ``CS_CAM_AT_SPLINE``: declares a list of "at" camera points that forms a spline
+- ``CS_CAM_EYE_SPLINE_REL_TO_PLAYER`` and ``CS_CAM_AT_SPLINE_REL_TO_PLAYER``: same as the 2 above except these are relative to the player's position and yaw.
+- ``CS_MISC_LIST``: declares a list of various miscellaneous commands, they're all self-explanatory, you can find the list [here](https://github.com/zeldaret/oot/blob/master/include/z64cutscene.h#L167-L204)
+- ``CS_MISC``: defines a single misc command
+- ``CS_LIGHT_SETTING_LIST``: declares a list of light settings commands
+- ``CS_LIGHT_SETTING``: changes the light settings to the specified index, the lighting is defined in the scene
+- ``CS_RUMBLE_CONTROLLER_LIST``: declares a list of controller rumble settings
+- ``CS_RUMBLE_CONTROLLER``: makes the controller rumble, you can control the duration and the strength
+- ``CS_ACTOR_CUE_LIST`` and ``CS_PLAYER_CUE_LIST``: declares an actor cue list, this also defines the channel to use (except for player cues)
+- ``CS_ACTOR_CUE`` and ``CS_PLAYER_CUE``: defines the action to execute at the desired frame, also defines the position/rotation
+- ``CS_TEXT_LIST``: declares a list of textboxes to display on-screen, when a textbox is present the cutscene won't continue until it's closed if the textbox isn't closing automatically
+- ``CS_TEXT``: starts a textbox at the desired frame
+- ``CS_TEXT_NONE``:
+- ``CS_TEXT_OCARINA_ACTION``: defines an ocarina action, this is used when learning new songs during a cutscene
+- ``CS_TRANSITION``: fills the screen with a single color to create transitions, "half" types are used in the intro cutscene to make that "night" effect in Link's house, "trigger instance" is used in the intro part where the Deku Tree talks to Navi to keep the screen white for the duration of the command
+- ``CS_START_SEQ_LIST``: declares a list of ``CS_START_SEQ`` commands
+- ``CS_START_SEQ``: starts a sequence
+- ``CS_STOP_SEQ_LIST``: declares a list of ``CS_STOP_SEQ`` commands
+- ``CS_STOP_SEQ``: stops a sequence
+- ``CS_FADE_OUT_SEQ_LIST``: declares a list of ``CS_FADE_OUT_SEQ`` commands
+- ``CS_FADE_OUT_SEQ``: fades out a sequence player on the specified frame range
+- ``CS_TIME_LIST``: declares a list of ``CS_TIME`` commands
+- ``CS_TIME``: changes the time of day to the specified hour and minute
+- ``CS_DESTINATION``: defines a scene change (a new destination), specific destination types are doing other actions like changing the player's age or setting flags
+
+### Cutscene Motion with Fast64
+Fast64 has an implementation for creating camera shots and actor cues.
+
+- A camera shot is defined by an Armature object and the camera motion is defined by the armature's bones. Due to the game's spline algorithm for camera motion, you always need one more key point at each end, to define how the camera is moving when it approaches the last normal point. So, the minimum number of bones in the armature is 4, if you want the camera to move between the positions indicated by bones 2 to 3.
+
+When the shot / armature is selected, in the Object Properties pane there are controls for the start frame of that shot and whether it's normal, relative to Link, or the single Eye/AT point mode. When a particular key point / bone is selected, you have controls for the number of frames, view angle (FoV), and roll of the camera at that position.
+
+At export, camera shots are sorted by name, so you should name them with something they will be in the correct order with at export (e.g. Shot01, Shot02, etc.). The bones / key frames are also sorted by name, so their names must be in the order you want the motion to have. These should both be previewed correctly (i.e. if it looks right in Blender, it should work right in game)
+
+When you add a new bone by duplicating the last bone in the sequence, you must switch out of edit mode and back in for the previewer to properly handle that new bone. This only needs to be done after adding bones; you can preview while editing bones normally. This is due to how Blender represents bones differently in edit mode vs. object mode.
+
+- An Actor Cue list is defined by an empty object and child objects. The list object lets you control which the "command type" (defines the channel to use) and each child object defines the action ID. Those child objects are called "Actor Cue Point". Each cue's ending frame is defined by the next cue's starting frame. The next one also defines the end position of the previous cue, this is the design made by the original OoT devs that's why it can look a bit weird at first glance.
+
+### Details about the camera
+Additional informations about the camera, you can skip reading this. This is ported from [zcamedit's readme](https://github.com/sauraen/zcamedit#details).
+
+The camera system in game is weird, this is partly why the previewer exists. If the previewer is not behaving as you expect, it's probably working correctly! (Of course if the behavior differs between Blender and in-game, please report a bug.)
+
+First of all, the system is based on four-point spline interpolation. This means, in the simplest case you have four values A-B-C-D, and the output changes from B to C over the duration, except with the initial trajectory based on A-B and with the final trajectory based on C-D so you get a nice curve. This is used separately to interpolate eye (camera position) and at (target look-at position) as well as camera roll and view angle. If you have more values (with the caveats below), the system will move through all the values except the start and end values. So basically you need an extra camera point at the beginning and at the end to set how the camera is starting and stopping.
+
+Now, the game's version of this is weird for two reasons:
+
+