12#include "spdlog/spdlog.h"
25 std::shared_ptr<Mesh> createMesh(
const std::shared_ptr<GraphicsDevice>& device,
26 const std::vector<float>& vertices,
const std::vector<uint32_t>& indices)
32 auto mesh = std::make_shared<Mesh>();
34 std::vector<uint8_t> vertexBytes(vertices.size() *
sizeof(
float));
35 std::memcpy(vertexBytes.data(), vertices.data(), vertexBytes.size());
37 vbOptions.
data = std::move(vertexBytes);
38 auto vertexFormat = std::make_shared<VertexFormat>(14 *
static_cast<int>(
sizeof(
float)),
true,
false);
39 auto vertexBuffer = device->createVertexBuffer(vertexFormat,
static_cast<int>(vertices.size() / 14), vbOptions);
41 std::vector<uint8_t> indexBytes(indices.size() *
sizeof(uint32_t));
42 std::memcpy(indexBytes.data(), indices.data(), indexBytes.size());
43 auto indexBuffer = device->createIndexBuffer(
INDEXFORMAT_UINT32,
static_cast<int>(indices.size()), indexBytes);
48 primitive.baseVertex = 0;
49 primitive.count =
static_cast<int>(indices.size());
50 primitive.indexed =
true;
52 mesh->setVertexBuffer(vertexBuffer);
53 mesh->setIndexBuffer(indexBuffer, 0);
54 mesh->setPrimitive(primitive, 0);
58 bounds.setHalfExtents(1.0f, 1.0f, 1.0f);
59 mesh->setAabb(bounds);
64 std::vector<float> appendVertex(
float x,
float y,
float z)
71 1.0f, 0.0f, 0.0f, 1.0f,
76 std::shared_ptr<Mesh> createSkyBoxMesh(
const std::shared_ptr<GraphicsDevice>& device,
const float yOffset)
78 const float he = 1.0f;
79 const float minY = -he + yOffset;
80 const float maxY = he + yOffset;
89 const Vec3f corners[8] = {
100 const int faceAxes[6][3] = {
109 const float faceNormals[6][3] = {
111 { 0.0f, 0.0f, -1.0f},
113 { 0.0f, -1.0f, 0.0f},
118 std::vector<float> vertices;
119 std::vector<uint32_t> indices;
120 vertices.reserve(6u * 4u * 14u);
121 indices.reserve(6u * 6u);
123 uint32_t vcounter = 0;
124 constexpr int uSegments = 1;
125 constexpr int vSegments = 1;
127 for (
int side = 0; side < 6; ++side) {
128 for (
int i = 0; i <= uSegments; ++i) {
129 for (
int j = 0; j <= vSegments; ++j) {
130 const float u =
static_cast<float>(i) /
static_cast<float>(uSegments);
131 const float v =
static_cast<float>(j) /
static_cast<float>(vSegments);
133 const Vec3f c0 = corners[faceAxes[side][0]];
134 const Vec3f c1 = corners[faceAxes[side][1]];
135 const Vec3f c2 = corners[faceAxes[side][2]];
137 const Vec3f temp1 = {
138 c0.x + (c1.x - c0.x) * u,
139 c0.y + (c1.y - c0.y) * u,
140 c0.z + (c1.z - c0.z) * u
142 const Vec3f temp2 = {
143 c0.x + (c2.x - c0.x) * v,
144 c0.y + (c2.y - c0.y) * v,
145 c0.z + (c2.z - c0.z) * v
148 temp1.x + (temp2.x - c0.x),
149 temp1.y + (temp2.y - c0.y),
150 temp1.z + (temp2.z - c0.z)
153 vertices.insert(vertices.end(), {
155 faceNormals[side][0], faceNormals[side][1], faceNormals[side][2],
157 1.0f, 0.0f, 0.0f, 1.0f,
161 if (i < uSegments && j < vSegments) {
162 indices.push_back(vcounter +
static_cast<uint32_t
>(vSegments + 1));
163 indices.push_back(vcounter + 1u);
164 indices.push_back(vcounter);
165 indices.push_back(vcounter +
static_cast<uint32_t
>(vSegments + 1));
166 indices.push_back(vcounter +
static_cast<uint32_t
>(vSegments + 2));
167 indices.push_back(vcounter + 1u);
174 return createMesh(device, vertices, indices);
183 if (!device || !_scene || !node) {
184 spdlog::warn(
"SkyMesh: invalid args (device={}, scene={}, node={})", (
void*)device.get(), (
void*)_scene, (
void*)node);
188 _mesh = createMeshByType(device, type);
190 spdlog::warn(
"SkyMesh: createMeshByType returned null for type={}", type);
194 auto material = std::make_shared<Material>();
195 material->setName(
"SkyMaterial");
196 material->setIsSkybox(
true);
202 auto skyDepthState = std::make_shared<DepthState>();
203 skyDepthState->setDepthWrite(
false);
204 material->setDepthState(skyDepthState);
205 _material = material;
207 _meshInstance = std::make_unique<MeshInstance>(_mesh.get(), _material.get(), node);
209 auto layers = _scene->layers();
211 spdlog::warn(
"SkyMesh: scene has no layers — cannot add sky mesh instance to skybox layer");
217 skyLayer->addMeshInstances({_meshInstance.get()});
219 spdlog::warn(
"SkyMesh: skybox layer (LAYERID_SKYBOX={}) not found",
LAYERID_SKYBOX);
225 if (_scene && _meshInstance) {
226 auto layers = _scene->layers();
230 skyLayer->removeMeshInstances({_meshInstance.get()});
236 std::shared_ptr<Mesh> SkyMesh::createInfiniteMesh(
const std::shared_ptr<GraphicsDevice>& device)
const
238 return createSkyBoxMesh(device, 0.0f);
241 std::shared_ptr<Mesh> SkyMesh::createBoxMesh(
const std::shared_ptr<GraphicsDevice>& device)
const
244 return createSkyBoxMesh(device, 0.5f);
247 std::shared_ptr<Mesh> SkyMesh::createDomeMesh(
const std::shared_ptr<GraphicsDevice>& device)
const
251 constexpr int latitudeBands = 50;
252 constexpr int longitudeBands = 50;
253 constexpr float pi = 3.14159265358979323846f;
254 constexpr float radius = 0.5f;
255 constexpr float bottomLimit = 0.1f;
256 constexpr float curvatureRadius = 0.95f;
257 constexpr float curvatureRadiusSq = curvatureRadius * curvatureRadius;
260 std::vector<float> positions;
261 positions.reserve((latitudeBands + 1) * (longitudeBands + 1) * 3);
263 for (
int lat = 0; lat <= latitudeBands; ++lat) {
264 const float theta =
static_cast<float>(lat) * pi /
static_cast<float>(latitudeBands);
265 const float sinTheta = std::sin(theta);
266 const float cosTheta = std::cos(theta);
268 for (
int lon = 0; lon <= longitudeBands; ++lon) {
270 const float phi =
static_cast<float>(lon) * 2.0f * pi /
static_cast<float>(longitudeBands) - pi * 0.5f;
271 const float sinPhi = std::sin(phi);
272 const float cosPhi = std::cos(phi);
274 const float x = cosPhi * sinTheta;
275 const float y = cosTheta;
276 const float z = sinPhi * sinTheta;
278 positions.push_back(x * radius);
279 positions.push_back(y * radius);
280 positions.push_back(z * radius);
285 for (
size_t i = 0; i < positions.size(); i += 3) {
286 const float x = positions[i] / radius;
287 float y = positions[i + 1] / radius;
288 const float z = positions[i + 2] / radius;
295 if (x * x + z * z < curvatureRadiusSq) {
302 positions[i + 1] = y * radius;
306 std::vector<float> vertices;
307 vertices.reserve((latitudeBands + 1) * (longitudeBands + 1) * 14);
309 for (
size_t i = 0; i < positions.size(); i += 3) {
310 const auto packed = appendVertex(positions[i], positions[i + 1], positions[i + 2]);
311 vertices.insert(vertices.end(), packed.begin(), packed.end());
315 std::vector<uint32_t> indices;
316 indices.reserve(latitudeBands * longitudeBands * 6);
318 for (
int lat = 0; lat < latitudeBands; ++lat) {
319 for (
int lon = 0; lon < longitudeBands; ++lon) {
320 const uint32_t first =
static_cast<uint32_t
>(lat * (longitudeBands + 1) + lon);
321 const uint32_t second = first +
static_cast<uint32_t
>(longitudeBands + 1);
324 indices.push_back(first + 1);
325 indices.push_back(second);
326 indices.push_back(first);
327 indices.push_back(first + 1);
328 indices.push_back(second + 1);
329 indices.push_back(second);
333 return createMesh(device, vertices, indices);
337 const int latBands,
const int lonBands)
339 constexpr float pi = 3.14159265358979323846f;
345 constexpr float radius = 500000.0f;
347 std::vector<float> vertices;
348 vertices.reserve((latBands + 1) * (lonBands + 1) * 14);
350 for (
int lat = 0; lat <= latBands; ++lat) {
351 const float theta =
static_cast<float>(lat) * pi /
static_cast<float>(latBands);
352 const float sinTheta = std::sin(theta);
353 const float cosTheta = std::cos(theta);
355 for (
int lon = 0; lon <= lonBands; ++lon) {
356 const float phi =
static_cast<float>(lon) * 2.0f * pi /
static_cast<float>(lonBands);
357 const float sinPhi = std::sin(phi);
358 const float cosPhi = std::cos(phi);
360 const float x = cosPhi * sinTheta * radius;
361 const float y = cosTheta * radius;
362 const float z = sinPhi * sinTheta * radius;
364 const auto packed = appendVertex(x, y, z);
365 vertices.insert(vertices.end(), packed.begin(), packed.end());
369 std::vector<uint32_t> indices;
370 indices.reserve(latBands * lonBands * 6);
372 for (
int lat = 0; lat < latBands; ++lat) {
373 for (
int lon = 0; lon < lonBands; ++lon) {
374 const uint32_t first =
static_cast<uint32_t
>(lat * (lonBands + 1) + lon);
375 const uint32_t second = first +
static_cast<uint32_t
>(lonBands + 1);
377 indices.push_back(first + 1);
378 indices.push_back(second);
379 indices.push_back(first);
380 indices.push_back(first + 1);
381 indices.push_back(second + 1);
382 indices.push_back(second);
386 return createMesh(device, vertices, indices);
389 std::shared_ptr<Mesh> SkyMesh::createMeshByType(
const std::shared_ptr<GraphicsDevice>& device,
const int type)
const
393 return createBoxMesh(device);
395 return createDomeMesh(device);
400 return createInfiniteMesh(device);
Axis-Aligned Bounding Box defined by center and half-extents.
void setCenter(const Vector3 ¢er)
Hierarchical scene graph node with local/world transforms and parent-child relationships.
Container for the scene graph, lighting environment, fog, skybox, and layer composition.
static std::shared_ptr< Mesh > createSphereMesh(const std::shared_ptr< GraphicsDevice > &device, int latBands=64, int lonBands=64)
Create a full UV sphere mesh (no flattening). Used for atmosphere sky.
SkyMesh(const std::shared_ptr< GraphicsDevice > &device, Scene *scene, GraphNode *node, Texture *texture, int type)
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
constexpr int LAYERID_SKYBOX
Describes how vertex and index data should be interpreted for a draw call.
std::vector< uint8_t > data