Implement initial Unity-style asset library cache
This commit is contained in:
677
engine/src/Resources/BuiltinResources.cpp
Normal file
677
engine/src/Resources/BuiltinResources.cpp
Normal file
@@ -0,0 +1,677 @@
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <XCEngine/Core/Math/Bounds.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kBuiltinPrefix = "builtin://";
|
||||
constexpr const char* kBuiltinMeshPrefix = "builtin://meshes/";
|
||||
constexpr const char* kBuiltinMaterialPrefix = "builtin://materials/";
|
||||
constexpr const char* kBuiltinTexturePrefix = "builtin://textures/";
|
||||
constexpr const char* kBuiltinDefaultPrimitiveMaterialPath = "builtin://materials/default-primitive";
|
||||
constexpr const char* kBuiltinDefaultPrimitiveTexturePath = "builtin://textures/default-primitive-albedo";
|
||||
constexpr float kPi = 3.14159265358979323846f;
|
||||
|
||||
struct MeshBuffers {
|
||||
std::vector<StaticMeshVertex> vertices;
|
||||
std::vector<Core::uint32> indices;
|
||||
};
|
||||
|
||||
Math::Bounds ComputeBounds(const std::vector<StaticMeshVertex>& vertices) {
|
||||
if (vertices.empty()) {
|
||||
return Math::Bounds();
|
||||
}
|
||||
|
||||
Math::Vector3 min = vertices.front().position;
|
||||
Math::Vector3 max = vertices.front().position;
|
||||
for (const StaticMeshVertex& vertex : vertices) {
|
||||
min.x = std::min(min.x, vertex.position.x);
|
||||
min.y = std::min(min.y, vertex.position.y);
|
||||
min.z = std::min(min.z, vertex.position.z);
|
||||
max.x = std::max(max.x, vertex.position.x);
|
||||
max.y = std::max(max.y, vertex.position.y);
|
||||
max.z = std::max(max.z, vertex.position.z);
|
||||
}
|
||||
|
||||
Math::Bounds bounds;
|
||||
bounds.SetMinMax(min, max);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
StaticMeshVertex MakeVertex(
|
||||
const Math::Vector3& position,
|
||||
const Math::Vector3& normal,
|
||||
const Math::Vector3& tangent,
|
||||
const Math::Vector2& uv) {
|
||||
StaticMeshVertex vertex;
|
||||
vertex.position = position;
|
||||
vertex.normal = normal.Normalized();
|
||||
vertex.tangent = tangent.Normalized();
|
||||
vertex.bitangent = Math::Vector3::Cross(vertex.normal, vertex.tangent).Normalized();
|
||||
vertex.uv0 = uv;
|
||||
return vertex;
|
||||
}
|
||||
|
||||
void AppendQuad(
|
||||
MeshBuffers& buffers,
|
||||
const Math::Vector3& bottomLeft,
|
||||
const Math::Vector3& bottomRight,
|
||||
const Math::Vector3& topRight,
|
||||
const Math::Vector3& topLeft,
|
||||
const Math::Vector3& normal,
|
||||
const Math::Vector3& tangent) {
|
||||
const Core::uint32 baseIndex = static_cast<Core::uint32>(buffers.vertices.size());
|
||||
buffers.vertices.push_back(MakeVertex(bottomLeft, normal, tangent, Math::Vector2(0.0f, 0.0f)));
|
||||
buffers.vertices.push_back(MakeVertex(bottomRight, normal, tangent, Math::Vector2(1.0f, 0.0f)));
|
||||
buffers.vertices.push_back(MakeVertex(topRight, normal, tangent, Math::Vector2(1.0f, 1.0f)));
|
||||
buffers.vertices.push_back(MakeVertex(topLeft, normal, tangent, Math::Vector2(0.0f, 1.0f)));
|
||||
|
||||
buffers.indices.push_back(baseIndex + 0);
|
||||
buffers.indices.push_back(baseIndex + 1);
|
||||
buffers.indices.push_back(baseIndex + 2);
|
||||
buffers.indices.push_back(baseIndex + 0);
|
||||
buffers.indices.push_back(baseIndex + 2);
|
||||
buffers.indices.push_back(baseIndex + 3);
|
||||
}
|
||||
|
||||
void FlipTriangleWinding(MeshBuffers& buffers) {
|
||||
for (size_t index = 0; index + 2 < buffers.indices.size(); index += 3) {
|
||||
std::swap(buffers.indices[index + 1], buffers.indices[index + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
MeshBuffers CreateCubeMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
const float half = 0.5f;
|
||||
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(-half, -half, half),
|
||||
Math::Vector3(half, -half, half),
|
||||
Math::Vector3(half, half, half),
|
||||
Math::Vector3(-half, half, half),
|
||||
Math::Vector3::Forward(),
|
||||
Math::Vector3::Right());
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(half, -half, -half),
|
||||
Math::Vector3(-half, -half, -half),
|
||||
Math::Vector3(-half, half, -half),
|
||||
Math::Vector3(half, half, -half),
|
||||
Math::Vector3::Back(),
|
||||
Math::Vector3::Left());
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(-half, -half, -half),
|
||||
Math::Vector3(-half, -half, half),
|
||||
Math::Vector3(-half, half, half),
|
||||
Math::Vector3(-half, half, -half),
|
||||
Math::Vector3::Left(),
|
||||
Math::Vector3::Forward());
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(half, -half, half),
|
||||
Math::Vector3(half, -half, -half),
|
||||
Math::Vector3(half, half, -half),
|
||||
Math::Vector3(half, half, half),
|
||||
Math::Vector3::Right(),
|
||||
Math::Vector3::Back());
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(-half, half, half),
|
||||
Math::Vector3(half, half, half),
|
||||
Math::Vector3(half, half, -half),
|
||||
Math::Vector3(-half, half, -half),
|
||||
Math::Vector3::Up(),
|
||||
Math::Vector3::Right());
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(-half, -half, -half),
|
||||
Math::Vector3(half, -half, -half),
|
||||
Math::Vector3(half, -half, half),
|
||||
Math::Vector3(-half, -half, half),
|
||||
Math::Vector3::Down(),
|
||||
Math::Vector3::Right());
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
MeshBuffers CreateQuadMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
AppendQuad(
|
||||
buffers,
|
||||
Math::Vector3(-0.5f, -0.5f, 0.0f),
|
||||
Math::Vector3(0.5f, -0.5f, 0.0f),
|
||||
Math::Vector3(0.5f, 0.5f, 0.0f),
|
||||
Math::Vector3(-0.5f, 0.5f, 0.0f),
|
||||
Math::Vector3::Forward(),
|
||||
Math::Vector3::Right());
|
||||
return buffers;
|
||||
}
|
||||
|
||||
MeshBuffers CreatePlaneMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
constexpr int kSegments = 10;
|
||||
constexpr float kSize = 10.0f;
|
||||
const float halfSize = kSize * 0.5f;
|
||||
const float step = kSize / static_cast<float>(kSegments);
|
||||
|
||||
for (int z = 0; z <= kSegments; ++z) {
|
||||
for (int x = 0; x <= kSegments; ++x) {
|
||||
const float px = -halfSize + static_cast<float>(x) * step;
|
||||
const float pz = -halfSize + static_cast<float>(z) * step;
|
||||
const float u = static_cast<float>(x) / static_cast<float>(kSegments);
|
||||
const float v = static_cast<float>(z) / static_cast<float>(kSegments);
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
Math::Vector3(px, 0.0f, pz),
|
||||
Math::Vector3::Up(),
|
||||
Math::Vector3::Right(),
|
||||
Math::Vector2(u, v)));
|
||||
}
|
||||
}
|
||||
|
||||
const int rowStride = kSegments + 1;
|
||||
for (int z = 0; z < kSegments; ++z) {
|
||||
for (int x = 0; x < kSegments; ++x) {
|
||||
const Core::uint32 i0 = static_cast<Core::uint32>(z * rowStride + x);
|
||||
const Core::uint32 i1 = i0 + 1;
|
||||
const Core::uint32 i2 = i0 + static_cast<Core::uint32>(rowStride);
|
||||
const Core::uint32 i3 = i2 + 1;
|
||||
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i3);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i3);
|
||||
}
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
MeshBuffers CreateUvSphereMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
constexpr int kLongitudeSegments = 24;
|
||||
constexpr int kLatitudeSegments = 16;
|
||||
constexpr float kRadius = 0.5f;
|
||||
|
||||
for (int latitude = 0; latitude <= kLatitudeSegments; ++latitude) {
|
||||
const float v = static_cast<float>(latitude) / static_cast<float>(kLatitudeSegments);
|
||||
const float theta = v * kPi;
|
||||
const float sinTheta = std::sin(theta);
|
||||
const float cosTheta = std::cos(theta);
|
||||
|
||||
for (int longitude = 0; longitude <= kLongitudeSegments; ++longitude) {
|
||||
const float u = static_cast<float>(longitude) / static_cast<float>(kLongitudeSegments);
|
||||
const float phi = u * (2.0f * kPi);
|
||||
const float sinPhi = std::sin(phi);
|
||||
const float cosPhi = std::cos(phi);
|
||||
|
||||
const Math::Vector3 normal(cosPhi * sinTheta, cosTheta, sinPhi * sinTheta);
|
||||
Math::Vector3 tangent(-sinPhi, 0.0f, cosPhi);
|
||||
if (tangent.SqrMagnitude() <= 0.000001f) {
|
||||
tangent = Math::Vector3::Right();
|
||||
}
|
||||
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
normal * kRadius,
|
||||
normal,
|
||||
tangent,
|
||||
Math::Vector2(u, 1.0f - v)));
|
||||
}
|
||||
}
|
||||
|
||||
const int stride = kLongitudeSegments + 1;
|
||||
for (int latitude = 0; latitude < kLatitudeSegments; ++latitude) {
|
||||
for (int longitude = 0; longitude < kLongitudeSegments; ++longitude) {
|
||||
const Core::uint32 i0 = static_cast<Core::uint32>(latitude * stride + longitude);
|
||||
const Core::uint32 i1 = i0 + 1;
|
||||
const Core::uint32 i2 = i0 + static_cast<Core::uint32>(stride);
|
||||
const Core::uint32 i3 = i2 + 1;
|
||||
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i3);
|
||||
}
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
MeshBuffers CreateCylinderMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
constexpr int kRadialSegments = 24;
|
||||
constexpr float kRadius = 0.5f;
|
||||
constexpr float kHalfHeight = 1.0f;
|
||||
|
||||
for (int ring = 0; ring <= 1; ++ring) {
|
||||
const float y = ring == 0 ? -kHalfHeight : kHalfHeight;
|
||||
const float v = static_cast<float>(ring);
|
||||
for (int segment = 0; segment <= kRadialSegments; ++segment) {
|
||||
const float u = static_cast<float>(segment) / static_cast<float>(kRadialSegments);
|
||||
const float angle = u * (2.0f * kPi);
|
||||
const float cosAngle = std::cos(angle);
|
||||
const float sinAngle = std::sin(angle);
|
||||
const Math::Vector3 normal(cosAngle, 0.0f, sinAngle);
|
||||
const Math::Vector3 tangent(-sinAngle, 0.0f, cosAngle);
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
Math::Vector3(cosAngle * kRadius, y, sinAngle * kRadius),
|
||||
normal,
|
||||
tangent,
|
||||
Math::Vector2(u, v)));
|
||||
}
|
||||
}
|
||||
|
||||
const int sideStride = kRadialSegments + 1;
|
||||
for (int segment = 0; segment < kRadialSegments; ++segment) {
|
||||
const Core::uint32 i0 = static_cast<Core::uint32>(segment);
|
||||
const Core::uint32 i1 = i0 + 1;
|
||||
const Core::uint32 i2 = i0 + static_cast<Core::uint32>(sideStride);
|
||||
const Core::uint32 i3 = i2 + 1;
|
||||
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i3);
|
||||
}
|
||||
|
||||
const auto appendCap = [&](bool topCap) {
|
||||
const float y = topCap ? kHalfHeight : -kHalfHeight;
|
||||
const Math::Vector3 normal = topCap ? Math::Vector3::Up() : Math::Vector3::Down();
|
||||
const Core::uint32 centerIndex = static_cast<Core::uint32>(buffers.vertices.size());
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
Math::Vector3(0.0f, y, 0.0f),
|
||||
normal,
|
||||
Math::Vector3::Right(),
|
||||
Math::Vector2(0.5f, 0.5f)));
|
||||
|
||||
for (int segment = 0; segment <= kRadialSegments; ++segment) {
|
||||
const float u = static_cast<float>(segment) / static_cast<float>(kRadialSegments);
|
||||
const float angle = u * (2.0f * kPi);
|
||||
const float cosAngle = std::cos(angle);
|
||||
const float sinAngle = std::sin(angle);
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
Math::Vector3(cosAngle * kRadius, y, sinAngle * kRadius),
|
||||
normal,
|
||||
Math::Vector3::Right(),
|
||||
Math::Vector2(cosAngle * 0.5f + 0.5f, sinAngle * 0.5f + 0.5f)));
|
||||
}
|
||||
|
||||
for (int segment = 0; segment < kRadialSegments; ++segment) {
|
||||
const Core::uint32 rim0 = centerIndex + 1 + static_cast<Core::uint32>(segment);
|
||||
const Core::uint32 rim1 = rim0 + 1;
|
||||
if (topCap) {
|
||||
buffers.indices.push_back(centerIndex);
|
||||
buffers.indices.push_back(rim0);
|
||||
buffers.indices.push_back(rim1);
|
||||
} else {
|
||||
buffers.indices.push_back(centerIndex);
|
||||
buffers.indices.push_back(rim1);
|
||||
buffers.indices.push_back(rim0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
appendCap(true);
|
||||
appendCap(false);
|
||||
return buffers;
|
||||
}
|
||||
|
||||
MeshBuffers CreateCapsuleMeshBuffers() {
|
||||
MeshBuffers buffers;
|
||||
constexpr int kRadialSegments = 24;
|
||||
constexpr int kHemisphereSegments = 8;
|
||||
constexpr float kRadius = 0.5f;
|
||||
constexpr float kHalfCylinderHeight = 0.5f;
|
||||
|
||||
struct RingDefinition {
|
||||
float y = 0.0f;
|
||||
float radius = 0.0f;
|
||||
Math::Vector3 normalBase = Math::Vector3::Zero();
|
||||
};
|
||||
|
||||
std::vector<RingDefinition> rings;
|
||||
rings.reserve(static_cast<size_t>(kHemisphereSegments * 2 + 2));
|
||||
|
||||
for (int step = 0; step <= kHemisphereSegments; ++step) {
|
||||
const float t = static_cast<float>(step) / static_cast<float>(kHemisphereSegments);
|
||||
const float angle = -0.5f * kPi + t * (0.5f * kPi);
|
||||
const float ringRadius = std::cos(angle) * kRadius;
|
||||
const float y = std::sin(angle) * kRadius - kHalfCylinderHeight;
|
||||
const Math::Vector3 normalBase(0.0f, std::sin(angle), 0.0f);
|
||||
rings.push_back({ y, ringRadius, normalBase });
|
||||
}
|
||||
|
||||
for (int step = 0; step <= kHemisphereSegments; ++step) {
|
||||
const float t = static_cast<float>(step) / static_cast<float>(kHemisphereSegments);
|
||||
const float angle = t * (0.5f * kPi);
|
||||
const float ringRadius = std::cos(angle) * kRadius;
|
||||
const float y = std::sin(angle) * kRadius + kHalfCylinderHeight;
|
||||
const Math::Vector3 normalBase(0.0f, std::sin(angle), 0.0f);
|
||||
rings.push_back({ y, ringRadius, normalBase });
|
||||
}
|
||||
|
||||
for (size_t ringIndex = 0; ringIndex < rings.size(); ++ringIndex) {
|
||||
const float v = rings.size() > 1
|
||||
? static_cast<float>(ringIndex) / static_cast<float>(rings.size() - 1)
|
||||
: 0.0f;
|
||||
for (int segment = 0; segment <= kRadialSegments; ++segment) {
|
||||
const float u = static_cast<float>(segment) / static_cast<float>(kRadialSegments);
|
||||
const float angle = u * (2.0f * kPi);
|
||||
const float cosAngle = std::cos(angle);
|
||||
const float sinAngle = std::sin(angle);
|
||||
const Math::Vector3 radial(cosAngle, 0.0f, sinAngle);
|
||||
|
||||
Math::Vector3 normal(
|
||||
radial.x * rings[ringIndex].radius,
|
||||
rings[ringIndex].normalBase.y * kRadius,
|
||||
radial.z * rings[ringIndex].radius);
|
||||
normal = normal.Normalized();
|
||||
if (normal.SqrMagnitude() <= 0.000001f) {
|
||||
normal = rings[ringIndex].y >= 0.0f ? Math::Vector3::Up() : Math::Vector3::Down();
|
||||
}
|
||||
|
||||
const Math::Vector3 tangent(-sinAngle, 0.0f, cosAngle);
|
||||
buffers.vertices.push_back(MakeVertex(
|
||||
Math::Vector3(radial.x * rings[ringIndex].radius, rings[ringIndex].y, radial.z * rings[ringIndex].radius),
|
||||
normal,
|
||||
tangent,
|
||||
Math::Vector2(u, 1.0f - v)));
|
||||
}
|
||||
}
|
||||
|
||||
const int stride = kRadialSegments + 1;
|
||||
for (size_t ringIndex = 0; ringIndex + 1 < rings.size(); ++ringIndex) {
|
||||
for (int segment = 0; segment < kRadialSegments; ++segment) {
|
||||
const Core::uint32 i0 = static_cast<Core::uint32>(ringIndex * stride + static_cast<size_t>(segment));
|
||||
const Core::uint32 i1 = i0 + 1;
|
||||
const Core::uint32 i2 = i0 + static_cast<Core::uint32>(stride);
|
||||
const Core::uint32 i3 = i2 + 1;
|
||||
|
||||
buffers.indices.push_back(i0);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i1);
|
||||
buffers.indices.push_back(i2);
|
||||
buffers.indices.push_back(i3);
|
||||
}
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
Mesh* BuildMeshResource(
|
||||
const Containers::String& path,
|
||||
const char* displayName,
|
||||
MeshBuffers&& buffers) {
|
||||
if (buffers.vertices.empty() || buffers.indices.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* mesh = new Mesh();
|
||||
IResource::ConstructParams params;
|
||||
params.name = Containers::String(displayName);
|
||||
params.path = path;
|
||||
params.guid = ResourceGUID::Generate(path);
|
||||
params.memorySize = 0;
|
||||
mesh->Initialize(params);
|
||||
|
||||
mesh->SetVertexData(
|
||||
buffers.vertices.data(),
|
||||
buffers.vertices.size() * sizeof(StaticMeshVertex),
|
||||
static_cast<Core::uint32>(buffers.vertices.size()),
|
||||
sizeof(StaticMeshVertex),
|
||||
VertexAttribute::Position |
|
||||
VertexAttribute::Normal |
|
||||
VertexAttribute::Tangent |
|
||||
VertexAttribute::Bitangent |
|
||||
VertexAttribute::UV0);
|
||||
|
||||
if (buffers.vertices.size() > 65535u) {
|
||||
mesh->SetIndexData(
|
||||
buffers.indices.data(),
|
||||
buffers.indices.size() * sizeof(Core::uint32),
|
||||
static_cast<Core::uint32>(buffers.indices.size()),
|
||||
true);
|
||||
} else {
|
||||
std::vector<Core::uint16> compactIndices;
|
||||
compactIndices.reserve(buffers.indices.size());
|
||||
for (Core::uint32 index : buffers.indices) {
|
||||
compactIndices.push_back(static_cast<Core::uint16>(index));
|
||||
}
|
||||
|
||||
mesh->SetIndexData(
|
||||
compactIndices.data(),
|
||||
compactIndices.size() * sizeof(Core::uint16),
|
||||
static_cast<Core::uint32>(compactIndices.size()),
|
||||
false);
|
||||
}
|
||||
|
||||
const Math::Bounds bounds = ComputeBounds(buffers.vertices);
|
||||
mesh->SetBounds(bounds);
|
||||
|
||||
MeshSection section = {};
|
||||
section.baseVertex = 0;
|
||||
section.vertexCount = static_cast<Core::uint32>(buffers.vertices.size());
|
||||
section.startIndex = 0;
|
||||
section.indexCount = static_cast<Core::uint32>(buffers.indices.size());
|
||||
section.materialID = 0;
|
||||
section.bounds = bounds;
|
||||
mesh->AddSection(section);
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) {
|
||||
auto* material = new Material();
|
||||
IResource::ConstructParams params;
|
||||
params.name = Containers::String("Default Primitive Material");
|
||||
params.path = path;
|
||||
params.guid = ResourceGUID::Generate(path);
|
||||
params.memorySize = 0;
|
||||
material->Initialize(params);
|
||||
|
||||
MaterialRenderState renderState = {};
|
||||
renderState.cullMode = MaterialCullMode::Back;
|
||||
material->SetRenderState(renderState);
|
||||
material->SetRenderQueue(MaterialRenderQueue::Geometry);
|
||||
material->SetTexture(
|
||||
Containers::String("baseColorTexture"),
|
||||
ResourceManager::Get().Load<Texture>(GetBuiltinDefaultPrimitiveTexturePath()));
|
||||
material->RecalculateMemorySize();
|
||||
return material;
|
||||
}
|
||||
|
||||
Texture* BuildDefaultPrimitiveTexture(const Containers::String& path) {
|
||||
static const unsigned char kTexturePixels[4] = { 214, 214, 214, 255 };
|
||||
|
||||
auto* texture = new Texture();
|
||||
IResource::ConstructParams params;
|
||||
params.name = Containers::String("Default Primitive Albedo");
|
||||
params.path = path;
|
||||
params.guid = ResourceGUID::Generate(path);
|
||||
params.memorySize = 0;
|
||||
texture->Initialize(params);
|
||||
|
||||
if (!texture->Create(
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TextureType::Texture2D,
|
||||
TextureFormat::RGBA8_UNORM,
|
||||
kTexturePixels,
|
||||
sizeof(kTexturePixels))) {
|
||||
delete texture;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsBuiltinResourcePath(const Containers::String& path) {
|
||||
return path.StartsWith(kBuiltinPrefix);
|
||||
}
|
||||
|
||||
bool IsBuiltinMeshPath(const Containers::String& path) {
|
||||
return path.StartsWith(kBuiltinMeshPrefix);
|
||||
}
|
||||
|
||||
bool IsBuiltinMaterialPath(const Containers::String& path) {
|
||||
return path.StartsWith(kBuiltinMaterialPrefix);
|
||||
}
|
||||
|
||||
bool IsBuiltinTexturePath(const Containers::String& path) {
|
||||
return path.StartsWith(kBuiltinTexturePrefix);
|
||||
}
|
||||
|
||||
const char* GetBuiltinPrimitiveDisplayName(BuiltinPrimitiveType primitiveType) {
|
||||
switch (primitiveType) {
|
||||
case BuiltinPrimitiveType::Cube: return "Cube";
|
||||
case BuiltinPrimitiveType::Sphere: return "Sphere";
|
||||
case BuiltinPrimitiveType::Capsule: return "Capsule";
|
||||
case BuiltinPrimitiveType::Cylinder: return "Cylinder";
|
||||
case BuiltinPrimitiveType::Plane: return "Plane";
|
||||
case BuiltinPrimitiveType::Quad: return "Quad";
|
||||
default: return "Primitive";
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType primitiveType) {
|
||||
switch (primitiveType) {
|
||||
case BuiltinPrimitiveType::Cube: return Containers::String("builtin://meshes/cube");
|
||||
case BuiltinPrimitiveType::Sphere: return Containers::String("builtin://meshes/sphere");
|
||||
case BuiltinPrimitiveType::Capsule: return Containers::String("builtin://meshes/capsule");
|
||||
case BuiltinPrimitiveType::Cylinder: return Containers::String("builtin://meshes/cylinder");
|
||||
case BuiltinPrimitiveType::Plane: return Containers::String("builtin://meshes/plane");
|
||||
case BuiltinPrimitiveType::Quad: return Containers::String("builtin://meshes/quad");
|
||||
default: return Containers::String();
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String GetBuiltinDefaultPrimitiveMaterialPath() {
|
||||
return Containers::String(kBuiltinDefaultPrimitiveMaterialPath);
|
||||
}
|
||||
|
||||
Containers::String GetBuiltinDefaultPrimitiveTexturePath() {
|
||||
return Containers::String(kBuiltinDefaultPrimitiveTexturePath);
|
||||
}
|
||||
|
||||
bool TryParseBuiltinPrimitiveType(const Containers::String& path, BuiltinPrimitiveType& outPrimitiveType) {
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cube)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Cube;
|
||||
return true;
|
||||
}
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Sphere)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Sphere;
|
||||
return true;
|
||||
}
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Capsule)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Capsule;
|
||||
return true;
|
||||
}
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Cylinder)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Cylinder;
|
||||
return true;
|
||||
}
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Plane)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Plane;
|
||||
return true;
|
||||
}
|
||||
if (path == GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType::Quad)) {
|
||||
outPrimitiveType = BuiltinPrimitiveType::Quad;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadResult CreateBuiltinMeshResource(const Containers::String& path) {
|
||||
BuiltinPrimitiveType primitiveType = BuiltinPrimitiveType::Cube;
|
||||
if (!TryParseBuiltinPrimitiveType(path, primitiveType)) {
|
||||
return LoadResult(Containers::String("Unknown builtin mesh: ") + path);
|
||||
}
|
||||
|
||||
MeshBuffers buffers;
|
||||
switch (primitiveType) {
|
||||
case BuiltinPrimitiveType::Cube:
|
||||
buffers = CreateCubeMeshBuffers();
|
||||
break;
|
||||
case BuiltinPrimitiveType::Sphere:
|
||||
buffers = CreateUvSphereMeshBuffers();
|
||||
break;
|
||||
case BuiltinPrimitiveType::Capsule:
|
||||
buffers = CreateCapsuleMeshBuffers();
|
||||
break;
|
||||
case BuiltinPrimitiveType::Cylinder:
|
||||
buffers = CreateCylinderMeshBuffers();
|
||||
break;
|
||||
case BuiltinPrimitiveType::Plane:
|
||||
buffers = CreatePlaneMeshBuffers();
|
||||
break;
|
||||
case BuiltinPrimitiveType::Quad:
|
||||
buffers = CreateQuadMeshBuffers();
|
||||
break;
|
||||
default:
|
||||
return LoadResult(Containers::String("Unsupported builtin mesh: ") + path);
|
||||
}
|
||||
|
||||
FlipTriangleWinding(buffers);
|
||||
|
||||
Mesh* mesh = BuildMeshResource(path, GetBuiltinPrimitiveDisplayName(primitiveType), std::move(buffers));
|
||||
if (mesh == nullptr) {
|
||||
return LoadResult(Containers::String("Failed to create builtin mesh: ") + path);
|
||||
}
|
||||
|
||||
return LoadResult(mesh);
|
||||
}
|
||||
|
||||
LoadResult CreateBuiltinMaterialResource(const Containers::String& path) {
|
||||
if (path != GetBuiltinDefaultPrimitiveMaterialPath()) {
|
||||
return LoadResult(Containers::String("Unknown builtin material: ") + path);
|
||||
}
|
||||
|
||||
Material* material = BuildDefaultPrimitiveMaterial(path);
|
||||
if (material == nullptr) {
|
||||
return LoadResult(Containers::String("Failed to create builtin material: ") + path);
|
||||
}
|
||||
|
||||
return LoadResult(material);
|
||||
}
|
||||
|
||||
LoadResult CreateBuiltinTextureResource(const Containers::String& path) {
|
||||
if (path != GetBuiltinDefaultPrimitiveTexturePath()) {
|
||||
return LoadResult(Containers::String("Unknown builtin texture: ") + path);
|
||||
}
|
||||
|
||||
Texture* texture = BuildDefaultPrimitiveTexture(path);
|
||||
if (texture == nullptr) {
|
||||
return LoadResult(Containers::String("Failed to create builtin texture: ") + path);
|
||||
}
|
||||
|
||||
return LoadResult(texture);
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user