载入中...
搜索中...
未找到
VegetationPresetMesh.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <bit>
5#include <charconv>
6#include <cmath>
7#include <limits>
8#include <new>
9
10namespace eve::asset_import {
11namespace {
12
13float clamp01(float value) { return std::clamp(value, 0.f, 1.f); }
14
15std::vector<std::size_t> elementIds(const asset::CanonicalMeshData& mesh, std::size_t count) {
16 std::vector<std::size_t> parent(count);
17 for (std::size_t i = 0; i < count; ++i) parent[i] = i;
18 auto root = [&](std::size_t value) {
19 while (parent[value] != value) {
22 }
23 return value;
24 };
25 for (std::size_t i = 0; i + 2 < mesh.indices.size(); i += 3) {
26 const auto a = mesh.indices[i], b = mesh.indices[i + 1], c = mesh.indices[i + 2];
27 if (a >= count || b >= count || c >= count) continue;
28 const auto ra = root(a), rb = root(b), rc = root(c);
29 parent[rb] = ra;
30 parent[rc] = ra;
31 }
32 std::map<std::size_t, std::size_t> ids;
33 std::vector<std::size_t> result(count);
34 for (std::size_t i = 0; i < count; ++i) {
35 const auto r = root(i);
36 auto [found, inserted] = ids.try_emplace(r, ids.size());
37 result[i] = found->second;
38 }
39 return result;
40}
41
42std::vector<float> four(const asset::CanonicalMeshData& mesh, const char* name, std::size_t count,
43 std::array<float, 4> fallback) {
44 const auto found = mesh.attributes.find(name);
45 if (found != mesh.attributes.end() && found->second.components == 4 && found->second.values.size() == count * 4)
46 return found->second.values;
47 std::vector<float> result(count * 4);
48 for (std::size_t i = 0; i < count; ++i)
49 std::copy(fallback.begin(), fallback.end(), result.begin() + std::ptrdiff_t(i * 4));
50 return result;
51}
52
53Result<int> integer(std::string_view value) {
54 int result = 0;
55 auto [end, ec] = std::from_chars(value.data(), value.data() + value.size(), result);
56 if (ec != std::errc{} || end != value.data() + value.size())
57 return Result<int>::failure(Diagnostic::error(DiagnosticCode::ParseError, "mesh rule option is not an integer",
58 {}, {}, "asset.import.vegetation-preset.mesh"));
59 return Result<int>::success(result);
60}
61
62Result<std::vector<float>> mask(const asset::CanonicalMeshData& mesh, const std::vector<std::string>& rule,
63 const std::map<std::string, VegetationPresetImage>& textures, std::size_t count,
64 float radius, float height, float seed) {
65 std::vector<float> result(count, 1.f);
66 if (rule.empty() || rule[0] == "NONE") return Result<std::vector<float>>::success(std::move(result));
67 if (rule.size() < 2)
68 return Result<std::vector<float>>::failure(Diagnostic::error(
69 DiagnosticCode::ParseError, "mesh mask rule lacks option", {}, {}, "asset.import.vegetation-preset.mesh"));
70 auto option = integer(rule[1]);
71 if (!option) return Result<std::vector<float>>::failure(option.status());
72 if (rule[0] == "GET_MASK_FROM_CHANNEL") {
73 auto color = four(mesh, "COLOR_0", count, {1, 1, 1, 1});
74 const int channel = option.value();
75 for (std::size_t i = 0; i < count; ++i) {
76 if (channel < 4)
77 result[i] = color[i * 4 + channel];
78 else {
79 const int uv = (channel - 4) / 4, component = (channel - 4) % 4;
80 auto values = four(mesh, ("_UNITY_UV" + std::to_string(uv)).c_str(), count, {0, 0, 0, 0});
81 result[i] = values[i * 4 + component];
82 }
83 }
84 } else if (rule[0] == "GET_MASK_PROCEDURAL") {
85 const int mode = option.value();
86 const auto elements = (mode == 2 || mode == 3) ? elementIds(mesh, count) : std::vector<std::size_t>{};
87 const auto elementCount =
88 elements.empty() ? std::size_t(0) : *std::max_element(elements.begin(), elements.end()) + 1;
89 for (std::size_t i = 0; i < count; ++i) {
90 const float x = mesh.positions[i * 3], y = mesh.positions[i * 3 + 1], z = mesh.positions[i * 3 + 2];
91 const float h = height > 0 ? y / height : 0.f, radial = radius > 0 ? std::hypot(x, z) / radius : 0.f;
92 switch (mode) {
93 case 0: result[i] = 0; break;
94 case 1: result[i] = 1; break;
95 case 2: {
96 std::uint32_t hash = std::uint32_t(elements[i]) ^ std::bit_cast<std::uint32_t>(seed);
97 hash ^= hash >> 16;
98 hash *= 0x7feb352du;
99 hash ^= hash >> 15;
100 hash *= 0x846ca68bu;
101 hash ^= hash >> 16;
102 result[i] = float(hash & 0x00ffffffu) / float(0x01000000u);
103 break;
104 }
105 case 3:
106 result[i] = std::fmod(float(elements[i]) / std::max<std::size_t>(elementCount, 1) * seed, 1.f);
107 break;
108 case 4: result[i] = clamp01(h); break;
109 case 5: result[i] = clamp01(std::sqrt(x * x + y * y + z * z) / std::max(radius, 1e-6f)); break;
110 case 6: result[i] = clamp01((radial - .1f) / .9f); break;
111 case 7: {
112 const float cap = clamp01((h - .8f) / .2f), base = clamp01(h / .1f);
113 result[i] = clamp01(clamp01((radial - .1f) / .9f) + cap) * base;
114 break;
115 }
116 case 8: result[i] = 1.f - clamp01(h); break;
117 case 9: result[i] = clamp01(-mesh.normals[i * 3 + 1] * .5f + .5f); break;
118 case 10: result[i] = clamp01(mesh.normals[i * 3 + 1] * .5f + .5f); break;
119 case 11: result[i] = clamp01((clamp01(h) - .2f) / .8f); break;
120 case 12: result[i] = clamp01((clamp01(h) - .4f) / .6f); break;
121 case 13: result[i] = clamp01((clamp01(h) - .6f) / .4f); break;
122 case 14: result[i] = 1.f - std::pow(1.f - clamp01(h), 4.f); break;
123 case 15:
124 result[i] = std::pow(clamp01(std::sqrt(x * x + y * y + z * z) / std::max(radius, 1e-6f)), 2.f);
125 break;
126 case 16: result[i] = std::pow(clamp01((radial - .1f) / .9f), 2.f); break;
127 case 17: {
128 const float cap = clamp01((h - .8f) / .2f), base = clamp01(h / .1f);
129 const float capsule = clamp01(clamp01((radial - .1f) / .9f) + cap) * base;
130 result[i] = capsule * capsule;
131 break;
132 }
133 case 18: result[i] = x / std::max(radius, 1e-6f); break;
134 case 19: result[i] = h; break;
135 case 20: result[i] = z / std::max(radius, 1e-6f); break;
136 default:
137 return Result<std::vector<float>>::failure(
138 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported procedural mesh mask mode", {}, {},
139 "asset.import.vegetation-preset.mesh"));
140 }
141 }
142 } else if (rule[0] == "GET_MASK_FROM_TEXTURE") {
143 if (rule.size() < 3)
144 return Result<std::vector<float>>::failure(Diagnostic::error(DiagnosticCode::ParseError,
145 "texture mask lacks property", {}, {},
146 "asset.import.vegetation-preset.mesh"));
147 const auto image = textures.find(rule[2]);
148 if (image == textures.end()) return Result<std::vector<float>>::success(std::move(result));
149 const auto pixels = std::uint64_t(image->second.width) * image->second.height;
150 if (!image->second.width || !image->second.height || pixels > 16ull * 1024ull * 1024ull ||
151 image->second.pixels.size() != std::size_t(pixels) * 4 || option.value() < 0 || option.value() > 3)
152 return Result<std::vector<float>>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument,
153 "invalid texture mask input", {}, {},
154 "asset.import.vegetation-preset.mesh"));
155 int coord = 0;
156 for (std::size_t i = 3; i + 1 < rule.size(); ++i)
157 if (rule[i] == "GET_COORD") {
158 auto parsed = integer(rule[i + 1]);
159 if (!parsed) return Result<std::vector<float>>::failure(parsed.status());
160 coord = parsed.value();
161 }
162 auto uv = four(mesh, ("_UNITY_UV" + std::to_string(coord)).c_str(), count, {0, 0, 0, 0});
163 const auto texel = [&](std::uint32_t x, std::uint32_t y) {
164 return image->second.pixels[(std::size_t(y) * image->second.width + x) * 4 + option.value()] / 255.f;
165 };
166 for (std::size_t i = 0; i < count; ++i) {
167 const float px = std::clamp(uv[i * 4] * image->second.width - .5f, 0.f, float(image->second.width - 1));
168 const float py =
169 std::clamp(uv[i * 4 + 1] * image->second.height - .5f, 0.f, float(image->second.height - 1));
170 const auto x0 = std::uint32_t(px), y0 = std::uint32_t(py), x1 = std::min(x0 + 1, image->second.width - 1),
171 y1 = std::min(y0 + 1, image->second.height - 1);
172 result[i] = std::lerp(std::lerp(texel(x0, y0), texel(x1, y0), px - x0),
173 std::lerp(texel(x0, y1), texel(x1, y1), px - x0), py - y0);
174 }
175 } else if (rule[0] == "GET_MASK_3RD_PARTY") {
176 const auto uv2 = four(mesh, "_UNITY_UV2", count, {0, 0, 0, 0});
177 const auto uv1 = four(mesh, "_UNITY_UV1", count, {0, 0, 0, 0});
178 const auto uv3 = four(mesh, "_UNITY_UV3", count, {0, 0, 0, 0});
179 for (std::size_t i = 0; i < count; ++i) {
180 const float x = mesh.positions[i * 3], y = mesh.positions[i * 3 + 1], z = mesh.positions[i * 3 + 2];
181 if (option.value() == 0 || option.value() == 1) {
182 const float packed = uv2[i * 4], scale = uv2[i * 4 + 1];
183 const float pivotX = std::fmod(packed, 1.f) * 2.f - 1.f;
184 const float pivotZ = std::fmod(32768.f * packed, 1.f) * 2.f - 1.f;
185 const float pivotY = std::sqrt(1.f - clamp01(pivotX * pivotX + pivotZ * pivotZ));
186 if (option.value() == 0) {
187 result[i] = std::sqrt(std::pow(x - pivotX * scale, 2.f) + std::pow(y - pivotY * scale, 2.f) +
188 std::pow(z - pivotZ * scale, 2.f)) /
189 std::max(radius, 1e-6f);
190 } else {
191 auto repeat = [](float value) { return value - std::floor(value); };
192 result[i] = packed < .01f ? 0.f
193 : repeat(pivotX * scale * 33.3f) + repeat(pivotY * scale * 33.3f) +
194 repeat(pivotZ * scale * 33.3f);
195 }
196 } else if (option.value() == 2) {
197 const float ax = uv1[i * 4 + 2] - x, ay = uv1[i * 4 + 3] - y, az = uv2[i * 4 + 3] - z;
198 result[i] = uv3[i * 4 + 3] == 0.f ? clamp01(y / std::max(height, 1e-6f))
199 : std::sqrt(ax * ax + ay * ay + az * az) *
200 (uv1[i * 4 + 3] * uv3[i * 4 + 3]) / std::max(radius, 1e-6f);
201 } else {
202 return Result<std::vector<float>>::failure(
203 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported third-party vegetation mask mode", {},
204 {}, "asset.import.vegetation-preset.mesh"));
205 }
206 }
207 } else {
208 return Result<std::vector<float>>::failure(
209 Diagnostic::error(DiagnosticCode::Unsupported, "mesh mask source requires texture or vendor adapter", {},
210 {}, "asset.import.vegetation-preset.mesh"));
211 }
212 if (rule.size() >= 3 && rule.back().starts_with("ACTION_")) {
213 const auto& action = rule.back();
214 if (action == "ACTION_ONE_MINUS")
215 for (auto& v : result) v = 1.f - v;
216 else if (action == "ACTION_NEGATIVE")
217 for (auto& v : result) v = -v;
218 else if (action == "ACTION_POWER_2")
219 for (auto& v : result) v *= v;
220 else if (action == "ACTION_MULTIPLY_BY_HEIGHT")
221 for (std::size_t i = 0; i < count; ++i)
222 result[i] *= clamp01(mesh.positions[i * 3 + 1] / std::max(height, 1e-6f));
223 else if (action == "ACTION_CLAMP_NEGATIVE_VALUES") {
224 for (std::size_t i = 0; i < count; ++i)
225 if (mesh.positions[i * 3 + 1] < 0) result[i] = 0;
226 } else if (action == "ACTION_FRACTIONAL_VALUES") {
227 for (auto& v : result) v -= std::floor(v);
228 } else if (action == "ACTION_REMAP_01") {
229 auto [lo, hi] = std::minmax_element(result.begin(), result.end());
230 const float range = *hi - *lo;
231 for (auto& v : result) v = range == 0 ? 0 : (v - *lo) / range;
232 } else
233 return Result<std::vector<float>>::failure(Diagnostic::error(DiagnosticCode::Unsupported,
234 "unsupported mesh mask action", {}, {},
235 "asset.import.vegetation-preset.mesh"));
236 }
237 return Result<std::vector<float>>::success(std::move(result));
238}
239
240float packPair(float x, float y) { return std::floor(clamp01(x) * 2047.f) * 2048.f + std::floor(clamp01(y) * 2047.f); }
241
242void normalize3(float& x, float& y, float& z) {
243 const float length = std::sqrt(x * x + y * y + z * z);
244 if (length > 1e-8f) {
245 x /= length;
246 y /= length;
247 z /= length;
248 }
249}
250
251Result<void> applyNormals(asset::CanonicalMeshData& mesh, const std::vector<std::string>& rule, float height) {
252 if (rule.size() != 2 || rule[0] != "GET_NORMALS_PROCEDURAL")
253 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Unsupported, "unsupported normal rule", {}, {},
254 "asset.import.vegetation-preset.mesh"));
255 auto mode = integer(rule[1]);
256 if (!mode || mode.value() < 0 || mode.value() > 6)
257 return Result<void>::failure(Diagnostic::error(DiagnosticCode::ParseError, "invalid normal mode", {}, {},
258 "asset.import.vegetation-preset.mesh"));
259 const std::size_t count = mesh.positions.size() / 3;
260 if (mode.value() == 0) {
261 std::fill(mesh.normals.begin(), mesh.normals.end(), 0.f);
262 for (std::size_t i = 0; i + 2 < mesh.indices.size(); i += 3) {
263 const auto a = mesh.indices[i], b = mesh.indices[i + 1], c = mesh.indices[i + 2];
264 if (a >= count || b >= count || c >= count)
266 "mesh index out of range", {}, {},
267 "asset.import.vegetation-preset.mesh"));
268 const float ax = mesh.positions[b * 3] - mesh.positions[a * 3],
269 ay = mesh.positions[b * 3 + 1] - mesh.positions[a * 3 + 1],
270 az = mesh.positions[b * 3 + 2] - mesh.positions[a * 3 + 2];
271 const float bx = mesh.positions[c * 3] - mesh.positions[a * 3],
272 by = mesh.positions[c * 3 + 1] - mesh.positions[a * 3 + 1],
273 bz = mesh.positions[c * 3 + 2] - mesh.positions[a * 3 + 2];
274 const float nx = ay * bz - az * by, ny = az * bx - ax * bz, nz = ax * by - ay * bx;
275 for (auto v : {a, b, c}) {
276 mesh.normals[v * 3] += nx;
277 mesh.normals[v * 3 + 1] += ny;
278 mesh.normals[v * 3 + 2] += nz;
279 }
280 }
281 for (std::size_t i = 0; i < count; ++i)
282 normalize3(mesh.normals[i * 3], mesh.normals[i * 3 + 1], mesh.normals[i * 3 + 2]);
283 return Result<void>::success();
284 }
285 for (std::size_t i = 0; i < count; ++i) {
286 float tx = 0, ty = 1, tz = 0, blend = 1;
287 if (mode.value() <= 3) {
288 blend = mode.value() == 1 ? clamp01(mesh.positions[i * 3 + 1] / std::max(height, 1e-6f))
289 : mode.value() == 2 ? clamp01(clamp01(mesh.positions[i * 3 + 1] / std::max(height, 1e-6f)) + .5f)
290 : 1.f;
291 } else {
292 tx = mesh.positions[i * 3];
293 ty = mesh.positions[i * 3 + 1];
294 tz = mesh.positions[i * 3 + 2];
295 normalize3(tx, ty, tz);
296 blend = mode.value() == 4 ? .5f : mode.value() == 5 ? .75f : 1.f;
297 }
298 mesh.normals[i * 3] = std::lerp(mesh.normals[i * 3], tx, blend);
299 mesh.normals[i * 3 + 1] = std::lerp(mesh.normals[i * 3 + 1], ty, blend);
300 mesh.normals[i * 3 + 2] = std::lerp(mesh.normals[i * 3 + 2], tz, blend);
301 }
302 return Result<void>::success();
303}
304
305Result<void> recalculateTangents(asset::CanonicalMeshData& mesh) {
306 const auto count = mesh.positions.size() / 3;
307 const auto uv = mesh.texcoords.find(0);
308 if (uv == mesh.texcoords.end()) {
309 mesh.attributes.erase("TANGENT");
310 return Result<void>::success();
311 }
312 if (uv->second.size() != count * 2)
314 "canonical UV0 has an invalid tangent input size", {}, {},
315 "asset.import.vegetation-preset.mesh"));
316 std::vector<float> tan1(count * 3), tan2(count * 3);
317 for (std::size_t k = 0; k + 2 < mesh.indices.size(); k += 3) {
318 const auto a = mesh.indices[k], b = mesh.indices[k + 1], c = mesh.indices[k + 2];
319 if (a >= count || b >= count || c >= count)
321 "mesh index out of range while rebuilding tangents", {}, {},
322 "asset.import.vegetation-preset.mesh"));
323 const float x1 = mesh.positions[b * 3] - mesh.positions[a * 3],
324 x2 = mesh.positions[c * 3] - mesh.positions[a * 3],
325 y1 = mesh.positions[b * 3 + 1] - mesh.positions[a * 3 + 1],
326 y2 = mesh.positions[c * 3 + 1] - mesh.positions[a * 3 + 1],
327 z1 = mesh.positions[b * 3 + 2] - mesh.positions[a * 3 + 2],
328 z2 = mesh.positions[c * 3 + 2] - mesh.positions[a * 3 + 2],
329 s1 = uv->second[b * 2] - uv->second[a * 2], s2 = uv->second[c * 2] - uv->second[a * 2],
330 t1 = uv->second[b * 2 + 1] - uv->second[a * 2 + 1],
331 t2 = uv->second[c * 2 + 1] - uv->second[a * 2 + 1], det = s1 * t2 - s2 * t1;
332 if (std::abs(det) < 1e-12f) continue;
333 const float r = 1.f / det, sx = (x1 * t2 - x2 * t1) * r, sy = (y1 * t2 - y2 * t1) * r,
334 sz = (z1 * t2 - z2 * t1) * r, tx = (x2 * s1 - x1 * s2) * r, ty = (y2 * s1 - y1 * s2) * r,
335 tz = (z2 * s1 - z1 * s2) * r;
336 for (const auto vertex : {a, b, c}) {
337 tan1[vertex * 3] += sx;
338 tan1[vertex * 3 + 1] += sy;
339 tan1[vertex * 3 + 2] += sz;
340 tan2[vertex * 3] += tx;
341 tan2[vertex * 3 + 1] += ty;
342 tan2[vertex * 3 + 2] += tz;
343 }
344 }
345 asset::CanonicalMeshAttribute tangents{4, std::vector<float>(count * 4)};
346 for (std::size_t i = 0; i < count; ++i) {
347 const float nx = mesh.normals[i * 3], ny = mesh.normals[i * 3 + 1], nz = mesh.normals[i * 3 + 2];
348 float tx = tan1[i * 3], ty = tan1[i * 3 + 1], tz = tan1[i * 3 + 2];
349 const float projection = nx * tx + ny * ty + nz * tz;
350 tx -= nx * projection;
351 ty -= ny * projection;
352 tz -= nz * projection;
353 if (std::sqrt(tx * tx + ty * ty + tz * tz) <= 1e-8f) {
354 if (std::abs(ny) < .999f) {
355 tx = nz;
356 ty = 0;
357 tz = -nx;
358 } else {
359 tx = 1;
360 ty = 0;
361 tz = 0;
362 }
363 }
364 normalize3(tx, ty, tz);
365 const float bx = ny * tz - nz * ty, by = nz * tx - nx * tz, bz = nx * ty - ny * tx;
366 tangents.values[i * 4] = tx;
367 tangents.values[i * 4 + 1] = ty;
368 tangents.values[i * 4 + 2] = tz;
369 tangents.values[i * 4 + 3] = bx * tan2[i * 3] + by * tan2[i * 3 + 1] + bz * tan2[i * 3 + 2] < 0 ? -1.f : 1.f;
370 }
371 mesh.attributes["TANGENT"] = std::move(tangents);
372 return Result<void>::success();
373}
374} // namespace
375
378 const std::map<std::string, VegetationPresetImage>& textures, float variationSeed) {
379 const std::size_t count = source.positions.size() / 3;
380 if (!count || source.positions.size() != count * 3 || source.normals.size() != count * 3 ||
381 !std::isfinite(variationSeed))
383 "invalid mesh geometry or seed", {}, {},
384 "asset.import.vegetation-preset.mesh"));
385 try {
386 float radius = 0, height = 0;
387 for (std::size_t i = 0; i < count; ++i) {
388 radius =
389 std::max(radius, std::max(std::abs(source.positions[i * 3]), std::abs(source.positions[i * 3 + 2])));
390 height = std::max(height, std::abs(source.positions[i * 3 + 1]));
391 }
392 auto color = four(source, "COLOR_0", count, {1, 1, 1, 1});
393 auto uv0 = four(source, "_UNITY_UV0", count, {0, 0, 0, 0});
394 auto uv1 = four(source, "_UNITY_UV1", count, {0, 0, 0, 0});
395 auto apply = [&](const char* ruleName, unsigned component) -> Result<void> {
396 auto found = candidate.meshRules.find(ruleName);
397 if (found == candidate.meshRules.end()) return Result<void>::success();
398 auto values = mask(source, found->second, textures, count, radius, height, variationSeed);
399 if (!values) return Result<void>::failure(values.status());
400 for (std::size_t i = 0; i < count; ++i) color[i * 4 + component] = values.value()[i];
401 return Result<void>::success();
402 };
403 for (auto [name, component] : {std::pair<const char*, unsigned>{"SetVariation", 0u},
404 {"SetOcclusion", 1u},
405 {"SetDetailMask", 2u},
406 {"SetHeight", 3u}}) {
407 auto result = apply(name, component);
408 if (!result) return Result<asset::CanonicalMeshData>::failure(result.status());
409 }
410 auto motion2 =
411 candidate.meshRules.contains("SetMotion2")
412 ? mask(source, candidate.meshRules.at("SetMotion2"), textures, count, radius, height, variationSeed)
413 : Result<std::vector<float>>::success(std::vector<float>(count, 1));
414 auto motion3 =
415 candidate.meshRules.contains("SetMotion3")
416 ? mask(source, candidate.meshRules.at("SetMotion3"), textures, count, radius, height, variationSeed)
417 : Result<std::vector<float>>::success(std::vector<float>(count, 1));
418 if (!motion2) return Result<asset::CanonicalMeshData>::failure(motion2.status());
419 if (!motion3) return Result<asset::CanonicalMeshData>::failure(motion3.status());
420 const float packedBounds = packPair(height / 100.f, radius / 100.f);
421 for (std::size_t i = 0; i < count; ++i) {
422 uv0[i * 4 + 2] = packPair(motion2.value()[i], motion3.value()[i]);
423 uv0[i * 4 + 3] = packedBounds;
424 }
425 if (auto found = candidate.meshRules.find("SetDetailCoord"); found != candidate.meshRules.end() &&
426 found->second.size() >= 2 &&
427 found->second[0] == "GET_COORD_FROM_CHANNEL") {
428 auto option = integer(found->second[1]);
429 if (!option) return Result<asset::CanonicalMeshData>::failure(option.status());
430 auto coord = four(source, ("_UNITY_UV" + std::to_string(option.value())).c_str(), count, {0, 0, 0, 0});
431 for (std::size_t i = 0; i < count; ++i) {
432 uv1[i * 4 + 2] = coord[i * 4];
433 uv1[i * 4 + 3] = coord[i * 4 + 1];
434 }
435 }
436 source.attributes["COLOR_0"] = {4, std::move(color)};
437 source.attributes["_UNITY_UV0"] = {4, std::move(uv0)};
438 source.attributes["_UNITY_UV1"] = {4, std::move(uv1)};
439 auto uv3 = four(source, "_UNITY_UV3", count, {0, 0, 0, 0});
440 if (auto found = candidate.meshRules.find("SetPivots"); found != candidate.meshRules.end()) {
441 if (found->second.size() == 1 && found->second[0] == "NONE") {
442 source.attributes["_UNITY_UV3"] = {4, std::move(uv3)};
443 } else {
444 if (found->second.size() != 2 || found->second[0] != "GET_PIVOTS_PROCEDURAL" || found->second[1] != "0")
446 Diagnostic::error(DiagnosticCode::Unsupported, "unsupported pivot rule", {}, {},
447 "asset.import.vegetation-preset.mesh"));
448 const auto ids = elementIds(source, count);
449 const auto groups = *std::max_element(ids.begin(), ids.end()) + 1;
450 std::vector<double> sumX(groups), sumZ(groups);
451 std::vector<std::size_t> sizes(groups);
452 for (std::size_t i = 0; i < count; ++i) {
453 sumX[ids[i]] += source.positions[i * 3];
454 sumZ[ids[i]] += source.positions[i * 3 + 2];
455 ++sizes[ids[i]];
456 }
457 for (std::size_t i = 0; i < count; ++i) {
458 uv3[i * 4] = float(sumX[ids[i]] / sizes[ids[i]]);
459 uv3[i * 4 + 1] = -float(sumZ[ids[i]] / sizes[ids[i]]);
460 uv3[i * 4 + 2] = 0;
461 uv3[i * 4 + 3] = 0;
462 }
463 source.attributes["_UNITY_UV3"] = {4, std::move(uv3)};
464 }
465 } else {
466 source.attributes["_UNITY_UV3"] = {4, std::move(uv3)};
467 }
468 if (auto found = candidate.meshRules.find("SetNormals"); found != candidate.meshRules.end()) {
469 auto changed = applyNormals(source, found->second, height);
470 if (!changed) return Result<asset::CanonicalMeshData>::failure(changed.status());
471 changed = recalculateTangents(source);
472 if (!changed) return Result<asset::CanonicalMeshData>::failure(changed.status());
473 }
475 } catch (const std::bad_alloc&) {
477 "mesh conversion allocation failed", {}, {},
478 "asset.import.vegetation-preset.mesh"));
479 }
480}
481
483 asset::CanonicalMeshData source, float variationSeed) {
484 static const std::map<std::string, VegetationPresetImage> noTextures;
485 return executeVegetationMeshRules(candidate, std::move(source), noTextures, variationSeed);
486}
487} // namespace eve::asset_import
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
int root
Definition AnimSmr.cpp:119
int mask
float uv
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int ay
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
int az
Definition CaveMesh.cpp:113
int by
Definition CaveMesh.cpp:114
float py
float nx
float nz
float ny
std::map< std::string, Var > values
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
vk::UniqueImage image
glm::uvec4 ids
float u
Definition Grass.cpp:233
double r
std::vector< float > positions
float v
std::int32_t c
float blend
int h
std::vector< Colorf > px
std::uint32_t height
Range range
std::array< float, 3 > scale
std::int32_t parent
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
uint32_t groups
Definition OnnxGpgpu.cpp:39
float radius
std::string action
Definition PlayHost.cpp:117
std::uint32_t seed
Definition PointSet.cpp:807
float begin
uint8_t * pixels
Mesh * mesh
bool found
std::uint32_t count
std::size_t elements
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 success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
EVENGINE_API_PLATFORM Result< asset::CanonicalMeshData > executeVegetationMeshRules(const VegetationConversionCandidate &candidate, asset::CanonicalMeshData source, float variationSeed=1.f)
Execute preset mask and coordinate rules into canonical TVE authoring streams.
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
Owning CPU mesh snapshot; UV arrays are packed ST pairs indexed by source set number....
Owning unpublished material, mesh and texture candidate mutated transactionally by preset commands.
std::map< std::string, std::vector< std::string > > meshRules
glm::vec4 color