sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
20.3 kB
DefineClass.ComponentLight = {
__parents = { "CObject" },
flags = { cfLight = true, cofComponentLight = true, }
}
for name, func in pairs(ComponentLightFunctions) do
ComponentLight[name] = func
end
local lightmodel_lights = {"A", "B", "C", "D"}
DefineClass.Light = {
__parents = { "Object", "InvisibleObject", "ComponentAttach", "ComponentLight" },
flags = { cfConstructible = false, gofRealTimeAnim = true, efShadow = false, efSunShadow = false,
gofDetailClass0 = true, gofDetailClass1 = true, -- to match 'Eye Candy' default of DetailClass
}, -- ATTN: cfEditorCallback added below when Platform.editor
-- Properties, editable in the editor's property window (Ctrl+O)
properties = {
{ id = "DetailClass", name = "Detail Class", editor = "dropdownlist",
items = {"Default", "Essential", "Optional", "Eye Candy"}, default = "Eye Candy",
},
{ category = "Visuals", id = "Color", editor = "color", autoattach_prop = true, dont_save = function(obj)
if not IsValid(obj) then return false end
if obj:GetLightmodelColorIndexNumber() ~= 0 then return true end
return false
end,
read_only = function(obj)
if not IsValid(obj) then return false end
if obj:GetLightmodelColorIndexNumber() ~= 0 then return true end
return false
end,
},
{ category = "Visuals", id = "LightmodelColorIndex", editor = "set", items = lightmodel_lights, max_items_in_set = 1, default = {}, autoattach_prop = true,},
{ category = "Visuals", id = "OriginalColor", editor = "color", default = RGB(255, 255, 255), no_edit = true, dont_save = true },
{ category = "Visuals", id = "Intensity", editor = "number", min = 0, max = 255, slider = true, autoattach_prop = true, },
{ category = "Visuals", id = "Exterior", editor = "bool", autoattach_prop = true, },
{ category = "Visuals", id = "Interior", editor = "bool", autoattach_prop = true, },
{ category = "Visuals", id = "InteriorAndExteriorWhenHasShadowmap", editor = "bool", autoattach_prop = true, },
{ category = "Visuals", id = "Volume", helper = "volume", editor = "object", default = false, base_class = "Volume" },
{ category = "Visuals", id = "ConstantIntensity", editor = "number", default = 0, autoattach_prop = true, slider = true, max = 127, min = -128 },
{ category = "Visuals", id = "AttenuationShape", editor = "number", default = 0, autoattach_prop = true, slider = true, max = 255, min = 0 },
{ category = "Visuals", id = "CastShadows", editor = "bool", autoattach_prop = true },
{ category = "Visuals", id = "DetailedShadows", editor = "bool", autoattach_prop = true },
{ id = "ColorModifier", editor = false },
{ id = "Occludes", editor = false },
{ id = "Walkable", editor = false },
{ id = "ApplyToGrids", editor = false },
{ id = "Collision", editor = false },
{ id = "Color1", editor = false },
{ id = "ParentSIModulation", editor = "number", default = 100, min = 0, max = 255, slider = true, autoattach_prop = true, no_edit = function(o)
return IsKindOf(o, "CObject")
end, help = "To be used by the AutoAttach system."},
},
Color = RGB(255,255,255),
Intensity = 100,
Interior = true,
Exterior = true,
InteriorAndExteriorWhenHasShadowmap = true,
CastShadows = false,
DetailedShadows = false,
Init = function(self)
self:SetColor(self.Color)
self:SetIntensity(self.Intensity)
self:SetInterior(self.Interior)
self:SetExterior(self.Exterior)
self:SetInteriorAndExteriorWhenHasShadowmap(self.InteriorAndExteriorWhenHasShadowmap)
self:SetConstantIntensity(0)
self:SetAttenuationShape(0)
self:SetLightmodelColorIndex(empty_table)
self:SetCastShadows(self.CastShadows)
self:SetDetailedShadows(self.DetailedShadows)
end,
GetCastShadows = function(self) return self:GetLightFlags(const.elfCastShadows) end,
SetCastShadows = function(self, value)
self.CastShadows = value
if value then
self:SetLightFlags(const.elfCastShadows)
else
self:ClearLightFlags(const.elfCastShadows)
end
end,
GetDetailedShadows = function(self) return self:GetLightFlags(const.elfDetailedShadows) end,
SetDetailedShadows = function(self, value)
self.DetailedShadows = value
if value then
self:SetLightFlags(const.elfDetailedShadows)
else
self:ClearLightFlags(const.elfDetailedShadows)
end
end,
GetColor = function(self)
local index = self:GetLightmodelColorIndexNumber()
if index ~= 0 then return GetSceneParamColor("LightColor" .. index) end
return self:GetColor0()
end,
SetColor = function(self, rgb) self:SetColor0(rgb) self:SetColor1(rgb) end,
GetColor0 = function(self) return self:GetColorAtIndex(0) end,
GetColor1 = function(self) return self:GetColorAtIndex(1) end,
SetColor0 = function(self, rgb) self:SetColorAtIndex(0, rgb) self:SetColorModifier(rgb or 0) end,
SetColor1 = function(self, rgb) self:SetColorAtIndex(1, rgb) end,
GetExterior = function(self)
return self:GetLightFlags(const.elfExterior)
end,
SetExterior = function(self, value)
self.Exterior = value
if value then
self:SetLightFlags(const.elfExterior)
else
self:ClearLightFlags(const.elfExterior)
end
end,
GetInterior = function(self)
return self:GetLightFlags(const.elfInterior)
end,
SetInterior = function(self, value)
self.Interior = value
if value then
self:SetLightFlags(const.elfInterior)
else
self:ClearLightFlags(const.elfInterior)
end
end,
GetInteriorAndExteriorWhenHasShadowmap = function(self)
return self:GetLightFlags(const.elfInteriorAndExteriorWhenHasShadowmap)
end,
SetInteriorAndExteriorWhenHasShadowmap = function(self, value)
self.InteriorAndExteriorWhenHasShadowmap = value
if value then
self:SetLightFlags(const.elfInteriorAndExteriorWhenHasShadowmap)
else
self:ClearLightFlags(const.elfInteriorAndExteriorWhenHasShadowmap)
end
end,
SetLightmodelColorIndex = function(self, val)
local index = 0
local activated_key = false
for key, value in pairs(val or empty_table) do
if value then activated_key = key end
end
if activated_key then
index = table.find(lightmodel_lights, activated_key)
end
assert(index >= 0 and index <= 4)
-- maskset(old, const.elfColorIndexMask, index << const.elfColorIndexShift)
self:ClearLightFlags(const.elfColorIndexMask)
self:SetLightFlags(index << const.elfColorIndexShift)
end,
GetLightmodelColorIndex = function(self)
local index = self:GetLightmodelColorIndexNumber()
if index == 0 then return {} end
return { [lightmodel_lights[index]] = true }
end,
SetIntensity = function(self, value) self:SetIntensityAtIndex(0, value) self:SetIntensityAtIndex(1, value) end,
SetIntensity0 = function(self, value) self:SetIntensityAtIndex(0, value) end,
SetIntensity1 = function(self, value) self:SetIntensityAtIndex(1, value) end,
GetIntensity0 = function(self, value) return self:GetIntensityAtIndex(0) end,
GetIntensity1 = function(self, value) return self:GetIntensityAtIndex(1) end,
GetIntensity = function(self, value) return self:GetIntensityAtIndex(0), self:GetIntensityAtIndex(1) end,
GetAlwaysRenderable = function(self) return self:GetGameFlags(const.gofAlwaysRenderable) end,
SetAlwaysRenderable = function(self, value)
if value == true then
self:SetGameFlags(const.gofAlwaysRenderable)
else
self:ClearGameFlags(const.gofAlwaysRenderable)
end
end,
SetBehavior = function(self, b)
if b == "flicker" then
self:SetLightFlags(const.elfFlicker)
else
self:ClearLightFlags(const.elfFlicker)
end
end,
CurrTime = function(self)
if self:GetGameFlags( const.gofRealTimeAnim ) > 0 then
return RealTime()
end
return GameTime()
end,
Fade = function(self, color, intensity, time)
self:SetBehavior("fade")
self:SetTimes(self:CurrTime(), self:CurrTime() + time)
self:SetColor0(self:GetColor1())
self:SetColor1(color)
self:SetIntensity0(self:GetIntensity1())
self:SetIntensity1(intensity)
end,
Flicker = function(self, color, intensity, period, phase)
self:SetBehavior("flicker")
phase = self:CurrTime() - (phase or AsyncRand(period))
self:SetTimes(phase, phase + period * 300)
self:SetColor(color)
self:SetIntensity0(0)
self:SetIntensity1(intensity)
end,
Steady = function(self, color, intensity)
self:SetColor(color)
self:SetBehavior("fade")
self:SetTimes(-1,-1)
self:SetIntensity(intensity)
end,
SetParentSIModulation = function(self, value)
local parent = self:GetParent()
if parent then
parent:SetSIModulation(value)
end
end,
GetParentSIModulation = function(self)
local parent = self:GetParent()
if parent then
return parent:GetSIModulation()
end
return 100
end,
SetVolume = function(self, volume_obj)
self:SetTargetVolumeId(volume_obj and volume_obj.handle or 0)
end,
GetVolume = function(self)
local handle = self:GetTargetVolumeId()
if not handle or handle == 0 then return false end
return HandleToObject[handle]
end,
SetContourOuterID = empty_func,
}
function Light:OnEditorSetProperty(prop_id)
if prop_id == "DetailClass" then
self:DestroyRenderObj()
end
end
const.ShadowDirsComboItems = {
[LastSetBit(const.eLightDirX) + 1] = { name = "+X" },
[LastSetBit(const.eLightDirNegX) + 1] = { name = "-X" },
[LastSetBit(const.eLightDirY) + 1] = { name = "+Y" },
[LastSetBit(const.eLightDirNegY) + 1] = { name = "-Y" },
[LastSetBit(const.eLightDirZ) + 1] = { name = "+Z" },
[LastSetBit(const.eLightDirNegZ) + 1] = { name = "-Z" },
}
local shadowDirsDefault = 0
DefineClass.PointLight = {
__parents = { "Light" },
entity = "PointLight", -- needed by the editor
properties = {
{ category = "Visuals", id = "SourceRadius", name = "Source Radius (cm)", editor = "number", min = guic, max=20*guim, default = 10*guic, scale = guic, slider = true,
helper = "sradius", color = RGB(200, 200, 0), autoattach_prop = true, },
{ category = "Visuals", id = "AttenuationRadius", name = "Attenuation Radius", editor = "number", min = 0*guim, max=500*guim, default = 10*guim, scale = "m", slider = true,
helper = "sradius", color = RGB(255, 0, 0), autoattach_prop = true, },
{ category = "Visuals", id = "ShadowDirs", name = "Shadow Dirs (To disable)", editor = "flags", items = const.ShadowDirsComboItems, default = shadowDirsDefault, size = 6,
autoattach_prop = true, },
},
ShadowDirsDefault = shadowDirsDefault,
SourceRadius = 1*guic,
AttenuationRadius = 10*guim,
Init = function(self)
self:SetSourceRadius(self.SourceRadius)
self:SetAttenuationRadius(self.AttenuationRadius)
self:SetLightType(const.eLightTypePoint)
self:SetShadowDirs(shadowDirsDefault)
end,
}
DefineClass.LightFlicker = {
__parents = {"InitDone"},
entity = "PointLight", -- needed by the editor
properties = {
{ id = "Color", editor = false },
{ id = "Intensity", editor = false },
{ category = "Visuals", id = "Color0", editor = "color", default = RGB(255,255,255), autoattach_prop = true, },
{ category = "Visuals", id = "Intensity0", editor = "number", default = 0, min = 0, max = 255, slider = true, autoattach_prop = true, },
{ category = "Visuals", id = "Color1", editor = "color", default = RGB(255,255,255), autoattach_prop = true, },
{ category = "Visuals", id = "Intensity1", editor = "number", default = 100, min = 0, max = 255, slider = true, autoattach_prop = true, },
{ category = "Visuals", id = "Period", editor = "number", default = 500, min = 0, max = 100000, scale = 1000, slider = true, autoattach_prop = true, },
},
-- defaults
Period = 40000,
}
function LightFlicker:Init()
self:SetBehavior("flicker")
self:SetColor(self.Color)
self:SetIntensity0(0)
self:SetIntensity1(self.Intensity)
self:SetPeriod(self.Period)
end
function LightFlicker:GetPeriod()
local t0,t1 = self:GetTimes()
return t1 - t0
end
function LightFlicker:SetPeriod(period)
period = Max(period, 1)
local phase = AsyncRand(period)
local time = self:CurrTime()
if self:CurrTime() < phase then
self:SetTimes(0, period)
else
self:SetTimes(time - phase, time - phase + period)
end
end
DefineClass.PointLightFlicker = {
__parents = { "PointLight", "LightFlicker" },
}
DefineClass.SpotLightFlicker = {
__parents = { "SpotLight", "LightFlicker" },
}
DefineClass.MaskedLight = {
__parents = { "Light" },
properties = {
{ category = "Visuals", id = "Mask", editor = "browse", folder = "Textures/Misc/LightMasks", help = "Specifies the texture that is going to be applied to modify the light appearance" },
{ category = "Visuals", id = "AnimX", editor = "number", min = 1, max = 16, help = "How many cuts on the X axis are specified in the mask texture. The animation is traversed left to right." },
{ category = "Visuals", id = "AnimY", editor = "number", min = 1, max = 16, help = "How many cuts on the Y axis are specified in the mask texture. The animation is traversed top to bottom." },
{ category = "Visuals", id = "AnimPeriod", editor = "number", min = 0, max = 256, scale = 10, help = "The period of the animation. If zero is specified, the animation is not applied." },
{ category = "Visuals", id = "ScaleMask", editor = "bool", default = false, },
},
-- defaults
Mask = "Textures/Misc/LightMasks/angle-attn.tga",
ScaleMask = false,
AnimX = 1,
AnimY = 1,
AnimPeriod = 256,
Init = function(self)
self:SetMask(self.Mask)
self:SetScaleMask(self.ScaleMask)
self:SetAnimX(self.AnimX)
self:SetAnimY(self.AnimY)
self:SetAnimPeriod(self.AnimPeriod)
end,
GetScaleMask = function(self) return self:GetLightFlags(const.elfScaleMask) end,
SetScaleMask = function(self, scale)
if scale then
self:SetLightFlags(const.elfScaleMask)
else
self:ClearLightFlags(const.elfScaleMask)
end
end,
GetAnim = function(self, nshift)
local flags = self:GetAnimParams()
local anim_size = band(shift(flags, -nshift), const.elAnimMask) + 1
return anim_size
end,
SetAnim = function(self, nshift, num)
local flags = self:GetAnimParams()
local new_data = maskset(flags, shift(const.elAnimMask, nshift), shift(num-1, nshift))
self:SetAnimParams(new_data)
end,
GetAnimX = function(self) return self:GetAnim(const.elAnimXShift) end,
SetAnimX = function(self, num) self:SetAnim(const.elAnimXShift, num) end,
GetAnimY = function(self) return self:GetAnim(const.elAnimYShift) end,
SetAnimY = function(self, num) self:SetAnim(const.elAnimYShift, num) end,
GetMask = _GetCustomString,
SetMask = _SetCustomString,
GetAnimPeriod = function(self)
return shift(band(self:GetAnimParams(), const.elAnimPeriodMask), -const.elAnimPeriodShift)
end,
SetAnimPeriod = function(self, period)
local params = self:GetAnimParams()
local new_params = maskset(params, const.elAnimPeriodMask, shift(period, const.elAnimPeriodShift))
self:SetAnimParams(new_params )
end,
}
DefineClass.BoxLight = {
__parents = { "MaskedLight" },
entity = "PointLight", -- needed by the editor
properties = {
{ category = "Visuals", id = "BoxWidth", editor = "number", min = guim/10, max = 50*guim, slider = true, helper = "box3" },
{ category = "Visuals", id = "BoxHeight", editor = "number", min = guim/10, max = 50*guim, slider = true, helper = "box3" },
{ category = "Visuals", id = "BoxDepth", editor = "number", min = guim/10, max = 50*guim, slider = true, helper = "box3" },
},
-- defaults
BoxWidth = 5 * guim,
BoxHeight = 5 * guim,
BoxDepth = 5 * guim,
Init = function(self)
self:SetBoxWidth(self.BoxWidth)
self:SetBoxHeight(self.BoxHeight)
self:SetBoxDepth(self.BoxDepth)
self:SetLightType(const.eLightTypeBox)
end,
}
DefineClass.SpotLight = {
__parents = { "PointLight", "MaskedLight" },
entity = "PointLight", -- needed by the editor
properties = {
{ category = "Visuals", id = "ConeInnerAngle", editor = "number", min = 5, max = (180 - 5), default = 45, slider = true, helper = "spotlighthelper", autoattach_prop = true, },
{ category = "Visuals", id = "ConeOuterAngle", editor = "number", min = 5, max = (180 - 5), default = 90, slider = true, helper = "spotlighthelper", autoattach_prop = true, },
},
-- defaults
ConeInnerAngle = 45,
ConeOuterAngle = 90,
target_helper = false,
Init = function(self)
self:SetConeInnerAngle(self.ConeInnerAngle)
self:SetConeOuterAngle(self.ConeOuterAngle)
self:SetLightType(const.eLightTypeSpot)
end,
GetConeInnerAngle = function(self) return self:GetInnerAngle() end,
GetConeOuterAngle = function(self) return self:GetOuterAngle() end,
}
if Platform.developer then
function SpotLight:SetConeInnerAngle(v)
self:SetInnerAngle(v)
if (v > self:GetOuterAngle()) then
self:SetOuterAngle(v)
end
end
function SpotLight:SetConeOuterAngle(v)
self:SetOuterAngle(v)
if (v < self:GetInnerAngle()) then
self:SetInnerAngle(v)
end
end
else
function SpotLight:SetConeInnerAngle(v) self:SetInnerAngle(v) end
function SpotLight:SetConeOuterAngle(v) self:SetOuterAngle(v) end
end
function SpotLight:OnEditorSetProperty(...)
Light.OnEditorSetProperty(self, ...)
PropertyHelpers_UpdateAllHelpers(self)
end
function SpotLight:ConfigureTargetHelper()
if not self.target_helper or not IsValid(self.target_helper) then
self.target_helper = PlaceObject("SpotHelper")
self.target_helper.obj = self
end
local axis = self:GetOrientation()
local pos = self:GetVisualPos()
local o, closest, normal = IntersectSegmentWithClosestObj(pos, pos - axis * guim)
if closest and normal and o ~= self.target_helper then
self.target_helper:SetPos(closest)
else
local newPos = terrain.IntersectRay(pos, pos + axis)
if newPos then
self.target_helper:SetPos(newPos:SetZ(const.InvalidZ))
end
end
end
function OnMsg.EditorSelectionChanged(objs)
local isSpotLight = false
for _, obj in ipairs(objs) do
if obj.class == "SpotLight" then
isSpotLight = true
obj:ConfigureTargetHelper()
elseif obj.class == "SpotHelper" then
isSpotLight = true
end
end
if not isSpotLight then
MapForEach(true, "SpotHelper", function(spot_helper)
DoneObject(spot_helper)
end)
end
end
function OnMsg.EditorCallback(id, objects, ...)
if id == "EditorCallbackMove" or id == "EditorCallbackRotate" or id == "EditorCallbackPlace" then
for _, obj in ipairs(objects) do
if obj.class == "SpotLight" then
obj:ConfigureTargetHelper()
end
end
if id == "EditorCallbackMove" then
for _, obj in ipairs(objects) do
if obj.class == "SpotHelper" and obj.obj.class == "SpotLight" then
obj.obj:SetOrientation(Normalize(obj.obj:GetVisualPos() - obj:GetVisualPos()), 0)
end
end
end
elseif id == "EditorCallbackDelete" then
for _, obj in ipairs(objects) do
if obj.class == "SpotLight" then
DoneObject(obj.target_helper)
obj.target_helper = false
end
end
end
end
if Platform.developer and false then
function OnMsg.NewMapLoaded()
-- check if used lightmaps are available
local masks = {}
MapForEach("map", "Light", function(light) if light:HasMember("GetMask") then masks[light:GetMask()] = true end end)
for mask, _ in pairs(masks) do
local id = ResourceManager.GetResourceID(mask)
if id == const.InvalidResourceID then
printf("once", "Light mask texture '%s' is not present", mask)
end
end
end
end
function PointLight:ConfigureInvisibleObjectHelper(helper)
if not helper then return end
local important = self:GetDetailClass() == "Essential"
helper:SetScale(important and 100 or 60)
if important then
helper:SetColorModifier(self:GetCastShadows() and RGB(100, 10, 10) or RGB(20, 80, 100))
else
helper:SetColorModifier(self:GetCastShadows() and RGB(100, 30, 30) or RGB(40, 80, 100))
end
end
DefineClass.AttachLightPropertyObject = {
__parents = {"PropertyObject"},
properties = {
{category = "Lights", id = "AttachLight", name = "Attach Light", editor = "bool", default = true},
},
}
local detail_class_weight = {["Essential"] = 1, ["Optional"] = 2, ["Eye Candy"] = 3}
function GetLights(filter)
if GetMap() == "" then return end
local lights = MapGet("map", "Light", const.efVisible, filter) or empty_table
table.sort(lights, function(light1, light2)
local weight1 = detail_class_weight[light1:GetDetailClass()] or 4
local weight2 = detail_class_weight[light2:GetDetailClass()] or 4
if weight1 == weight2 then
return light1.handle < light2.handle
else
return weight1 < weight2
end
end)
return lights
end