Download CommonLua/Classes/CodeRenderableObject.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 41.1 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Classes/CodeRenderableObject.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/Classes/CodeRenderableObject.lua
-
curl -L -o CodeRenderableObject.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Classes/CodeRenderableObject.lua
41.1 kB
| local AppendVertex = pstr().AppendVertex | |
| local GetHeight = terrain.GetHeight | |
| local height_tile = const.HeightTileSize | |
| local InvalidZ = const.InvalidZ | |
| local KeepRefOneFrame = KeepRefOneFrame | |
| local SetCustomData | |
| local GetCustomData | |
| function OnMsg.Autorun() | |
| SetCustomData = ComponentCustomData.SetCustomData | |
| GetCustomData = ComponentCustomData.GetCustomData | |
| end | |
| DefineClass.CodeRenderableObject = | |
| { | |
| __parents = { "Object", "ComponentAttach", "ComponentCustomData" }, | |
| entity = "", | |
| flags = { | |
| gofAlwaysRenderable = true, cfCodeRenderable = true, cofComponentInterpolation = true, cfConstructible = false, | |
| efWalkable = false, efCollision = false, efApplyToGrids = false, efSelectable = false, efShadow = false, efSunShadow = false | |
| }, | |
| depth_test = false, | |
| zwrite = true, | |
| } | |
| DefineClass.Text = | |
| { | |
| --[[ | |
| Custom data layout: | |
| const.CRTextCCDIndexColorMain - base color, RGBA | |
| const.CRTextCCDIndexColorShadow - shadow color, RGBA | |
| const.CRTextCCDIndexFlags - flags: 0: depth test, 1, center | |
| const.CRTextCCDIndexText - text, as string - you must keep this string from being GCed while the Text object is alive | |
| const.CRTextCCDIndexFont - font_id, integer as returned by UIL.GetFontID | |
| const.CRTextCCDIndexShadowOffset - shadow offset | |
| const.CRTextCCDIndexOpacity - opacity interpolation parameters | |
| const.CRTextCCDIndexScale - scale interpolation parameters | |
| const.CRTextCCDIndexZOffset - z offset interpolation parameters | |
| ]] | |
| __parents = { "CodeRenderableObject" }, | |
| text = false, | |
| text_style = false, | |
| hide_in_editor = true, -- hide using the T button in the editor statusbar (or Alt-Shift-T shortcut) | |
| } | |
| local TextFlag_DepthTest = 1 | |
| local TextFlag_Center = 2 | |
| function Text:SetColor1(c) SetCustomData(self, const.CRTextCCDIndexColorMain, c) end | |
| function Text:SetColor2(c) SetCustomData(self, const.CRTextCCDIndexColorShadow, c) end | |
| function Text:GetColor1(c) return GetCustomData(self, const.CRTextCCDIndexColorMain) end | |
| function Text:GetColor2(c) return GetCustomData(self, const.CRTextCCDIndexColorShadow) end | |
| function Text:SetColor(c) | |
| self:SetColor1(c) | |
| self:SetColor2(RGB(0,0,0)) | |
| end | |
| function Text:GetColor(c) | |
| return self:GetColor1() | |
| end | |
| function Text:SetDepthTest(depth_test) | |
| local flags = GetCustomData(self, const.CRTextCCDIndexFlags) | |
| if depth_test then | |
| SetCustomData(self, const.CRTextCCDIndexFlags, FlagSet(flags, TextFlag_DepthTest)) | |
| else | |
| SetCustomData(self, const.CRTextCCDIndexFlags, FlagClear(flags, TextFlag_DepthTest)) | |
| end | |
| end | |
| function Text:GetDepthTest() | |
| return IsFlagSet(GetCustomData(self, const.CRTextCCDIndexFlags), TextFlag_DepthTest) | |
| end | |
| function Text:SetCenter(c) | |
| local flags = GetCustomData(self, const.CRTextCCDIndexFlags) | |
| if c then | |
| SetCustomData(self, const.CRTextCCDIndexFlags, FlagSet(flags, TextFlag_Center)) | |
| else | |
| SetCustomData(self, const.CRTextCCDIndexFlags, FlagClear(flags, TextFlag_Center)) | |
| end | |
| end | |
| function Text:GetCenter() | |
| return IsFlagSet(GetCustomData(self, const.CRTextCCDIndexFlags), TextFlag_Center) | |
| end | |
| function Text:SetText(txt) | |
| KeepRefOneFrame(self.text) | |
| self.text = txt | |
| SetCustomData(self, const.CRTextCCDIndexText, self.text) | |
| end | |
| function Text:GetText() | |
| return self.text | |
| end | |
| function Text:SetFontId(id) SetCustomData(self, const.CRTextCCDIndexFont, id) end | |
| function Text:GetFontId() return GetCustomData(self, const.CRTextCCDIndexFont) end | |
| function Text:SetShadowOffset(so) SetCustomData(self, const.CRTextCCDIndexShadowOffset, so) end | |
| function Text:GetShadowOffset() return GetCustomData(self, const.CRTextCCDIndexShadowOffset) end | |
| function Text:SetTextStyle(style, scale) | |
| local style = TextStyles[style] | |
| if not style then | |
| assert(false, string.format("Invalid text style '%s'", style)) | |
| return | |
| end | |
| scale = scale or terminal.desktop.scale:y() | |
| local font, height, base_height = style:GetFontIdHeightBaseline(scale) | |
| self:SetFontId(font, height, base_height) | |
| self:SetColor(style.TextColor) | |
| self:SetShadowOffset(style.ShadowSize) | |
| self.text_style = style | |
| end | |
| function Text:SetOpacityInterpolation(v0, t0, v1, t1) | |
| -- opacities are 0..100, 7 bits | |
| -- times are encoded as ms/10, 8 bits each | |
| v0 = v0 or 100 | |
| v1 = v1 or v0 | |
| t0 = t0 or 0 | |
| t1 = t1 or 0 | |
| SetCustomData(self, const.CRTextCCDIndexOpacity, EncodeBits(v0, 7, v1, 7, t0/10, 8, t1/10, 8)) | |
| end | |
| function Text:SetScaleInterpolation(v0, t0, v1, t1) | |
| -- scales are encoded 0..127, scale in percent/4 - so range 0..500% | |
| -- times are encoded as ms/10, 8 bits each | |
| v0 = v0 or 100 | |
| v1 = v1 or v0 | |
| t0 = t0 or 0 | |
| t1 = t1 or 0 | |
| SetCustomData(self, const.CRTextCCDIndexScale, EncodeBits(v0/4, 7, v1/4, 7, t0/10, 8, t1/10, 8)) | |
| end | |
| function Text:SetZOffsetInterpolation(v0, t0, v1, t1) | |
| -- Z offsets are encoded 0..127, in guim/50 - so range 0..6.35 m | |
| -- times are encoded as ms/10, 8 bits each | |
| v0 = v0 or 0 | |
| v1 = v1 or v0 | |
| t0 = t0 or 0 | |
| t1 = t1 or 0 | |
| SetCustomData(self, const.CRTextCCDIndexZOffset, EncodeBits(v0/50, 7, v1/50, 7, t0/10, 8, t1/10, 8)) | |
| end | |
| function Text:Init() | |
| self:SetTextStyle(self.text_style or "EditorText") | |
| end | |
| function Text:Done() | |
| KeepRefOneFrame(self.text) | |
| self.text = nil | |
| end | |
| function Text:SetCustomData(idx, data) | |
| assert(idx ~= const.CRTextCCDIndexText, "Use SetText instead") | |
| return SetCustomData(self, idx, data) | |
| end | |
| DefineClass.TextEditor = { | |
| __parents = {"Text", "EditorVisibleObject"}, | |
| } | |
| function PlaceText(text, pos, color, editor_visibile_only) | |
| local obj = PlaceObject(editor_visibile_only and "TextEditor" or "Text") | |
| if pos then | |
| obj:SetPos(pos) | |
| end | |
| obj:SetText(text) | |
| if color then | |
| obj:SetColor(color) | |
| end | |
| return obj | |
| end | |
| function RemoveAllTexts() | |
| MapDelete("map", "Text") | |
| end | |
| local function GetMeshFlags() | |
| local flags = {} | |
| for name, value in pairs(const) do | |
| if string.starts_with(name, "mf") then | |
| flags[value] = name | |
| end | |
| end | |
| return flags | |
| end | |
| DefineClass.MeshParamSet = { | |
| __parents = { "PropertyObject" }, | |
| properties = { | |
| }, | |
| uniforms = false, | |
| uniforms_size = 0, | |
| } | |
| local uniform_sizes = { | |
| integer = 4, | |
| float = 4, | |
| color = 4, | |
| point2 = 8, | |
| point3 = 12, | |
| } | |
| function GetUniformMeta(properties) | |
| local uniforms = {} | |
| local offset = 0 | |
| for _, prop in ipairs(properties) do | |
| local uniform_type = prop.uniform | |
| if uniform_type then | |
| if type(uniform_type) ~= "string" then | |
| if prop.editor == "number" then | |
| uniform_type = prop.scale and prop.scale ~= 1 and "float" or "integer" | |
| elseif prop.editor == "point" then | |
| uniform_type = "point3" | |
| else | |
| uniform_type = prop.editor | |
| end | |
| end | |
| local size = uniform_sizes[uniform_type] | |
| if not size then | |
| assert(false, "Unknown uniform type.") | |
| end | |
| local space = 16 - (offset % 16) | |
| if space < size then | |
| table.insert(uniforms, {id = false, type = "padding", offset = offset, size = space}) | |
| offset = offset + space | |
| end | |
| table.insert(uniforms, {id = prop.id, type = uniform_type, offset = offset, size = size, scale = prop.scale}) | |
| offset = offset + size | |
| end | |
| end | |
| return uniforms, offset | |
| end | |
| function OnMsg.ClassesPostprocess() | |
| ClassDescendantsList("MeshParamSet", function(name, def) | |
| def.uniforms, def.uniforms_size = GetUniformMeta(def:GetProperties()) | |
| end) | |
| end | |
| function MeshParamSet:WriteBuffer(param_pstr, offset, getter) | |
| if not offset then | |
| offset = 0 | |
| end | |
| if not getter then | |
| getter = self.GetProperty | |
| end | |
| param_pstr = param_pstr or pstr("", self.uniforms_size) | |
| param_pstr:resize(offset) | |
| for _, prop in ipairs(self.uniforms) do | |
| local value | |
| if prop.type == "padding" then | |
| value = prop.size | |
| else | |
| value = getter(self, prop.id) | |
| end | |
| param_pstr:AppendUniform(prop.type, value, prop.scale) | |
| end | |
| return param_pstr | |
| end | |
| function MeshParamSet:ComposeBuffer(param_pstr, getter) | |
| return self:WriteBuffer(param_pstr, 0, getter) | |
| end | |
| DefineClass.Mesh = { | |
| __parents = { "CodeRenderableObject" }, | |
| properties = { | |
| { id = "vertices_len", read_only = true, dont_save = true, editor = "number", default = 0,}, | |
| { id = "CRMaterial", editor = "nested_obj", base_class = "CRMaterial", default = false, }, | |
| { id = "MeshFlags", editor = "flags", default = 0, items = GetMeshFlags }, | |
| { id = "DepthTest", editor = "bool", default = false, read_only = function(s) return not s.shader or s.shader.depth_test ~= "runtime" end, }, | |
| { id = "ShaderName", editor = "choice", default = "default_mesh", items = function() return table.keys2(ProceduralMeshShaders, "sorted") end}, | |
| }, | |
| --[[ | |
| Custom data layout: | |
| const.CRMeshCCDIndexGeometry - vertices, packed in a pstr | |
| const.CRMeshCCDIndexPipeline - shader; depth_test >> 31, 0 for off, 1 for on | |
| const.CRMeshCCDIndexMeshFlags - mesh flags | |
| const.CRMeshCCDIndexUniforms - uniforms, packed in a pstr | |
| const.CRMeshCCDIndexTexture0, const.CRMeshCCDIndexTexture1 - textures | |
| ]] | |
| vertices_pstr = false, | |
| uniforms_pstr = false, | |
| shader = false, | |
| textstyle_id = false, | |
| } | |
| function Mesh:GedTreeViewFormat() | |
| return string.format("%s (%s)", self.class, self.CRMaterial and self.CRMaterial.id or self:GetShaderName()) | |
| end | |
| function Mesh:Getvertices_len() | |
| return self.vertices_pstr and #self.vertices_pstr or 0 | |
| end | |
| function Mesh:GetShaderName() | |
| return self.shader and self.shader.name or "" | |
| end | |
| function Mesh:SetShaderName(value) | |
| self:SetShader(ProceduralMeshShaders[value]) | |
| end | |
| function Mesh:Init() | |
| self:SetShader(ProceduralMeshShaders.default_mesh) | |
| end | |
| function Mesh:SetMesh(vpstr) | |
| KeepRefOneFrame(self.vertices_pstr) | |
| local vertices_pstr = #(vpstr or "") > 0 and vpstr or nil -- an empty string would result in a crash :| | |
| self.vertices_pstr = vertices_pstr | |
| SetCustomData(self, const.CRMeshCCDIndexGeometry, vertices_pstr) | |
| end | |
| function Mesh:SetUniformSet(uniform_set) | |
| self:SetUniformsPstr(uniform_set:ComposeBuffer()) | |
| end | |
| function Mesh:SetUniformsPstr(uniforms_pstr) | |
| KeepRefOneFrame(self.uniforms_pstr) | |
| self.uniforms_pstr = uniforms_pstr | |
| SetCustomData(self, const.CRMeshCCDIndexUniforms, uniforms_pstr) | |
| end | |
| function Mesh:SetUniformsList(uniforms, isDouble) | |
| KeepRefOneFrame(self.uniforms_pstr) | |
| local count = Max(8, #uniforms) | |
| local uniforms_pstr = pstr("", count * 4) | |
| self.uniforms_pstr = uniforms_pstr | |
| for i = 1, count do | |
| if isDouble then | |
| uniforms_pstr:AppendUniform("double", uniforms[i] or 0) | |
| else | |
| uniforms_pstr:AppendUniform("float", uniforms[i] or 0, 1000) | |
| end | |
| end | |
| SetCustomData(self, const.CRMeshCCDIndexUniforms, uniforms_pstr) | |
| end | |
| function Mesh:SetUniforms(...) | |
| return self:SetUniformsList{...} | |
| end | |
| function Mesh:SetDoubleUniforms(...) | |
| return self:SetUniformsList({...}, true) | |
| end | |
| function Mesh:SetShader(shader, depth_test) | |
| assert(shader.shaderid) | |
| assert(shader.defines) | |
| assert(shader.ref_id > 0) | |
| if depth_test == nil then | |
| if shader.depth_test == "always" then | |
| depth_test = true | |
| elseif shader.depth_test == "never" then | |
| depth_test = false | |
| else | |
| depth_test = self:GetDepthTest() | |
| end | |
| end | |
| local depth_test_int = 0 | |
| if depth_test then | |
| depth_test_int = 1 | |
| assert(shader.depth_test == "runtime" or shader.depth_test == "always", "Tried to enable depth test for shader with depth_test = never") | |
| else | |
| depth_test_int = 0 | |
| assert(shader.depth_test == "runtime" or shader.depth_test == "never", "Tried to disable depth test for shader with depth_test = always") | |
| end | |
| SetCustomData(self, const.CRMeshCCDIndexPipeline, shader.ref_id | (depth_test_int << 31)) | |
| self.shader = shader | |
| end | |
| function Mesh:SetDepthTest(depth_test) | |
| assert(self.shader) | |
| self:SetShader(self.shader, depth_test) | |
| end | |
| function Mesh:SetCRMaterial(material) | |
| if type(material) == "string" then | |
| local new_material = CRMaterial:GetById(material, true) | |
| assert(new_material, "CRMaterial not found.") | |
| material = new_material | |
| end | |
| self.CRMaterial = material | |
| local depth_test = material.depth_test | |
| if depth_test == "default" then depth_test = nil end | |
| CodeRenderableLockCCD(self) | |
| self:SetShader(material:GetShader(), depth_test) | |
| self:SetUniformsPstr(material:GetDataPstr()) | |
| CodeRenderableUnlockCCD(self) | |
| end | |
| function Mesh:GetCRMaterial() | |
| return self.CRMaterial | |
| end | |
| if FirstLoad then | |
| MeshTextureRefCount = {} | |
| end | |
| local function ModifyMeshTextureRefCount(id, change) | |
| if id == 0 then return end | |
| local old = MeshTextureRefCount[id] or 0 | |
| local new = old + change | |
| if new == 0 then | |
| MeshTextureRefCount[id] = nil | |
| ProceduralMeshReleaseResource(id) | |
| else | |
| MeshTextureRefCount[id] = new | |
| end | |
| end | |
| function Mesh:SetTexture(idx, resource_id) | |
| assert(idx >= 0 and idx <= 1) | |
| if self:GetTexture(idx) == resource_id then return end | |
| ModifyMeshTextureRefCount(self:GetTexture(idx), -1) | |
| SetCustomData(self, const.CRMeshCCDIndexTexture0 + idx, resource_id or 0) | |
| ModifyMeshTextureRefCount(resource_id, 1) | |
| end | |
| function Mesh:GetTexture(idx) | |
| assert(idx >= 0 and idx <= 1) | |
| return GetCustomData(self, const.CRMeshCCDIndexTexture0 + idx) or 0 | |
| end | |
| function Mesh:Done() | |
| KeepRefOneFrame(self.vertices_pstr) | |
| KeepRefOneFrame(self.uniforms_pstr) | |
| self.vertices_pstr = nil | |
| self.uniforms_pstr = nil | |
| self:SetTexture(0, 0) | |
| self:SetTexture(1, 0) | |
| end | |
| function OnMsg.DoneMap() | |
| for key, value in pairs(MeshTextureRefCount) do | |
| ProceduralMeshReleaseResource(key) | |
| end | |
| MeshTextureRefCount = {} | |
| end | |
| function Mesh:SetCustomData(idx, data) | |
| assert(idx > const.CRMeshCCDIndexGeometry, "Use SetMesh instead!") | |
| return SetCustomData(self, idx, data) | |
| end | |
| function Mesh:GetDepthTest() return (GetCustomData(self, const.CRMeshCCDIndexPipeline) >> 31) == 1 end | |
| function Mesh:SetMeshFlags(flags) SetCustomData(self, const.CRMeshCCDIndexMeshFlags, flags) end | |
| function Mesh:GetMeshFlags() return GetCustomData(self, const.CRMeshCCDIndexMeshFlags) end | |
| function Mesh:AddMeshFlags(flags) self:SetMeshFlags(flags | self:GetMeshFlags()) end | |
| function Mesh:ClearMeshFlags(flags) self:SetMeshFlags(~flags & self:GetMeshFlags()) end | |
| function Mesh.ColorFromTextStyle(id) | |
| assert(TextStyles[id]) | |
| return TextStyles[id].TextColor | |
| end | |
| function AppendCircleVertices(vpstr, center, radius, color, strip) | |
| local HSeg = 32 | |
| vpstr = vpstr or pstr("", 1024) | |
| color = color or RGB(254, 127, 156) | |
| center = center or point30 | |
| local x0, y0, z0 | |
| for i = 0, HSeg do | |
| local x, y, z = RotateRadius(radius, MulDivRound(360 * 60, i, HSeg), center, true) | |
| AppendVertex(vpstr, x, y, z, color) | |
| if not strip then | |
| if i ~= 0 then | |
| AppendVertex(vpstr, x, y, z, color) | |
| if i == HSeg then | |
| AppendVertex(vpstr, x0, y0, z0) | |
| end | |
| else | |
| x0, y0, z0 = x, y, z | |
| end | |
| end | |
| end | |
| return vpstr | |
| end | |
| function AppendTileVertices(vstr, x, y, z, tile_size, color, offset_z, get_height) | |
| offset_z = offset_z or 0 | |
| z = z or InvalidZ | |
| local d = tile_size / 2 | |
| local x1, y1, z1 = x - d, y - d | |
| local x2, y2, z2 = x + d, y - d | |
| local x3, y3, z3 = x - d, y + d | |
| local x4, y4, z4 = x + d, y + d | |
| get_height = get_height or GetHeight | |
| if z ~= InvalidZ and z ~= get_height(x, y) then | |
| z = z + offset_z | |
| z1, z2, z3, z4 = z, z, z, z | |
| else | |
| z1 = get_height(x1, y1) + offset_z | |
| z2 = get_height(x2, y2) + offset_z | |
| z3 = get_height(x3, y3) + offset_z | |
| z4 = get_height(x4, y4) + offset_z | |
| end | |
| AppendVertex(vstr, x1, y1, z1, color) | |
| AppendVertex(vstr, x2, y2, z2, color) | |
| AppendVertex(vstr, x3, y3, z3, color) | |
| AppendVertex(vstr, x4, y4, z4, color) | |
| AppendVertex(vstr, x2, y2, z2, color) | |
| AppendVertex(vstr, x3, y3, z3, color) | |
| end | |
| function GetSizePstrTile() | |
| return 6 * const.pstrVertexSize | |
| end | |
| function AppendTorusVertices(vpstr, radius1, radius2, axis, color, normal) | |
| local HSeg = 32 | |
| local VSeg = 10 | |
| vpstr = vpstr or pstr("", 1024) | |
| local rad1 = Rotate(axis, 90 * 60) | |
| rad1 = Cross(axis, rad1) | |
| rad1 = Normalize(rad1) | |
| rad1 = MulDivRound(rad1, radius1, 4096) | |
| for i = 1, HSeg do | |
| local localCenter1 = RotateAxis(rad1, axis, MulDivRound(360 * 60, i, HSeg)) | |
| local localCenter2 = RotateAxis(rad1, axis, MulDivRound(360 * 60, i - 1, HSeg)) | |
| local lastUpperPt, lastPt | |
| if not normal or not IsPointInFrontOfPlane(point(0, 0, 0), normal, (localCenter1 + localCenter2) / 2) then | |
| for j = 0, VSeg do | |
| local rad2 = MulDivRound(localCenter1, radius2, radius1) | |
| local localAxis = Cross(rad2, axis) | |
| local pt = RotateAxis(rad2, localAxis, MulDivRound(360 * 60, j, VSeg)) | |
| pt = localCenter1 + pt | |
| rad2 = MulDivRound(localCenter2, radius2, radius1) | |
| localAxis = Cross(rad2, axis) | |
| local upperPt = RotateAxis(rad2, localAxis, MulDivRound(360 * 60, j, VSeg)) | |
| upperPt = localCenter2 + upperPt | |
| if j ~= 0 then | |
| AppendVertex(vpstr, pt, color) | |
| AppendVertex(vpstr, lastPt) | |
| AppendVertex(vpstr, upperPt) | |
| AppendVertex(vpstr, upperPt, color) | |
| AppendVertex(vpstr, lastUpperPt) | |
| AppendVertex(vpstr, lastPt) | |
| end | |
| lastPt = pt | |
| lastUpperPt = upperPt | |
| end | |
| end | |
| end | |
| return vpstr | |
| end | |
| function AppendConeVertices(vpstr, center, displacement, radius1, radius2, axis, angle, color, offset) | |
| local HSeg = 10 | |
| vpstr = vpstr or pstr("", 1024) | |
| center = center or point(0, 0, 0) | |
| displacement = displacement or point(0, 0, 30 * guim) | |
| axis = axis or axis_z | |
| angle = angle or 0 | |
| offset = offset or point(0, 0, 0) | |
| color = color or RGB(254, 127, 156) | |
| local lastPt, lastUpperPt | |
| for i = 0, HSeg do | |
| local rad = point(radius1, 0, 0) | |
| local pt = center + Rotate(rad, MulDivRound(360 * 60, i, HSeg)) | |
| local upperRad = point(radius2, 0, 0) | |
| local upperPt = center + displacement + Rotate(upperRad, MulDivRound(360 * 60, i, HSeg)) | |
| pt = RotateAxis(pt, axis, angle * 60) + offset | |
| upperPt = RotateAxis(upperPt, axis, angle * 60) + offset | |
| if i ~= 0 then | |
| AppendVertex(vpstr, pt, color) | |
| AppendVertex(vpstr, lastPt) | |
| AppendVertex(vpstr, upperPt) | |
| if radius2 ~= 0 then | |
| AppendVertex(vpstr, upperPt, color) | |
| AppendVertex(vpstr, lastUpperPt) | |
| AppendVertex(vpstr, lastPt) | |
| end | |
| end | |
| lastPt = pt | |
| lastUpperPt = upperPt | |
| end | |
| return vpstr | |
| end | |
| DefineClass.Polyline = | |
| { | |
| __parents = { "Mesh" }, | |
| } | |
| function Polyline:Init() | |
| self:SetMeshFlags(const.mfWorldSpace) | |
| self:SetShader(ProceduralMeshShaders.default_polyline) | |
| end | |
| DefineClass.Vector = { | |
| __parents = {"Polyline"}, | |
| } | |
| function Vector:Set (a, b, col) | |
| col = col or RGB(255, 255, 255) | |
| a = ValidateZ(a) | |
| b = ValidateZ(b) | |
| self:SetPos(a) | |
| local vpstr = pstr("", 1024) | |
| AppendVertex(vpstr, a, col) | |
| AppendVertex(vpstr, b) | |
| local ab = b - a | |
| local cb = (ab * 5) / 100 | |
| local f = cb:Len() / 4 | |
| local c = b - cb | |
| local n = 4 | |
| local ps = GetRadialPoints (n, c, cb, f) | |
| for i = 1 , n/2 do | |
| AppendVertex(vpstr, ps[i]) | |
| AppendVertex(vpstr, ps[i + n/2]) | |
| AppendVertex(vpstr, b) | |
| end | |
| self:SetMesh(vpstr) | |
| end | |
| function Vector:GetA() | |
| return self:GetPos() | |
| end | |
| function ShowVector(vector, origin, color, time) | |
| local v = PlaceObject("Vector") | |
| origin = origin:z() and origin or point(origin:x(), origin:y(), GetWalkableZ(origin)) | |
| vector = vector:z() and vector or point(vector:x(), vector:y(), 0) | |
| v:Set(origin, origin + vector, color) | |
| if time then | |
| CreateGameTimeThread(function() | |
| Sleep(time) | |
| DoneObject(v) | |
| end) | |
| end | |
| return v | |
| end | |
| DefineClass.Segment = { | |
| __parents = {"Polyline"}, | |
| } | |
| function Segment:Init() | |
| self:SetDepthTest(false) | |
| end | |
| function Segment:Set (a, b, col) | |
| col = col or RGB(255, 255, 255) | |
| a = ValidateZ(a) | |
| b = ValidateZ(b) | |
| self:SetPos(a) | |
| local vpstr = pstr("", 1024) | |
| AppendVertex(vpstr, a, col) | |
| AppendVertex(vpstr, b) | |
| self:SetMesh(vpstr) | |
| end | |
| -- After loading the code renderables from C, fix their string custom data in the Lua | |
| function OnMsg.PersistLoad(_dummy_) | |
| MapForEach(true, "Text", function(obj) | |
| SetCustomData(obj, const.CRTextCCDIndexText, obj.text or 0) | |
| end) | |
| MapForEach(true, "Mesh", function(obj) | |
| CodeRenderableLockCCD(obj) | |
| SetCustomData(obj, const.CRMeshCCDIndexGeometry, obj.vertices_pstr or 0) | |
| SetCustomData(obj, const.CRMeshCCDIndexUniforms, obj.uniforms_pstr or 0) | |
| CodeRenderableUnlockCCD(obj) | |
| end) | |
| end | |
| ---- | |
| function PlaceTerrainCircle(center, radius, color, step, offset, max_steps) | |
| step = step or guim | |
| offset = offset or guim | |
| local steps = Min(Max(12, (44 * radius) / (7 * step)), max_steps or 360) | |
| local last_pt | |
| local mapw, maph = terrain.GetMapSize() | |
| local vpstr = pstr("", 1024) | |
| for i = 0,steps do | |
| local x, y = RotateRadius(radius, MulDivRound(360*60, i, steps), center, true) | |
| x = Clamp(x, 0, mapw - height_tile) | |
| y = Clamp(y, 0, maph - height_tile) | |
| AppendVertex(vpstr, x, y, offset, color) | |
| end | |
| local line = PlaceObject("Polyline") | |
| line:SetMesh(vpstr) | |
| line:SetPos(center) | |
| line:AddMeshFlags(const.mfTerrainDistorted) | |
| return line | |
| end | |
| local function GetTerrainPointsPStr(vpstr, pt1, pt2, step, offset, color) | |
| step = step or guim | |
| offset = offset or guim | |
| local diff = pt2 - pt1 | |
| local steps = Max(2, 1 + diff:Len2D() / step) | |
| local mapw, maph = terrain.GetMapSize() | |
| vpstr = vpstr or pstr("", 1024) | |
| for i=1,steps do | |
| local pos = pt1 + MulDivRound(diff, i - 1, steps - 1) | |
| local x, y = pos:xy() | |
| x = Clamp(x, 0, mapw - height_tile) | |
| y = Clamp(y, 0, maph - height_tile) | |
| AppendVertex(vpstr, x, y, offset, color) | |
| end | |
| return vpstr | |
| end | |
| function PlaceTerrainLine(pt1, pt2, color, step, offset) | |
| local vpstr = GetTerrainPointsPStr(false, pt1, pt2, step, offset, color) | |
| local line = PlaceObject("Polyline") | |
| line:SetMesh(vpstr) | |
| line:SetPos((pt1 + pt2) / 2) | |
| line:AddMeshFlags(const.mfTerrainDistorted) | |
| return line | |
| end | |
| function PlaceTerrainBox(box, color, step, offset, mesh_obj, depth_test) | |
| local p = {box:ToPoints2D()} | |
| local m | |
| for i = 1, #p do | |
| m = GetTerrainPointsPStr(m, p[i], p[i + 1] or p[1], step, offset, color) | |
| end | |
| mesh_obj = mesh_obj or PlaceObject("Polyline") | |
| if depth_test ~= nil then | |
| mesh_obj:SetDepthTest(depth_test) | |
| end | |
| mesh_obj:SetMesh(m) | |
| mesh_obj:SetPos(box:Center()) | |
| mesh_obj:AddMeshFlags(const.mfTerrainDistorted) | |
| return mesh_obj | |
| end | |
| function PlaceTerrainPoly(p, color, step, offset, mesh_obj) | |
| local m | |
| local center = p[1] + ((p[1] - p[3]) / 2) | |
| for i = 1, #p do | |
| m = GetTerrainPointsPStr(m, p[i], p[i + 1] or p[1], step, offset, color) | |
| end | |
| mesh_obj = mesh_obj or PlaceObject("Polyline") | |
| mesh_obj:SetMesh(m) | |
| mesh_obj:SetPos(center) | |
| return mesh_obj | |
| end | |
| function PlacePolyLine(pts, clrs, depth_test) | |
| local line = PlaceObject("Polyline") | |
| line:SetEnumFlags(const.efVisible) | |
| if depth_test ~= nil then | |
| line:SetDepthTest(depth_test) | |
| end | |
| local vpstr = pstr("", 1024) | |
| local clr | |
| local pt0 | |
| for i, pt in ipairs(pts) do | |
| if IsValidPos(pt) then | |
| pt0 = pt0 or pt | |
| clr = type(clrs) == "table" and clrs[i] or clrs or clr | |
| AppendVertex(vpstr, pt, clr) | |
| end | |
| end | |
| line:SetMesh(vpstr) | |
| if pt0 then | |
| line:SetPos(pt0) | |
| end | |
| return line | |
| end | |
| function AppendSplineVertices(spline, color, step, min_steps, max_steps, vpstr) | |
| step = step or guim | |
| min_steps = min_steps or 7 | |
| max_steps = max_steps or 1024 | |
| local len = BS3_GetSplineLength3D(spline) | |
| local steps = Clamp(len / step, min_steps, max_steps) | |
| vpstr = vpstr or pstr("", (steps + 2) * const.pstrVertexSize) | |
| local x, y, z | |
| local x0, y0, z0 = BS3_GetSplinePos(spline, 0) | |
| AppendVertex(vpstr, x0, y0, z0, color) | |
| for i = 1,steps-1 do | |
| local x, y, z = BS3_GetSplinePos(spline, i, steps) | |
| AppendVertex(vpstr, x, y, z, color) | |
| end | |
| local x1, y1, z1 = BS3_GetSplinePos(spline, steps, steps) | |
| AppendVertex(vpstr, x1, y1, z1, color) | |
| return vpstr, point((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2) | |
| end | |
| function PlaceSpline(spline, color, depth_test, step, min_steps, max_steps) | |
| local line = PlaceObject("Polyline") | |
| line:SetEnumFlags(const.efVisible) | |
| if depth_test ~= nil then | |
| line:SetDepthTest(depth_test) | |
| end | |
| local vpstr, pos = AppendSplineVertices(spline, color, step, min_steps, max_steps) | |
| line:SetMesh(vpstr) | |
| line:SetPos(pos) | |
| return line | |
| end | |
| function PlaceSplines(splines, color, depth_test, start_idx, step, min_steps, max_steps) | |
| local line = PlaceObject("Polyline") | |
| line:SetEnumFlags(const.efVisible) | |
| if depth_test ~= nil then | |
| line:SetDepthTest(depth_test) | |
| end | |
| local count = #(splines or "") | |
| local pos = point30 | |
| local vpstr = pstr("", count * 128 * const.pstrVertexSize) | |
| for i = (start_idx or 1), count do | |
| local _, posi = AppendSplineVertices(splines[i], color, step, min_steps, max_steps, vpstr) | |
| pos = pos + posi | |
| end | |
| if count > 0 then | |
| pos = pos / count | |
| end | |
| line:SetMesh(vpstr) | |
| line:SetPos(pos) | |
| return line | |
| end | |
| function PlaceBox(box, color, mesh_obj, depth_test) | |
| local p1, p2, p3, p4 = box:ToPoints2D() | |
| local minz, maxz = box:minz(), box:maxz() | |
| local vpstr = pstr("", 1024) | |
| if minz and maxz then | |
| if minz >= maxz - 1 then | |
| for _, p in ipairs{p1, p2, p3, p4, p1} do | |
| local x, y = p:xy() | |
| AppendVertex(vpstr, x, y, minz, color) | |
| end | |
| else | |
| for _, z in ipairs{minz, maxz} do | |
| for _, p in ipairs{p1, p2, p3, p4, p1} do | |
| local x, y = p:xy() | |
| AppendVertex(vpstr, x, y, z, color) | |
| end | |
| end | |
| AppendVertex(vpstr, p2:SetZ(maxz), color) | |
| AppendVertex(vpstr, p2:SetZ(minz), color) | |
| AppendVertex(vpstr, p3:SetZ(minz), color) | |
| AppendVertex(vpstr, p3:SetZ(maxz), color) | |
| AppendVertex(vpstr, p4:SetZ(maxz), color) | |
| AppendVertex(vpstr, p4:SetZ(minz), color) | |
| end | |
| else | |
| local z = terrain.GetHeight(p1) | |
| for _, p in ipairs{p2, p3, p4} do | |
| z = Max(z, terrain.GetHeight(p)) | |
| end | |
| for _, p in ipairs{p1, p2, p3, p4, p1} do | |
| local x, y = p:xy() | |
| AppendVertex(vpstr, x, y, z, color) | |
| end | |
| end | |
| mesh_obj = mesh_obj or PlaceObject("Polyline") | |
| if depth_test ~= nil then | |
| mesh_obj:SetDepthTest(depth_test) | |
| end | |
| mesh_obj:SetMesh(vpstr) | |
| mesh_obj:SetPos(box:Center()) | |
| return mesh_obj | |
| end | |
| function PlaceVector(pos, vec, color, depth_test) | |
| vec = vec or 10*guim | |
| vec = type(vec) == "number" and point(0, 0, vec) or vec | |
| return PlacePolyLine({pos, pos + vec}, color, depth_test) | |
| end | |
| function CreateTerrainCursorCircle(radius, color) | |
| color = color or RGB(23, 34, 122) | |
| radius = radius or 30 * guim | |
| local line = CreateCircleMesh(radius, color) | |
| line:SetPos(GetTerrainCursor()) | |
| line:SetMeshFlags(const.mfOffsetByTerrainCursor + const.mfTerrainDistorted + const.mfWorldSpace) | |
| return line | |
| end | |
| function CreateTerrainCursorSphere(radius, color) | |
| color = color or RGB(23, 34, 122) | |
| radius = radius or 30 * guim | |
| local line = PlaceObject("Mesh") | |
| line:SetMesh(CreateSphereVertices(radius, color)) | |
| line:SetShader(ProceduralMeshShaders.mesh_linelist) | |
| line:SetPos(GetTerrainCursor()) | |
| line:SetMeshFlags(const.mfOffsetByTerrainCursor + const.mfTerrainDistorted + const.mfWorldSpace) | |
| return line | |
| end | |
| function CreateOrientationMesh(pos) | |
| local o_mesh = Mesh:new() | |
| pos = pos or point(0, 0, 0) | |
| o_mesh:SetShader(ProceduralMeshShaders.mesh_linelist) | |
| local r = guim/4 | |
| local vpstr = pstr("", 1024) | |
| AppendVertex(vpstr, point(0, 0, 0), RGB(255, 0, 0)) | |
| AppendVertex(vpstr, point(r, 0, 0)) | |
| AppendVertex(vpstr, point(0, 0, 0), RGB(0, 255, 0)) | |
| AppendVertex(vpstr, point(0, r, 0)) | |
| AppendVertex(vpstr, point(0, 0, 0), RGB(0, 0, 255)) | |
| AppendVertex(vpstr, point(0, 0, r)) | |
| o_mesh:SetMesh(vpstr) | |
| o_mesh:SetPos(pos) | |
| return o_mesh | |
| end | |
| function CreateSphereMesh(radius, color, precision) | |
| local sphere_mesh = Mesh:new() | |
| sphere_mesh:SetMesh(CreateSphereVertices(radius, color)) | |
| sphere_mesh:SetShader(ProceduralMeshShaders.mesh_linelist) | |
| return sphere_mesh | |
| end | |
| function PlaceSphere(center, radius, color, depth_test) | |
| local sphere = CreateSphereMesh(radius, color) | |
| if depth_test ~= nil then | |
| sphere:SetDepthTest(depth_test) | |
| end | |
| sphere:SetPos(center) | |
| return sphere | |
| end | |
| function ShowMesh(time, func, ...) | |
| local ok, meshes = procall(func, ...) | |
| if not ok or not meshes then | |
| return | |
| end | |
| return CreateRealTimeThread(function(meshes, time) | |
| Msg("ShowMesh") | |
| WaitMsg("ShowMesh", time) | |
| if IsValid(meshes) then | |
| DoneObject(meshes) | |
| else | |
| DoneObjects(meshes) | |
| end | |
| end, meshes, time) | |
| end | |
| function CreateCircleMesh(radius, color, center) | |
| local circle_mesh = Mesh:new() | |
| circle_mesh:SetMesh(AppendCircleVertices(nil, center, radius, color, true)) | |
| circle_mesh:SetShader(ProceduralMeshShaders.default_polyline) | |
| return circle_mesh | |
| end | |
| function PlaceCircle(center, radius, color, depth_test) | |
| local circle = CreateCircleMesh(radius, color) | |
| if depth_test ~= nil then | |
| circle:SetDepthTest(depth_test) | |
| end | |
| circle:SetPos(center) | |
| return circle | |
| end | |
| function CreateConeMesh(center, displacement, radius1, radius2, axis, angle, color) | |
| local circle_mesh = Mesh:new() | |
| circle_mesh:SetMesh(AppendConeVertices(nil, center, displacement, radius1, radius2, axis, angle, color)) | |
| circle_mesh:SetShader(ProceduralMeshShaders.mesh_linelist) | |
| return circle_mesh | |
| end | |
| function CreateCylinderMesh(center, displacement, radius, axis, angle, color) | |
| local circle_mesh = Mesh:new() | |
| circle_mesh:SetMesh(AppendConeVertices(nil, center, displacement, radius, radius, axis, angle, color)) | |
| circle_mesh:SetShader(ProceduralMeshShaders.default_mesh) | |
| return circle_mesh | |
| end | |
| function CreateMoveGizmo() | |
| local g_MoveGizmo = MoveGizmo:new() | |
| CreateRealTimeThread(function() | |
| while true do | |
| g_MoveGizmo:OnMousePos(GetTerrainCursor()) | |
| Sleep(100) | |
| end | |
| end) | |
| end | |
| function CreateTerrainCursorTorus(radius1, radius2, axis, angle, color) | |
| color = color or RGB(255, 0, 0) | |
| radius1 = radius1 or 2.3 * guim | |
| radius2 = radius2 or 0.15 * guim | |
| axis = axis or axis_y | |
| angle = angle or 90 | |
| local line = PlaceObject("Mesh") | |
| local vpstr = pstr("", 1024) | |
| local normal = selo():GetPos() - camera.GetEye() | |
| local b = selo():GetPos() | |
| local bigTorusAxis, bigTorusAngle = GetAxisAngle(normal, axis_z) | |
| bigTorusAxis = Normalize(bigTorusAxis) | |
| bigTorusAngle = 180 - bigTorusAngle / 60 | |
| vpstr = AppendTorusVertices(vpstr, point(0, 0, 0), 2.3 * guim, 0.15 * guim, bigTorusAxis, bigTorusAngle, RGB(128, 128, 128)) | |
| vpstr = AppendTorusVertices(vpstr, point(0, 0, 0), 2.3 * guim, 0.15 * guim, axis_y, 90, RGB(255, 0, 0), normal, b) | |
| vpstr = AppendTorusVertices(vpstr, point(0, 0, 0), 2.3 * guim, 0.15 * guim, axis_x, 90, RGB(0, 255, 0), normal, b) | |
| vpstr = AppendTorusVertices(vpstr, point(0, 0, 0), 2.3 * guim, 0.15 * guim, axis_z, 0, RGB(0, 0, 255), normal, b) | |
| vpstr = AppendTorusVertices(vpstr, point(0, 0, 0), 3.5 * guim, 0.15 * guim, bigTorusAxis, bigTorusAngle, RGB(0, 192, 192)) | |
| line:SetMesh(vpstr) | |
| line:SetPos(selo():GetPos()) | |
| return line | |
| end | |
| function CreateObjSurfaceMesh(obj, surface_flag, color1, color2) | |
| if not IsValidPos(obj) then | |
| return | |
| end | |
| local v_pstr = pstr("", 1024) | |
| ForEachSurface(obj, surface_flag, function(pt1, pt2, pt3, v_pstr, color1, color2) | |
| local color | |
| if color1 and color2 then | |
| local rand = xxhash(pt1, pt2, pt3) % 1024 | |
| color = InterpolateRGB(color1, color2, rand, 1024) | |
| end | |
| v_pstr:AppendVertex(pt1, color) | |
| v_pstr:AppendVertex(pt2, color) | |
| v_pstr:AppendVertex(pt3, color) | |
| end, v_pstr, color1, color2) | |
| local mesh = PlaceObject("Mesh") | |
| mesh:SetMesh(v_pstr) | |
| mesh:SetPos(obj:GetPos()) | |
| mesh:SetMeshFlags(const.mfWorldSpace) | |
| mesh:SetDepthTest(true) | |
| if color1 and not color2 then | |
| mesh:SetColorModifier(color1) | |
| end | |
| return mesh | |
| end | |
| function FlatImageMesh(texture, width, height, glow_size, glow_period, glow_color) | |
| local text = PlaceObject("Mesh") | |
| local vpstr = pstr("", 1024) | |
| local color = RGB(255,255,255) | |
| local half_size_x = width or 1000 | |
| local half_size_y = height or 1000 | |
| glow_size = glow_size or 0 | |
| glow_period = glow_period or 0 | |
| glow_color = glow_color or RGB(255,255,255) | |
| AppendVertex(vpstr, point(-half_size_x, -half_size_y, 0), color, 0, 0) | |
| AppendVertex(vpstr, point(half_size_x, -half_size_y, 0), color, 1, 0) | |
| AppendVertex(vpstr, point(-half_size_x, half_size_y, 0), color, 0, 1) | |
| AppendVertex(vpstr, point(half_size_x, -half_size_y, 0), color, 1, 0) | |
| AppendVertex(vpstr, point(half_size_x, half_size_y, 0), color, 1, 1) | |
| AppendVertex(vpstr, point(-half_size_x, half_size_y, 0), color, 0, 1) | |
| text:SetMesh(vpstr) | |
| if texture then | |
| local use_sdf = false | |
| local padding = 0 | |
| local low_edge = 0 | |
| local high_edge = 0 | |
| if glow_size > 0 then | |
| use_sdf = true | |
| padding = 16 | |
| low_edge = 490 | |
| high_edge = 510 | |
| end | |
| text:SetTexture(0, ProceduralMeshBindResource("texture", texture, false, 0)) | |
| if glow_size > 0 then | |
| text:SetTexture(1, ProceduralMeshBindResource("texture", texture, true, 0, const.fmt_unorm16_c1)) | |
| text:SetShader(ProceduralMeshShaders.default_ui_sdf) | |
| else | |
| text:SetShader(ProceduralMeshShaders.default_ui) | |
| end | |
| local r, g, b = GetRGB(glow_color) | |
| text:SetUniforms(low_edge, high_edge, glow_size, glow_period, r, g, b) | |
| end | |
| return text | |
| end | |
| DefineClass.FlatTextMesh = { | |
| __parents = { "Mesh" }, | |
| properties = { | |
| {id = "font_id", editor = "number", read_only = true, default = 0, category = "Rasterize" }, | |
| {id = "text_style_id", editor = "preset_id", preset_class = "TextStyle", editor_preview = true, default = false, category = "Rasterize" }, | |
| {id = "text_scale", editor = "number", default = 1000, category = "Rasterize" }, | |
| {id = "text", editor = "text", default = "", category = "Rasterize" }, | |
| {id = "padding", editor = "number", default = 0, category = "Rasterize", help = "How much pixels to leave around the text(for effects)"}, | |
| {id = "width", editor = "number", default = 0, category = "Present", help = "In meters. Leave 0 to calculate automatically"}, | |
| {id = "height", editor = "number", default = 0, category = "Present", help = "In meters. Leave 0 to calculate automatically"}, | |
| {id = "text_color", editor = "color", default = RGB(255,255,255), category = "Present"}, | |
| {id = "effect_type", editor = "choice", items = {"none", "glow"}, default = "glow", category = "Present" }, | |
| {id = "effect_color", editor = "color", default = RGB(255,255,255), category = "Present"}, | |
| {id = "effect_size", editor = "number", default = 0, help = "In pixels from the rasterized image.", category = "Present" }, | |
| {id = "effect_period", editor = "number", default = 0, help = "1 pulse per each period seconds. ", category = "Present"}, | |
| } | |
| } | |
| function FlatTextMesh:Init() | |
| self:Recreate() | |
| end | |
| function FlatTextMesh:FetchEffectsFromTextStyle() | |
| local text_style = TextStyles[self.text_style_id] | |
| if not text_style then return end | |
| self.text_color = text_style.TextColor | |
| self.effect_type = text_style.ShadowType == "glow" and "glow" or "none" | |
| self.effect_color = text_style.ShadowColor | |
| self.effect_size = text_style.ShadowSize | |
| self.textstyle_id = self.text_style_id | |
| end | |
| function FlatTextMesh:SetColorFromTextStyle(text_style_id) | |
| self.text_style_id = text_style_id | |
| self.textstyle_id = text_style_id | |
| self:FetchEffectsFromTextStyle() | |
| self:Recreate() | |
| end | |
| function FlatTextMesh:CalculateSizes(max_width, max_height, default_scale) | |
| local width_pixels, height_pixels = UIL.MeasureText(self.text, self.font_id) | |
| local scale = 0 | |
| if max_width == 0 and max_height == 0 then | |
| scale = default_scale or 10000 | |
| elseif max_width == 0 then | |
| max_width = 1000000 | |
| elseif max_height == 0 then | |
| max_height = 1000000 | |
| end | |
| if scale == 0 then | |
| local scale1 = MulDivRound(max_width, 1000, width_pixels) | |
| local scale2 = MulDivRound(max_height, 1000, height_pixels) | |
| scale = Min(scale1, scale2) | |
| end | |
| self.width = MulDivRound(width_pixels, scale, 1000) | |
| self.height = MulDivRound(height_pixels, scale, 1000) | |
| end | |
| function FlatTextMesh:Recreate() | |
| local text_style = TextStyles[self.text_style_id] | |
| if not text_style then return end | |
| local font_id = text_style:GetFontIdHeightBaseline(self.text_scale) | |
| self.font_id = font_id | |
| local effect_type = self.effect_type | |
| local use_sdf = false | |
| local padding = 0 | |
| if effect_type == "glow" then | |
| use_sdf = true | |
| padding = 16 | |
| end | |
| local width_pixels, height_pixels = UIL.MeasureText(self.text, font_id) | |
| local width = self.width | |
| local height = self.height | |
| if width == 0 and height == 0 then | |
| local default_scale = 10000 | |
| width = MulDivRound(width_pixels, default_scale, 1000) | |
| height = MulDivRound(height_pixels, default_scale, 1000) | |
| end | |
| if width == 0 then | |
| width = MulDivRound(width_pixels, height, height_pixels) | |
| end | |
| if height == 0 then | |
| height = MulDivRound(height_pixels, width, width_pixels) | |
| end | |
| --add for padding | |
| width = width + MulDivRound(width, padding * 2 * 1000, width_pixels * 1000) | |
| height = height + MulDivRound(height, padding * 2 * 1000, height_pixels * 1000) | |
| local vpstr = pstr("", 1024) | |
| local half_size_x = (width or 1000) / 2 | |
| local half_size_y = (height or 1000) / 2 | |
| local color = self.text_color | |
| AppendVertex(vpstr, point(-half_size_x, -half_size_y, 0), color, 0, 0) | |
| AppendVertex(vpstr, point(half_size_x, -half_size_y, 0), color, 1, 0) | |
| AppendVertex(vpstr, point(-half_size_x, half_size_y, 0), color, 0, 1) | |
| AppendVertex(vpstr, point(half_size_x, -half_size_y, 0), color, 1, 0) | |
| AppendVertex(vpstr, point(half_size_x, half_size_y, 0), color, 1, 1) | |
| AppendVertex(vpstr, point(-half_size_x, half_size_y, 0), color, 0, 1) | |
| self:SetMesh(vpstr) | |
| self:SetTexture(0, ProceduralMeshBindResource("text", self.text, font_id, use_sdf, padding)) | |
| local r, g, b = GetRGB(self.effect_color) | |
| self:SetUniforms(use_sdf and 1000 or 0, 0, self.effect_size * 1000, self.effect_period, r, g, b) | |
| self:SetShader(ProceduralMeshShaders.default_ui) | |
| end | |
| function TestUIRenderables() | |
| local pt = GetTerrainCursor() + point(0, 0, 100) | |
| for i = 0, 4 do | |
| local height = 700 | |
| local space = 5000 | |
| local text = FlatTextMesh:new({ | |
| text_style_id = "ProcMeshDefault", | |
| text_scale = 500 + 400 * i, | |
| text = "Hello world", | |
| height = height, | |
| }) | |
| text:SetPos(pt + point(i * space, 0, 0)) | |
| text = FlatTextMesh:new({ | |
| text_style_id = "ProcMeshDefaultFX", | |
| text_scale = 500 + 400 * i, | |
| text = "Hello world", | |
| height = height, | |
| effect_type = "glow", | |
| effect_size = 8, | |
| effect_period = 200, | |
| effect_color = RGB(255, 0, 0), | |
| }) | |
| text:SetPos(pt + point(i * space, 3000, 0)) | |
| text:SetGameFlags(const.gofRealTimeAnim) | |
| local mesh = FlatImageMesh("UI/MercsPortraits/Buns", 1000, 1000, 200 * i, 1000, RGB(255, 255, 255)) | |
| mesh:SetPos(pt + point(i * space, 6000, 0)) | |
| mesh = FlatImageMesh("UI/MercsPortraits/Buns", 1000, 1000) | |
| mesh:SetPos(pt + point(i * space, 9000, 0)) | |
| end | |
| end | |
| function DebugShowMeshes() | |
| local meshes = MapGet("map", "Mesh") | |
| OpenGedGameObjectEditor(meshes, true) | |
| end | |
| -- Represents combination of shader & the data the shader accepts. Provides "properties" interface for the underlying raw bits sent to the shader. | |
| -- Inherits from PersistedRenderVars(is a preset) and provides minimalistic default logic for updating meshes on the fly. | |
| local function depth_test_values(obj) | |
| local tbl = {{value = "default", text = "default"}} | |
| local shader_id = obj.shader_id | |
| local shader_data = ProceduralMeshShaders[shader_id] | |
| if shader_data then | |
| if shader_data.depth_test == "runtime" or shader_data.depth_test == "never" then | |
| table.insert(tbl, {value = false, text = "never"}) | |
| end | |
| if shader_data.depth_test == "runtime" or shader_data.depth_test == "always" then | |
| table.insert(tbl, {value = true, text = "always"}) | |
| end | |
| end | |
| return tbl | |
| end | |
| DefineClass.CRMaterial = { | |
| __parents = {"PersistedRenderVars", "MeshParamSet"}, | |
| properties = { | |
| { id = "ShaderName", editor = "choice", default = "default_mesh", items = function() return table.keys2(ProceduralMeshShaders, "sorted") end, read_only = true,}, | |
| { id = "depth_test", editor = "choice", items = depth_test_values }, | |
| }, | |
| group = "CRMaterial", | |
| depth_test = "default", | |
| cloned_from = false, | |
| shader_id = "default_mesh", | |
| shader = false, | |
| pstr_buffer = false, | |
| dirty = false, | |
| } | |
| function CRMaterial:GetError() | |
| if not self.shader_id then | |
| return "CRMaterial without a shader_id." | |
| end | |
| if not ProceduralMeshShaders[self.shader_id] then | |
| return "ShaderID " .. self.shader_id .. " is not valid." | |
| end | |
| end | |
| function CRMaterial:GetShader() | |
| if self.shader then | |
| return self.shader | |
| end | |
| if self.shader_id then | |
| return ProceduralMeshShaders[self.shader_id] | |
| end | |
| return false | |
| end | |
| ------- Prevent triggering Preset logic on cloned materials. Probably should be implemented smarter, maybe by __index table reference, so even clones are live updated by editor | |
| function CRMaterial:SetId(value) | |
| if self.cloned_from then | |
| self.id = value | |
| else | |
| PersistedRenderVars.SetId(self, value) | |
| end | |
| end | |
| function CRMaterial:SetGroup(value) | |
| if self.cloned_from then | |
| self.group = value | |
| else | |
| PersistedRenderVars.SetGroup(self, value) | |
| end | |
| end | |
| function CRMaterial:Register(...) | |
| if self.cloned_from then | |
| return | |
| end | |
| return PersistedRenderVars.Register(self, ...) | |
| end | |
| function CRMaterial:Clone() | |
| local obj = _G[self.class]:new({cloned_from = self.id}) | |
| obj:CopyProperties(self) | |
| return obj | |
| end | |
| function CRMaterial:GetDataPstr() | |
| if self.dirty or not self.pstr_buffer then | |
| self:Recreate() | |
| end | |
| return self.pstr_buffer | |
| end | |
| function CRMaterial:GetShaderName() | |
| return self.shader and self.shader.id or self.shader_id | |
| end | |
| function CRMaterial:Recreate() | |
| self.dirty = false | |
| self.pstr_buffer = self:WriteBuffer() | |
| end | |
| function CRMaterial:OnPreSave() | |
| self.pstr_buffer = nil | |
| end | |
| function CRMaterial:Apply() | |
| self:Recreate() | |
| if CurrentMap ~= "" then | |
| MapGet("map", "Mesh", function(o) | |
| local omtrl = o.CRMaterial | |
| if omtrl == self then | |
| o:SetCRMaterial(self) | |
| elseif (omtrl and omtrl.id == self.id) then | |
| for _, prop in ipairs(omtrl:GetProperties()) do | |
| local value = rawget(omtrl, prop.id) | |
| if value == nil or (not prop.read_only and not prop.no_edit) then | |
| omtrl:SetProperty(prop.id, self:GetProperty(prop.id)) | |
| end | |
| end | |
| omtrl:Recreate() | |
| o:SetCRMaterial(omtrl) | |
| end | |
| end) | |
| end | |
| end | |
| DefineClass.CRM_DebugMeshMaterial = { | |
| __parents = {"CRMaterial"}, | |
| shader_id = "debug_mesh", | |
| properties = { | |
| }, | |
| } | |