Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions fast64_internal/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -2058,3 +2058,20 @@ def get_include_data(include: str, strip: bool = False):

# return the data as a string
return data


def get_new_object(
name: str, data: Optional[Any], selectObject: bool, parentObj: Optional[bpy.types.Object]
) -> bpy.types.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
2 changes: 2 additions & 0 deletions fast64_internal/z64/collection_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def getCollection(objName, collectionType, subIndex):
collection = obj.ootAlternateSceneHeaders.cutsceneHeaders
elif collectionType == "Light":
collection = getCollectionFromIndex(obj, "lightList", subIndex, False)
elif collectionType == "ToD Light":
collection = getCollectionFromIndex(obj, "tod_lights", subIndex, False)
elif collectionType == "Exit":
collection = getCollectionFromIndex(obj, "exitList", subIndex, False)
elif collectionType == "Object":
Expand Down
20 changes: 5 additions & 15 deletions fast64_internal/z64/cutscene/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from dataclasses import dataclass, field
from bpy.types import Object
from typing import Optional

from ...utility import get_new_object
from ...game_data import game_data
from .motion.utility import getBlenderPosition, getBlenderRotation, getRotation, getInteger

Expand Down Expand Up @@ -498,26 +500,14 @@ class Cutscene:
class CutsceneObjectFactory:
"""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)
return get_new_object(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)
newArmatureObject = get_new_object(name, newArmatureData, selectObject, parentObj)
return newArmatureObject

def getNewCutsceneObject(self, name: str, frameCount: int, parentObj: Object):
Expand Down Expand Up @@ -595,7 +585,7 @@ 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 = get_new_object(name, newCamera, False, parentObj)
newCameraObj.data.display_size = displaySize
newCameraObj.data.clip_start = clipStart
newCameraObj.data.clip_end = clipEnd
Expand Down
44 changes: 21 additions & 23 deletions fast64_internal/z64/exporter/scene/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from bpy.types import Object

from ....utility import PluginError, CData, exportColor, ootGetBaseOrCustomLight, hexOrDecInt, indent
from ...scene.properties import OOTSceneHeaderProperty, OOTLightProperty
from ...scene.properties import OOTSceneHeaderProperty, OOTLightProperty, OOTLightGroupProperty
from ...utility import getEvalParamsInt
from ..utility import Utility

Expand All @@ -14,6 +14,7 @@ class EnvLightSettings:
"""This class defines the information of one environment light setting"""

envLightMode: str
setting_name: str
ambientColor: tuple[int, int, int]
light1Color: tuple[int, int, int]
light1Dir: tuple[int, int, int]
Expand Down Expand Up @@ -65,6 +66,7 @@ def from_data(raw_data: str, not_zapd_assets: bool):
lights.append(
EnvLightSettings(
"Custom",
"Custom Light Settings",
tuple(colors_and_dirs[0]),
tuple(colors_and_dirs[1]),
tuple(colors_and_dirs[2]),
Expand Down Expand Up @@ -94,11 +96,9 @@ def getDirectionValues(self, vector: tuple[int, int, int]):

return ", ".join(f"{v - 0x100 if v > 0x7F else v:5}" for v in vector)

def getEntryC(self, index: int):
def getEntryC(self):
"""Returns an environment light entry"""

isLightingCustom = self.envLightMode == "Custom"

vectors = [
(self.ambientColor, "Ambient Color", self.getColorValues),
(self.light1Dir, "Diffuse0 Direction", self.getDirectionValues),
Expand All @@ -113,21 +113,8 @@ def getEntryC(self, index: int):
(f"{self.zFar}", "Fog Far"),
]

lightDescs = ["Dawn", "Day", "Dusk", "Night"]

if not isLightingCustom and self.envLightMode == "LIGHT_MODE_TIME":
# TODO: Improve the lighting system.
# Currently Fast64 assumes there's only 4 possible settings for "Time of Day" lighting.
# This is not accurate and more complicated,
# for now we are doing ``index % 4`` to avoid having an OoB read in the list
# but this will need to be changed the day the lighting system is updated.
lightDesc = f"// {lightDescs[index % 4]} Lighting\n"
else:
isIndoor = not isLightingCustom and self.envLightMode == "LIGHT_MODE_SETTINGS"
lightDesc = f"// {'Indoor' if isIndoor else 'Custom'} No. {index + 1} Lighting\n"

lightData = (
(indent + lightDesc)
(indent + f"// {self.setting_name}\n")
+ (indent + "{\n")
+ "".join(
indent * 2 + f"{'{ ' + vecToC(vector) + ' },':26} // {desc}\n" for (vector, desc, vecToC) in vectors
Expand All @@ -152,12 +139,22 @@ def new(name: str, props: OOTSceneHeaderProperty):
envLightMode = Utility.getPropValue(props, "skyboxLighting")
lightList: dict[str, OOTLightProperty] = {}
settings: list[EnvLightSettings] = []
is_custom = props.skyboxLighting == "Custom"

if not is_custom and envLightMode == "LIGHT_MODE_TIME":
tod_lights: list[OOTLightGroupProperty] = [props.timeOfDayLights] + list(props.tod_lights)

if envLightMode == "LIGHT_MODE_TIME":
todLights = props.timeOfDayLights
lightList = {"Dawn": todLights.dawn, "Day": todLights.day, "Dusk": todLights.dusk, "Night": todLights.night}
for i, tod_light in enumerate(tod_lights):
for tod_type in ["Dawn", "Day", "Dusk", "Night"]:
setting_name = (
f"Default Settings ({tod_type})" if i == 0 else f"Light Settings No. {i} ({tod_type})"
)
lightList[setting_name] = getattr(tod_light, tod_type.lower())
else:
lightList = {str(i): light for i, light in enumerate(props.lightList)}
is_indoor = not is_custom and envLightMode == "LIGHT_MODE_SETTINGS"
lightList = {
f"{'Indoor' if is_indoor else 'Custom'} No. {i + 1}": light for i, light in enumerate(props.lightList)
}

for setting_name, lightProp in lightList.items():
try:
Expand All @@ -166,6 +163,7 @@ def new(name: str, props: OOTSceneHeaderProperty):
settings.append(
EnvLightSettings(
envLightMode,
setting_name,
exportColor(lightProp.ambient),
light1[0],
light1[1],
Expand Down Expand Up @@ -199,7 +197,7 @@ def getC(self):

# .c
lightSettingsC.source = (
(lightName + " = {\n") + "".join(light.getEntryC(i) for i, light in enumerate(self.settings)) + "};\n\n"
(lightName + " = {\n") + "".join(light.getEntryC() for i, light in enumerate(self.settings)) + "};\n\n"
)

return lightSettingsC
Expand Down
126 changes: 89 additions & 37 deletions fast64_internal/z64/importer/scene_header.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import math
import os
import re
import bpy
import mathutils
Expand All @@ -8,12 +7,12 @@
from typing import Optional

from ...game_data import game_data
from ...utility import PluginError, readFile, parentObject, hexOrDecInt, gammaInverse
from ...utility import PluginError, get_new_object, parentObject, hexOrDecInt, gammaInverse
from ...f3d.f3d_parser import parseMatrices
from ..exporter.scene.general import EnvLightSettings
from ..model_classes import OOTF3DContext
from ..scene.properties import OOTSceneHeaderProperty, OOTLightProperty
from ..utility import getEvalParams, setCustomProperty
from ..utility import setCustomProperty
from .constants import headerNames
from .utility import getDataMatch, stripName, parse_commands_data
from .classes import SharedSceneData
Expand Down Expand Up @@ -58,7 +57,7 @@ def parseDirection(index: int, values: tuple[int, int, int]) -> tuple[float, flo


def parseLight(
lightHeader: OOTLightProperty, index: int, rotation: mathutils.Euler, color: mathutils.Vector
lightHeader: OOTLightProperty, index: int, rotation: mathutils.Euler, color: mathutils.Vector, desc: str
) -> bpy.types.Object | None:
setattr(lightHeader, f"useCustomDiffuse{index}", rotation != "Zero" and rotation != "Default")

Expand All @@ -67,8 +66,8 @@ def parseLight(
setattr(lightHeader, f"diffuse{index}", color + (1,))
return None
else:
light = bpy.data.lights.new("Light", "SUN")
lightObj = bpy.data.objects.new("Light", light)
light = bpy.data.lights.new(f"{desc} Diffuse {index} Light", "SUN")
lightObj = bpy.data.objects.new(f"{desc} Diffuse {index}", light)
bpy.context.scene.collection.objects.link(lightObj)
setattr(lightHeader, f"diffuse{index}Custom", lightObj.data)
lightObj.rotation_euler = rotation
Expand All @@ -77,6 +76,39 @@ def parseLight(
return lightObj


def set_light_props(
parent_obj: bpy.types.Object,
light_props: OOTLightProperty,
header_index: int,
index: int,
light_entry: EnvLightSettings,
desc: str,
):
ambient_col = parseColor(light_entry.ambientColor)
diffuse0_dir = parseDirection(0, light_entry.light1Dir)
diffuse0_col = parseColor(light_entry.light1Color)
diffuse1_dir = parseDirection(1, light_entry.light2Dir)
diffuse1_col = parseColor(light_entry.light2Color)
fog_col = parseColor(light_entry.fogColor)

light_props.ambient = ambient_col + (1,)

lightObj0 = parseLight(light_props, 0, diffuse0_dir, diffuse0_col, desc)
lightObj1 = parseLight(light_props, 1, diffuse1_dir, diffuse1_col, desc)

if lightObj0 is not None:
parentObject(parent_obj, lightObj0)
lightObj0.location = [4 + header_index * 2, 0, -index * 2]
if lightObj1 is not None:
parentObject(parent_obj, lightObj1)
lightObj1.location = [4 + header_index * 2, 2, -index * 2]

light_props.fogColor = fog_col + (1,)
light_props.fogNear = light_entry.fogNear
light_props.z_far = light_entry.zFar
light_props.transitionSpeed = light_entry.blendRate


def parseLightList(
sceneObj: bpy.types.Object,
sceneHeader: OOTSceneHeaderProperty,
Expand All @@ -86,41 +118,61 @@ def parseLightList(
sharedSceneData: SharedSceneData,
):
lightData = getDataMatch(sceneData, lightListName, ["LightSettings", "EnvLightSettings"], "light list", strip=True)
lightList = EnvLightSettings.from_data(lightData, sharedSceneData.not_zapd_assets)

# I currently don't understand the light list format in respect to this lighting flag.
# So we'll set it to custom instead.
if sceneHeader.skyboxLighting != "Custom":
sceneHeader.skyboxLightingCustom = sceneHeader.skyboxLighting
sceneHeader.skyboxLighting = "Custom"
sceneHeader.tod_lights.clear()
sceneHeader.lightList.clear()

lightList = EnvLightSettings.from_data(lightData, sharedSceneData.not_zapd_assets)
lights_empty = None
if len(lightList) > 0:
lights_empty = get_new_object(f"{sceneObj.name} Lights (header {headerIndex})", None, False, sceneObj)
lights_empty.ootEmptyType = "None"

parent_obj = lights_empty if lights_empty is not None else sceneObj

custom_value = None
if sceneHeader.skyboxLighting == "Custom":
# try to convert the custom value to an int
try:
custom_value = hexOrDecInt(sceneHeader.skyboxLightingCustom)
except:
custom_value = None

# for older decomps, make sure it's using the right thing for convenience
if custom_value is not None and custom_value <= 1:
sceneHeader.skyboxLighting = "LIGHT_MODE_TIME" if custom_value == 0 else "LIGHT_MODE_SETTINGS"

for i, lightEntry in enumerate(lightList):
if sceneHeader.skyboxLighting == "LIGHT_MODE_TIME":
new_tod_light = sceneHeader.tod_lights.add() if i > 0 else None

settings_name = "Default Settings" if i == 0 else f"Light Settings {i}"
sub_lights_empty = get_new_object(f"(Header {headerIndex}) {settings_name}", None, False, parent_obj)
sub_lights_empty.ootEmptyType = "None"

for tod_type in ["Dawn", "Day", "Dusk", "Night"]:
desc = f"{settings_name} ({tod_type})"

if i == 0:
set_light_props(
sub_lights_empty,
getattr(sceneHeader.timeOfDayLights, tod_type.lower()),
headerIndex,
i,
lightEntry,
desc,
)
else:
assert new_tod_light is not None
set_light_props(
sub_lights_empty, getattr(new_tod_light, tod_type.lower()), headerIndex, i, lightEntry, desc
)
else:
settings_name = "Indoor" if sceneHeader.skyboxLighting != "Custom" else "Custom"
desc = f"{settings_name} {i}"

for index, lightEntry in enumerate(lightList):
ambientColor = parseColor(lightEntry.ambientColor)
diffuseDir0 = parseDirection(0, lightEntry.light1Dir)
diffuseColor0 = parseColor(lightEntry.light1Color)
diffuseDir1 = parseDirection(1, lightEntry.light2Dir)
diffuseColor1 = parseColor(lightEntry.light2Color)
fogColor = parseColor(lightEntry.fogColor)

lightHeader = sceneHeader.lightList.add()
lightHeader.ambient = ambientColor + (1,)

lightObj0 = parseLight(lightHeader, 0, diffuseDir0, diffuseColor0)
lightObj1 = parseLight(lightHeader, 1, diffuseDir1, diffuseColor1)

if lightObj0 is not None:
parentObject(sceneObj, lightObj0)
lightObj0.location = [4 + headerIndex * 2, 0, -index * 2]
if lightObj1 is not None:
parentObject(sceneObj, lightObj1)
lightObj1.location = [4 + headerIndex * 2, 2, -index * 2]

lightHeader.fogColor = fogColor + (1,)
lightHeader.fogNear = lightEntry.fogNear
lightHeader.z_far = lightEntry.zFar
lightHeader.transitionSpeed = lightEntry.blendRate
# indoor and custom modes shares the same properties
set_light_props(parent_obj, sceneHeader.lightList.add(), headerIndex, i, lightEntry, desc)


def parseExitList(sceneHeader: OOTSceneHeaderProperty, sceneData: str, exitListName: str):
Expand Down
Loading