function LocalToEarthTime(time) return time end function EarthToLocalTime(time) return time end g_ClassesToHideInCubemaps = { "EditorVisibleObject", "EditorEntityObject", "ShaderBall" } function SetIceStrength() end -------------------------------------- Lightmodel Features -------------------------------------- LightmodelFeatureToProperties = false local function GetFeatureValuesHash(lm, feature) local prop_ids = LightmodelFeatureToProperties[feature] if not prop_ids then return 0 end local values = {} for _, prop in ipairs(prop_ids) do values[prop.id] = lm:GetProperty(prop.id) end return table.hash(values) end function CollectUniqueParts(feature) local hash_to_lmlist = {} -- fetch properties local preset_prop_id = "preset_" .. feature for _, lm in pairs(LightmodelPresets) do local part_id = lm:GetProperty(preset_prop_id) if not part_id or part_id == "" then local hash = GetFeatureValuesHash(lm, feature) local lm_list = hash_to_lmlist[hash] or {} table.insert(lm_list, lm.id) hash_to_lmlist[hash] = lm_list end end return hash_to_lmlist end function LightmodelEquivalenceByValue(lm, feature) local hash_to_list = CollectUniqueParts(feature) local current_hash = GetFeatureValuesHash(lm, feature) return hash_to_list[current_hash] or {} end function LightmodelFeatures() local p = {} for id in pairs(LightmodelFeatureToProperties) do if type(id) == "string" then table.insert(p, id) end end return p end DefineClass.LightmodelFeaturePreset = { __parents = {"Preset"}, PresetClass = "LightmodelFeaturePreset", properties = { {id = "Group", category = "Preset", editor = "choice", items = LightmodelFeatures, default = false, }, {category = "Diagnostics", id = "LightmodelRefs", read_only = true, dont_save = true, editor="preset_id_list", preset_class = "LightmodelPreset", default = false, }, {category = "Diagnostics", id = "EquivalentLMs", read_only = true, dont_save = true, editor="number", default = 0, buttons = { { name = "List", func = "ListEquivalentLMs"} } }, }, EditorMenubarName = "Lightmodel features", EditorMenubar = "Editors.Art", } function LightmodelFeaturePreset:GetFeature() return self.group end function LightmodelFeaturePreset:GetProperties() local group = self.group if not group then return self.properties end local prop_ids = LightmodelFeatureToProperties[group] if not prop_ids then return self.properties end local properties = table.copy(self.properties) table.iappend(properties, prop_ids) return properties end function LightmodelFeaturePreset:GetLightmodelRefs() local ids = {} local prop_id = "preset_" .. self:GetFeature() for lm_id, lm in pairs(LightmodelPresets) do local referenced = lm:GetProperty(prop_id) if referenced == self.id then table.insert(ids, lm_id) end end return ids end function LightmodelFeaturePreset:GetEquivalentLMs() local list = LightmodelEquivalenceByValue(self, self:GetFeature()) return #list end function LightmodelFeaturePreset:ListEquivalentLMs(root, prop_id, ged, param) local list = LightmodelEquivalenceByValue(self, self:GetFeature()) ged:ShowMessage("EquivalentLMs", table.concat(list, "\n")) end function LightmodelFeaturePreset:IsLMProperty(prop_id) local prop = self:GetPropertyMetadata(prop_id) if prop and prop.feature and prop.feature == self:GetFeature() then return prop end return false end function LightmodelFeaturePreset:OnEditorSetProperty(prop_id, old_value, ged, multi) if not self:IsLMProperty(prop_id) then return false end local value = self:GetProperty(prop_id) for _, lm_id in ipairs(self:GetLightmodelRefs()) do local lm = LightmodelPresets[lm_id] lm:SetProperty(prop_id, value) ObjModified(lm) if LightmodelOverride and LightmodelOverride == lm then lm:OnEditorSetProperty(prop_id, old_value, ged) end end end DefineClass.LightmodelPart = { __parents = {"PropertyObject"}, properties = { }, PresetClass = "LightmodelPart", } -------------------------------------- END OF Lightmodel Features -------------------------------------- -- generate migration properties, RainTypeItems = { { value = "RainLight", text = "Light" }, { value = "RainMedium", text = "Medium" }, { value = "RainHeavy", text = "Heavy" }, } DefineClass.LightmodelRain = { __parents = {"LightmodelPart"}, lightmodel_feature = "rain", lightmodel_category = "Rain", group = "LightmodelRain", properties = { { name = "Rain", id = "rain_enable", editor = "bool", default = false, help = "Switches on and off rain." }, { name = "Rain Type", id = "rain_type", editor = "combo", default = "RainMedium", items = RainTypeItems, no_edit = PropChecker("rain_enable", false) }, { name = "Rain Color", id = "rain_color", editor = "color", default = RGBA(255, 255, 255, 255), help = "Models the light properties of the comprising liquid." }, { name = "Drops Count", id = "rain_drops_count", editor = "number", slider = true, min = 1, max = const.RainMaxDropsCount, default = 1, help = "Mean rain drops count per cubic meter." }, { name = "Drop Radius", id = "rain_drop_radius", editor = "number", slider = true, min = const.RainMinDropRadius, max = const.RainMaxDropRadius, default = const.RainMinDropRadius, help = "Mean radius used to model properties such as terminal velocity, volume, sprey in microns." }, { name = "Ground Wetness", id = "rain_ground_wetness", editor = "number", slider = true, min = 0, max = 100, default = 50, scale = 100, help = "How much material properties will be changed by rain shaders." }, { name = "Lightning", id = "lightning_enable", editor = "bool", default = false, blend = const.LightningBlendThreshold }, { name = "Lightning Delay Start", id = "lightning_delay_start", editor = "number", default = 15000, scale = "sec", slider = true }, { name = "Lightning Interval Min", id = "lightning_interval_min", editor = "number", default = 3000, scale = "sec", slider = true }, { name = "Lightning Interval Max", id = "lightning_interval_max", editor = "number", default = 120000, scale = "sec", slider = true }, { name = "Lightning Chance", id = "lightning_strike_chance", editor = "number", default = 40, scale = "%", min = 0, max = 100, slider = true, help = "out of 100, rest are distant thunder." }, { name = "Lightning Chance Vertical", id = "lightning_vertical_chance", editor = "number", default = 60, scale = "%", min = 0, max = 100, slider = true, help = "If the lightning strike is vertical, otherwise it's horizontal." }, }, } function OnMsg.LightmodelSetSceneParams(view, lm_buf, time, start_offset) SetSceneParam(view, "RainEnable", lm_buf.rain_enable and 1 or 0, 0, start_offset) SetSceneParamColor(view, "RainColor", lm_buf.rain_color, time, start_offset) SetSceneParam(view, "RainDropsCount", lm_buf.rain_drops_count, time, start_offset) SetSceneParam(view, "RainDropRadius", lm_buf.rain_drop_radius, time, start_offset) SetSceneParam(view, "RainGroundWetness", lm_buf.rain_ground_wetness, time, start_offset) end local default_raw_path = insideHG() and ConvertToBenderProjectPath("/DaVinci Resolve/RAW Screenshots/") or ConvertToOSPath("AppData/RAW Screenshots") DefineClass.LightmodelColorGrading = { __parents = {"LightmodelPart"}, lightmodel_feature = "color_grading", lightmodel_category = "Color Grading", group = "LightmodelColorGrading", properties = { { name = "Gamma", id = "gamma", editor = "color", default = RGB(128, 128, 128), buttons = {{name = "Gray", func = "Gray"}}, help = "Performs nonlinear gamma correction." }, { name = "Desaturation", id = "desaturation", editor = "number", default = 0, min = -100, max = 100, scale = 100, slider = true, help = "Performs LDR color desaturation as part of tone mapping." }, { name = "Base Color Desat", id = "base_color_desat", editor = "number", default = 0, min = -1000, max = 1000, scale = 1000, slider = true, help = "Performs color desaturation on the diffuse texture" }, { name = "LUT", id = "grading_lut", editor = "preset_id", default = "Default", preset_class = "GradingLUTSource", help = string.format("Grading LUT in %s %s.", GetColorSpaceName(hr.ColorGradingLUTColorSpace), GetColorGammaName(hr.ColorGradingLUTColorGamma)) }, { name = "RAW Screenshot Path", id = "raw_screenshot_path", dont_save = true, editor = "browse", default = default_raw_path, filter = "OpenEXR (*.exr)|*.exr", os_path = true, allow_missing = true, folder = { { default_raw_path, os_path = true } }, buttons = {{name = "Screenshot", func = "CaptureRAWScreenshot"}}}, { name = "Post Grading LUT Path", id = "post_grading_lut_path", editor = "browse", default = "", filter = "LUT (*.cube)|*.cube", folder = { "svnAssets/Source/Editor/DeVinci Resolve/Viewing LUTs/" }, allow_missing = true}, { name = "Post Grading LUT Size", id = "post_grading_lut_size", editor = "choice", default = 65, items = { 16, 17, 32, 33, 64, 65 }, buttons = {{name = "Capture", func = "CapturePostGradingLUT"}} }, }, } function LightmodelColorGrading:CaptureRAWScreenshot(root, prop_id, ged) hr.PostProcRAWOutputPath = self.raw_screenshot_path end function LightmodelColorGrading:CapturePostGradingLUT(root, prop_id, ged) if self.post_grading_lut_path == "" then return end ExportToneMappingLUT(self.post_grading_lut_size, self.post_grading_lut_path) end function LightmodelColorGrading:Setpost_grading_lut_path(value) self.post_grading_lut_path = AppendDefaultExtension(value, ".cube") end function LightmodelColorGrading:Setraw_screenshot_path(value) self.raw_screenshot_path = AppendDefaultExtension(value, ".exr") end function OnMsg.LightmodelSetSceneParams(view, lm_buf, time, start_offset) SetSceneParamColor(view, "Gamma", lm_buf.gamma, time, start_offset, not "gamma-encoded") SetSceneParam(view, "Desaturation", lm_buf.desaturation, time, start_offset) SetSceneParam(view, "BaseColorDesat", lm_buf.base_color_desat, time, start_offset) local grading_lut_preset_id = lm_buf.grading_lut if grading_lut_preset_id == "" or not GradingLUTs[lm_buf.grading_lut] then grading_lut_preset_id = "Default" end local grading_lut_resource_id = ResourceManager.GetResourceID(GradingLUTs[grading_lut_preset_id]:GetResourcePath()) SetGradingLUT(view, grading_lut_resource_id, time, start_offset) end DefineClass.LightmodelOpticalAnomalies = { __parents = {"LightmodelPart"}, lightmodel_feature = "optical_anomalies", lightmodel_category = "Optical Anomalies", group = "LightmodelOpticalAnomalies", properties = { { category = "Vignette", name = "Tint Color", id = "vignette_tint_color", editor = "color", default = RGBA(0,0,0,0), help = "Vignette tint color." }, { category = "Vignette", name = "Tint Start", id = "vignette_tint_start", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 1.0, default = 0.7, help = "Start radius of the gradient towards pure color." }, { category = "Vignette", name = "Tint Feather", id = "vignette_tint_feather", editor = "number", slider = true, float = true, step = 0.001, min = 0.0, max = 1.0, default = 1.0, help = "How large the gradient towards pure color is." }, { category = "Vignette", name = "Darken Opacity", id = "vignette_darken_opacity", editor = "number", float = true, slider = true, step = 0.001, min = 0, max = 1.0, default = 0.0, help = "The opacity of the vignette layer." }, { category = "Vignette", name = "Darken Start", id = "vignette_darken_start", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 1.0, default = 0.7, help = "Start radius of the gradient towards black." }, { category = "Vignette", name = "Darken Feather", id = "vignette_darken_feather", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 1.0, default = 1.0, help = "How large the gradient towards black is." }, { category = "Vignette", name = "Circularity", id = "vignette_circularity", editor = "number", float = true, slider = true, step = 0.01, min = 0.0, max = 1.0, default = 0.0, help = "Control gradient roundness." }, { category = "Chromatic Aberration", name = "Intensity", id = "chromatic_aberration_intensity", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 10.0, default = 0.0 }, { category = "Chromatic Aberration", name = "Start", id = "chromatic_aberration_start", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 1.0, default = 0.0 }, { category = "Chromatic Aberration", name = "Feather", id = "chromatic_aberration_feather", editor = "number", float = true, slider = true, step = 0.001, min = 0.0, max = 1.0, default = 1.0 }, { category = "Chromatic Aberration", name = "Circularity", id = "chromatic_aberration_circularity", editor = "number", float = true, slider = true, step = 0.01, min = 0.0, max = 1.0, default = 0.0 }, }, } function OnMsg.LightmodelSetSceneParams(view, lm_buf, time, start_offset) local vignette_tint_max = lm_buf:GetPropertyMetadata("vignette_tint_start").max local vignette_tint_end = lm_buf.vignette_tint_start + (vignette_tint_max - lm_buf.vignette_tint_start) * lm_buf.vignette_tint_feather SetSceneParamColor(view, "VignetteTintColor", lm_buf.vignette_tint_color, time, start_offset) SetSceneParamFloat(view, "VignetteTintStart", lm_buf.vignette_tint_start, time, start_offset) SetSceneParamFloat(view, "VignetteTintEnd", vignette_tint_end, time, start_offset) SetSceneParamFloat(view, "VignetteCircularity", lm_buf.vignette_circularity, time, start_offset) local vignette_darken_max = lm_buf:GetPropertyMetadata("vignette_darken_start").max local vignette_darken_end = lm_buf.vignette_darken_start + (vignette_darken_max - lm_buf.vignette_darken_start) * lm_buf.vignette_darken_feather SetSceneParamFloat(view, "VignetteDarkenOpacity", lm_buf.vignette_darken_opacity, time, start_offset) SetSceneParamFloat(view, "VignetteDarkenStart", lm_buf.vignette_darken_start, time, start_offset) SetSceneParamFloat(view, "VignetteDarkenEnd", vignette_darken_end, time, start_offset) local chromatic_aberration_max = lm_buf:GetPropertyMetadata("chromatic_aberration_start").max local chromatic_aberration_end = lm_buf.chromatic_aberration_start + (chromatic_aberration_max - lm_buf.chromatic_aberration_start) * lm_buf.chromatic_aberration_feather SetSceneParamFloat(view, "ChromaticAberrationIntensity", lm_buf.chromatic_aberration_intensity, time, start_offset) SetSceneParamFloat(view, "ChromaticAberrationStart", lm_buf.chromatic_aberration_start, time, start_offset) SetSceneParamFloat(view, "ChromaticAberrationEnd", chromatic_aberration_end, time, start_offset) SetSceneParamFloat(view, "ChromaticAberrationCircularity", lm_buf.chromatic_aberration_circularity, time, start_offset) end DefineClass.LightmodelTranslucency = { __parents = {"LightmodelPart"}, lightmodel_feature = "translucency", lightmodel_category = "Translucency", group = "LightmodelTranslucency", properties = { { name = "Scale", id = "translucency_scale", editor = "number", float = true, slider = true, step = 0.001, default = 0.3, min = 0.0, max = 1.0, help = "Translucency overall scale" }, { name = "Distort Sun Direction", id = "translucency_distort_sun_dir", editor = "number", float = true, slider = true, default = 0.12, min = 0.0, max = 2.0, help = "How much to distort the sun direction toward the material normal" }, { name = "Sun Falloff", id = "translucency_sun_falloff", editor = "number", float = true, slider = true, step = 0.001, default = 30.0, min = 0.0, max = 50.0, help = "Power that controls how fast the sun contribution falls off when with the difference of direction between the view and the sun" }, { name = "Sun Scale", id = "translucency_sun_scale", editor = "number", float = true, slider = true, step = 0.001, default = 0.2, min = 0.0, max = 1.0, help = "Sunlight contribution scale" }, { name = "Ambient Scale", id = "translucency_ambient_scale", editor = "number", float = true, slider = true, step = 0.001, default = 0.02, min = 0.0, max = 1.0, help = "Ambient contribution scale" }, { name = "Base Luminance", id = "translucency_base_luminance", editor = "number", float = true, slider = true, step = 0.001, default = 1.335, min = 0.0, max = 3.0, help = "Assumed base light luminance" }, { name = "Base Color Temperature", id = "translucency_base_k", editor = "number", float = true, slider = true, step = 1.0, default = 3600.0, min = 1000.0, max = 6500.0, help = "Assumed sunlight base temperature in degrees K" }, { name = "Reduce Color Temperature", id = "translucency_reduce_k", editor = "number", float = true, slider = true, step = 1.0, default = 1000.0, min = 0.0, max = 5000.0, help = "Color reduction temperature in degrees K" }, { name = "Desaturation", id = "translucency_desaturation", editor = "number", float = true, slider = true, step = 0.001, default = 0.3, min = 0.0, max = 1.0, help = "Amount to desaturate the color of translucent light" }, }, } function OnMsg.LightmodelSetSceneParams(view, lm_buf, time, start_offset) SetSceneParamFloat(view, "TranslucencyScale", lm_buf.translucency_scale, time, start_offset) SetSceneParamFloat(view, "TranslucencySunDirDistort", lm_buf.translucency_distort_sun_dir, time, start_offset) SetSceneParamFloat(view, "TranslucencySunFalloff", lm_buf.translucency_sun_falloff, time, start_offset) SetSceneParamFloat(view, "TranslucencySunScale", lm_buf.translucency_sun_scale, time, start_offset) SetSceneParamFloat(view, "TranslucencyAmbientScale", lm_buf.translucency_ambient_scale, time, start_offset) SetSceneParamFloat(view, "TranslucencyBaseLuminance", lm_buf.translucency_base_luminance, time, start_offset) SetSceneParamFloat(view, "TranslucencyBaseK", lm_buf.translucency_base_k, time, start_offset) SetSceneParamFloat(view, "TranslucencyReduceK", lm_buf.translucency_reduce_k, time, start_offset) SetSceneParamFloat(view, "TranslucencyDesaturate", lm_buf.translucency_desaturation, time, start_offset) end DefineClass.LightmodelClouds = { __parents = {"LightmodelPart"}, lightmodel_feature = "clouds", lightmodel_category = "Clouds", group = "LightmodelClouds", properties = { { category = "Cloud shadows", feature = "clouds", name = "Clouds shadow strength", id = "clouds_strength", default = 0, editor = "number", slider = true, min = 0, max = 1000, scale = 1000, help = "Maximum clouds darkness. Clouds source texture path is CommonAssets/System/clouds.dds." }, { category = "Cloud shadows", feature = "clouds", name = "Clouds shadow coverage", id = "clouds_coverage", default = 500, editor = "number", slider = true, min = 0, max = 1000, scale = 1000, help = "How much of the surface is covered with clouds." }, { category = "Cloud shadows", feature = "clouds", name = "Clouds shadow smoothness", id = "clouds_smoothness", default = 300, editor = "number", slider = true, min = 0, max = 1000, scale = 1000, help = "How sharp are the edges of the clouds." }, { category = "Cloud shadows", feature = "clouds", name = "Clouds shadow scrub", id = "clouds_phase", default = 0, editor = "number", slider = true, min = 0, max = 1000, scale = 1000, help = "Scrub back and forth to see clouds move.", dont_save = true }, { category = "Cloud shadows", feature = "clouds", name = "Clouds shadow scale", id = "clouds_scale", default = 1000, editor = "number", slider = true, min = 300, max = 10000, scale = 1000, help = "When interpolating lightmodels the clouds scale should be the same, otherwise the clouds will appear to move rapidly." }, { category = "Cloud shadows", feature = "clouds", name = "Oscillation period", id = "clouds_osci_period", default = 5000, editor = "number", slider = true, min = 2000, max = 3600 * 1000, scale = 1000, }, { category = "Cloud shadows", feature = "clouds", name = "Oscillation amplitude", id = "clouds_osci_amplitude", default = 0, editor = "number", slider = true, min = 0, max = 2000, scale = 1000, }, { category = "Cloud shadows", feature = "clouds", name = "Clouds direction", id = "clouds_dir", default = 0, editor = "number", scale = "deg", slider = true, min = 0, max = 360*60, help = "The direction of cloud movement in degrees." }, { category = "Cloud shadows", feature = "clouds", name = "Wind strength", id = "clouds_wind_strength", default = 0, editor = "number", scale = 1000, slider = true, min = 0, max = 1000, help = "Should clouds be affected by wind?" }, { category = "Cloud shadows", feature = "clouds", name = "Clouds speed (m/s)", id = "clouds_speed", default = 3000, editor = "number", scale = 1000, slider = true, min = 0, max = 50*guim, help = "Clouds movement in meters per second." }, } } function OnMsg.LightmodelSetSceneParams(view, lm_buf, time, start_offset) if not config.LightModelUnusedFeatures["clouds"] then SetSceneParam(view, "CloudsStrength", lm_buf.clouds_strength, time, start_offset) SetSceneParam(view, "CloudsCoverage", lm_buf.clouds_coverage, time, start_offset) SetSceneParam(view, "CloudsSmoothness", lm_buf.clouds_smoothness, time, start_offset) SetSceneParam(view, "CloudsSpeed", lm_buf.clouds_speed, time, start_offset) SetSceneParam(view, "CloudsPhase", lm_buf.clouds_phase, time, start_offset) SetSceneParam(view, "CloudsDirectionSP", lm_buf.clouds_dir, time, start_offset) SetSceneParam(view, "CloudsWindStrength", lm_buf.clouds_wind_strength, time, start_offset) SetSceneParam(view, "CloudsScale", lm_buf.clouds_scale, time, start_offset) SetSceneParam(view, "CloudsOsciPeriod", lm_buf.clouds_osci_period, time, start_offset) SetSceneParam(view, "CloudsOsciAmplitude", lm_buf.clouds_osci_amplitude, time, start_offset) end end AutoExposureInstructions = [[To adjust exposure: - turn off via button to the right - view a representative mid-brightness scene - reset Exposure to default value - adjust brightness using sun intensity, envmaps exposure, and if all else fails, Exposure - switch to split mode using button to the right - adjust Auto Exposure until the two parts of the screen match in brightness - verify by moving the camera to a dark and to a bright spot]] CubemapInstructions = [[Usually the sky is fetched from the cubemap, but when capturing the cubemap the sky is unavailable and approximated by a slower method. - Please use the "Cubemap capture preview" toggle to see the result of the slower method in real time. - Make sure to use this for "Bake" lightmodels. ]] local sky_custom_sun_ro = function(self) return not self.sky_custom_sun or self.use_time_of_day end local shadow_range_ro = function(self) return not self.shadow end local custom_sun_ro = function(self) return self.use_time_of_day end local tod_ro = function(self) return not self.use_time_of_day end DefineClass.Lightmodel = { __parents = { "PropertyObject" }, properties = { { category = "Sun", feature = "phys_sky", name = "Sun diffuse color", id = "sun_diffuse_color", editor = "color", default = RGB(255, 255, 255), help = "The color of sunlight.\nAffects the diffuse contribution of the Sun." }, { category = "Sun", feature = "phys_sky", name = "Sun diffuse intensity", id = "sun_intensity", editor = "number", slider=true, min = 0, max = 2500, default = 100, help = "The intensity of the sun diffuse contribution." }, { category = "Sun", feature = "phys_sky", name = "Sun specular intensity", id = "sun_angular_radius", editor = "number", slider=true, min = 0, max = 2500, default = 100, help = "The intensity of the sun specular contribution." }, { category = "Sun", feature = "shadow", name = "Shadow", id = "shadow", editor = "number", slider = true, min = 0, max = 1000, scale = 1000, default = 1000, help = "Shadow strength." }, { category = "Sun", feature = "shadow", name = "Shadow range", id = "shadow_range", editor = "number", slider = true, min = 0, max = 10000 * guim, scale = "m", default = 500 * guim, help = "Limits the distance at which the shadows are visible.", read_only = shadow_range_ro }, { category = "Sun (dev tools)", feature = "sun_path", id = "_help", editor = "help", help = "These are not saved; use to tweak sun parameters for testing." }, { category = "Sun (dev tools)", feature = "sun_path", name = "Sunrise time", id = "sunrise_time", editor = "number", default = 8*60, help = "", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "Sunset time", id = "sunset_time", editor = "number", default = 20*60, help = "", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "Earth time info", id = "sun_earthtime_info", editor = "text", default = "", help = "", dont_save = true, read_only = true}, { category = "Sun (dev tools)", feature = "sun_path", name = "Sunrise azi", id = "sunrise_azi", editor = "number", default = 54*60, slider = true, min = 0*60, max = 360*60, scale = "deg", help = "Azimuth is from 0Deg North, 90Deg East ... 360Deg. Sunrise azi + Sunset azi generally should make 360Deg.", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "Sunset azi", id = "sunset_azi", editor = "number", default = 306*60, help = "Azimuth is from 0Deg North, 90Deg East ... 360Deg. Sunrise azi + Sunset azi generally should make 360Deg.", slider = true, min = 0*60, max = 360*60, scale = "deg", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "Sun max elevation", id = "sun_max_elevation", editor = "number", default = 70*60, help = "This is the maximum angle from the horizon to the center of the sun disk (reached at noon).", slider = true, min = 0, max = 89*60, scale = "deg", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "Sun shadow min", id = "sun_shadow_min", editor = "number", default = 15*60, help = "This is the min elevation that the sun will cast shadows from to avoid super long shadows.", slider = true, min = 0, max = 89*60, scale = "deg", dont_save = true }, { category = "Sun (dev tools)", feature = "sun_path", name = "North rotation", id = "sun_nr", editor = "number", default = 0, help = "Add this angle to all azimuths to effectively rotate where North is on the map.", slider = true, min = 0*60, max = 360*60, scale = "deg", dont_save = true }, { category = "Sun Pos", feature = "phys_sky", name = "Use time of day", id = "use_time_of_day", editor = "bool", default = true, help = "Sun position is determined by the current time of day setup." }, { category = "Sun Pos", feature = "phys_sky", name = "Start Time (h)", id = "time", editor = "number", default = 12*60, scale = 60, min = 0, max = 24*60, slider = true, help = "This is the time when the blending to this model will start. View shows it ignoring blending.", read_only = tod_ro, buttons = {{name = "View", func = "PreviewStart"}}}, { category = "Sun Pos", feature = "phys_sky", name = "End Time", id = "time_next", editor = "text", default = "", help = "This is the time when the blending to next model will start. View shows it ignoring blending.", read_only = true, dont_save = true, buttons = {{name = "View", func = "PreviewEnd"}}}, { category = "Sun Pos", feature = "phys_sky", name = "Blend Duration (m)", id = "blend_time", editor = "number", default = 60, help = "Controls the duration of the blend. View buttons take into account the blending, i.e. 'Start' shows accurately the end of the previous model.", read_only = tod_ro, buttons = {{name = "Start", func = "PreviewBlendStart"}, {name = "End", func = "PreviewBlendEnd"}, {name = "Preview", func = "PreviewBlend"}}}, { category = "Sun Pos", feature = "phys_sky", name = "Sun alt", id = "sun_alt", min = -1800, max = 1800, editor = "number", slider = true, scale = 10, default = 250, help = "At what angle (in 1/10 degrees) relative to the horizon (height) is the Sun.", read_only = custom_sun_ro }, { category = "Sun Pos", feature = "phys_sky", name = "Sun shadow alt", id = "sun_shadow_height", editor = "number", default = 0, slider = true, scale = 10, min = 0, max = 1800, help = "0 = Use sun alt for shadow direction. > 0 forces shadows as if the sun is at this altitutude", read_only = custom_sun_ro}, { category = "Sun Pos", feature = "phys_sky", name = "Sun azi", id = "sun_azi", min = 0, max = 360, editor = "number" , slider = true, default = 180, help = "The position of the Sun relative to the world, specified as an angle in degrees.", read_only = custom_sun_ro }, { category = "Sun Pos", feature = "phys_sky", name = "Sky custom sun", id = "sky_custom_sun", editor = "bool" , default = false, help = "If checked allows overriding the disk sun position with a separate custom one for the sun in the sky.", read_only = custom_sun_ro }, { category = "Sun Pos", feature = "phys_sky", name = "Sky custom sun azi", id = "sky_custom_sun_azi", min = 0, max = 360, editor = "number" , slider = true, default = 180, help = "The azimuth of Sun in the Sky, specified as an angle in degrees." , read_only = sky_custom_sun_ro }, { category = "Sun Pos", feature = "phys_sky", name = "Sky custom sun alt", id = "sky_custom_sun_alt", min = -1800, max = 1800, editor = "number", slider = true, default = 0, help = "The altitude of Sun in the Sky, specified as an angle in degrees.", read_only = sky_custom_sun_ro}, { category = "Sky", feature = "phys_sky", id = "__", editor = "help", name = "Cubemap help", help = CubemapInstructions, }, { category = "Sky", feature = "phys_sky", name = "Mie coefs", id = "mie_coefs", editor = "color", default = RGB(210, 210, 210), help = "Mie coefficients control sun color. It is also affected by the Rayleight param." }, { category = "Sky", feature = "phys_sky", name = "Rayleigh coefs", id = "ray_coefs", editor = "color", default = RGB(55, 130, 221), help = "Rayleigh coefficient control sky color. It is also affected by the Mie param." }, { category = "Sky", feature = "phys_sky", name = "Mie scale height", id = "mie_sh", editor = "number", slider = true, min=100, max = 10000, default = 1200, help = "Mie scale height controls the height at which the atmosphere is half dense for this scattering." }, { category = "Sky", feature = "phys_sky", name = "Rayleigh scale height", id = "ray_sh", editor = "number", slider = true, min=1000, max = 16000, default = 7994, help = "Rayleigh scale height controls the height at which the atmosphere is half dense for this scattering." }, { category = "Sky", feature = "phys_sky", name = "Mie Shape", id = "mie_mc", editor = "number", slider = true, min=750, max = 999, default = 860, help = "The G param (mean cosine) controls the asymmetry (shape) of the mie phase function." }, { category = "Sky", feature = "phys_sky", name = "Exposure", id = "sky_exp", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "The exposure control in 1/100 EV. Intensity change is pow(2, E / 100)" }, { category = "Sky", feature = "phys_sky", name = "Sky IS", id = "sky_is", editor=false, dont_save = true, default = false, help = "Toggles realtime update of sky contribution with importance sampling (for lightmodel tweak only)" }, { category = "Sky", feature = "phys_sky", name = "Cubemap capture preview", editor = "bool", dont_save = true, id = "cubemap_capture_preview", default = false, help = "Mostly enables Sky importance sampling, among other things" }, { category = "Cubemap", feature = "phys_sky", name = "Exterior Env map", id = "exterior_envmap", default = "PainterStudioDaylight", editor = "dropdownlist", help = "The current exterior environment map texture.", items = function() return GetEnvMapsList("Exterior") end }, { category = "Cubemap", feature = "phys_sky", name = "Exterior Env Exposure", id = "ext_env_exposure", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "The exterior env exposure control in 1/100 EV. Intensity change is pow(2, E / 100)" }, { category = "Cubemap", feature = "phys_sky", name = "Exterior Env Map Image", id = "ExteriorEnvmapImage", editor = "image", default = "", dont_save = true, img_size = 128, img_box = 1 }, { category = "Cubemap", feature = "phys_sky", name = "Interior Env map", id = "interior_envmap", default = "PainterStudioDaylight", editor = "dropdownlist", help = "The current interior environment map texture.", items = function() return GetEnvMapsList("Interior") end }, { category = "Cubemap", feature = "phys_sky", name = "Interior Env Exposure", id = "int_env_exposure", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "The interior env exposure control in 1/100 EV. Intensity change is pow(2, E / 100)" }, { category = "Cubemap", feature = "phys_sky", name = "Interior Env Map Image", id = "InteriorEnvmapImage", editor = "image", default = "", dont_save = true, img_size = 128, img_box = 1 }, { category = "Night Sky", feature = "phys_sky", name = "Stars Intensity", id = "stars_intensity", min = 0, max = 1000, scale = 1000, editor = "number", slider = true, default = 0, help = "Controls the brightness of the stars." }, { category = "Night Sky", feature = "phys_sky", name = "Stars Blue tint", id = "stars_blue_tint", min = 0, max = 100, scale = 100, editor = "number", slider = true, default = 30, help = "Faint stars are tinted blue to simulate human perception in low-light conditions." }, { category = "Night Sky", feature = "phys_sky", name = "MilkyWay Intensity", id = "mw_intensity", min = 0, max = 1000, scale = 1000, editor = "number", slider = true, default = 0, help = "Controls the brightness of the milky way texture." }, { category = "Night Sky", feature = "phys_sky", name = "MilkyWay Blue tint", id = "mw_blue_tint", min = 0, max = 100, scale = 100, editor = "number", slider = true, default = 30, help = "MilkyWay is tinted blue to simulate human perception in low-light conditions." }, { category = "Night Sky", feature = "phys_sky", name = "Rotation", id = "stars_rotation", min = 0, max = 3600, editor = "number", slider = true, default = 0, scale = 10, help = "Rotation angle of the stars around the celestial pole" }, { category = "Night Sky", feature = "phys_sky", name = "Celestial Pole Altitude", id = "stars_pole_alt", min = 0, max = 1800, editor = "number", slider = true, default = 0, scale = 10, help = "Celestial pole altitude in degrees. Approximately equal to the observer's latitude position on Earth." }, { category = "Night Sky", feature = "phys_sky", name = "Celestial Pole Azimuth", id = "stars_pole_azi", min = 0, max = 3600, editor = "number", slider = true, default = 0, scale = 10, help = "Celestial pole azimuth in degrees. Should point to North on Earth. Related to the Sun azimuth." }, { category = "Env Capture", feature = "phys_sky", name = "Sky Exp Exterior Adjust", id = "env_exterior_capture_sky_exp", editor="number", default = "", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "Adjusts the sky exposure in the captured exterior cubemap"}, { category = "Env Capture", feature = "phys_sky", name = "Sun Int Exterior Adjust", id = "env_exterior_capture_sun_int", editor="number", default = "", min = -2500, max = 2500, default = 0, editor = "number" , slider = true, help = "Adjusts the sun intensity in the captured exterior cubemap"}, { category = "Env Capture", feature = "phys_sky", name = "Exterior Capture Pos", id = "env_exterior_capture_pos", editor="point", default = InvalidPos(), helper = "absolute_pos", help = "The position to capture the exterior env map from.", buttons = {{name = "View", func = "ViewExteriorEnvPos" }, {name ="Use Shaderball", func = "UseSelectionAsExteriorEnvPos"}}, scale = "m"}, { category = "Env Capture", feature = "phys_sky", name = "Sky Exp Interior Adjust", id = "env_interior_capture_sky_exp", editor="number", default = "", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "Adjusts the sky exposure in the captured interior cubemap"}, { category = "Env Capture", feature = "phys_sky", name = "Sun Int Interior Adjust", id = "env_interior_capture_sun_int", editor="number", default = "", min = -2500, max = 2500, default = 0, editor = "number" , slider = true, help = "Adjusts the sun intensity in the captured interior cubemap"}, { category = "Env Capture", feature = "phys_sky", name = "Interior Capture Pos", id = "env_interior_capture_pos", editor="point", default = InvalidPos(), helper = "absolute_pos", help = "The position to capture the interior env map from.", buttons = {{name = "View", func = "ViewInteriorEnvPos" }, {name ="Use Shaderball", func = "UseSelectionAsInteriorEnvPos"}}, scale = "m"}, { category = "Env Capture", feature = "phys_sky", name = "Map for Cubemaps", id = "env_capture_map", editor="text", default = "", read_only=true, help = "The map name on which the position of the last capture is"}, { category = "Env Capture", feature = "phys_sky", name = "Capture", id = "env_capture", editor="text", default = "", read_only=true, help = "Click to capture cubemaps", buttons = {{name = "Exterior", func = "CaptureExteriorEnvmap"}, {name = "Interior", func = "CaptureInteriorEnvmap"}, {name = "Both", func = "CaptureBothEnvmaps"}}}, { category = "Env Capture", feature = "phys_sky", name = "View Map", id = "env_view_site", editor = "dropdownlist", items = {"Exterior", "Interior"}, default = "Exterior", dont_save = true, buttons = {{name = "View", func = "ViewEnv"}, {name = "Hide", func = "HideEnv"}}}, { category = "Env Capture", feature = "phys_sky", name = "Convert HDR Pano", id = "hdr_pano", editor = "browse", default = false, filter = "Radiance HDR File|*.hdr", default = "", dont_save = true, buttons = {{name = "Exterior", func = "ConvertExteriorEnvmap"}, {name = "Interior", func = "ConvertInteriorEnvmap"}}}, { category = "Env Capture", feature = "phys_sky", name = "Lightmodel for Capture", id = "lm_capture", editor = "preset_id", preset_class = "LightmodelPreset", default = "", help = "Indicates which light model should be used for the baking.", no_validate = true, }, { category = "Fog", feature = "fog", name = "Fog color", id = "fog_color", default = RGB(180, 180, 180), editor = "color", help = "The default color of the fog." }, { category = "Fog", feature = "fog", name = "Fog density", id = "fog_density", default = 20, min = 0, max = 2000, scale = 100, editor = "number", slider = true, help = "The fog thickness." }, { category = "Fog", feature = "fog", name = "Fog height falloff", id = "fog_height_falloff" , default = 1500, min = 1, max = 2500, editor = "number", slider = true, help = "The fog thickness change with height" }, { category = "Fog", feature = "fog", name = "Fog start (m)", id = "fog_start", default = 0, min = 0, max = 1000 * 1000, scale = 1000, editor = "number" , slider = true, help = "The distance to fog start, in meters."}, { category = "Water", feature = "water", name = "Water Color", id = "water_color", editor = "color", default = RGB(127, 127, 127), help = "The color of the terrain water" }, { category = "Water", feature = "water", name = "Opacity Modifier", id = "absorption_coef", min = 0, max = 100, default = 100, scale = 100, editor = "number", slider = true, help = "How much light does the water absorb" }, { category = "Water", feature = "water", name = "Reflection Modifier", id = "minimum_depth", min = 0, max = 200, default = 100, scale = 100, editor = "number", slider = true, help = "How deep will the water appear minimally" }, { category = "Ice", feature = "ice", name = "Ice color", id = "ice_color", editor = "color", default = RGB(255, 255, 255), help = "The color of the ice that will affect the buildings and rocks" }, { category = "Ice", feature = "ice", name = "Ice strength", id = "ice_strength", min = 0, max = 100, default = 0, scale = 100, editor = "number", slider = true, help = "How strong will be ice get" }, { category = "Snow", feature = "snow", name = "Snow color", id = "snow_color", editor = "color", default = RGB(167, 167, 167), help = "The color of the snow of the terrain" }, { category = "Snow", feature = "snow", name = "Snow direction X", id = "snow_dir_x", editor = "number", slider = true, default = 0, min = -1000, max = 1000, scale = 1000, help = "Snowfall direction X" }, { category = "Snow", feature = "snow", name = "Snow direction Y", id = "snow_dir_y", editor = "number", slider = true, default = 0, min = -1000, max = 1000, scale = 1000, help = "Snowfall direction Y" }, { category = "Snow", feature = "snow", name = "Snow direction Z", id = "snow_dir_z", editor = "number", slider = true, default = 1000, min = -1000, max = 1000, scale = 1000, help = "Snowfall direction Z" }, { category = "Snow", feature = "snow", name = "Snow strength", id = "snow_str", editor = "number", slider = true, default = 0, min = 0, max = 1000, scale = 1000, help = "The constant snow strength" }, { category = "Snow", feature = "snow", name = "Snow", id = "snow_enable", editor = "bool", default = false, }, { category = "Wind", id = "wind", name = "Wind", editor = "preset_id", default = false, preset_class = "WindDef" }, { category = "Heat Haze", id = "enable_heat_haze", name = "Enable Heat Haze", editor = "bool", default = false }, { category = "Distance Blur and Desaturation", feature = "dist_blur_desat", name = "Blur", id = "pp_blur", min = 0, max = 100, default = 0, editor = "number",slider = true, help = "The intensity of the blur effect for distant objects." }, { category = "Distance Blur and Desaturation", feature = "dist_blur_desat", name = "Blur distance", id = "pp_blur_distance", min = 0, max = 600, default = 0, editor = "number", slider = true, help = "The distance at which the distant objects start to get blurry." }, { category = "Distance Blur and Desaturation", feature = "dist_blur_desat", name = "Desaturation", id = "pp_desaturation", min = 0, max = 100, default = 0, editor = "number", slider = true, help = "How intense is the desaturation of colors of the distant objects." }, { category = "Distance Blur and Desaturation", feature = "dist_blur_desat", name = "Desaturation distance", id = "pp_desaturation_distance", min = 0, max = 600, default = 0, editor = "number", slider = true, help = "The distance at which the distant objects' colors get desaturated." }, { category = "Exposure", feature = "autoexposure", id="_", editor="help", default=false, help = AutoExposureInstructions, buttons = {{name = "[On]", func = "AutoExposureOn"},{name = "[Off]", func = "AutoExposureOff"}, {name = "[Split]", func = "AutoExposureSplit"}, {name="[Debug]", func="AutoExposureDebugToggle"}} }, { category = "Exposure", feature = "autoexposure", name = "Exposure", id = "exposure", min = -200, max = 200, default = 0, editor = "number" , slider = true, help = "The global exposure control in 1/100 EV. Intensity change is pow(2, E / 100)" }, { category = "Exposure", feature = "autoexposure", name = "Auto Exposure (AE)", id="ae_key_bias", min = -3000000, max = 3000000, default = 0, scale = 1000000, editor = "number" , slider = true, help = "Exposure key value multiplier." }, { category = "Exposure", feature = "autoexposure", name = "Scene lum min", id="ae_lum_min", min = -14*10000, max = 20*10000, default = -14*10000, scale = 10000, editor = "number" , slider = true, help = "Clamps average scene luminance.", no_edit = true }, { category = "Exposure", feature = "autoexposure", name = "Scene lum max", id="ae_lum_max", min = -14*10000, max = 20*10000, default = 20*10000, scale = 10000, editor = "number" , slider = true, help = "Clamps average scene luminance.", no_edit = true }, { category = "Exposure", feature = "autoexposure", name = "Adaptation speed bright",id="ae_adapt_speed_bright", min = 1, max = 1500, default = 500, scale = 100, editor = "number" , slider = true, help = "How fast the eye adapts to brighter scenes.", no_edit = true }, { category = "Exposure", feature = "autoexposure", name = "Adaptation speed dark", id="ae_adapt_speed_dark", min = 1, max = 1500, default = 500, scale = 100, editor = "number" , slider = true, help = "How fast the eye adapts to darker scenes.", no_edit = true }, { category = "Exposure", feature = "autoexposure", name = "AE Lum Min", id="ae_darkness_ign", min = 0, max = 99, default = 20, scale = "%", editor = "number" , slider = true, help = "From what percentage of the luminance of the scene to consider." }, { category = "Exposure", feature = "autoexposure", name = "AE Lum Max", id="ae_brightness_ign", min = 1, max = 100, default = 95, scale = "%", editor = "number" , slider = true, help = "Until what percentage of the luminance of the scene to consider." }, { category = "Exposure", feature = "autoexposure", name = "Use Constant Exposure", id="ae_disable", editor="bool", default=false, help = "Turn autoexposure on/off", }, { category = "Bloom", feature = "hdr bloom", name = "Strength", id = "pp_bloom_strength", min = 0, max = 100, default = 0, scale = 100, editor = "number", slider = true, help = "How much Bloom affects the resulting picture." }, { category = "Bloom", feature = "hdr bloom", name = "Threshold", id = "pp_bloom_threshold", min = 0, max = 100, default = 100, scale = 100, editor = "number", slider = true, help = "The luminance threshold after which colours bloom." }, { category = "Bloom", feature = "hdr bloom", name = "Contrast", id = "pp_bloom_contrast", min = 0, max = 100, default = 0, scale = 100, editor = "number", slider = true, help = "The contrast of the final Bloom effect." }, { category = "Bloom", feature = "hdr bloom", name = "Bloom colorization", id = "pp_bloom_colorization", min = 0, max = 100, scale = 100, default = 30, editor = "number", slider = true, help = "The mixing ratio between the original Bloom color and the tint." }, { category = "Bloom", feature = "hdr bloom", name = "Inner tint", id = "pp_bloom_inner_tint", default = RGB(180, 180, 180), editor = "color", help = "Bloom effect's inner tint." }, { category = "Bloom", feature = "hdr bloom", name = "Outer tint", id = "pp_bloom_outer_tint", default = RGB(180, 180, 180), editor = "color", help = "Bloom effect's outer tint." }, { category = "Bloom", feature = "hdr bloom", name = "Mip2 radius", id = "pp_bloom_mip2_radius" , min = 1, max = 64, default = 16, editor = "number", slider = true, help = "Gauss blur radius for mip2." }, { category = "Bloom", feature = "hdr bloom", name = "Mip3 radius", id = "pp_bloom_mip3_radius" , min = 4, max = 64, default = 16, editor = "number", slider = true, help = "Gauss blur radius for mip3." }, { category = "Bloom", feature = "hdr bloom", name = "Mip4 radius", id = "pp_bloom_mip4_radius" , min = 8, max = 64, default = 32, editor = "number", slider = true, help = "Gauss blur radius for mip4." }, { category = "Bloom", feature = "hdr bloom", name = "Mip5 radius", id = "pp_bloom_mip5_radius" , min = 8, max = 64, default = 32, editor = "number", slider = true, help = "Gauss blur radius for mip5." }, { category = "Bloom", feature = "hdr bloom", name = "Mip6 radius", id = "pp_bloom_mip6_radius" , min = 8, max = 64, default = 32, editor = "number", slider = true, help = "Gauss blur radius for mip6." }, { category = "Exposure", name = "Emissive Boost", id = "emissive_boost", min = 100, max = 10000, default = 100, scale = 10000, editor = "number", slider = true }, { category = "Exposure", name = "Particle Exposure Additive", id = "ps_exposure", min = -1000, max = 1000, default = 0, editor = "number" , slider = true, help = "EV Control in 1/100 EV to adjust particle brightness when blending. Intensity change is pow(2, E / 100)" }, { category = "Other", name = "AO texture darkness", id = "ao_lower_limit", editor = "number", slider = true, min = 0, max = 255, default = 0, scale = 255, help = "Fits the values of the Ambient Occlusion Map of all meshes in a new range that has the specified minimum value." }, { category = "Other", name = "SSAO strength", id = "pp_ssao_strength", min = 0, max = 200, editor = "number" , slider = true, default = 0, scale = 100, help = "The intensity of Screen-Space Ambient Occlusion in percents." }, { category = "Other", feature = "three_point_lighting", name = "Three Point Lighting", id = "three_point_lighting", editor = "preset_id", default = "", preset_class = "ThreePointLighting" }, { category = "Other", feature = "Unit_Lighting", name = "Unit Lighting Strength", id = "unit_lighting_strength", editor = "number", slider = true, min = 0, max = 100, default = 50, scale = 100, help = "The intensity of the Unit Lighting effect." }, { category = "Other", feature = "Unit_Lighting", name = "Unit Lighting Contrast", id = "unit_lighting_contrast", editor = "number", slider = true, min = 0, max = 100, default = 0, scale = 100, help = "The contrast strength of the Unit Lighting effect." }, { category = "Lights", name = "Light Shadows", id = "light_shadows", editor = "number", slider = true, min = 0, max = 1000, scale = 1000, default = 1000, feature = "shadow", help = "Shadows from lights strength." }, { category = "Lights", name = "LightColorA", id = "lightcolor1", editor = "color", default = RGB(255, 255, 255), alpha = false, help = "This color can be used by point&spot lights." }, { category = "Lights", name = "LightColorB", id = "lightcolor2", editor = "color", default = RGB(255, 255, 255), alpha = false, help = "This color can be used by point&spot lights." }, { category = "Lights", name = "LightColorC", id = "lightcolor3", editor = "color", default = RGB(255, 255, 255), alpha = false, help = "This color can be used by point&spot lights." }, { category = "Lights", name = "LightColorD", id = "lightcolor4", editor = "color", default = RGB(255, 255, 255), alpha = false, help = "This color can be used by point&spot lights." }, { category = "Lights", feature = "night", name = "Night Lights", id = "night", blend = const.NightBlendThreshold, editor = "bool", default = false, help = "Determines whether the lights should be switched on or off." }, }, } DefineClass.LightmodelPreset = { __parents = { "Preset", "Lightmodel" }, GlobalMap = "LightmodelPresets", GedEditor = "LightmodelEditor", EditorMenubarName = "Lightmodels", EditorMenubar = "Editors.Art", EditorShortcut = "Ctrl-M", EditorIcon = "CommonAssets/UI/Icons/bulb ecology energy lamp light power.png", ValidateAfterSave = true, PropertyTabs = { { TabName = "Preset", Categories = { Preset = true }, }, { TabName = "Sun", Categories = { Sun = true, ["Sun Path"] = true, ["Sun Pos"] = true, } }, { TabName = "Sky", Categories = { Sky = true, ["Night Sky"] = true, ["Env Capture"] = true, ["Cubemap"] = true, } }, { TabName = "Weather", Categories = { Fog = true, Rain = true, Clouds = true, ["Cloud shadows"] = true, ["Wind"] = true, } }, { TabName = "Environment", Categories = { Water = true, Snow = true, Ice = true, Frost = true, } }, { TabName = "Effects", Categories = { Exposure = true, Bloom = true, Other = true, Vignette = true, ["Chromatic Aberration"] = true, ["Color Grading"] = true, Lights = true, } }, } } DefineClass.SHDiffuseIrradiance = { __parents = { "Preset" }, properties = { { name = "SH9 Coefficients", id = "sh9_coefficients", editor = "text", default = "", read_only = true }, }, EditorMenubarName = "", EditorMenubar = false, } function DoSetLightmodel(view, lm_buf, time, start_offset) if view < 1 or view > camera.GetViewCount() then return end start_offset = start_offset or 0 SetSceneParam(view, "UseTimeOfDay", lm_buf.use_time_of_day and 1 or 0, 0, start_offset) if not lm_buf.use_time_of_day then local prev_azi = (GetSceneParam(view, "SunWidth") / 1000 + 360) % 360 local azi = (lm_buf.sun_azi + mapdata.MapOrientation) % 360 if abs(azi - prev_azi) > 180 then -- we better interpolate through 360 if azi > 180 then prev_azi = prev_azi + 360 else azi = azi + 360 end end SetSceneParam(view, "SunWidth", prev_azi * 1000, 0, start_offset) SetSceneParam(view, "SunWidth", azi * 1000, time, start_offset) SetSceneParam(view, "SunHeight", lm_buf.sun_alt * 100, time, start_offset) if lm_buf.sky_custom_sun then SetSceneParam(view, "SkySunAzi", lm_buf.sky_custom_sun_azi * 1000, time, start_offset) SetSceneParam(view, "SkySunAlt", lm_buf.sky_custom_sun_alt * 100, time, start_offset) else SetSceneParam(view, "SkySunAzi", -1, 0, 0) SetSceneParam(view, "SkySunAlt", -1, 0, 0) end SetSceneParam(view, "SunShadowHeight", lm_buf.sun_shadow_height * 100, time, start_offset) end SetSceneParam(view, "Shadow", lm_buf.shadow, time, start_offset) if lm_buf.shadow then SetSceneParam(view, "ShadowRange", lm_buf.shadow_range, time, start_offset) end SetSceneParamColor(view, "MieCoefs", lm_buf.mie_coefs, time, start_offset) SetSceneParamColor(view, "RayCoefs", lm_buf.ray_coefs, time, start_offset) SetSceneParam(view, "MieSH", lm_buf.mie_sh, time, start_offset) SetSceneParam(view, "RaySH", lm_buf.ray_sh, time, start_offset) SetSceneParam(view, "MieMC", lm_buf.mie_mc, time, start_offset) SetSceneParam(view, "SkyExp", lm_buf.sky_exp, time, start_offset) SetSceneParamColor(view, "SunDiffuseColor", lm_buf.sun_diffuse_color, time, start_offset, false) SetSceneParam(view, "SunIntensity", lm_buf.sun_intensity, time, start_offset) SetSceneParam(view, "StarsIntensity", lm_buf.stars_intensity, time, start_offset) SetSceneParam(view, "StarsBlueTint", lm_buf.stars_blue_tint, time, start_offset) SetSceneParam(view, "StarsRotation", lm_buf.stars_rotation, time, start_offset) SetSceneParam(view, "StarsPoleAlt", lm_buf.stars_pole_alt, time, start_offset) SetSceneParam(view, "StarsPoleAzi", lm_buf.stars_pole_azi, time, start_offset) SetSceneParam(view, "MilkyWayIntensity", lm_buf.mw_intensity, time, start_offset) SetSceneParam(view, "MilkyWayBlueTint", lm_buf.mw_blue_tint, time, start_offset) SetSceneParam(view, "SunAngularRadius", lm_buf.sun_angular_radius, time, start_offset) SetSceneParam(view, "GlobalExposure", lm_buf.exposure, time, start_offset) SetSceneParam(view, "EmissiveBoost", lm_buf.emissive_boost, time, start_offset) SetSceneParam(view, "ExtEnvExposure", lm_buf.ext_env_exposure, time, start_offset) SetSceneParam(view, "IntEnvExposure", lm_buf.int_env_exposure, time, start_offset) SetSceneParam(view, "ParticleExposure", lm_buf.ps_exposure, time, start_offset) SetSceneParamColor(view, "FogColor", lm_buf.fog_color, time, start_offset) SetSceneParam(view, "FogGlobalDensity", lm_buf.fog_density, time, start_offset) SetSceneParam(view, "FogHeightFalloff", lm_buf.fog_height_falloff, time, start_offset) SetSceneParam(view, "FogStart", lm_buf.fog_start, time, start_offset) SetIceStrength(lm_buf.ice_strength, "Lightmodel", view, time, start_offset) SetSceneParamColor(view, "IceColor", lm_buf.ice_color, time, start_offset) SetSceneParamColor(view, "SnowColor", lm_buf.snow_color, time, start_offset) SetSceneParam(view, "SnowDirX", lm_buf.snow_dir_x, time, start_offset) SetSceneParam(view, "SnowDirY", lm_buf.snow_dir_y, time, start_offset) SetSceneParam(view, "SnowDirZ", lm_buf.snow_dir_z, time, start_offset) SetSceneParam(view, "SnowStr", lm_buf.snow_str, time, start_offset) SetSceneParamColor(view, "WaterColor", lm_buf.water_color, time, start_offset) SetSceneParam(view, "AbsorptionCoef", lm_buf.absorption_coef, time, start_offset) SetSceneParam(view, "MinimumDepth", lm_buf.minimum_depth, time, start_offset) SetSceneParam(view, "AutoExposureKeyBias", lm_buf.ae_key_bias, time, start_offset) SetSceneParam(view, "AutoExposureLumMin", lm_buf.ae_lum_min, time, start_offset) SetSceneParam(view, "AutoExposureLumMax", lm_buf.ae_lum_max, time, start_offset) SetSceneParam(view, "AutoExposureAdaptSpeedBright", lm_buf.ae_adapt_speed_bright, time, start_offset) SetSceneParam(view, "AutoExposureAdaptSpeedDark", lm_buf.ae_adapt_speed_dark, time, start_offset) SetSceneParam(view, "AutoExposureBrightnessIgnorance", 100 - lm_buf.ae_brightness_ign, time, start_offset) SetSceneParam(view, "AutoExposureDarknessIgnorance", lm_buf.ae_darkness_ign, time, start_offset) SetSceneParam(view, "AutoExposureDisable", lm_buf.ae_disable and 1 or 0, time, start_offset) if not config.LightModelUnusedFeatures["dist_blur_desat"] then SetSceneParamVector(view, "PostProc", 0, lm_buf.pp_blur, time, start_offset) SetSceneParamVector(view, "PostProc", 1, lm_buf.pp_desaturation, time, start_offset) SetSceneParamVector(view, "PostProc", 2, lm_buf.pp_blur_distance, time, start_offset) SetSceneParamVector(view, "PostProc", 3, lm_buf.pp_desaturation_distance, time, start_offset) end SetSceneParamColor(view, "BloomInnerTint", lm_buf.pp_bloom_inner_tint, time, start_offset) SetSceneParamColor(view, "BloomOuterTint", lm_buf.pp_bloom_outer_tint, time, start_offset) SetSceneParamVector(view, "Bloom", 0, lm_buf.pp_bloom_strength, time, start_offset) SetSceneParamVector(view, "Bloom", 1, lm_buf.pp_bloom_threshold, time, start_offset) SetSceneParamVector(view, "Bloom", 2, lm_buf.pp_bloom_contrast, time, start_offset) SetSceneParamVector(view, "Bloom", 3, lm_buf.pp_bloom_colorization, time, start_offset) SetSceneParamVector(view, "BloomRadii", 0, lm_buf.pp_bloom_mip2_radius, time, start_offset) SetSceneParamVector(view, "BloomRadii", 1, lm_buf.pp_bloom_mip3_radius, time, start_offset) SetSceneParamVector(view, "BloomRadii", 2, lm_buf.pp_bloom_mip4_radius, time, start_offset) SetSceneParamVector(view, "BloomRadii", 3, lm_buf.pp_bloom_mip5_radius, time, start_offset) SetSceneParamVector(view, "BloomRadii", 4, lm_buf.pp_bloom_mip6_radius, time, start_offset) SetSceneParamVector(view, "AOLowerLimit", 0, lm_buf.ao_lower_limit, time, start_offset) SetSceneParamVector(view, "SSAO", 0, lm_buf.pp_ssao_strength, time, start_offset) SetPostProcPredicate("heat_haze", lm_buf.enable_heat_haze) if not config.LightModelUnusedFeatures["three_point_lighting"] then if lm_buf.three_point_lighting ~= "" then Presets.ThreePointLighting.ThreePointLightingRenderVars[lm_buf.three_point_lighting]:Apply() table.change_base(hr, { EnableThreePointLighting = 1 }) else table.change_base(hr, { EnableThreePointLighting = 0 }) end end -- set up the cube map for the env mapped objects if lm_buf.exterior_envmap and lm_buf.exterior_envmap ~= "" then local exterior_sh = Presets.SHDiffuseIrradiance.Default[lm_buf.exterior_envmap .. "Exterior"] if not exterior_sh then print("once", "Lightmodel", lm_buf.exterior_envmap .. "Exterior", "needs to be recaptured!") end local err = SetCubemap(view, string.format("Textures/Cubemaps/%sExterior", lm_buf.exterior_envmap), exterior_sh and Decode64(exterior_sh.sh9_coefficients) or "", 0, time, start_offset) if err then print("SetCubemap failed", err) end end if lm_buf.interior_envmap and lm_buf.interior_envmap ~= "" then local interior_sh = Presets.SHDiffuseIrradiance.Default[lm_buf.interior_envmap .. "Interior"] if not interior_sh then print("once", "Lightmodel", lm_buf.interior_envmap .. "Interior", "needs to be recaptured!") end local err = SetCubemap(view, string.format("Textures/Cubemaps/%sInterior", lm_buf.interior_envmap), interior_sh and Decode64(interior_sh.sh9_coefficients) or "", 1, time, start_offset) if err then print("SetCubemap failed", err) end end SetSceneParam(view, "LightShadows", lm_buf.light_shadows, time, start_offset) SetSceneParam(view, "GameSpecificData0", lm_buf.unit_lighting_strength, time, start_offset) SetSceneParam(view, "GameSpecificData1", lm_buf.unit_lighting_contrast, time, start_offset) for i = 1, 4 do SetSceneParamColor(view, "LightColor" .. i, lm_buf["lightcolor" .. i], time, start_offset) end Msg("LightmodelSetSceneParams", view, lm_buf, time, start_offset) end MapVar("CurrentLightmodel", {}) MapVar("LastSetLightmodel", {}) if FirstLoad then LightmodelOverride = false end function GetEnvMapsList(site) local maps = { "" } for _,v in ipairs(io.listfiles("Textures/Cubemaps")) do local map = string.match(v, "Textures/Cubemaps/(.*)" .. site .. "Env%.dds") if map then maps[#maps + 1] = map end end table.sort(maps) return maps end function PreloadLightmodelCubemaps(lightmodel) if not lightmodel or lightmodel == "" then return end local lm_buf = LightmodelPresets[lightmodel] if not lm_buf then return end local function preload_path(path) local image_id = ResourceManager.GetResourceID(path) if image_id == const.InvalidResourceID then printf("once", "Could not load image %s!", path or "") return end local image = AsyncGetResource(image_id) return image end local exterior_map = string.format("Textures/Cubemaps/%sExterior", lm_buf.exterior_envmap) local interior_map = string.format("Textures/Cubemaps/%sInterior", lm_buf.interior_envmap) local result = { Done = function(self) if self.exterior_specular then self.exterior_specular:ReleaseRef() end if self.interior_specular then self.interior_specular:ReleaseRef() end end, exterior_specular = preload_path(exterior_map .. "Specular"), interior_specular = preload_path(interior_map .. "Specular"), } return result end function SetLightmodelOverride(view, lightmodel) view = view or 1 lightmodel = LightmodelPresets[lightmodel] or lightmodel lightmodel = type(lightmodel) == "table" and lightmodel or false if LightmodelOverride ~= lightmodel then LightmodelOverride = lightmodel SetLightmodel(view, LastSetLightmodel and LastSetLightmodel[1] or lightmodel, 0, "from_override") end end function SetLightmodel(view, lightmodel, time, from_override) if not CurrentLightmodel then return end --not on map view = view or 1 time = time or 0 lightmodel = LightmodelPresets[lightmodel] or lightmodel if type(lightmodel) ~= "table" then if type(lightmodel) == "string" then assert(false, "lightmodel not found: " .. tostring(lightmodel)) end lightmodel = LightmodelPresets.ArtPreview end if view < 1 or view > camera.GetViewCount() then return end if LastSetLightmodel then LastSetLightmodel[view] = lightmodel end lightmodel = LightmodelOverride or lightmodel local prev_lm = CurrentLightmodel[view] if prev_lm and not IsKindOf(prev_lm, "LightmodelPreset") then setmetatable(prev_lm, LightmodelPreset) -- !!! backwards compatibility end CurrentLightmodel[view] = lightmodel hr.TODForceTime = -1 local override = LightmodelOverride == lightmodel if override then if lightmodel.use_time_of_day then hr.TODForceTime = LocalToEarthTime(lightmodel.time*1000) end end --override is true when overriding and false when restoring --from_override is true for both calls Msg("LightmodelChange", view, lightmodel, time, prev_lm, from_override) DoSetLightmodel(view, lightmodel, time) Msg("AfterLightmodelChange", view, lightmodel, time, prev_lm, from_override) end do local unused_features = config.LightModelUnusedFeatures or empty_table for _, prop in ipairs(Lightmodel.properties) do if prop.feature and unused_features[prop.feature] then prop.no_edit = true end end end function LightmodelPreset:ListEquivalentLMs(root, prop_id, ged, param) local feature = string.match(prop_id, "preset_(.+)") local list = LightmodelEquivalenceByValue(self, feature) ged:ShowMessage("Equivalent Lightmodels", table.concat(list, "\n")) end function LightmodelPreset:SetFeaturePreset(ref_preset) if not ref_preset then return end local feature = ref_preset.group if not feature then return end local feature_data = LightmodelFeatureToProperties[feature] for _, prop in ipairs(feature_data) do self:SetProperty(prop.id, ref_preset:GetProperty(prop.id)) end end function LightmodelPreset:SetFeaturePresetId(feature, feature_preset_id) local presets = Presets.LightmodelFeaturePreset or empty_table local feature_group = presets[feature] or empty_table self:SetFeaturePreset(feature_group[feature_preset_id]) end local function EarlyClassDescendants(classdefs, target_class, callback) local cache = {} local function EarlyIsKindOf(obj_class, target_class) local cache_hit = cache[obj_class] if cache_hit ~= nil then return cache_hit end if obj_class == target_class then cache[obj_class] = true return true end local class = classdefs[obj_class] for _, parent in ipairs(class and class.__parents) do if(EarlyIsKindOf(parent, target_class)) then return true end end cache[obj_class] = false return false end for class_name in pairs(classdefs) do if EarlyIsKindOf(class_name, target_class) then callback(class_name, classdefs[class_name]) end end end function OnMsg.ClassesGenerate(classdefs) LightmodelFeatureToProperties = {} local properties = {} EarlyClassDescendants(classdefs, "LightmodelPart", function(class_name, classdef) local classProperties = {} if classdef.GetLightmodelProperties then classdef:GetLightmodelProperties(classProperties) else table.iappend(classProperties, classdef.properties) end local uses_preset = function(chain_func) return function(self, ...) local preset_id = "preset_" .. classdef.lightmodel_feature local uses_preset = self[preset_id] and self[preset_id] ~= "" if uses_preset then return uses_preset end if type(chain_func) == "function" then return chain_func(self, ...) end if chain_func then return chain_func end return false end end for k, prop in ipairs(classProperties) do if not prop.category then prop.category = classdef.lightmodel_category end if not prop.feature then prop.feature = classdef.lightmodel_feature end local lm_prop = table.copy(prop) lm_prop.dont_save = uses_preset(lm_prop.dont_save) lm_prop.read_only = uses_preset(lm_prop.read_only) properties[#properties + 1] = lm_prop for _,button in ipairs(lm_prop.buttons or empty_table) do if not Lightmodel[button.func] and classdef[button.func] then Lightmodel[button.func] = classdef[button.func] elseif Lightmodel[button.func] and not classdef[button.func] then classdef[button.func] = Lightmodel[button.func] end end end end) for _, prop in ipairs(properties) do local feature = prop.feature if feature then local feature_list = LightmodelFeatureToProperties[feature] if not feature_list then feature_list = {} LightmodelFeatureToProperties[feature] = feature_list end table.insert(feature_list, prop) end end -- Generate Additional LM properties for feature, feature_data in pairs(LightmodelFeatureToProperties) do local preset_feature_prop_id = "preset_" .. feature feature_data.preset_feature_prop_id = preset_feature_prop_id table.insert(Lightmodel.properties, { id = preset_feature_prop_id, editor = "preset_id", preset_class = "LightmodelFeaturePreset", preset_group = feature, default = "", category = (feature_data[1] or empty_table).category, buttons = {{ name = "List", func = "ListEquivalentLMs"}} }) table.iappend(Lightmodel.properties, feature_data) end end function OnMsg.DataLoaded() for _, lm in pairs(LightmodelPresets) do for feature, data in pairs(LightmodelFeatureToProperties) do local preset_id = lm:GetProperty(data.preset_feature_prop_id) lm:SetFeaturePresetId(feature, preset_id) end end end local lightmodel_properties function OnMsg.ClassesBuilt() lightmodel_properties = {} local preset = LightmodelPreset for _, prop_meta in ipairs(preset:GetProperties()) do if prop_meta.category ~= "Preset" and not prop_eval(prop_meta.no_edit, preset, prop_meta) and not prop_eval(prop_meta.read_only, preset, prop_meta)then lightmodel_properties[#lightmodel_properties + 1] = prop_meta end end end function BlendLightmodels(result, lm1, lm2, num, denom) SuspendObjModified("BlendLightmodels") local firstHalf = 2*num < denom for _, prop_meta in ipairs(lightmodel_properties) do local prop_id = prop_meta.id local v1 = lm1:GetProperty(prop_id) local v2 = lm2:GetProperty(prop_id) local value local prop_blend = prop_meta.blend if num >= denom or v1 == v2 or prop_blend == "set" then value = v2 elseif num <= 0 or prop_blend == "suppress" then value = v1 else value = v2 local prop_editor = prop_meta.editor if prop_editor == "number" or prop_editor == "point" then value = Lerp(v1, v2, num, denom) elseif prop_editor == "color" then value = InterpolateRGB(v1, v2, num, denom) elseif type(prop_blend) == "number" and prop_blend ~= 50 then -- "blend" here is a number indicating the descrete value change threshold if prop_editor == "bool" then value = v1 and 100 * num < prop_blend * denom or v2 and 100 * num >= (100 - prop_blend) * denom elseif 100 * num < prop_blend * denom then value = v1 end elseif firstHalf then value = v1 end end result:SetProperty(prop_id, value) end ResumeObjModified("BlendLightmodels") end function LightmodelPreset:GetInteriorEnvmapImage() return "Textures/Cubemaps/Thumbnails/" .. self.interior_envmap .. "Interior.jpg" end function LightmodelPreset:GetExteriorEnvmapImage() return "Textures/Cubemaps/Thumbnails/" .. self.exterior_envmap .. "Exterior.jpg" end function LightmodelPreset:Sethdr_pano(value) self.hdr_pano = value end local function AppendDefaultExtension(path, default_extension) if path and path ~= "" then local _, _, extension = SplitPath(path) if not extension or extension == "" then path = path .. default_extension end end return path end if Platform.developer then function LightmodelPreset:Setsky_custom_sun(b) self.sky_custom_sun = b if b then self.sky_custom_sun_alt = self.sun_alt self.sky_custom_sun_azi = self.sun_azi else self.sky_custom_sun_alt = 0 self.sky_custom_sun_azi = 180 end ObjModified(self) end end function LightmodelPreset:Setuse_time_of_day(b) self.use_time_of_day = b ObjModified(self) end function LightmodelPreset:Setshadow(b) self.shadow = b ObjModified(self) end function LightmodelPreset:Setae_brightness_ign(b) self.ae_brightness_ign = b if (self.ae_brightness_ign - self.ae_darkness_ign <= 1) then self.ae_darkness_ign = self.ae_brightness_ign - 1 end end function LightmodelPreset:Setae_darkness_ign(b) self.ae_darkness_ign = b if (self.ae_brightness_ign - self.ae_darkness_ign <= 1) then self.ae_brightness_ign = self.ae_darkness_ign + 1 end end function LightmodelPreset:Getsun_earthtime_info() local sunrise = hr.TODSunriseTime local sunset = hr.TODSunsetTime local noon = sunrise + (sunset - sunrise) / 2 return string.format("Sunrise %02d:%02d Noon %02d:%02d Sunset %02d:%02d", sunrise / 60, sunrise % 60, noon / 60, noon % 60, sunset / 60, sunset % 60 ) end function LightmodelPreset:Getsunrise_time() return EarthToLocalTime(hr.TODSunriseTime) end function LightmodelPreset:Getsunrise_azi() return hr.TODSunriseAzi end function LightmodelPreset:Getsunset_time() return EarthToLocalTime(hr.TODSunsetTime) end function LightmodelPreset:Getsunset_azi() return hr.TODSunsetAzi end function LightmodelPreset:Getsun_max_elevation() return hr.TODSunMaxElevation end function LightmodelPreset:Getsun_shadow_min() return hr.TODSunShadowMinAltitude end function LightmodelPreset:Setsun_shadow_min(v) hr.TODSunShadowMinAltitude = v end function LightmodelPreset:Getsun_nr() return hr.TODNorthRotation end function LightmodelPreset:Setsun_nr(v) hr.TODNorthRotation = v end function LightmodelPreset:Setsunrise_time(v) hr.TODSunriseTime = LocalToEarthTime(v) ObjModified(self) end function LightmodelPreset:Setsunrise_azi(v) hr.TODSunriseAzi = v ObjModified(self) end function LightmodelPreset:Setsunset_time(v) hr.TODSunsetTime = LocalToEarthTime(v) ObjModified(self) end function LightmodelPreset:Setsunset_azi(v) hr.TODSunsetAzi = v ObjModified(self) end function LightmodelPreset:Setsun_max_elevation(v) hr.TODSunMaxElevation = v ObjModified(self) end function LightmodelPreset:Settime(v) self.time = v if hr.TODForceTime >= 0 then hr.TODForceTime = LocalToEarthTime(self.time*1000) end end function LightmodelPreset:GetListName() return self.group --string.match(self.id, "(%w+)_") or "*" end function LightmodelPreset:Gettime_next(v) local list_name = self:GetListName() local next_lm = FindNextLightmodel(list_name, self.time+1) if not next_lm then return "" end return string.format("%02d:%02d (%s)", next_lm.time / 60, next_lm.time % 60, next_lm.id) end function LightmodelPreset:PreviewStart() if not self:EditorCheck(true) then return end SetLightmodelOverride(1, self.id) hr.TODForceTime = LocalToEarthTime(self.time*1000) end function LightmodelPreset:PreviewEnd() if not self:EditorCheck(true) then return end local list_name = self:GetListName() local next_lm = FindNextLightmodel(list_name, self.time+1) SetLightmodelOverride(1, self.id) hr.TODForceTime = LocalToEarthTime(next_lm.time*1000) end function LightmodelPreset:PreviewBlendStart() if not self:EditorCheck(true) then return end local list_name = self:GetListName() local prev_lm = FindPrevLightmodel(list_name, self.time - 1) SetLightmodelOverride(1, prev_lm.id) hr.TODForceTime = LocalToEarthTime(self.time*1000) end function LightmodelPreset:PreviewBlendEnd() if not self:EditorCheck(true) then return end SetLightmodelOverride(1, self.id) hr.TODForceTime = LocalToEarthTime((self.time + self.blend_time)*1000) end function LightmodelPreset:PreviewBlend() if IsEditorActive() then print("Lightmodel blending preview works only outside the in-game editor!") return end if not self:EditorCheck(true) then return end CreateRealTimeThread(function() local list_name = self:GetListName() local prev_lm = FindPrevLightmodel(list_name, self.time - 1) CancelRendering() SetLightmodelOverride(1, false) SetLightmodel(1, prev_lm.id, 0) Sleep(50) ResumeRendering() local start_time = LocalToEarthTime(self.time*1000) local end_time = LocalToEarthTime((self.time+self.blend_time)*1000) hr.TODForceTime = start_time Sleep(1000) local blend_time = MulDivRound(self.blend_time, const.HourDuration, 60) SetLightmodel(1, self.id, blend_time) local step = MulDivRound(60*1000, 10, const.HourDuration) CreateGameTimeThread(function() for time = start_time, end_time, step do hr.TODForceTime = time Sleep(10) end self:PreviewBlendEnd(self) end) end) end function LightmodelsCombo() return table.keys2(LightmodelPresets, true, "") end DefineConstInt("Disaster", "LightningHorizontalMaxDistance", 600, "m") DefineConstInt("Disaster", "LightningHorizontalMinDistance", 300, "m") DefineConstInt("Disaster", "LightningVerticalMaxDistance", 600, "m") DefineConstInt("Disaster", "LightningVerticalMinDistance", 300, "m") function WaitLightingStrike(view) local lm = CurrentLightmodel[view] if not lm or not lm.lightning_enable then return end Sleep(AsyncRand(lm.lightning_interval_min, lm.lightning_interval_max)) lm = CurrentLightmodel[view] if not lm or not lm.lightning_enable then return end local eye, lookat, _, _, _, fov = GetCamera() if AsyncRand(100) < lm.lightning_strike_chance then local disaster = const.Disaster local min, max, lightning_fx if AsyncRand(100) < lm.lightning_vertical_chance then min, max, lightning_fx = disaster.LightningVerticalMinDistance, disaster.LightningVerticalMaxDistance, "LightningFarVertical" else min, max, lightning_fx = disaster.LightningHorizontalMinDistance, disaster.LightningHorizontalMaxDistance, "LightningFarHorizontal" end local fov_safe_area = 20 * 60 fov = Max(Min(fov + fov_safe_area, 360 * 60), 0) local rot_angle_in_frustum = AsyncRand(fov) - DivRound(fov, 2) + camera.GetYaw() local rot_radius = AsyncRand(min, max) local pos = RotateRadius(rot_radius, rot_angle_in_frustum, lookat):SetTerrainZ() PlayFX(lightning_fx, "start", pos, pos, pos) else local pos = RotateRadius(100 * guim, AsyncRand(60 * 360), eye) PlayFX("LightningThunderAround", "start", pos) end return true end if FirstLoad then LightningThreads = false end function UpdateLightingThread(view) local lm = CurrentLightmodel[view] local lightning_thread = LightningThreads and LightningThreads[view] if not lm or not lm.lightning_enable then DeleteThread(lightning_thread) if LightningThreads then LightningThreads[view] = nil end return end if IsValidThread(lightning_thread) then return end LightningThreads = LightningThreads or {} LightningThreads[view] = CreateMapRealTimeThread(function(view) local lm = CurrentLightmodel[view] Sleep(lm and lm.lightning_delay_start or 0) while WaitLightingStrike(view) do end LightningThreads[view] = nil end, view) end function OnMsg.DoneMap() for view, thread in pairs(LightningThreads) do DeleteThread(thread) end LightningThreads = false end function OnMsg.LoadGame() for view in pairs(CurrentLightmodel) do UpdateLightingThread(view) end end function OnMsg.LightmodelChange(view, lm, time, prev_lm) local target_fx_class = "View" .. view PlayFX("SetLightmodel", "end", prev_lm and prev_lm.id or "", target_fx_class) PlayFX("SetLightmodel", "start", lm.id, target_fx_class) UpdateLightingThread(view) end function OnMsg.DoneMap() if not CurrentLightmodel then return end for view = 1, 1 do local target_fx_class = "View" .. view local lm = CurrentLightmodel[view] if lm then if lm.night then PlayFX("Day", "end", lm.id or "", target_fx_class) else PlayFX("Night", "end", lm.id or "", target_fx_class) end PlayFX("Rain", "end", lm.id or "", target_fx_class) PlayFX("Stormy", "end", lm.id or "", target_fx_class) PlayFX("SetLightmodel", "end", lm.id or "", target_fx_class) end end end function OnMsg.GedClosing(id) local app = GedConnections[id] if app.app_template == "LightmodelEditor" then hr.AutoExposureMode = EngineOptions.EyeAdaptation == "On" and 1 or 0 hr.EnablePostProcExposureSplit = 0 end end function OnMsg.GatherFXActions(list) list[#list+1] = "Day" list[#list+1] = "Night" list[#list+1] = "Rain" list[#list+1] = "SetLightmodel" end if FirstLoad then LightmodelLists = false end function UpdateLightmodelLists() local lists = {} ForEachPreset(LightmodelPreset, function(lm, group_list) if lm.use_time_of_day then local list_name = lm:GetListName() local list = lists[list_name] or {} local entry = { time = lm.time, blend_time = lm.blend_time, id = lm.id } list[#list+1] = entry lists[list_name] = list end end) for list_name, list in pairs(lists) do table.sort(list, function(lm1, lm2) return lm1.time < lm2.time end) end LightmodelLists = lists end OnMsg.BinAssetsLoaded = UpdateLightmodelLists function FindNextLightmodel(list_name, time_of_day) local list = LightmodelLists and LightmodelLists[list_name] if not list then return end assert(time_of_day >= -24*60 and time_of_day < 2*24*60) -- time of time_of_day = (time_of_day + 2*24*60) % (24*60) for i = 1, #list do local lm = list[i] if lm.time >= time_of_day then return LightmodelPresets[lm.id] end end return list[1] end function FindPrevLightmodel(list_name, time_of_day) local list = LightmodelLists and LightmodelLists[list_name] if not list then return end assert(time_of_day >= -24*60 and time_of_day < 2*24*60) -- time of time_of_day = (time_of_day + 2*24*60) % (24*60) for i = #list, 1, -1 do local lm = list[i] if lm.time <= time_of_day then return LightmodelPresets[lm.id] end end return list[#list] end ------------- Editor only actions ----------------- function OnMsg.GedOpened(ged_id) local conn = GedConnections[ged_id] if conn and conn.app_template == "LightmodelEditor" then local root = conn:ResolveObj("root") local active_lightmodel = LightmodelPreset.GetInitialSelection() if active_lightmodel then local selection = { table.find(root, root[active_lightmodel.group]), table.find(root[active_lightmodel.group], active_lightmodel), } conn:Send("rfnApp", "SetSelection", "root", selection) end end end function LightmodelPreset.GetInitialSelection() local id, val = next(LightmodelPresets) if not id then return end return LightmodelPresets[CurrentLightmodel and CurrentLightmodel[1] and CurrentLightmodel[1].id] or val end if FirstLoad then ChangeLightmodelOverrideThread = false CelestialPoleDebugThread = false end function SetLightmodelOverrideDelay(view, lm) if ChangeLightmodelOverrideThread then DeleteThread(ChangeLightmodelOverrideThread) end ChangeLightmodelOverrideThread = CreateRealTimeThread(function() Sleep(100) SetLightmodelOverride(view, lm) ChangeLightmodelOverrideThread = false end) end function LightmodelPreset:OnEditorSelect(selection, ged) if not self:EditorCheck() then return end if IsKindOf(ged:ResolveObj("SelectedPreset"), "GedMultiSelectAdapter") then return end if selection then SetLightmodelOverrideDelay(1, self) FXCache = false else SetLightmodelOverrideDelay(1, false) end end local function AdjustColor(obj, prop, brightness) local h, s, v = UIL.RGBtoHSV(GetRGB(obj[prop])) obj[prop] = RGB(UIL.HSVtoRGB(h, s, MulDivRound(v, brightness, 100))) end local function DebugMarkPole() hr.SkyCelestialPoleDebug = 1 DeleteThread(CelestialPoleDebugThread) CelestialPoleDebugThread = CreateRealTimeThread(function() Sleep(60000) hr.SkyCelestialPoleDebug = 0 end) end function LightmodelPreset:Getcubemap_capture_preview() return table.changed(hr, "CubemapCapturePreview") and true end function LightmodelPreset:OnEditorSetProperty(prop_id, old_value, ged) if prop_id == "env_view_site" then if table.changed(hr, "ViewEnv") then self:ViewEnv() end return elseif prop_id == "sky_is" then if self[prop_id] then hr.DeferFlags = hr.DeferFlags | const.DeferFlagSkyIS else hr.DeferFlags = hr.DeferMode & ~const.DeferFlagSkyIS end return elseif prop_id == "cubemap_capture_preview" then if self[prop_id] then CubemapCaptureMode(true, "CubemapCapturePreview") else CubemapCaptureMode(false, "CubemapCapturePreview") end elseif prop_id == "env_exterior_capture_pos" or prop_id == "env_interior_capture_pos" then self.env_capture_map = GetMapName() ObjModified(self) elseif prop_id == "stars_rotation" or prop_id == "stars_pole_alt" or prop_id == "stars_pole_azi" then DelayedCall(50, DebugMarkPole) elseif prop_id == "Group" or prop_id == "use_time_of_day" or prop_id == "time" or prop_id == "blend_time" then UpdateLightmodelLists() end for feature, feature_data in pairs(LightmodelFeatureToProperties) do if prop_id == feature_data.preset_feature_prop_id then self:SetFeaturePresetId(feature, self:GetProperty(prop_id)) end end if self:EditorCheck() then DoSetLightmodel(1, self, 0) DelayedCall(300, function(object) Msg("LightmodelChange", 1, object, 0, object) end, self) end end function LightmodelPreset:Gray(root, prop, ged) local r, g, b = GetRGB( self[prop] ) local gray = (33 * r + 50 * g + 17 * b) / 100 return GedSetProperty(ged, self, prop, RGB(gray, gray, gray)) -- a call to the Ged Op for undo support end function LightmodelPreset:ViewExteriorEnvPos() editor.ClearSelWithUndoRedo() ViewPos(self.env_exterior_capture_pos, 20*guim) end function LightmodelPreset:ViewInteriorEnvPos() editor.ClearSelWithUndoRedo() ViewPos(self.env_interior_capture_pos, 20*guim) end if FirstLoad then g_LightmodelViewEnvLastCam = false end function LightmodelPreset:ViewEnv() local envmap_name = self.id .. self.env_view_site local envmap_path = "Textures/Cubemaps/" .. envmap_name if not io.exists(envmap_path .. "Env.dds") then assert(not "Env map must be captured before viewed!") return end local envmap_sh = Presets.SHDiffuseIrradiance.Default[envmap_name] envmap_sh = envmap_sh and Decode64(envmap_sh.sh9_coefficients) DoSetLightmodel(1, LightmodelPresets.ArtPreview, 0) SetCubemap(1, envmap_path, envmap_sh, 0, 0, 0) SetCubemap(1, envmap_path, envmap_sh, 1, 0, 0) if not table.changed(hr, "ViewEnv") then table.change(hr, "ViewEnv", { DeferFlags = const.DeferFlagEnvMapOnly, RenderCodeRenderables = 0, RenderTransparent = 0, RenderParticles = 0, RenderLights = 0, EnableScreenSpaceReflections = 0, }) if not g_LightmodelViewEnvLastCam then g_LightmodelViewEnvLastCam = { GetCamera() } cameraFly.Activate(1) end end end function LightmodelPreset:HideEnv() if table.changed(hr, "ViewEnv") then table.restore(hr, "ViewEnv") DoSetLightmodel(0, self, 0) if g_LightmodelViewEnvLastCam then SetCamera(table.unpack(g_LightmodelViewEnvLastCam)) g_LightmodelViewEnvLastCam = false end end end function LightmodelPreset:UseSelectionAsExteriorEnvPos() if IsEditorActive() and #editor.GetSel() > 0 then if IsKindOf(editor.GetSel()[1], "ShaderBall") then self.env_exterior_capture_pos = editor.GetSel()[1]:GetBSphere() self.env_capture_map = GetMapName() ObjModified(self) else print("Please use a Shaderball object to mark the desired environment capture position") end end end function LightmodelPreset:UseSelectionAsInteriorEnvPos() if IsEditorActive() and #editor.GetSel() > 0 then if IsKindOf(editor.GetSel()[1], "ShaderBall") then self.env_interior_capture_pos = editor.GetSel()[1]:GetBSphere() self.env_capture_map = GetMapName() ObjModified(self) else print("Please use a Shaderball object to mark the desired environment capture position") end end end if FirstLoad then g_LMCaptureQueue = {} g_LMCaptureThread = false end function LightmodelPreset:CaptureEnvmap(site, ged) if self.env_capture_map ~= GetMapName() then if MapData[self.env_capture_map] then ChangeMap(self.env_capture_map) else assert(false, "Capture map doesn't exist") return "Capture map doesn't exist: " .. self.env_capture_map end end local env_capture_pos = site == "Interior" and self.env_interior_capture_pos or self.env_exterior_capture_pos if not env_capture_pos:IsValidZ() then return "Capture position must not be a 2D position (on the terrain)" end local map_x, map_y = terrain.GetMapSize() if env_capture_pos:x() > map_x or env_capture_pos:y() > map_y or env_capture_pos:x() < 0 or env_capture_pos:y() < 0 then assert(false, "Camera is out of map!") g_CapturingLightmodel = false return "Camera is out of map!" end local lightmodel_for_capture = LightmodelPresets[self.lm_capture] or self local sky_exp = GetSceneParam("SkyExp") local sun_int = GetSceneParam("SunIntensity") DoSetLightmodel(1, lightmodel_for_capture, 0) SetSceneParam(1, "RainEnable", 0, 0, 0) if site == "Exterior" then SetSceneParam(1, "SkyExp", lightmodel_for_capture.sky_exp + lightmodel_for_capture.env_exterior_capture_sky_exp, 0, 0) SetSceneParam(1, "SunIntensity", lightmodel_for_capture.sun_intensity + lightmodel_for_capture.env_exterior_capture_sun_int , 0, 0) else SetSceneParam(1, "SkyExp", lightmodel_for_capture.sky_exp + lightmodel_for_capture.env_interior_capture_sky_exp, 0, 0) SetSceneParam(1, "SunIntensity", lightmodel_for_capture.sun_intensity + lightmodel_for_capture.env_interior_capture_sun_int, 0, 0) end -- hide undesired objects from environment local objs_to_restore = {} MapForEach("map", g_ClassesToHideInCubemaps or "EditorVisibleObject", function(object) if object:GetEnumFlags(const.efVisible) > 0 then table.insert(objs_to_restore, object) object:ClearEnumFlags(const.efVisible) end end) WaitNextFrame(10) HDRCubemapExportAll("Textures/Cubemaps/", self.id, site, env_capture_pos) WaitNextFrame(10) -- return hidden objects in environment if needed for _, object in pairs(objs_to_restore) do if IsValid(object) then object:SetEnumFlags(const.efVisible) end end self:SetProperty("sky_is", false) self:SetProperty(site:lower() .. "_envmap", self.id) SetSceneParam(1, "SkyExp", sky_exp, 0, 0) SetSceneParam(1, "SunIntensity", sun_int, 0, 0) WaitNextFrame(10) ObjModified(self) DoSetLightmodel(1, self, 0) if ged then local result = ged:Send("rfnApp", "ReloadImage", ConvertToOSPath("Textures/Cubemaps/Thumbnails/" .. self.id .. site .. ".jpg")) assert(not result) end end function LMCaptureThread() while g_LMCaptureQueue[1] do local lm, site, ged = table.unpack(g_LMCaptureQueue[1]) table.remove(g_LMCaptureQueue, 1) lm:CaptureEnvmap(site, ged) print(lm.id) end g_LMCaptureThread = false end function AddLMCapture(lm, site, ged) table.insert(g_LMCaptureQueue, {lm, site, ged}) if not g_LMCaptureThread then g_LMCaptureThread = CreateRealTimeThread( LMCaptureThread ) end end function LightmodelPreset:CaptureExteriorEnvmap(root, prop_id, ged) if not self:EditorCheck(true) then return end AddLMCapture(self, "Exterior", ged) end function LightmodelPreset:CaptureInteriorEnvmap(root, prop_id, ged) if not self:EditorCheck(true) then return end AddLMCapture(self, "Interior", ged) end function LightmodelPreset:CaptureBothEnvmaps(root, prop_id, ged) if not self:EditorCheck(true) then return end AddLMCapture(self, "Exterior", ged) AddLMCapture(self, "Interior", ged) end function LightmodelPreset:ConvertHDRPano(site, ged) if not self:EditorCheck(true) or self.hdr_pano == "" then return end local _, base_name, _ = SplitPath(pano_path) HDRCubemapFromPano("Textures/Cubemaps/", base_name, site, self.hdr_pano) if ged then local result = ged:Send("rfnApp", "ReloadImage", ConvertToOSPath("Textures/Cubemaps/Thumbnails/" .. base_name .. site .. ".jpg")) assert(not result) end end function LightmodelPreset:ConvertExteriorEnvmap(root, prop_id, ged) self:ConvertHDRPano("Exterior", ged) end function LightmodelPreset:ConvertInteriorEnvmap(root, prop_id, ged) self:ConvertHDRPano("Interior", ged) end function LightmodelPreset:EditorCheck(print_err) if CurrentMap == "" then if print_err then print("Load a map to access this Lightmodel action.") end return false end return true end function LightmodelPreset:GetCubemapWarning() if not MapData[self.env_capture_map] then return string.format("Map for capturing cubemaps doesn't exist: %s", self.env_capture_map) end end function LightmodelPreset:GetWarning() local cubemap_warning = self:GetCubemapWarning() if cubemap_warning then return cubemap_warning end end function LightmodelPreset:AutoExposureOn() hr.AutoExposureMode = 1 hr.EnablePostProcExposureSplit = 0 end function LightmodelPreset:AutoExposureOff() hr.AutoExposureMode = 0 hr.EnablePostProcExposureSplit = 0 end function LightmodelPreset:AutoExposureSplit() hr.EnablePostProcExposureSplit = 1 end function LightmodelPreset:AutoExposureDebugToggle() ToggleHR "AutoExposureDebug" end function LightmodelEditorTakeScreenshots(ged, obj) local lightmodels = obj:IsKindOf("GedMultiSelectAdapter") and obj.__objects or { obj } local prefix = os.date("%Y%m%d_%H%M%S_", os.time()) local items = {} AsyncCreatePath("AppData/LightmodelScreenshots") for i, lm in ipairs(lightmodels) do DoSetLightmodel(1, lm, 0) WaitNextFrame(3) LockCamera("Screenshot") local filename = string.format("AppData/LightmodelScreenshots/%s_%s.png", prefix, lm.id) MovieWriteScreenshot(filename, 0, 16, false) items[#items+1] = ScreenshotItem:new{ display_name = lm.id, file_path = filename } UnlockCamera("Screenshot") WaitNextFrame(1) end local sdv = OpenGedApp("ScreenshotDiffViewer", items) for _, s in ipairs(items) do sdv:Send("rfnApp", "rfnSetSelectedFilePath", s.file_path, true) end end