diff --git a/__init__.py b/__init__.py index afc83898d..392edb4d7 100644 --- a/__init__.py +++ b/__init__.py @@ -462,7 +462,7 @@ def register(): bpy.types.Scene.ignoreTextureRestrictions = bpy.props.BoolProperty(name="Ignore Texture Restrictions") bpy.types.Scene.fullTraceback = bpy.props.BoolProperty(name="Show Full Error Traceback", default=False) bpy.types.Scene.gameEditorMode = bpy.props.EnumProperty( - name="Game", default="SM64", items=gameEditorEnum, update=gameEditorUpdate + name="Game", default="OOT", items=gameEditorEnum, update=gameEditorUpdate ) 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) diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 775eeb186..494710072 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -1,6 +1,7 @@ # Macros are all copied over from gbi.h from typing import Sequence import bpy, os, enum +from struct import pack from ..utility import * @@ -1391,7 +1392,7 @@ def RM_OPA_CI(clk): """ MOVEMEM indices - + Each of these indexes an entry in a dmem table which points to a 1-4 word block of dmem in which to store a 1-4 word DMA. @@ -1435,7 +1436,7 @@ def RM_OPA_CI(clk): """ MOVEWORD indices - + Each of these indexes an entry in a dmem table which points to a word in dmem in dmem where an immediate word will be stored. @@ -1983,6 +1984,11 @@ def to_binary(self): + bytearray(self.colorOrNormal) ) + def to_soh_xml(self): + baseStr = "" + data = baseStr.format(pX = self.position[0], pY = self.position[1], pZ = self.position[2], s = self.uv[0], t = self.uv[1], r = self.colorOrNormal[0], g = self.colorOrNormal[1], b = self.colorOrNormal[2], a = self.colorOrNormal[3]) + return data + def to_c(self): if bpy.context.scene.decomp_compatible: return ( @@ -2089,6 +2095,18 @@ def to_c(self): data.source += "};\n\n" return data + def to_soh_xml(self): + data = "" + + data += "\n" + + for vert in self.vertices: + data += "\t" + vert.to_soh_xml() + "\n" + + data += "\n" + + return data + def to_sm64_decomp_s(self): data = self.name + ":\n" for vert in self.vertices: @@ -2174,6 +2192,18 @@ def to_c(self, f3d): raise PluginError("Invalid GfxList format: " + str(self.DLFormat)) return data + def to_soh_xml(self, modelDirPath, objectPath): + data = "\n" + for command in self.commands: + if isinstance(command, (SPDisplayList, SPBranchList, SPVertex, DPSetTextureImage)): + data += "\t" + command.to_soh_xml(objectPath) + "\n" + else: + data += "\t" + command.to_soh_xml() + "\n" + + data += "\n\n" + + return data + def to_sm64_decomp_s(self): data = "glabel " + self.name + "\n" for command in self.commands: @@ -2299,7 +2329,7 @@ def addLight(self, key, value, fMaterial): self.lights[key] = value def addMesh(self, name, namePrefix, drawLayer, isSkinned, contextObj): - meshName = getFMeshName(name, namePrefix, drawLayer, isSkinned) + meshName = getFMeshName(self, name, namePrefix, drawLayer, isSkinned) checkUniqueBoneNames(self, meshName, name) self.meshes[meshName] = FMesh(meshName, self.DLFormat) @@ -2518,6 +2548,32 @@ def to_c_material_revert(self, gfxFormatter): data.append(self.materialRevert.to_c(self.f3d)) return data + # OTRTODO + def to_soh_xml(self, modelDirPath, objectPath): + data = "" + + #data += "\n" + for name, mesh in self.meshes.items(): + meshStatic = mesh.to_soh_xml(modelDirPath, objectPath) + data += meshStatic + #data += "\n" + + #data += "\n" + for name, lod in self.LODGroups.items(): + lodStatic = lod.to_soh_xml(modelDirPath) + data += lodStatic + #data += "\n" + + #data += "\n".format(itemCnt = len(self.materials.items())) + for materialKey, (fMaterial, texDimensions) in self.materials.items(): + data += fMaterial.to_soh_xml(modelDirPath, objectPath) + #data += "\n" + + + self.texturesSavedLastExport = self.save_soh_textures(modelDirPath) + self.freePalettes() + return data + def to_c(self, textureExportSettings: TextureExportSettings, gfxFormatter: GfxFormatter): texCSeparate = textureExportSettings.texCSeparate savePNG = textureExportSettings.savePNG @@ -2601,6 +2657,88 @@ def save_textures(self, exportPath): image.filepath = oldpath return texturesSaved + def save_soh_textures(self, exportPath): + # TODO: Saving texture should come from FImage + texturesSaved = 0 + + for (image, texInfo), texture in self.textures.items(): + if texInfo[1] == "PAL": + continue + imageFileName = texture.name + imageOutName = texture.filename[:-6] + format = -1; + + match texture.fmt: + case "G_IM_FMT_RGBA": + match texture.bitSize: + case "G_IM_SIZ_16b": + format = 2 + case "G_IM_SIZ_32b": + format = 1; + case "G_IM_FMT_CI": + match texture.bitSize: + case "G_IM_SIZ_4b": + format = 3; + case "G_IM_SIZ_8b": + format = 4; + case "G_IM_FMT_I": + match texture.bitSize: + case "G_IM_SIZ_4b": + format = 5; + case "G_IM_SIZ_8b": + format = 6; + case "G_IM_FMT_IA": + match texture.bitSize: + case "G_IM_SIZ_4b": + format = 7; + case "G_IM_SIZ_8b": + format = 8; + case "G_IM_SIZ_16b": + format = 9; + + bpy.path.abspath(image.filepath) + + isPacked = image.packed_file is not None + if not isPacked: + image.pack() + oldpath = image.filepath + try: + image.filepath = bpy.path.abspath(os.path.join(exportPath, imageFileName)) + print(imageFileName) + with open(image.filepath, "wb") as file: + # Write OTR Header + # I - Endianness + # I - Resource Type + # I - Game Version + # Q - Magic ID + # I - Resource Version + # QI - Empty space + # QQQI - Fill until 64 bytes + + # Write Texture Header + # I - Texture Type + # I - Width + # I - Height + # I - Flags + # f - H Scale + # f - V Scale + # I - Data Size + + file.write(pack("".format( + self.color[0], + self.color[1], + self.color[2], + self.normal[0], + self.normal[1], + self.normal[2] + ) + return data + def to_c(self): return ",".join( ( @@ -3056,6 +3294,14 @@ def __hash__(self): def to_binary(self): return bytearray(self.color + [0x00] + self.color + [0x00]) + def to_soh_xml(self): + data = "".format( + self.color[0], + self.color[1], + self.color[2] + ) + return data + def to_c(self): return ", ".join( ( @@ -3101,6 +3347,15 @@ def to_binary(self): + self.y2.to_bytes(4, "big") ) + def to_soh_xml(self): + data = "".format( + self.x1, + self.y1, + self.x2, + self.y2 + ) + return data + def to_c(self): return ( "Hilite " @@ -3167,6 +3422,13 @@ def to_binary(self): data += self.l[i].to_binary() return data + def to_soh_xml(self): + data = "".format(len(self.l)) + for light in self.l: + data += "\t" + light.to_soh_xml() + "\n" + data += "" + return data + def to_c(self): data = CData() data.header = "extern Lights" + str(len(self.l)) + " " + self.name + ";\n" @@ -3197,6 +3459,13 @@ def __init__(self, name): def to_binary(self): return self.l[0].to_binary() + self.l[1].to_binary() + def to_soh_xml(self): + data = "" + for light in self.l: + data += "\t" + light.to_soh_xml() + "\n" + data += "" + return data + def to_c(self): # {{}} => lookat, light array, # {{}} => light, light_t @@ -3367,6 +3636,10 @@ def to_binary(self, f3d, segments): else: return gsDma1p(f3d.G_MTX, matPtr, MTX_SIZE, self.param) + def to_soh_xml(self): + data = "{path}\" Param=\"{param}\"/>".format(path=str(self.matrix), param=self.param) + return data + def to_c(self, static=True): header = "gsSPMatrix(" if static else "gSPMatrix(glistp++, " if not static and bpy.context.scene.decomp_compatible: @@ -3414,6 +3687,11 @@ def to_binary(self, f3d, segments): else: return gsDma1p(f3d.G_VTX, vertPtr, VTX_SIZE * self.count, (self.count - 1) << 4 | self.index) + def to_soh_xml(self, objectPath): + baseStr = "" + data = baseStr.format(parent = objectPath, vertexPath = self.vertList.name, bufferIndex = self.index, vertexOffset=self.offset, count = self.count) + return data + def to_c(self, static=True): header = "gsSPVertex(" if static else "gSPVertex(glistp++, " if not static and bpy.context.scene.decomp_compatible: @@ -3454,6 +3732,12 @@ def to_binary(self, f3d, segments): else: return gsDma1p(f3d.G_MOVEMEM, vpPtr, VP_SIZE, f3d.G_MV_VIEWPORT) + def to_soh_xml(self): + data = "".format( + self.viewport.name + ) + return data + def to_c(self, static=True): header = "gsSPViewport(" if static else "gSPViewport(glistp++, " return header + "&" + self.viewport.name + ")" @@ -3476,6 +3760,12 @@ def to_binary(self, f3d, segments): dlPtr = int.from_bytes(encodeSegmentedAddr(self.displayList.startAddress, segments), "big") return gsDma1p(f3d.G_DL, dlPtr, 0, f3d.G_DL_PUSH) + def to_soh_xml(self, objectPath): + name = self.displayList.name + baseStr = "" + data = baseStr.format(path = ">" + name if "0x" in name else (objectPath + "/" + name)) + return data + def to_c(self, static=True): if static: return "gsSPDisplayList(" + self.displayList.name + ")" @@ -3502,6 +3792,11 @@ def __init__(self, displayList): def get_ptr_offsets(self, f3d): return [4] + def to_soh_xml(self): + baseStr = "{path}\"/>" + data = baseStr.format(path = self.displayList.name) + return data + def to_binary(self, f3d, segments): dlPtr = int.from_bytes(encodeSegmentedAddr(self.displayList.startAddress, segments), "big") return gsDma1p(f3d.G_DL, dlPtr, 0, f3d.G_DL_NOPUSH) @@ -3627,6 +3922,10 @@ def to_binary(self, f3d, segments): return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(v0 = self.v0, v1 = self.v1, v2 = self.v2) + return data + def to_c(self, static=True): header = "gsSP1Triangle(" if static else "gSP1Triangle(glistp++, " return header + str(self.v0) + ", " + str(self.v1) + ", " + str(self.v2) + ", " + str(self.flag) + ")" @@ -3651,6 +3950,12 @@ def to_binary(self, f3d, segments): words = _SHIFTL(f3d.G_LINE3D, 24, 8), _gsSPLine3D_w1f(self.v0, self.v1, 0, self.flag, f3d) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format( + self.v0, self.v1, self.flag + ) + return data + def to_c(self, static=True): header = "gsSPLine3D(" if static else "gSPLine3D(glistp++, " return header + str(self.v0) + ", " + str(self.v1) + ", " + str(self.flag) + ")" @@ -3676,6 +3981,12 @@ def to_binary(self, f3d, segments): words = _SHIFTL(f3d.G_LINE3D, 24, 8), _gsSPLine3D_w1f(self.v0, self.v1, self.wd, self.flag, f3d) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format( + self.v0, self.v1, self.wd, self.flag + ) + return data + def to_c(self, static=True): header = "gsSPLineW3D(" if static else "gSPLineW3D(glistp++, " return header + str(self.v0) + ", " + str(self.v1) + ", " + str(self.wd) + ", " + str(self.flag) + ")" @@ -3711,6 +4022,11 @@ def to_binary(self, f3d, segments): return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + baseStr = "" + data = baseStr.format(v00 = self.v00, v01 = self.v01, v02 = self.v02, flag0 = self.flag0, v10 = self.v10, v11 = self.v11, v12 = self.v12, flag1 = self.flag1) + return data + def to_c(self, static=True): header = "gsSP2Triangles(" if static else "gSP2Triangles(glistp++, " return ( @@ -3773,6 +4089,10 @@ def to_c(self, static=True): header = "gsSPCullDisplayList(" if static else "gSPCullDisplayList(glistp++, " return header + str(self.vstart) + ", " + str(self.vend) + ")" + def to_soh_xml(self): + data = "".format(start = self.vstart, end = self.vend) + return data + def to_sm64_decomp_s(self): return "gsSPCullDisplayList " + str(self.vstart) + ", " + str(self.vend) @@ -3788,6 +4108,12 @@ def __init__(self, segment, base): def to_binary(self, f3d, segments): return gsMoveWd(f3d.G_MW_SEGMENT, (self.segment) * 4, self.base, f3d) + def to_soh_xml(self): + data = "".format( + self.segment, self.base + ) + return data + def to_c(self, static=True): header = "gsSPSegment(" if static else "gSPSegment(glistp++, " return header + str(self.segment) + ", " + "0x" + format(self.base, "X") + ")" @@ -3820,6 +4146,10 @@ def to_c(self, static=True): header = "gsSPClipRatio(" if static else "gSPClipRatio(glistp++, " return header + str(self.ratio) + ")" + def to_soh_xml(self): + data = "".format(ratio = self.ratio) + return data + def to_sm64_decomp_s(self): return "gsSPClipRatio " + str(self.ratio) @@ -3847,6 +4177,12 @@ def to_binary(self, f3d, segments): else: return gsMoveWd(f3d.G_MW_POINTS, (self.vtx) * 40 + (self.where), self.val, f3d) + def to_soh_xml(self): + data = "".format( + self.vtx, self.where, self.val + ) + return data + def to_c(self, static=True): header = "gsSPModifyVertex(" if static else "gSPModifyVertex(glistp++, " return header + str(self.vtx) + ", " + str(self.where) + ", " + str(self.val) + ")" @@ -3884,6 +4220,12 @@ def to_binary(self, f3d, segments): + words1[1].to_bytes(4, "big") ) + def to_soh_xml(self): + data = "".format( + self.dl.name, self.vtx, self.zval + ) + return data + def to_c(self, static=True): dlName = self.dl.name header = "gsSPBranchLessZraw(" if static else "gSPBranchLessZraw(glistp++, " @@ -3914,6 +4256,12 @@ def __init__(self, n): def to_binary(self, f3d, segments): return gsMoveWd(f3d.G_MW_NUMLIGHT, f3d.G_MWO_NUMLIGHT, f3d.NUML(self.n), f3d) + def to_soh_xml(self): + data = "".format( + self.n + ) + return data + def to_c(self, static=True): header = "gsSPNumLights(" if static else "gSPNumLights(glistp++, " return header + str(self.n) + ")" @@ -3942,6 +4290,12 @@ def to_binary(self, f3d, segments): data = gsDma1p(f3d.G_MOVEMEM, lightPtr, LIGHT_SIZE, (lightIndex[self.n] - 1) * 2 + f3d.G_MV_L0) return data + def to_soh_xml(self): + data = "".format( + self.light.name, self.n + ) + return data + def to_c(self, static=True): header = "gsSPLight(" if static else "gSPLight(glistp++, " if not static and bpy.context.scene.decomp_compatible: @@ -3968,6 +4322,12 @@ def to_binary(self, f3d, segments): f3d.G_MW_LIGHTCOL, f3d.getLightMWO_b(self.n), self.col, f3d ) + def to_soh_xml(self): + data = "".format( + self.n, self.col + ) + return data + def to_c(self, static=True): header = "gsSPLightColor(" if static else "gSPLightColor(glistp++, " return header + str(self.n) + ", 0x" + format(self.col, "08X") + ")" @@ -4009,6 +4369,11 @@ def to_binary(self, f3d, segments): ) return data + # OTRTODO + def to_soh_xml(self): + data = "" + return data + def to_c(self, static=True): header = ( "gsSPSetLights" + str(len(self.lights.l)) + "(" @@ -4056,6 +4421,12 @@ def to_binary(self, f3d, segments): light0Ptr = int.from_bytes(encodeSegmentedAddr(self.la.startAddress, segments), "big") return gsSPLookAtX(light0Ptr, f3d) + gsSPLookAtY(light0Ptr + 16, f3d) + def to_soh_xml(self): + data = "".format( + self.la.name + ) + return data + def to_c(self, static=True): header = "gsSPLookAt(" if static else "gSPLookAt(glistp++, " return header + "&" + self.la.name + ")" @@ -4083,6 +4454,12 @@ def to_binary(self, f3d, segments): ((self.height - 1) * 4 + self.hilite.y1) & 0xFFF, ).to_binary(f3d, segments) + def to_soh_xml(self): + data = "".format( + self.tile, self.hilite.name, self.width, self.height + ) + return data + def to_c(self, static=True): header = "gsDPSetHilite1Tile(" if static else "gDPSetHilite1Tile(glistp++, " return ( @@ -4129,6 +4506,12 @@ def to_binary(self, f3d, segments): ((self.height - 1) * 4 + self.hilite.y2) & 0xFFF, ).to_binary(f3d, segments) + def to_soh_xml(self): + data = "".format( + self.tile, self.hilite.name, self.width, self.height + ) + return data + def to_c(self, static=True): header = "gsDPSetHilite2Tile(" if static else "gDPSetHilite2Tile(glistp++, " return ( @@ -4167,6 +4550,12 @@ def __init__(self, fm, fo): def to_binary(self, f3d, segments): return gsMoveWd(f3d.G_MW_FOG, f3d.G_MWO_FOG, (_SHIFTL(self.fm, 16, 16) | _SHIFTL(self.fo, 0, 16)), f3d) + def to_soh_xml(self): + data = "".format( + self.fm, self.fo + ) + return data + def to_c(self, static=True): header = "gsSPFogFactor(" if static else "gSPFogFactor(glistp++, " return header + str(self.fm) + ", " + str(self.fo) + ")" @@ -4194,6 +4583,12 @@ def to_binary(self, f3d, segments): f3d, ) + def to_soh_xml(self): + data = "".format( + self.minVal, self.maxVal + ) + return data + def to_c(self, static=True): header = "gsSPFogPosition(" if static else "gSPFogPosition(glistp++, " return header + str(self.minVal) + ", " + str(self.maxVal) + ")" @@ -4233,6 +4628,13 @@ def to_binary(self, f3d, segments): return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "" + + data += "".format(s = self.s, t = self.t, level = self.level, tile = self.tile, on = self.on) + + return data + def to_c(self, static=True): header = "gsSPTexture(" if static else "gSPTexture(glistp++, " return ( @@ -4277,6 +4679,12 @@ def __init__(self, s): def to_binary(self, f3d, segments): return gsMoveWd(f3d.G_MW_PERSPNORM, 0, (self.s), f3d) + def to_soh_xml(self): + data = "".format( + self.s + ) + return data + def to_c(self, static=True): header = "gsSPPerspNormalize(" if static else "gSPPerspNormalize(glistp++, " return header + str(self.s) + ")" @@ -4300,6 +4708,10 @@ 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 to_soh_xml(self): + data = "" + return data + def to_c(self, static=True): return "gsSPEndDisplayList()" if static else "gSPEndDisplayList(glistp++)" @@ -4369,6 +4781,25 @@ def to_binary(self, f3d, segments): else: raise PluginError("GeometryMode only available in F3DEX_GBI_2.") + def to_soh_xml(self): + data = " 0 else "0") + ", " @@ -4400,6 +4831,17 @@ def to_binary(self, f3d, segments): words = _SHIFTL(f3d.G_SETGEOMETRYMODE, 24, 8), word return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + # OTRTODO + def to_soh_xml(self): + data = "".format(mode = self.mode) + + return data + def to_c(self, static=True): header = "gsDPPipelineMode(" if static else "gDPPipelineMode(glistp++, " return header + self.mode + ")" @@ -4556,6 +5037,10 @@ def to_c(self, static=True): header = "gsDPSetCycleType(" if static else "gDPSetCycleType(glistp++, " return header + self.mode + ")" + def to_soh_xml(self): + data = "" + return data + def to_sm64_decomp_s(self): return "gsDPSetCycleType " + self.mode @@ -4575,6 +5060,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_TP_PERSP return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_TEXTPERSP, 1, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetTexturePersp(" if static else "gDPSetTexturePersp(glistp++, " return header + self.mode + ")" @@ -4600,6 +5091,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_TD_DETAIL return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_TEXTDETAIL, 2, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetTextureDetail(" if static else "gDPSetTextureDetail(glistp++, " return header + self.mode + ")" @@ -4623,6 +5120,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_TL_LOD return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_TEXTLOD, 1, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetTextureLOD(" if static else "gDPSetTextureLOD(glistp++, " return header + self.mode + ")" @@ -4639,6 +5142,10 @@ class DPSetTextureLUT: def __init__(self, mode): self.mode = mode + def to_soh_xml(self): + data = "".format(mode=self.mode) + return data + def to_binary(self, f3d, segments): if self.mode == "G_TT_NONE": modeVal = f3d.G_TT_NONE @@ -4675,6 +5182,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_TF_BILERP return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_TEXTFILT, 2, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetTextureFilter(" if static else "gDPSetTextureFilter(glistp++, " return header + self.mode + ")" @@ -4700,6 +5213,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_TC_FILT return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_TEXTCONV, 3, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetTextureConvert(" if static else "gDPSetTextureConvert(glistp++, " return header + self.mode + ")" @@ -4723,6 +5242,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_CK_KEY return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_COMBKEY, 1, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetCombineKey(" if static else "gDPSetCombineKey(glistp++, " return header + self.mode + ")" @@ -4759,6 +5284,12 @@ def to_binary(self, f3d, segments): modeVal = f3d.G_CD_DISABLE return gsSPSetOtherMode(f3d.G_SETOTHERMODE_H, f3d.G_MDSFT_COLORDITHER, 1, modeVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetColorDither(" if static else "gDPSetColorDither(glistp++, " return header + self.mode + ")" @@ -4789,6 +5320,12 @@ def to_binary(self, f3d, segments): else: raise PluginError("SetAlphaDither not available in HW v1.") + def to_soh_xml(self): + data = "".format( + self.mode + ) + return data + def to_c(self, static=True): header = "gsDPSetAlphaDither(" if static else "gDPSetAlphaDither(glistp++, " return header + self.mode + ")" @@ -4814,6 +5351,12 @@ def to_binary(self, f3d, segments): maskVal = f3d.G_AC_DITHER return gsSPSetOtherMode(f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_ALPHACOMPARE, 2, maskVal, f3d) + def to_soh_xml(self): + data = "".format( + self.mask + ) + return data + def to_c(self, static=True): header = "gsDPSetAlphaCompare(" if static else "gDPSetAlphaCompare(glistp++, " return header + self.mask + ")" @@ -4837,6 +5380,12 @@ def to_binary(self, f3d, segments): srcVal = f3d.G_ZS_PRIM return gsSPSetOtherMode(f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_ZSRCSEL, 1, srcVal, f3d) + def to_soh_xml(self): + data = "".format( + self.src + ) + return data + def to_c(self, static=True): header = "gsDPSetDepthSource(" if static else "gDPSetDepthSource(glistp++, " return header + self.src + ")" @@ -4900,6 +5449,19 @@ def to_binary(self, f3d, segments): else: return gsSPSetOtherMode(f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_RENDERMODE, 29, flagWord, f3d) + def to_soh_xml(self): + data = "".format(path = ">" + name if "0x" in name else (objectPath + "/" + name), fmt = self.fmt, siz = self.siz, width = self.width) + + return data + def to_c(self, static=True): header = "gsDPSetTextureImage(" if static else "gDPSetTextureImage(glistp++, " header += self.fmt + ", " + self.siz + ", " + str(self.width) + ", " @@ -5045,6 +5613,12 @@ def to_binary(self, f3d, segments): ) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + baseStr = "" + data = baseStr.format(a0=self.a0,b0=self.b0,c0=self.c0,d0=self.d0,aa0=self.Aa0,ab0=self.Ab0,ac0=self.Ac0,ad0=self.Ad0,a1=self.a1,b1=self.b1,c1=self.c1,d1=self.d1,aa1=self.Aa1,ab1=self.Ab1,ac1=self.Ac1,ad1=self.Ad1) + + return data + def to_c(self, static=True): a0 = self.a0 # 'G_CCMUX_' + self.a0 b0 = self.b0 # 'G_CCMUX_' + self.b0 @@ -5163,6 +5737,12 @@ def __init__(self, r, g, b, a): def to_binary(self, f3d, segments): return sDPRGBColor(f3d.G_SETENVCOLOR, self.r, self.g, self.b, self.a) + def to_soh_xml(self): + data = "".format( + r=self.r, g=self.g, b=self.b, a=self.a + ) + return data + def to_c(self, static=True): header = "gsDPSetEnvColor(" if static else "gDPSetEnvColor(glistp++, " return header + str(self.r) + ", " + str(self.g) + ", " + str(self.b) + ", " + str(self.a) + ")" @@ -5184,6 +5764,12 @@ def __init__(self, r, g, b, a): def to_binary(self, f3d, segments): return sDPRGBColor(f3d.G_SETBLENDCOLOR, self.r, self.g, self.b, self.a) + def to_soh_xml(self): + data = "".format( + self.r, self.g, self.b, self.a + ) + return data + def to_c(self, static=True): header = "gsDPSetBlendColor(" if static else "gDPSetBlendColor(glistp++, " return header + str(self.r) + ", " + str(self.g) + ", " + str(self.b) + ", " + str(self.a) + ")" @@ -5205,6 +5791,12 @@ def __init__(self, r, g, b, a): def to_binary(self, f3d, segments): return sDPRGBColor(f3d.G_SETFOGCOLOR, self.r, self.g, self.b, self.a) + def to_soh_xml(self): + data = "".format( + self.r, self.g, self.b, self.a + ) + return data + def to_c(self, static=True): header = "gsDPSetFogColor(" if static else "gDPSetFogColor(glistp++, " return header + str(self.r) + ", " + str(self.g) + ", " + str(self.b) + ", " + str(self.a) + ")" @@ -5223,6 +5815,12 @@ def __init__(self, d): def to_binary(self, f3d, segments): return gsDPSetColor(f3d.G_SETFILLCOLOR, self.d) + def to_soh_xml(self): + data = "".format( + self.d + ) + return data + def to_c(self, static=True): header = "gsDPSetFillColor(" if static else "gDPSetFillColor(glistp++, " return header + str(self.d) + ")" @@ -5242,6 +5840,12 @@ def __init__(self, z=0, dz=0): def to_binary(self, f3d, segments): return gsDPSetColor(f3d.G_SETPRIMDEPTH, _SHIFTL(self.z, 16, 16) | _SHIFTL(self.dz, 0, 16)) + def to_soh_xml(self): + data = "".format( + self.z, self.dz + ) + return data + def to_c(self, static=True): header = "gsDPSetPrimDepth(" if static else "gDPSetPrimDepth(glistp++, " return header + str(self.z) + ", " + str(self.dz) + ")" @@ -5268,6 +5872,10 @@ def to_binary(self, f3d, segments): ) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(m=self.m, l=self.l, r=self.r, g=self.g, b=self.b, a=self.a) + return data + def to_c(self, static=True): header = "gsDPSetPrimColor(" if static else "gDPSetPrimColor(glistp++, " return ( @@ -5315,6 +5923,10 @@ def to_binary(self, f3d, segments): words = _SHIFTL(f3d.G_RDPSETOTHERMODE, 24, 8) | _SHIFTL(self.mode0, 0, 24), self.mode1 return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "" + return data + def to_c(self, static=True): header = "gsDPSetOtherMode(" if static else "gDPSetOtherMode(glistp++, " @@ -5355,6 +5967,11 @@ def __init__(self, t, uls, ult, lrs, lrt): def to_binary(self, f3d, segments): return gsDPLoadTileGeneric(f3d.G_SETTILESIZE, self.t, self.uls, self.ult, self.lrs, self.lrt) + def to_soh_xml(self): + data = "".format(t=self.t, uls=self.uls, ult=self.ult, lrs=self.lrs, lrt=self.lrt) + + return data + def to_c(self, static=True): header = "gsDPSetTileSize(" if static else "gDPSetTileSize(glistp++, " return ( @@ -5400,6 +6017,10 @@ def __init__(self, t, uls, ult, lrs, lrt): def to_binary(self, f3d, segments): return gsDPLoadTileGeneric(f3d.G_LOADTILE, self.t, self.uls, self.ult, self.lrs, self.lrt) + def to_soh_xml(self): + data = "".format(t=self.t,uls=self.uls,ult=self.ult,lrs=self.lrs,lrt=self.lrt) + return data + def to_c(self, static=True): header = "gsDPLoadTile(" if static else "gDPLoadTile(glistp++, " return ( @@ -5471,6 +6092,12 @@ def to_binary(self, f3d, segments): ) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + baseStr = "" + data = baseStr.format(fmt=self.fmt, siz=self.siz, line=self.line, tmem=self.tmem, tile=self.tile, pal=self.palette, cms0=self.cms[0], cms1=self.cms[1], cmt0=self.cmt[0], cmt1=self.cmt[1], maskS=self.masks, shiftS=self.shifts, maskT=self.maskt, shiftT=self.shiftt) + + return data + def to_c(self, static=True): # no tabs/line breaks, breaks macros header = "gsDPSetTile(" if static else "gDPSetTile(glistp++, " @@ -5566,6 +6193,11 @@ def to_c(self, static=True): + ")" ) + def to_soh_xml(self): + data = "".format(tile=self.tile, uls=self.uls, ult=self.ult, lrs=self.lrs, dxt=self.dxt) + + return data + def to_sm64_decomp_s(self): return ( "gsDPLoadBlock " @@ -5593,6 +6225,10 @@ def to_binary(self, f3d, segments): words = _SHIFTL(f3d.G_LOADTLUT, 24, 8), _SHIFTL((self.tile), 24, 3) | _SHIFTL((self.count), 14, 10) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(tile=self.tile, count=self.count) + return data + def to_c(self, static=True): header = "gsDPLoadTLUTCmd(" if static else "gDPLoadTLUTCmd(glistp++, " return header + str(self.tile) + ", " + str(self.count) + ")" @@ -5675,6 +6311,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "gsDPLoadTextureBlock(" if static else "gDPLoadTextureBlock(glistp++, " return ( @@ -5808,6 +6450,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "gsDPLoadTextureBlockYuv(" if static else "gDPLoadTextureBlockYuv(glistp++, " return ( @@ -5947,6 +6595,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "_gsDPLoadTextureBlock(" if static else "_gDPLoadTextureBlock(glistp++, " return ( @@ -6080,6 +6734,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "gsDPLoadTextureBlock_4b(" if static else "gDPLoadTextureBlock_4b(glistp++, " return ( @@ -6215,6 +6875,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.uls, self.ult, self.lrs, self.lrt, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "gsDPLoadTextureTile(" if static else "gDPLoadTextureTile(glistp++, " return ( @@ -6364,6 +7030,12 @@ def to_binary(self, f3d, segments): ).to_binary(f3d, segments) ) + def to_soh_xml(self): + data = "".format( + self.timg.name, self.fmt, self.width, self.height, self.pal, self.cms[0], self.cms[1], self.cmt[0], self.cmt[1], self.masks, self.maskt, self.shifts, self.shiftt + ) + return data + def to_c(self, static=True): header = "gsDPLoadTextureTile_4b(" if static else "gDPLoadTextureTile_4b(glistp++, " return ( @@ -6476,6 +7148,12 @@ def to_binary(self, f3d, segments): 0, ).to_binary(f3d, segments) + def to_soh_xml(self): + data = "".format( + self.pal, self.dram.name + ) + return data + def to_c(self, static=True): header = "gsDPLoadTLUT_pal16(" if static else "gDPLoadTLUT_pal16(glistp++, " return header + str(self.pal) + ", " + "&" + self.dram.name + ")" @@ -6524,6 +7202,12 @@ def to_binary(self, f3d, segments): 0, ).to_binary(f3d, segments) + def to_soh_xml(self): + data = "".format( + self.dram.name + ) + return data + def to_c(self, static=True): header = "gsDPLoadTLUT_pal256(" if static else "gDPLoadTLUT_pal256(glistp++, " return header + "&" + self.dram.name + ")" @@ -6574,6 +7258,10 @@ def to_binary(self, f3d, segments): 0, ).to_binary(f3d, segments) + def to_soh_xml(self): + data = "".format(self.count, self.tmemaddr, self.dram.name) + return data + def to_c(self, static=True): header = "gsDPLoadTLUT(" if static else "gDPLoadTLUT(glistp++, " return header + str(self.count) + ", " + str(self.tmemaddr) + ", " + "&" + self.dram.name + ")" @@ -6609,6 +7297,10 @@ def to_binary(self, f3d, segments): ), (_SHIFTL(self.k2, 27, 5) | _SHIFTL(self.k3, 18, 9) | _SHIFTL(self.k4, 9, 9) | _SHIFTL(self.k5, 0, 9)) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(self.k0, self.k1, self.k2, self.k3, self.k4, self.k5) + return data + def to_c(self, static=True): header = "gsDPSetConvert(" if static else "gDPSetConvert(glistp++, " return ( @@ -6659,6 +7351,10 @@ def to_binary(self, f3d, segments): ) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(self.wr, self.cr, self.sr) + return data + def to_c(self, static=True): header = "gsDPSetKeyR(" if static else "gDPSetKeyR(glistp++, " return header + str(self.cR) + ", " + str(self.sR) + ", " + str(self.wR) + ")" @@ -6685,6 +7381,10 @@ def to_binary(self, f3d, segments): ) return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big") + def to_soh_xml(self): + data = "".format(self.cg, self.sg, self.wg, self.cb, self.sb, self.wb) + return data + def to_c(self, static=True): header = "gsDPSetKeyGB(" if static else "gDPSetKeyGB(glistp++, " return ( @@ -6764,6 +7464,12 @@ def to_binary(self, f3d, segments): + words[3].to_bytes(4, "big") ) + def to_soh_xml(self): + data = "".format( + self.xl, self.yl, self.xh, self.yh, self.tile, self.s, self.t, self.dsdx, self.dtdy + ) + return data + def to_c(self, static=True): header = "gsSPTextureRectangle(" if static else "gSPTextureRectangle(glistp++, " return ( @@ -6829,6 +7535,12 @@ def __init__(self, xl, yl, xh, yh, tile, s, t, dsdx=4 << 10, dtdy=1 << 10): def to_binary(self, f3d, segments): raise PluginError("SPScisTextureRectangle not implemented for binary.") + def to_soh_xml(self): + data = "".format( + self.xl, self.yl, self.xh, self.yh, self.tile, self.s, self.t, self.dsdx, self.dtdy + ) + return data + def to_c(self, static=True): if static: raise PluginError("SPScisTextureRectangle is dynamic only.") @@ -6892,6 +7604,9 @@ def __init__(self): def to_binary(self, f3d, segments): return gsDPNoParam(f3d.G_RDPFULLSYNC) + def to_soh_xml(self): + return "" + def to_c(self, static=True): return "gsDPFullSync()" if static else "gDPFullSync(glistp++)" @@ -6909,6 +7624,9 @@ def __init__(self): def to_binary(self, f3d, segments): return gsDPNoParam(f3d.G_RDPTILESYNC) + def to_soh_xml(self): + return "" + def to_c(self, static=True): return "gsDPTileSync()" if static else "gDPTileSync(glistp++)" @@ -6929,6 +7647,9 @@ def to_binary(self, f3d, segments): def to_c(self, static=True): return "gsDPPipeSync()" if static else "gDPPipeSync(glistp++)" + def to_soh_xml(self): + return "" + def to_sm64_decomp_s(self): return "gsDPPipeSync" @@ -6943,6 +7664,9 @@ def __init__(self): def to_binary(self, f3d, segments): return gsDPNoParam(f3d.G_RDPLOADSYNC) + def to_soh_xml(self): + return "" + def to_c(self, static=True): return "gsDPLoadSync()" if static else "gDPLoadSync(glistp++)" diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index a0fdedcca..7fb94ba2a 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -1865,6 +1865,7 @@ def load_handler(dummy): lib.reload() bpy.context.scene["f3d_lib_dir"] = None # force node reload! link_f3d_material_library() + break bpy.app.handlers.load_post.append(load_handler) diff --git a/fast64_internal/f3d/f3d_parser.py b/fast64_internal/f3d/f3d_parser.py index c42078dcb..4179501ff 100644 --- a/fast64_internal/f3d/f3d_parser.py +++ b/fast64_internal/f3d/f3d_parser.py @@ -540,7 +540,7 @@ def initContext(self): """ Restarts context, but keeps cached materials/textures. - Warning: calls initContext, make sure to save/restore preserved fields + Warning: calls initContext, make sure to save/restore preserved fields """ def clearGeometry(self): diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 5d5908673..7c3dc3a7b 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -248,19 +248,11 @@ def appendTile(self, sl, sh, tl, th): new_sh = max(sh, self.sh) new_tl = min(tl, self.tl) new_th = max(th, self.th) - newWidth = abs(new_sl - new_sh) + 1 - newHeight = abs(new_tl - new_th) + 1 - - tmemUsage = getTmemWordUsage(self.texFormat, newWidth, newHeight) * 8 * (2 if self.twoTextures else 1) - - if tmemUsage > self.tmemMax: - return False - else: - self.sl = new_sl - self.sh = new_sh - self.tl = new_tl - self.th = new_th - return True + self.sl = new_sl + self.sh = new_sh + self.tl = new_tl + self.th = new_th + return True def tryAdd(self, points): if len(points) == 0: @@ -1670,11 +1662,9 @@ def getTextureName(texProp: TextureProperty, fModelName: str, overrideName: str) name = tex.filepath else: name = texProp.tex_reference - texName = ( - fModelName - + "_" - + (getNameFromPath(name, True) + "_" + texFormat.lower() if overrideName is None else overrideName) - ) + + texName = ((getNameFromPath(name, True) if overrideName is None else overrideName)) + #texName = (fModelName + "_" + (getNameFromPath(name, True) + "_" + texFormat.lower() if overrideName is None else overrideName)) return texName @@ -1682,7 +1672,8 @@ def getTextureName(texProp: TextureProperty, fModelName: str, overrideName: str) def getTextureNameTexRef(texProp: TextureProperty, fModelName: str) -> str: texFormat = texProp.tex_format name = texProp.tex_reference - texName = fModelName + "_" + (getNameFromPath(name, True) + "_" + texFormat.lower()) + texName = fModelName + "_" + (getNameFromPath(name, True)) + #texName = fModelName + "_" + (getNameFromPath(name, True) + "_" + texFormat.lower()) return texName @@ -1739,14 +1730,14 @@ def saveTextureIndex( nextTmem = tmem + getTmemWordUsage(texFormat, width, height) - if not (bpy.context.scene.ignoreTextureRestrictions or fMaterial.useLargeTextures): - if nextTmem > (512 if texFormat[:2] != "CI" else 256): - raise PluginError( - 'Error in "' - + propName - + '": Textures are too big. Max TMEM size is 4k ' - + "bytes, ex. 2 32x32 RGBA 16 bit textures.\nNote that texture width will be internally padded to 64 bit boundaries." - ) + #if not (bpy.context.scene.ignoreTextureRestrictions or fMaterial.useLargeTextures): + #if nextTmem > (512 if texFormat[:2] != "CI" else 256): + # raise PluginError( + # 'Error in "' + # + propName + # + '": Textures are too big. Max TMEM size is 4k ' + # + "bytes, ex. 2 32x32 RGBA 16 bit textures.\nNote that texture width will be internally padded to 64 bit boundaries." + # ) if width > 1024 or height > 1024: raise PluginError('Error in "' + propName + '": Any side of an image cannot be greater ' + "than 1024.") @@ -2821,6 +2812,42 @@ def getWriteMethodFromEnum(enumVal): else: return matWriteMethodEnumDict[enumVal] +def exportF3DtoXML(dirPath, obj, DLFormat, transformMatrix, f3dType, isHWv1, texDir, objectPath, savePNG, texSeparate, name, matWriteMethod): + + fModel = FModel(f3dType, isHWv1, name, DLFormat, matWriteMethod) + fMesh = exportF3DCommon(obj, fModel, transformMatrix, True, name, DLFormat, not savePNG) + + modelDirPath = os.path.join(dirPath, objectPath) + + if not os.path.exists(modelDirPath): + os.makedirs(modelDirPath) + + #gfxFormatter = GfxFormatter(ScrollMethod.Vertex, 64) + exportData = fModel.to_soh_xml(modelDirPath, objectPath) + staticData = exportData + #dynamicData = exportData.dynamicData + #texC = exportData.textureData + + #if DLFormat == DLFormat.Static: + #staticData.append(dynamicData) + #else: + #geoString = writeMaterialFiles( + # dirPath, + # modelDirPath, + # '#include "actors/' + toAlnum(name) + '/header.h"', + # '#include "actors/' + toAlnum(name) + '/material.inc.h"', + # dynamicData.header, + # dynamicData.source, + # "", + # True, + #) + + #if texSeparate: + #texCFile = open(os.path.join(modelDirPath, "texture.inc.c"), "w", newline="\n") + #texCFile.write(texC.source) + #texCFile.close() + + #writeXMLData(staticData, os.path.join(modelDirPath, "model.xml")) def exportF3DtoC( dirPath, obj, DLFormat, transformMatrix, f3dType, isHWv1, texDir, savePNG, texSeparate, name, matWriteMethod @@ -2920,12 +2947,13 @@ def execute(self, context): f3dType = context.scene.f3d_type isHWv1 = context.scene.isHWv1 texDir = bpy.context.scene.DLTexDir + objectPath = bpy.context.scene.internalObjectPath savePNG = bpy.context.scene.saveTextures separateTexDef = bpy.context.scene.DLSeparateTextureDef DLName = bpy.context.scene.DLName matWriteMethod = getWriteMethodFromEnum(context.scene.matWriteMethod) - exportF3DtoC( + exportF3DtoXML( exportPath, obj, dlFormat, @@ -2933,6 +2961,7 @@ def execute(self, context): f3dType, isHWv1, texDir, + objectPath, savePNG, separateTexDef, DLName, @@ -2971,6 +3000,7 @@ def draw(self, context): col.operator(F3D_ExportDL.bl_idname) prop_split(col, context.scene, "DLName", "Name") + prop_split(col, context.scene, "internalObjectPath", "Internal Game Path") prop_split(col, context.scene, "DLExportPath", "Export Path") prop_split(col, context.scene, "blenderF3DScale", "Scale") prop_split(col, context.scene, "matWriteMethod", "Material Write Method") diff --git a/fast64_internal/oot/c_writer/oot_level_c.py b/fast64_internal/oot/c_writer/oot_level_c.py index fadcc16aa..88ed1b1d3 100644 --- a/fast64_internal/oot/c_writer/oot_level_c.py +++ b/fast64_internal/oot/c_writer/oot_level_c.py @@ -2,6 +2,7 @@ from ...f3d.f3d_gbi import ScrollMethod from ..oot_f3d_writer import OOTGfxFormatter from ..oot_collision import ootCollisionToC +from ..oot_collision import ootCollisionToSohXML from ..oot_cutscene import ootCutsceneDataToC from ..oot_utility import indent from ..oot_constants import ootRoomShapeStructs, ootRoomShapeEntryStructs @@ -780,6 +781,11 @@ def ootSceneCollisionToC(scene): sceneCollisionC.append(ootCollisionToC(scene.collision)) return sceneCollisionC +# Writes the collision data for a scene +def ootSceneCollisionToSohXML(scene): + sceneCollisionSohXML = ootCollisionToSohXML(scene.collision) + return sceneCollisionSohXML + # scene is either None or an OOTScene. This can either be the main scene itself, # or one of the alternate / cutscene headers. @@ -812,6 +818,7 @@ def ootLevelToC(scene, textureExportSettings): levelC.sceneMainC = ootSceneMainToC(scene, 0) levelC.sceneTexturesC = ootSceneTexturesToC(scene, textureExportSettings) levelC.sceneCollisionC = ootSceneCollisionToC(scene) + levelC.sceneCollisionSohXML = ootSceneCollisionToSohXML(scene) levelC.sceneCutscenesC = ootSceneCutscenesToC(scene) for i in range(len(scene.rooms)): @@ -836,6 +843,7 @@ def __init__(self): self.sceneMainC = CData() self.sceneTexturesC = CData() self.sceneCollisionC = CData() + self.sceneCollisionSohXML = "" self.sceneCutscenesC = [] # Files for room segments self.roomMainC = {} diff --git a/fast64_internal/oot/oot_collision.py b/fast64_internal/oot/oot_collision.py index 4394a61b2..9c19f9c14 100644 --- a/fast64_internal/oot/oot_collision.py +++ b/fast64_internal/oot/oot_collision.py @@ -10,6 +10,7 @@ unhideAllAndGetHiddenList, hideObjsInList, writeCData, + writeXMLData, raisePluginError, ) @@ -272,6 +273,34 @@ def exportCollisionToC(originalObj, transformMatrix, includeChildren, name, isCu addIncludeFiles(folderName, path, name) +def exportCollisionToSohXML(originalObj, transformMatrix, includeChildren, name, isCustomExport, folderName, exportPath): + collision = OOTCollision(name) + collision.cameraData = OOTCameraData(name) + + if bpy.context.scene.exportHiddenGeometry: + hiddenObjs = unhideAllAndGetHiddenList(bpy.context.scene) + + # Don't remove ignore_render, as we want to resuse this for collision + obj, allObjs = ootDuplicateHierarchy(originalObj, None, True, OOTObjectCategorizer()) + + if bpy.context.scene.exportHiddenGeometry: + hideObjsInList(hiddenObjs) + + try: + exportCollisionCommon(collision, obj, transformMatrix, includeChildren, name) + ootCleanupScene(originalObj, allObjs) + except Exception as e: + ootCleanupScene(originalObj, allObjs) + raise Exception(str(e)) + + collisionXML = ootCollisionToSohXML(collision) + + data = collisionXML + + path = ootGetPath(exportPath, isCustomExport, "assets/objects/", folderName, False, False) + writeXMLData(data, os.path.join(path, name)) + + def updateBounds(position, bounds): if len(bounds) == 0: bounds.append([position[0], position[1], position[2]]) @@ -351,6 +380,10 @@ def ootCollisionVertexToC(vertex): return "{ " + str(vertex.position[0]) + ", " + str(vertex.position[1]) + ", " + str(vertex.position[2]) + " },\n" +def ootCollisionVertexToSohXML(vertex): + return "".format(X=str(vertex.position[0]), Y=str(vertex.position[1]), Z=str(vertex.position[2])) + + def ootCollisionPolygonToC(polygon, ignoreCamera, ignoreActor, ignoreProjectile, enableConveyor, polygonTypeIndex): return ( "{ " @@ -370,12 +403,33 @@ def ootCollisionPolygonToC(polygon, ignoreCamera, ignoreActor, ignoreProjectile, ) +def ootCollisionPolygonToSohXML(polygon, ignoreCamera, ignoreActor, ignoreProjectile, enableConveyor, polygonTypeIndex): + return "".format( + Type=str(polygonTypeIndex), + VertexA=str(polygon.convertShort02(ignoreCamera, ignoreActor, ignoreProjectile)), + VertexB=str(polygon.convertShort04(enableConveyor)), + VertexC=str(polygon.convertShort06()), + NormalX=str(polygon.normal[0] * 32767), + NormalY=str(polygon.normal[1] * 32767), + NormalZ=str(polygon.normal[2] * 32767), + Dist=str(polygon.distance), + ) + + def ootPolygonTypeToC(polygonType): return ( "{ " + format(polygonType.convertHigh(), "#010x") + ", " + format(polygonType.convertLow(), "#010x") + " },\n" ) +def ootPolygonTypeToSohXML(polygonType): + return "".format( + #might be reversed + Data1=polygonType.convertHigh(), + Data2=polygonType.convertLow(), + ) + + def ootWaterBoxToC(waterBox): return ( "{ " @@ -394,6 +448,17 @@ def ootWaterBoxToC(waterBox): ) +def ootWaterBoxToSohXML(waterBox): + return "".format( + Xmin=str(waterBox.low[0]), + Ysurface=str(waterBox.height), + Zmin=str(waterBox.low[1]), + XLength=str(waterBox.high[0] - waterBox.low[0]), + ZLength=str(waterBox.high[1] - waterBox.low[1]), + Properties=str(waterBox.propertyData()) + ) + + def ootCameraDataToC(camData): posC = CData() camC = CData() @@ -424,6 +489,24 @@ def ootCameraDataToC(camData): return posC, camC +def ootCameraDataToSohXML(camData): + posXML = "" + camXML = "" + exportPosData = False + if len(camData.camPosDict) > 0: + camPosIndex = 0 + for i in range(len(camData.camPosDict)): + camXML += ootCameraEntryToSohXML(camData.camPosDict[i], camData, camPosIndex) + if camData.camPosDict[i].hasPositionData: + posXML += ootCameraPosToSohXML(camData.camPosDict[i]) + camPosIndex += 3 + exportPosData = True + + if not exportPosData: + posXML = None + return posXML, camXML + + def ootCameraPosToC(camPos): return ( "\t{ " @@ -448,6 +531,20 @@ def ootCameraPosToC(camPos): ) +def ootCameraPosToSohXML(camPos): + return "".format( + PosX=str(camPos.position[0]), + PosY=str(camPos.position[1]), + PosZ=str(camPos.position[2]), + RotX=str(camPos.rotation[0]), + RotY=str(camPos.rotation[1]), + RotZ=str(camPos.rotation[2]), + FOV=str(camPos.fov), + JfifID=str(camPos.jfifID), + Unknown=str(camPos.unknown) + ) + + def ootCameraEntryToC(camPos, camData, camPosIndex): return " ".join( ( @@ -460,6 +557,15 @@ def ootCameraEntryToC(camPos, camData, camPosIndex): ) +def ootCameraEntryToSohXML(camPos, camData, camPosIndex): + return "".format( + SType=str(camPos.camSType), + NumData=("3" if camPos.hasPositionData else "0"), + CameraPosDataSeg=(camPosIndex if camPos.hasPositionData else "0") + ) + + + def ootCollisionToC(collision): data = CData() posC, camC = ootCameraDataToC(collision.cameraData) @@ -562,6 +668,57 @@ def ootCollisionToC(collision): return data +def ootCollisionToSohXML(collision): + data = " 0: + polygonIndex = 0 + for polygonType, polygons in collision.polygonGroups.items(): + data += ootPolygonTypeToSohXML(polygonType) + for polygon in polygons: + data += ootCollisionPolygonToSohXML( + polygon, + polygonType.ignoreCameraCollision, + polygonType.ignoreActorCollision, + polygonType.ignoreProjectileCollision, + polygonType.enableConveyor, + polygonIndex, + ) + polygonIndex += 1 + + pos, cam = ootCameraDataToSohXML(collision.cameraData) + + if pos is not None: + data += pos + data += cam + + for waterBox in collision.waterBoxes: + data += ootWaterBoxToSohXML(waterBox) + + data += "" + + return data + + class OOT_ExportCollision(bpy.types.Operator): # set bl_ properties bl_idname = "object.oot_export_collision" @@ -588,7 +745,8 @@ def execute(self, context): exportPath = bpy.path.abspath(context.scene.ootColExportPath) filepath = ootGetObjectPath(isCustomExport, exportPath, folderName) - exportCollisionToC(obj, finalTransform, includeChildren, name, isCustomExport, folderName, filepath) + #exportCollisionToC(obj, finalTransform, includeChildren, name, isCustomExport, folderName, filepath) + exportCollisionToSohXML(obj, finalTransform, includeChildren, name, isCustomExport, folderName, filepath) self.report({"INFO"}, "Success!") return {"FINISHED"} diff --git a/fast64_internal/oot/oot_f3d_writer.py b/fast64_internal/oot/oot_f3d_writer.py index 2a460c723..1eb6d21f1 100644 --- a/fast64_internal/oot/oot_f3d_writer.py +++ b/fast64_internal/oot/oot_f3d_writer.py @@ -192,7 +192,12 @@ def ootProcessVertexGroup( # This means everything will be saved to one mesh. fMesh = fModel.addMesh(vertexGroup, namePrefix, drawLayerOverride, False, bone) + for mat in meshObj.material_slots: + material = mat.material + fMaterial, texDimensions = saveOrGetF3DMaterial(material, fModel, meshObj, drawLayerOverride, convertTextureData) + for material_index, faces in groupFaces.items(): + matLen = len(meshObj.material_slots) material = meshObj.material_slots[material_index].material checkForF3dMaterialInFaces(meshObj, material) fMaterial, texDimensions = saveOrGetF3DMaterial( diff --git a/fast64_internal/oot/oot_level_writer.py b/fast64_internal/oot/oot_level_writer.py index 366dc166f..27918b3e5 100644 --- a/fast64_internal/oot/oot_level_writer.py +++ b/fast64_internal/oot/oot_level_writer.py @@ -31,6 +31,7 @@ checkObjectReference, writeCDataSourceOnly, writeCDataHeaderOnly, + writeXMLData, ) from .c_writer.oot_scene_bootup import ( @@ -161,10 +162,16 @@ def ootExportSceneToC( ootPreprendSceneIncludes(scene, levelC.sceneTexturesC), os.path.join(levelPath, scene.sceneName() + "_tex.c"), ) + + ''' writeCDataSourceOnly( ootPreprendSceneIncludes(scene, levelC.sceneCollisionC), os.path.join(levelPath, scene.sceneName() + "_col.c"), ) + ''' + + writeXMLData(levelC.sceneCollisionSohXML, os.path.join(levelPath, scene.sceneName() + "_collision")) + if levelC.sceneCutscenesIsUsed(): for i in range(len(levelC.sceneCutscenesC)): writeCDataSourceOnly( diff --git a/fast64_internal/oot/oot_skeleton.py b/fast64_internal/oot/oot_skeleton.py index 92c919217..dd6fdcce6 100644 --- a/fast64_internal/oot/oot_skeleton.py +++ b/fast64_internal/oot/oot_skeleton.py @@ -27,6 +27,7 @@ readFile, raisePluginError, writeCData, + writeXMLData, toAlnum, setOrigin, getGroupNameFromIndex, @@ -67,9 +68,9 @@ class OOTSkeletonExportSettings(bpy.types.PropertyGroup): flipbookUses2DArray: bpy.props.BoolProperty(name="Has 2D Flipbook Array", default=False) flipbookArrayIndex2D: bpy.props.IntProperty(name="Index if 2D Array", default=0, min=0) customAssetIncludeDir: bpy.props.StringProperty( - name="Asset Include Directory", - default="assets/objects/object_geldb", - description="Used in #include for including image files", + name="Skeleton Output Directory", + default="objects/object_geldb", + description="Used for handling object path", ) optimize: bpy.props.BoolProperty( name="Optimize", @@ -154,6 +155,36 @@ def isFlexSkeleton(self): def limbsName(self): return self.name + "Limbs" + def toSohXML(self, modelDirPath, objectPath): + limbData = "" + data = "" + + if self.limbRoot is None: + return data + + limbList = self.createLimbList() + isFlex = self.isFlexSkeleton() + + limbData += "\n".format(lc=self.getNumLimbs(), dlC=self.getNumDLs()) + else: + limbData += "Normal\" LimbCount=\"{lc}\">\n".format(lc=self.getNumLimbs()) + + for limb in limbList: + indLimbData = limb.toSohXML(self.hasLOD, objectPath) + + writeXMLData(indLimbData, os.path.join(modelDirPath, limb.name())) + + limbData += "\t\n".format(path= objectPath if len(objectPath) > 0 else ">", name=limb.name()) + + #limbData.append(data) + + limbData += "" + + return limbData + def toC(self): limbData = CData() data = CData() @@ -232,6 +263,23 @@ def __init__( self.children = [] self.inverseRotation = None + def toSohXML(self, isLOD, objectPath): + data = " 0 else ">") + DLName + + data += "LegTransX=\"{legTransX}\" LegTransY=\"{legTransY}\" LegTransZ=\"{legTransZ}\" ChildIndex=\"{firstChildIndex}\" SiblingIndex=\"{siblingIndex}\" DisplayList1=\"{displayList1}\"/>\n".format(legTransX=int(round(self.translation[0])),legTransY=int(round(self.translation[1])),legTransZ=int(round(self.translation[2])),firstChildIndex=self.firstChildIndex,siblingIndex=self.nextSiblingIndex,displayList1=DLName) + + return data + def toC(self, isLOD): if not isLOD: data = "StandardLimb " @@ -597,7 +645,7 @@ def ootProcessBone( # Some skeletons will override the current drawn DL for a limb. # If an override DL is not NULL but the non-override is NULL, then this causes issues. # Thus for cases where we remove geometry, we need to have a dummy DL. - elif bone.use_deform: + elif bone.use_deform or DL is None: DL = OOTDLReference("gEmptyDL") if isinstance(parentLimb, OOTSkeleton): @@ -636,6 +684,101 @@ def ootProcessBone( return nextIndex, lastMaterialName +def ootConvertArmatureToXML( + originalArmatureObj: bpy.types.Object, + convertTransformMatrix: mathutils.Matrix, + f3dType: str, + isHWv1: bool, + DLFormat: DLFormat, + savePNG: bool, + drawLayer: str, + settings: OOTSkeletonExportSettings, +): + if settings.mode != "Generic" and not settings.isCustom: + importInfo = ootSkeletonImportDict[settings.mode] + skeletonName = importInfo.skeletonName + folderName = importInfo.folderName + overlayName = importInfo.actorOverlayName + flipbookUses2DArray = importInfo.flipbookArrayIndex2D is not None + flipbookArrayIndex2D = importInfo.flipbookArrayIndex2D + isLink = importInfo.isLink + else: + skeletonName = toAlnum(settings.name) + folderName = settings.customAssetIncludeDir + overlayName = settings.actorOverlayName if not settings.isCustom else None + flipbookUses2DArray = settings.flipbookUses2DArray + flipbookArrayIndex2D = settings.flipbookArrayIndex2D if flipbookUses2DArray else None + isLink = False + + exportPath = bpy.path.abspath(settings.customPath) + isCustomExport = settings.isCustom + removeVanillaData = settings.removeVanillaData + optimize = settings.optimize + + fModel = OOTModel(f3dType, isHWv1, skeletonName, DLFormat, drawLayer) + skeleton, fModel = ootConvertArmatureToSkeletonWithMesh( + originalArmatureObj, convertTransformMatrix, fModel, skeletonName, not savePNG, drawLayer, optimize + ) + + if originalArmatureObj.ootSkeleton.LOD is not None: + lodSkeleton, fModel = ootConvertArmatureToSkeletonWithMesh( + originalArmatureObj.ootSkeleton.LOD, + convertTransformMatrix, + fModel, + skeletonName + "_lod", + not savePNG, + drawLayer, + optimize, + ) + else: + lodSkeleton = None + + if lodSkeleton is not None: + skeleton.hasLOD = True + limbList = skeleton.createLimbList() + lodLimbList = lodSkeleton.createLimbList() + + if len(limbList) != len(lodLimbList): + raise PluginError( + originalArmatureObj.name + + " cannot use " + + originalArmatureObj.ootSkeleton.LOD.name + + "as LOD because they do not have the same bone structure." + ) + + for i in range(len(limbList)): + limbList[i].lodDL = lodLimbList[i].DL + limbList[i].isFlex |= lodLimbList[i].isFlex + + #data = CData() + #data.source += '#include "ultra64.h"\n#include "global.h"\n' + #if not isCustomExport: + #data.source += '#include "' + folderName + '.h"\n\n' + #else: + #data.source += "\n" + + data = "" + + path = ootGetPath(exportPath, isCustomExport, "assets/objects/", folderName, False, True) + exportData = fModel.to_soh_xml(path, settings.customAssetIncludeDir) + skeletonXML = skeleton.toSohXML(path, settings.customAssetIncludeDir) + + data += exportData + data += skeletonXML + + #if isCustomExport: + # textureArrayData = writeTextureArraysNew(fModel, flipbookArrayIndex2D) + # data.append(textureArrayData) + + writeXMLData(data, os.path.join(path, skeletonName)) + + #if not isCustomExport: + # writeTextureArraysExisting(bpy.context.scene.ootDecompPath, overlayName, isLink, flipbookArrayIndex2D, fModel) + # addIncludeFiles(folderName, path, skeletonName) + # if removeVanillaData: + # ootRemoveSkeleton(path, folderName, skeletonName) + + def ootConvertArmatureToC( originalArmatureObj: bpy.types.Object, @@ -1140,7 +1283,6 @@ def execute(self, context): raisePluginError(self, e) return {"CANCELLED"} # must return a set - class OOT_ExportSkeleton(bpy.types.Operator): # set bl_ properties bl_idname = "object.oot_export_skeleton" @@ -1178,9 +1320,9 @@ def execute(self, context): saveTextures = bpy.context.scene.saveTextures isHWv1 = context.scene.isHWv1 f3dType = context.scene.f3d_type - drawLayer = armatureObj.ootDrawLayer + drawLayer = armatureObj.ootDrawLayer - ootConvertArmatureToC( + ootConvertArmatureToXML( armatureObj, finalTransform, f3dType, isHWv1, DLFormat.Static, saveTextures, drawLayer, exportSettings ) @@ -1213,9 +1355,8 @@ def draw(self, context): col.prop(exportSettings, "isCustom") if exportSettings.isCustom: prop_split(col, exportSettings, "name", "Skeleton") - prop_split(col, exportSettings, "folder", "Object" if not exportSettings.isCustom else "Folder") - prop_split(col, exportSettings, "customAssetIncludeDir", "Asset Include Path") - prop_split(col, exportSettings, "customPath", "Path") + prop_split(col, exportSettings, "customAssetIncludeDir", "Internal Game Path") + prop_split(col, exportSettings, "customPath", "Export Path") else: prop_split(col, exportSettings, "mode", "Mode") if exportSettings.mode == "Generic": diff --git a/fast64_internal/sm64/sm64_f3d_writer.py b/fast64_internal/sm64/sm64_f3d_writer.py index 3c93f09da..908d0c21b 100644 --- a/fast64_internal/sm64/sm64_f3d_writer.py +++ b/fast64_internal/sm64/sm64_f3d_writer.py @@ -976,6 +976,7 @@ def sm64_dl_writer_register(): bpy.types.Scene.levelDLExport = bpy.props.EnumProperty(items=level_enums, name="Level", default="WF") bpy.types.Scene.DLExportGeoPtr = bpy.props.StringProperty(name="Geolayout Pointer", default="132AA8") bpy.types.Scene.overwriteGeoPtr = bpy.props.BoolProperty(name="Overwrite geolayout pointer", default=False) + bpy.types.Scene.internalObjectPath = bpy.props.StringProperty(name="Directory", default="objects/gameplay_keep") bpy.types.Scene.DLExportPath = bpy.props.StringProperty(name="Directory", subtype="FILE_PATH") bpy.types.Scene.DLExportisStatic = bpy.props.BoolProperty(name="Static DL", default=True) bpy.types.Scene.DLDefinePath = bpy.props.StringProperty(name="Definitions Filepath", subtype="FILE_PATH") diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 1ed00826b..42271772d 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -128,11 +128,14 @@ def parentObject(parent, child): bpy.ops.object.parent_set(type="OBJECT", keep_transform=True) -def getFMeshName(vertexGroup, namePrefix, drawLayer, isSkinned): - fMeshName = toAlnum(namePrefix + ("_" if namePrefix != "" else "") + vertexGroup) +def getFMeshName(fModel, vertexGroup, namePrefix, drawLayer, isSkinned): + canMessBones = len(fModel.meshes) > 0 + #fMeshName = toAlnum(namePrefix + ("_" if namePrefix != "" else "") + vertexGroup) if canMessBones else toAlnum(namePrefix) + fMeshName = vertexGroup if canMessBones else toAlnum(namePrefix) if isSkinned: fMeshName += "_skinned" - fMeshName += "_mesh" + if canMessBones: + fMeshName += "_mesh" if drawLayer is not None: fMeshName += "_layer_" + str(drawLayer) return fMeshName @@ -287,6 +290,11 @@ def propertyGroupEquals(oldProp, newProp): return equivalent +def writeXMLData(data, xmlPath): + xmlFile = open(xmlPath, "w", newline="\n", encoding="utf-8") + xmlFile.write(data) + xmlFile.close() + def writeCData(data, headerPath, sourcePath): sourceFile = open(sourcePath, "w", newline="\n", encoding="utf-8") diff --git a/fast64_updater/fast64_updater_status.json b/fast64_updater/fast64_updater_status.json new file mode 100644 index 000000000..952445e49 --- /dev/null +++ b/fast64_updater/fast64_updater_status.json @@ -0,0 +1,9 @@ +{ + "last_check": "", + "backup_date": "", + "update_ready": false, + "ignore": false, + "just_restored": false, + "just_updated": false, + "version_text": {} +} \ No newline at end of file