载入中...
搜索中...
未找到
UnityNativeTexture.cpp
浏览该文件的文档.
1#include <charconv>
2#include <stdexcept>
3#include <string_view>
4#include "asset/SourceBc3.h"
5#include "asset/SourcePng.h"
9
10namespace eve::asset_import {
11namespace {
12// Line parsing avoids regex recursion on multi-megabyte serialized pixel data.
13std::string_view field(std::string_view text, std::string_view key) {
14 std::string_view found;
15 bool seen = false;
16 while (!text.empty()) {
17 const auto end = text.find('\n');
18 auto line = text.substr(0, end);
19 const auto first = line.find_first_not_of(" \t\r");
20 if (first != std::string_view::npos) line.remove_prefix(first);
21 if (line.starts_with(key) && line.size() > key.size() && line[key.size()] == ':') {
22 if (seen) throw std::runtime_error("duplicate Texture2D field");
23 seen = true;
24 line.remove_prefix(key.size() + 1);
25 const auto begin = line.find_first_not_of(" \t\r");
26 found = begin == std::string_view::npos ? std::string_view{} : line.substr(begin);
27 const auto last = found.find_last_not_of(" \t\r");
28 if (last != std::string_view::npos) found = found.substr(0, last + 1);
29 }
30 if (end == std::string_view::npos) break;
31 text.remove_prefix(end + 1);
32 }
33 if (!seen) throw std::runtime_error("missing Texture2D field");
34 return found;
35}
36std::uint64_t number(std::string_view text, std::string_view key) {
37 const auto value = field(text, key);
38 std::uint64_t out = 0;
39 const auto parsed = std::from_chars(value.data(), value.data() + value.size(), out);
40 if (parsed.ec != std::errc{} || parsed.ptr != value.data() + value.size())
41 throw std::runtime_error("invalid Texture2D integer");
42 return out;
43}
44} // namespace
46 const UnitySourceAsset& source) {
47 const auto& bytes = request.files.at(source.path);
48 const std::string_view text(reinterpret_cast<const char*>(bytes.data()), bytes.size());
49 if (text.find("\nTexture2D:") == std::string_view::npos)
51 DiagnosticCode::Unsupported, "native asset is not a text Texture2D", source.path, {}, "asset.import"));
52 try {
53 const auto width = number(text, "m_Width"), height = number(text, "m_Height");
54 const auto mips = number(text, "m_MipCount"), transfer = number(text, "m_ColorSpace");
55 if (number(text, "m_TextureFormat") != 12 || number(text, "m_TextureDimension") != 2 ||
56 number(text, "m_ImageCount") != 1 || transfer > 1 || number(text, "size") != 0 ||
57 !field(text, "path").empty())
59 DiagnosticCode::Unsupported, "requires embedded BC3 Texture2D with one image and known color space",
60 source.path, {}, "asset.import"));
61 if (!width || !height || width > 0xffffffffu || height > 0xffffffffu ||
63 throw std::runtime_error("Texture2D dimensions or mip count exceed budget");
64 std::uint64_t expected = 0, w = width, h = height;
65 for (std::uint64_t i = 0; i < mips; ++i) {
66 if (w == 1 && h == 1 && i + 1 < mips) throw std::runtime_error("excess Texture2D mip levels");
67 expected += ((w + 3) / 4) * ((h + 3) / 4) * 16;
68 w = std::max(std::uint64_t(1), w / 2);
69 h = std::max(std::uint64_t(1), h / 2);
70 }
71 const auto hex = field(text, "_typelessdata");
72 if (expected > request.limits.maximumDecodedBytes || expected != number(text, "m_CompleteImageSize") ||
73 expected != number(text, "image data") || hex.size() % 2 || hex.size() / 2 != expected)
74 throw std::runtime_error("Texture2D mip-chain size mismatch");
75 std::vector<std::uint8_t> blocks(std::size_t(expected), 0);
76 for (std::size_t i = 0; i < blocks.size(); ++i) {
77 unsigned value = 0;
78 const auto parsed = std::from_chars(hex.data() + i * 2, hex.data() + i * 2 + 2, value, 16);
79 if (parsed.ec != std::errc{} || parsed.ptr != hex.data() + i * 2 + 2)
80 throw std::runtime_error("invalid Texture2D hex");
81 blocks[i] = std::uint8_t(value);
82 }
83 std::vector<std::uint8_t> pixels;
84 std::size_t blockOffset = 0;
85 w = width;
86 h = height;
87 for (std::uint64_t level = 0; level < mips; ++level) {
88 const auto levelBytes = ((w + 3) / 4) * ((h + 3) / 4) * 16;
89 auto decoded = asset::detail::decodeBc3Rgba8(
90 std::span(blocks).subspan(blockOffset, std::size_t(levelBytes)), std::uint32_t(w), std::uint32_t(h),
92 if (!decoded) return Result<PreparedAssetImport>::failure(decoded.status());
93 const auto stride = std::size_t(w * 4);
94 for (std::size_t y = 0; y < h / 2; ++y)
95 for (std::size_t x = 0; x < stride; ++x)
96 std::swap(decoded.value()[y * stride + x], decoded.value()[(h - 1 - y) * stride + x]);
97 if (decoded.value().size() > request.limits.maximumDecodedBytes - pixels.size())
98 throw std::runtime_error("Texture2D decoded mip chain exceeds budget");
99 pixels.insert(pixels.end(), decoded.value().begin(), decoded.value().end());
100 blockOffset += std::size_t(levelBytes);
101 w = std::max(std::uint64_t(1), w / 2);
102 h = std::max(std::uint64_t(1), h / 2);
103 }
104 const auto basePixels = std::span(pixels).first(std::size_t(width * height * 4));
105 auto png = asset::detail::encodeSourcePng(std::uint32_t(width), std::uint32_t(height), basePixels,
107 if (!png) return Result<PreparedAssetImport>::failure(png.status());
108 auto identity = request.package;
109 identity.packageId = identity.packageId.child("unity:" + source.guid);
110 // Serialized ColorSpace.Linear (1) selects sRGB sampling, as in Unity texture export metadata.
111 auto out = prepareImageImport({identity, "unity:" + source.guid, std::move(png).takeValue(),
112 transfer == 1 ? ImageColorSpace::Srgb : ImageColorSpace::Linear, "color",
113 request.limits});
114 if (!out) return out;
115 auto& definitionEntry = out.value().entries.front();
116 auto parsed = Value::fromJson(std::string(definitionEntry.bytes.begin(), definitionEntry.bytes.end()));
117 if (!parsed || !parsed.value().isObject())
119 Diagnostic::error(DiagnosticCode::ParseError, "generated native texture definition is invalid",
120 source.path, {}, "asset.import"));
121 auto& object = *parsed.value().getIf<Value::Object>();
122 const auto oldBlob = object.at("blob").asString();
123 const auto newBlob = definitionEntry.path.substr(0, definitionEntry.path.rfind('/') + 1) + "source.rgba8-mips";
124 object["encoding"] = Value("rgba8-mips");
125 object["mipCount"] = Value(std::int64_t(mips));
126 object["blob"] = Value(newBlob);
127 auto encoded = parsed.value().toJson();
128 if (!encoded) return Result<PreparedAssetImport>::failure(encoded.status());
129 definitionEntry.bytes.assign(encoded.value().begin(), encoded.value().end());
130 for (auto& entry : out.value().entries) {
131 if (entry.path == oldBlob) {
132 entry.path = newBlob;
133 entry.bytes = std::move(pixels);
134 }
135 }
136 out.value().manifest.assets.front().contentHash = detail::sha256(definitionEntry.bytes);
137 out.value().findings.push_back({source.path, "Texture2D.BC3", ImportDisposition::Translated,
138 "complete embedded BC3 mip chain decoded to RGBA8; source row order and "
139 "transfer converted to canonical image semantics"});
140 return out;
141 } catch (const std::exception& error) {
144 }
145}
146} // namespace eve::asset_import
double value
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
const GltfImportRequest & request
std::uint32_t key
std::int32_t first
int h
std::uint32_t height
std::uint32_t width
std::string text
std::uint64_t bytes
int level
std::string error
Definition Package.cpp:60
float begin
uint8_t * pixels
double number
bool found
bool mips
Direct Unity text-serialization adapter for TerrainData and Prefab assets.
const UnitySourceAsset & source
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
Id128 child(std::string_view role) const noexcept
Derives a deterministic child UUID for a hierarchical identity.
Definition Identity.h:192
Result< std::vector< std::uint8_t > > encodeSourcePng(std::uint32_t width, std::uint32_t height, std::span< const std::uint8_t > pixels, std::uint64_t maximumBytes)
Encode top-down RGBA8 source pixels without transfer conversion.
Definition SourcePng.cpp:6
Result< std::vector< std::uint8_t > > decodeBc3Rgba8(std::span< const std::uint8_t > bytes, std::uint32_t width, std::uint32_t height, std::uint64_t maximumDecodedBytes)
Decode one BC3/DXT5 mip level into tightly packed RGBA8 in source row order.
Definition SourceBc3.cpp:6
std::string sha256(std::span< const std::uint8_t > bytes)
Sha 256.
Result< PreparedAssetImport > prepareUnityNativeTexture(const UnityProjectImportRequest &request, const UnitySourceAsset &source)
Convert embedded BC3 text Texture2D to an owning image candidate.
EVENGINE_API_PLATFORM Result< PreparedAssetImport > prepareImageImport(const ImageImportRequest &request)
Decode image metadata and prepare one canonical eve.image source asset.
const EditorValue * field(const EditorValue &value, const char *name)
In-memory Unity project slice; keys are normalized project-relative paths.
Immutable-by-convention owning source index entry; no borrowed source addresses.
Definition UnitySource.h:38