File size: 14,606 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
---
--- Sets up the rendering environment for capturing billboards.
---
--- This function performs the following steps:
--- - Changes the current map to an empty map
--- - Waits for 5 frames
--- - Activates the `cameraMax` camera and sets its viewport, field of view, and locks it
--- - Changes the video mode to the billboard screenshot capture size
--- - Configures various rendering settings for billboard capture, such as:
--- - Setting the object LOD cap to 100
--- - Disabling terrain rendering
--- - Disabling auto-exposure
--- - Disabling subsurface scattering
--- - Setting the resolution to 100% with SMAA upscaling
--- - Disabling shadows
--- - Setting the near and far planes to 100 and 100,000 respectively
--- - Enabling orthographic projection with a Y scale of 1000
--- - Deletes the current map and waits for 3 frames
---
function SetupBillboardRendering()
ChangeMap("__Empty")
WaitNextFrame(5)
cameraMax.Activate(1)
camera.SetViewport(box(0, 0, 1000000, 1000000))
camera.SetFovX(83 * 60)
camera.Lock(1)
ChangeVideoMode(hr.BillboardScreenshotCaptureSize, hr.BillboardScreenshotCaptureSize, 0, false, false)
table.change(hr, "BillboardCapture",
{ObjectLODCapMax=100, ObjectLODCapMin=100, RenderBillboards=0, RenderTerrain=0, AutoExposureMode=0,
EnableSubsurfaceScattering=0, ResolutionPercent=100, ResolutionUpscale="smaa", MaxFps=0, Shadowmap=0,
NearZ=100, FarZ=100000, Ortho=1, OrthoYScale=1000})
MapDelete("map", nil)
WaitNextFrame(3)
end
---
--- Defines a class for billboard objects.
---
--- This class inherits from `EntityClass` and has the following properties:
---
--- - `flags`: A table of flags, including `efHasBillboard` which indicates that this object has a billboard.
--- - `ignore_axis_error`: A boolean flag that determines whether to ignore errors related to the object's axis.
---
DefineClass.BillboardObject = {__parents={"EntityClass"}, flags={efHasBillboard=true}, ignore_axis_error=false}
---
--- Returns an error message if the billboard object has an invalid axis.
---
--- If the object has a billboard (`efHasBillboard` flag is set) and `ignore_axis_error` is false, this function checks the object's visual axis. If the X or Y axis is non-zero, or the Z axis is non-positive, it returns an error message indicating that the billboard object should have the default axis.
---
--- @return string|nil An error message if the billboard object has an invalid axis, or nil if the axis is valid or `ignore_axis_error` is true.
function BillboardObject:GetError()
end
function BillboardObject:GetError()
if self:GetEnumFlags(const.efHasBillboard) ~= 0 and not self.ignore_axis_error then
local x, y, z = self:GetVisualAxisXYZ()
if x ~= 0 or y ~= 0 or z <= 0 then
return "Billboard objects should have default axis"
end
end
end
---
--- Returns a sorted list of all `BillboardObject` classes and their descendants.
---
--- This function traverses the class hierarchy starting from the `BillboardObject` class, and collects all valid entities that are instances of `BillboardObject` or its descendants. The resulting list is sorted by the class name.
---
--- @return table A table of `BillboardObject` class definitions, sorted by class name.
function BillboardsTree()
end
function BillboardsTree()
local billboard_classes = {}
ClassDescendantsList("BillboardObject", function(name, classdef, billboard_classes)
if IsValidEntity(classdef:GetEntity()) then
table.insert(billboard_classes, classdef)
end
end, billboard_classes)
table.sortby_field(billboard_classes, "class")
return billboard_classes
end
---
--- Bakes a billboard for the selected object in the GED.
---
--- This function resolves the selected object in the GED and then calls `BakeEntityBillboard` to generate a billboard for that object.
---
--- @param ged The GED object.
function GedBakeBillboard(ged)
end
function GedBakeBillboard(ged)
local obj = ged:ResolveObj("SelectedObject")
if not obj then
return
end
BakeEntityBillboard(obj:GetEntity())
end
---
--- Bakes a billboard for the specified entity.
---
--- This function generates a billboard for the given entity by executing an external command. The command is constructed using the entity's name and executed asynchronously. If an error occurs during the billboard generation, it is printed to the console.
---
--- @param entity string The name of the entity to generate a billboard for.
function BakeEntityBillboard(entity)
end
function BakeEntityBillboard(entity)
if not entity then
return
end
local cmd = string.format("cmd /c Build GenerateBillboards --billboard_entity=%s", entity)
local dir = ConvertToOSPath("svnProject/")
local err = AsyncExec(cmd, dir, true, true)
if err then
print("Failed to create billboard for %s: %s", entity, err)
end
end
---
--- Spawns a billboard object at the cursor position, with a random offset.
---
--- This function resolves the selected object in the GED and then places multiple instances of that object in a grid pattern around the cursor position, with a random offset applied to each instance.
---
--- @param ged The GED object.
function GedSpawnBillboard(ged)
end
function GedSpawnBillboard(ged)
local obj = ged:ResolveObj("SelectedObject")
if not obj then
return
end
local pos = GetTerrainCursorXY(UIL.GetScreenSize() / 2)
local step = 20 * guim
SuspendPassEdits("spawn billboards")
for y = -50, 50 do
for x = -50, 50 do
local o = PlaceObject(obj.class)
local curr_pos = pos + point(x * step + (AsyncRand(21) - 11) * guim, y * step + (AsyncRand(21) - 11) * guim)
local real_pos = point(curr_pos:x(), curr_pos:y(), terrain.GetHeight(curr_pos:x(), curr_pos:y()))
o:SetPos(curr_pos)
end
end
ResumePassEdits("spawn billboards")
end
---
--- Spawns a grid of billboard objects around the cursor position, with a random offset.
---
--- This function resolves the selected object in the GED and then places multiple instances of that object in a grid pattern around the cursor position, with a random offset applied to each instance. This can be used to debug billboard rendering.
---
--- @param ged The GED object.
function GedDebugBillboards(ged)
end
function GedDebugBillboards(ged)
hr.BillboardDebug = 1
hr.BillboardDistanceModifier = 10000
hr.ObjectLODCapMax = 100
hr.ObjectLODCapMin = 100
local pos = GetTerrainCursorXY(UIL.GetScreenSize() / 2)
local step = 12 * guim
local billboard_entities = {}
for k, v in ipairs(GetClassAndDescendantsEntities("BillboardObject")) do
if IsValidEntity(v) then
billboard_entities[#billboard_entities + 1] = v
end
end
local i = 1
for y = -10, 10 do
for x = -5, 5 do
local entity = billboard_entities[i]
if i == #billboard_entities then
i = 0
end
i = i + 1
local o = PlaceObject(entity)
local curr_pos = pos + point(x * step * 2, y * step)
local real_pos = point(curr_pos:x(), curr_pos:y(), terrain.GetHeight(curr_pos:x(), curr_pos:y()))
o:SetPos(curr_pos)
end
end
end
---
--- Generates billboards for all billboard objects in the game.
---
--- This function executes an external command to generate billboards for all billboard objects in the game. It uses the `GenerateBillboards` function to create the billboards.
---
--- @param ged The GED object.
function GedBakeAllBillboards(ged)
end
function GedBakeAllBillboards(ged)
local cmd = string.format("cmd /c Build GenerateBillboards")
local dir = ConvertToOSPath("svnProject/")
local err = AsyncExec(cmd, dir, true, true)
if err then
print("Failed to create billboards!")
end
end
---
--- Generates billboards for all billboard objects in the game.
---
--- This function executes an external command to generate billboards for all billboard objects in the game. It uses the `GenerateBillboards` function to create the billboards.
---
--- @param ged The GED object.
function GedBakeAllBillboards(ged)
end
function GenerateBillboards(specific_entity)
CreateRealTimeThread(function()
SetupBillboardRendering()
local billboard_entities = {}
if specific_entity then
billboard_entities[specific_entity] = true
else
ClassDescendantsList("BillboardObject", function(name, classdef, billboard_entities)
local ent = classdef:GetEntity()
if IsValidEntity(ent) then
billboard_entities[ent] = true
end
end, billboard_entities)
end
local o = PlaceObject("Shapeshifter")
o:SetPos(point(0, 0))
local OctahedronSize = hr.BillboardScreenshotGridWidth - 1
local screenshot_downsample = hr.BillboardScreenshotCaptureSize / hr.BillboardScreenshotSize
local unneeded_lods
local power = 1
for i = 0, 10 do
if power == screenshot_downsample then
unneeded_lods = i
break
end
power = power * 2
end
local dir = ConvertToOSPath("svnAssets/BuildCache/win32/Billboards/")
AsyncCreatePath("svnAssets/BuildCache/win32/Billboards/")
for ent, _ in pairs(billboard_entities) do
hr.MipmapLodBias = unneeded_lods * 1000
o:ChangeEntity(ent)
local bbox = o:GetEntityBBox()
local bbox_center = bbox:Center()
local camera_target = o:GetVisualPos() + bbox_center
WaitNextFrame(5)
local dlc_name = EntitySpecPresets[ent].save_in
if dlc_name ~= "" then
dlc_name = dlc_name .. "\\"
end
local curr_dir = dir .. dlc_name
local err = AsyncCreatePath(curr_dir)
assert(not err)
local _, radius = o:GetBSphere()
local draw_radius = (radius * 173) / 100
local max_range = radius * OctahedronSize
local half_max = (max_range * 173) / 100 + (hr.BillboardScreenshotGridWidth % 2 == 0 and 1 or 0)
local bc_atlas = curr_dir .. ent .. "_bc.tga"
local nm_atlas = curr_dir .. ent .. "_nm.tga"
local rt_atlas = curr_dir .. ent .. "_rt.tga"
local siao_atlas = curr_dir .. ent .. "_siao.tga"
local depth_atlas = curr_dir .. ent .. "_dep.tga"
local borders = curr_dir .. ent .. "_bor.dds"
local id = 0
hr.OrthoX = radius * 2
BeginCaptureBillboardEntity(bc_atlas, nm_atlas, rt_atlas, siao_atlas, depth_atlas, borders)
for y = 0, OctahedronSize do
for x = 0, OctahedronSize do
local curr_x, curr_y, curr_z = BillboardMap(x, y, OctahedronSize, half_max)
local pos = SetLen(point(curr_x, curr_y, curr_z), draw_radius)
SetCamera(camera_target + pos, camera_target)
WaitNextFrame(1)
CaptureBillboardFrame(draw_radius, id)
WaitNextFrame(1)
id = id + 1
end
end
WaitNextFrame(1)
end
WaitNextFrame(100)
quit()
end)
end
---
--- Checks if the given object has a billboard associated with it.
---
--- @param obj table The object to check for a billboard.
--- @return boolean True if the object has a billboard, false otherwise.
function HasBillboard(obj)
return hr.BillboardEntities and IsValid(obj) and IsValidEntity(obj:GetEntity())
and not not table.find(hr.BillboardEntities, obj:GetEntity())
end
---
--- Gets a list of all billboard entities in the game.
---
--- @param err_print function An optional function to call if there are any errors finding billboard entities.
--- @return table A table of all valid billboard entity names.
function GetBillboardEntities(err_print)
if hr.BillboardDirectory then
hr.BillboardDirectory = "Textures/Billboards/"
local suffix = Platform.playstation and "_bc.hgt" or "_bc.dds"
local err, textures = AsyncListFiles("Textures/Billboards", "*" .. suffix, "relative")
local billboard_entities = {}
for _, entity in ipairs(GetClassAndDescendantsEntities("BillboardObject")) do
local check_texture = not Platform.developer or Platform.console or table.find(textures, entity .. suffix)
if not check_texture then
err_print("Entity %s is marked as a billboard entity, but has no billboard textures!", entity)
end
if IsValidEntity(entity) and check_texture then
billboard_entities[#billboard_entities + 1] = entity
end
end
hr.BillboardEntities = billboard_entities
end
end
---
--- Stress tests the billboards in the game by randomly placing and removing tree objects.
---
--- This function creates a real-time thread that continuously places and removes tree objects
--- at random positions within a certain radius. The function keeps track of the number of
--- objects placed and removed, and sleeps for a short period of time after every 1000 iterations.
---
--- This function is likely used for testing and debugging purposes to ensure the billboards
--- are rendering correctly and efficiently.
---
--- @function StressTestBillboards
--- @return nil
function StressTestBillboards()
CreateRealTimeThread(function()
local count = 0
while true do
local pos = point((1000 + AsyncRand(4144)) * guim, (1000 + AsyncRand(4144)) * guim)
local o = MapGetFirst(pos:x(), pos:y(), 100, "Tree_01")
if o then
DoneObject(o)
local new = PlaceObject("Tree_01")
local curr_pos = point((1000 + AsyncRand(4144)) * guim, (1000 + AsyncRand(4144)) * guim)
local real_pos = point(curr_pos:x(), curr_pos:y(), terrain.GetHeight(curr_pos:x(), curr_pos:y()))
new:SetPos(real_pos)
end
count = count + 1
if count == 1000 then
count = 0
Sleep(100)
end
end
end)
end
function OnMsg.ClassesPostprocess()
CreateRealTimeThread(function()
GetBillboardEntities(function(...) printf("once", ...) end)
end)
end |