|
EngineBinAssetsPrints = {} |
|
EngineBinAssetsPrint = CreatePrint { "", output = function(s) table.insert(EngineBinAssetsPrints, s) end } |
|
EngineBinAssetsPrintf = CreatePrint { "", output = function(s) table.insert(EngineBinAssetsPrints, s) end, format = string.format } |
|
EngineBinAssetsError = CreatePrint { "EB_ERROR", output = function(s) table.insert(EngineBinAssetsPrints, s) end } |
|
EngineBinAssetsErrorf = CreatePrint { "EB_ERROR", output = function(s) table.insert(EngineBinAssetsPrints, s) end, format = string.format } |
|
|
|
local function MarkTestEntityTextures(texture_list) |
|
local tex_map = {} |
|
local test_assets_path = ConvertToOSPath("svnAssets/Bin/Common/Materials/") |
|
for texture_id, path in pairs(texture_list) do |
|
local texture_name = path:sub(path:find("[^\\]*$")) |
|
tex_map[texture_name] = { |
|
id = texture_id, |
|
path = path, |
|
skip = string.starts_with(path, test_assets_path), |
|
} |
|
end |
|
return tex_map; |
|
end |
|
|
|
local function AddTextureHashes(textures_data, cached_hashes) |
|
local cached_hashes = cached_hashes or {} |
|
for texture_id, texture_data in pairs(textures_data) do |
|
local tex_hash = cached_hashes[texture_id] |
|
if not tex_hash then |
|
local err, hash = AsyncFileToString(texture_data.path, nil, nil, "hash") |
|
if err then |
|
EngineBinAssetsError("AsyncFileToString(" .. texture_data.path .. ") failed: " .. err) |
|
return err |
|
else |
|
tex_hash = tostring(hash) |
|
end |
|
end |
|
texture_data.hash = tex_hash |
|
end |
|
end |
|
|
|
local function GetTexturesCachedHashes() |
|
local err, hash_list = FileToLuaValue("svnAssets/BuildCache/TextureHashes.lua") |
|
if err then |
|
EngineBinAssetsError("Could not load hash map with material textures!") |
|
end |
|
|
|
local cached_hashes = {} |
|
for filename, v in pairs(hash_list or empty_table) do |
|
cached_hashes[filename:sub(filename:find("[^/]*$"))] = tostring(v.hash) |
|
end |
|
|
|
return cached_hashes |
|
end |
|
|
|
function MarkUniqueTextures(textures_data) |
|
local sorted_texture_ids = table.keys(textures_data) |
|
table.sort(sorted_texture_ids, function (a, b) return #a == #b and a < b or #a < #b end) |
|
local unique_hashes = {} |
|
for _, texture_id in ipairs(sorted_texture_ids) do |
|
local tex_data = textures_data[texture_id] |
|
if not tex_data.skip then |
|
local tex_alias = unique_hashes[tex_data.hash] |
|
|
|
if tex_alias then |
|
tex_data.alias = tex_alias |
|
elseif not tex_data.hash then |
|
EngineBinAssetsError("Missing texture hash for(" .. texture_id .. ")") |
|
else |
|
|
|
unique_hashes[tex_data.hash] = texture_id |
|
end |
|
end |
|
end |
|
local num_tex = table.count(textures_data) |
|
local num_unique = table.count(unique_hashes) |
|
EngineBinAssetsPrintf("Entity textures: %d (%d Unique, %d Duplicates)", num_tex, num_unique, num_tex - num_unique) |
|
return unique_hashes |
|
end |
|
|
|
local function RemapMaterialTextures(mat_name, textures_data, used_tex, ent_textures) |
|
local num_sub_mats = GetNumSubMaterials(mat_name) |
|
for subi=1, num_sub_mats do |
|
local props = GetMaterialProperties(mat_name, subi-1) |
|
local new_props = false |
|
for prop, value in sorted_pairs(props) do |
|
if type(value) == "string" then |
|
local tex_name = value:sub(value:find("[^/]*$")) |
|
if tex_name then |
|
local tex_data = textures_data[tex_name] |
|
if tex_data then |
|
if tex_data.alias then |
|
new_props = new_props or {} |
|
new_props[prop] = textures_data[tex_data.alias].id |
|
|
|
ent_textures[tex_data.alias] = true |
|
|
|
local alias_data = textures_data[tex_data.alias] |
|
alias_data.used_in = alias_data.used_in or {} |
|
table.insert(alias_data.used_in, mat_name) |
|
else |
|
used_tex[tex_name] = tex_data.path |
|
ent_textures[tex_name] = true |
|
|
|
tex_data.used_in = tex_data.used_in or {} |
|
table.insert(tex_data.used_in, mat_name) |
|
end |
|
end |
|
elseif value:find("<unknown>", 1, "plain") then |
|
EngineBinAssetsPrintf("once", "Missing texture Textures/%s in material %s", value:match("Textures/(.*)%.<unknown>"), mat_name) |
|
end |
|
end |
|
end |
|
if new_props then |
|
SetMaterialProperties(mat_name, subi-1, new_props) |
|
end |
|
end |
|
end |
|
|
|
local function BuildTextureIdx(used_tex) |
|
local tex_idx = {} |
|
local textures = table.keys(used_tex) |
|
table.sort(textures, function (a, b) return #a == #b and a < b or #a < #b end) |
|
|
|
for _, tex_name in ipairs(textures) do |
|
tex_idx[#tex_idx+1] = string.format("Textures/%s=%s", tex_name, used_tex[tex_name]) |
|
end |
|
|
|
local filename = "entities.txt" |
|
local path = "svnAssets/BuildCache/win32/TextureIdx/" |
|
local err = AsyncCreatePath(path) |
|
assert(not err, "Error creating textures idx path: " .. tostring(err)) |
|
local err = StringToFileIfDifferent(path .. filename, table.concat(tex_idx, "\r\n")) |
|
assert(not err, "Error writing textures idx: " .. tostring(err)) |
|
end |
|
|
|
local function ShortenTexturePaths(textures_data) |
|
for _, texture_data in pairs(textures_data) do |
|
texture_data.path = string.match(texture_data.path, "\\([^\\]+Assets.+)$") |
|
end |
|
end |
|
|
|
function CollapseMaterialTextures(texture_list, entities, cached_hashes, shorten_paths) |
|
local textures_data = MarkTestEntityTextures(texture_list) |
|
AddTextureHashes(textures_data, cached_hashes) |
|
if shorten_paths then |
|
ShortenTexturePaths(textures_data) |
|
end |
|
MarkUniqueTextures(textures_data) |
|
|
|
local materials_seen, entity_textures, used_tex = {}, {}, {} |
|
for entity, _ in sorted_pairs(entities) do |
|
if entity:sub(1,1) ~= "#" then |
|
local ent_textures = {} |
|
local states = GetStates(entity) |
|
for si=1, #states do |
|
local state = GetStateIdx(states[si]) |
|
local num_lods = GetStateLODCount(entity, state) |
|
for li=1, num_lods do |
|
local material = GetStateMaterial(entity, state, li - 1) |
|
if not materials_seen[material] then |
|
RemapMaterialTextures(material, textures_data, used_tex, ent_textures) |
|
materials_seen[material] = entity |
|
entity_textures[entity] = ent_textures |
|
else |
|
local other_entity = materials_seen[material] |
|
entity_textures[entity] = entity_textures[other_entity] |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
return materials_seen, used_tex, textures_data |
|
end |
|
|
|
function CollapseAllTextures() |
|
local all_entities = GetAllEntities() |
|
local texture_list = CollectMtlReferencedTextures() |
|
local cached_hashes = GetTexturesCachedHashes() |
|
|
|
local materials_seen, used_tex, textures_data = CollapseMaterialTextures(texture_list, all_entities, cached_hashes, true) |
|
|
|
BuildTextureIdx(used_tex) |
|
|
|
return materials_seen, used_tex, textures_data |
|
end |
|
|
|
function CollapseEntitiesTextures(entities) |
|
local texture_list = {} |
|
for entity, _ in sorted_pairs(entities) do |
|
local states = GetStates(entity) |
|
for si=1, #states do |
|
local state = GetStateIdx(states[si]) |
|
local state_textures = GetStateMaterialTextures(entity, state) |
|
table.append(texture_list, state_textures) |
|
end |
|
end |
|
|
|
local materials_seen, used_tex, textures_data = CollapseMaterialTextures(texture_list, entities) |
|
|
|
return materials_seen, used_tex, textures_data |
|
end |