diff --git a/fast64_internal/f3d/occlusion_planes/exporter/classes.py b/fast64_internal/f3d/occlusion_planes/exporter/classes.py index bd14bf48b..5373d1699 100644 --- a/fast64_internal/f3d/occlusion_planes/exporter/classes.py +++ b/fast64_internal/f3d/occlusion_planes/exporter/classes.py @@ -1,47 +1,48 @@ from dataclasses import dataclass from mathutils import Vector -from typing import List +from bpy.types import Object from ....utility import CData, indent @dataclass class OcclusionPlaneCandidate: - v0: Vector - v1: Vector - v2: Vector - v3: Vector + verts: tuple[Vector, Vector, Vector, Vector] weight: float - def to_c(self): - def occVertexToC(vertex: Vector): - return indent * 3 + "{" + ", ".join([str(int(round(a))) for a in vertex]) + "},\n" + def vertex_to_c(self, vertex: Vector, indent_char: str): + coords = ", ".join([str(round(a)) for a in vertex]) + return f"{indent_char}{{{coords}}},\n" + def vertices_to_c(self, indent_char: str): + return "".join(map(self.vertex_to_c, self.verts, [indent_char] * 4)) + + def to_c(self, indent_char: str) -> str: return ( - indent - + "{\n" - + indent * 2 - + "{\n" - + "".join(map(occVertexToC, [self.v0, self.v1, self.v2, self.v3])) - + indent * 2 - + "},\n" - + indent * 2 - + str(self.weight) - + "f\n" - + indent - + "},\n" + f"{indent_char}{{\n" + f"{indent_char * 2}{{\n" + f"{self.vertices_to_c(indent_char * 3)}" + f"{indent_char * 2}}},\n" + f"{indent_char * 2}{self.weight}f\n" + f"{indent_char}}},\n" ) class OcclusionPlaneCandidatesList: - def __init__(self, ownerName): - self.planes: List[OcclusionPlaneCandidate] = [] - self.name: str = ownerName + "_occlusionPlaneCandidates" + def __init__(self, owner_name: str): + self.planes: list[OcclusionPlaneCandidate] = [] + self.name: str = owner_name + "_occlusionPlaneCandidates" + self.indent_char: str = indent + + def add_plane(self, obj: Object, verts: tuple[Vector, Vector, Vector, Vector], weight: float): + self.planes.append(OcclusionPlaneCandidate(verts, weight)) - def to_c(self): + def to_c(self) -> CData: cdata = CData() if len(self.planes) > 0: - name = "OcclusionPlaneCandidate " + self.name + "[" + str(len(self.planes)) + "]" - cdata.header = "extern " + name + ";\n" - cdata.source = name + " = {\n" + "".join(candidate.to_c() for candidate in self.planes) + "};\n\n" + name = f"OcclusionPlaneCandidate {self.name}[{len(self.planes)}]" + cdata.header = f"extern {name};\n" + + plane_c_code = "".join(candidate.to_c(self.indent_char) for candidate in self.planes) + cdata.source = f"{name} = {{\n{plane_c_code}}};\n\n" return cdata diff --git a/fast64_internal/f3d/occlusion_planes/exporter/functions.py b/fast64_internal/f3d/occlusion_planes/exporter/functions.py index dae6bc11c..db4893abb 100644 --- a/fast64_internal/f3d/occlusion_planes/exporter/functions.py +++ b/fast64_internal/f3d/occlusion_planes/exporter/functions.py @@ -58,7 +58,7 @@ def addOcclusionQuads( cross0 = edge30.cross(edge01) if cross0.dot(cross1) < 0.0 or cross0.dot(cross2) < 0.0 or cross0.dot(cross3) < 0.0: raise PluginError(f"Occlusion planes mesh {obj.name} contains a quad which is not convex.") - candidatesList.planes.append(OcclusionPlaneCandidate(verts[3], verts[2], verts[1], verts[0], weight)) + candidatesList.add_plane(obj, tuple(verts), weight) if includeChildren: for child in obj.children: diff --git a/fast64_internal/sm64/sm64_collision.py b/fast64_internal/sm64/sm64_collision.py index d3982cc49..ab018043c 100644 --- a/fast64_internal/sm64/sm64_collision.py +++ b/fast64_internal/sm64/sm64_collision.py @@ -392,7 +392,7 @@ def exportCollisionCommon(obj, transformMatrix, includeSpecials, includeChildren indices.append(index) collision.triangles[collisionType].append(CollisionTriangle(indices, specialParam, room)) if includeSpecials: - area = SM64_Area(areaIndex, "", "", "", None, None, [], name, None) + area = SM64_Area(areaIndex, "", "", "", None, None, [], name, None, False) # This assumes that only levels will export with included specials, # And that the collision exporter never will. start_process_sm64_objects(obj, area, transformMatrix, True) diff --git a/fast64_internal/sm64/sm64_geolayout_bone.py b/fast64_internal/sm64/sm64_geolayout_bone.py index 9bb807453..6c7d0d5b0 100644 --- a/fast64_internal/sm64/sm64_geolayout_bone.py +++ b/fast64_internal/sm64/sm64_geolayout_bone.py @@ -258,10 +258,11 @@ def draw(self, context): prop_split(col, geo_asm, "param", "Parameter") col.prop(obj, "ignore_render") col.prop(obj, "ignore_collision") - # if bpy.context.scene.f3d_type == "F3DEX3": - # box.prop(obj, "is_occlusion_planes") - # if obj.is_occlusion_planes and (not obj.ignore_render or not obj.ignore_collision): - # box.label(icon="INFO", text="Suggest Ignore Render & Ignore Collision.") + if bpy.context.scene.f3d_type == "F3DEX3": + col.prop(obj, "is_occlusion_planes") + if obj.is_occlusion_planes and (not obj.ignore_render or not obj.ignore_collision): + col.label(icon="INFO", text="Suggest Ignore Render & Ignore Collision.") + col.label(icon="ERROR", text="May not bet implemented in your repo yet.") if context.scene.exportInlineF3D: col.prop(obj, "bleed_independently") if obj_scale_is_unified(obj) and len(obj.modifiers) == 0: diff --git a/fast64_internal/sm64/sm64_level_writer.py b/fast64_internal/sm64/sm64_level_writer.py index 969446080..50e171d09 100644 --- a/fast64_internal/sm64/sm64_level_writer.py +++ b/fast64_internal/sm64/sm64_level_writer.py @@ -817,6 +817,13 @@ def include_proto(file_name): level_data.script_data += include_proto("spline.inc.c") level_data.header_data += splinesC.header + # Write occlusion planes + if len(area.occlusion_planes.planes) > 0: + occ_planes_c = area.occlusion_planes.to_c() + saveDataToFile(os.path.join(areaDir, "occlusion_planes.inc.c"), occ_planes_c.source) + level_data.script_data += include_proto("occlusion_planes.inc.c") + level_data.header_data += occ_planes_c.header + return level_data, fModel, uses_env_fx diff --git a/fast64_internal/sm64/sm64_objects.py b/fast64_internal/sm64/sm64_objects.py index ecf4a5a88..8dfd96821 100644 --- a/fast64_internal/sm64/sm64_objects.py +++ b/fast64_internal/sm64/sm64_objects.py @@ -1,3 +1,4 @@ +import dataclasses import math, bpy, mathutils from bpy.utils import register_class, unregister_class from bpy.types import UILayout @@ -34,6 +35,11 @@ DLFormat, upgrade_old_prop, ) +from ..f3d.occlusion_planes.exporter.functions import ( + addOcclusionQuads, + OcclusionPlaneCandidate, + OcclusionPlaneCandidatesList, +) from .sm64_constants import ( levelIDNames, @@ -511,9 +517,45 @@ def to_c(self, _depth=0): ) +@dataclasses.dataclass +class SM64_OcclusionPlaneCandidate(OcclusionPlaneCandidate): + room_num: int + + def to_c(self, indent_char: str) -> str: + return ( + f"{indent_char}{{\n" + f"{indent_char * 2}{{\n" + f"{self.vertices_to_c(indent_char * 3)}" + f"{indent_char * 2}}},\n" + f"{indent_char * 2}{self.weight}f\n" + f"{indent_char * 2}{self.room_num}\n" + f"{indent_char}}},\n" + ) + + +class SM64_OcclusionPlaneCandidatesList(OcclusionPlaneCandidatesList): + def __init__(self, ownerName: str, enable_rooms: bool): + super().__init__(ownerName) + self.indent_char: str = "\t" + self.enable_rooms = enable_rooms + + def add_plane(self, obj: bpy.types.Object, verts: list[Vector], weight: float): + self.planes.append(SM64_OcclusionPlaneCandidate(verts, weight, obj.room_num if self.enable_rooms else -1)) + + class SM64_Area: def __init__( - self, index, music_seq, music_preset, terrain_type, geolayout, collision, warpNodes, name, startDialog + self, + index, + music_seq, + music_preset, + terrain_type, + geolayout, + collision, + warpNodes, + name, + startDialog, + enable_rooms: bool, ): self.cameraVolumes = [] self.puppycamVolumes = [] @@ -533,6 +575,9 @@ def __init__( self.splines = [] self.startDialog = startDialog self.custom_cmds = [] + self.occlusion_planes: SM64_OcclusionPlaneCandidatesList = SM64_OcclusionPlaneCandidatesList( + self.name, enable_rooms + ) def macros_name(self): return self.name + "_macro_objs" @@ -556,6 +601,8 @@ def to_c_script(self, includeRooms, persistentBlockString: str = ""): if self.startDialog is not None: data += "\t\tSHOW_DIALOG(0x00, " + self.startDialog + "),\n" data += "\t\tTERRAIN_TYPE(" + self.terrain_type + "),\n" + if bpy.context.scene.f3d_type == "F3DEX3" and len(self.occlusion_planes.planes) > 0: + data += f"\t\tSETUP_OCCLUSION_PLANES({self.occlusion_planes.name}),\n" data += f"{persistentBlockString}\n" data += "\tEND_AREA(),\n" return data @@ -768,8 +815,12 @@ def exportAreaCommon(areaObj, transformMatrix, geolayout, collision, name): [areaObj.warpNodes[i].to_c() for i in range(len(areaObj.warpNodes))], name, areaObj.startDialog if areaObj.showStartDialog else None, + areaObj.enableRoomSwitch, ) + if bpy.context.scene.f3d_type == "F3DEX3": + addOcclusionQuads(areaObj, area.occlusion_planes, True, transformMatrix) + start_process_sm64_objects(areaObj, area, transformMatrix, False) return area