Files
DarkflameServer/dUgcServer/UgcModel.h
Aaron Kimbrell abf1cc1d80 feat: UGC server that makes and serves player models' meshes and icons
A new server (dUgcServer, started by master with enable_ugc_server=1) that
takes unprocessed ugc and ugc_modular_build rows from the database and makes
what the client downloads with UGCUSE3DSERVICES: an optimized NIF (hidden
faces removed, ambient occlusion baked into vertex colors) and a 128px DDS
icon for player models, rendered by a software rasterizer from the client's
LDD brick primitives, and icons for cars and rockets assembled from their
modules per ModularBuildComponent/ModuleComponent. It serves them, with the
models' LXFML, over HTTP in the client's UGCC<dc>/3DOPTIMIZED and
IMAGE128DDS layout with .gz and .checksum files, and keeps its folder under
a size cap.

Processing state lives in ugc.is_optimized plus new processed_at,
process_attempts and process_error columns (and the same on
ugc_modular_build). ServiceType::UGC is appended. NifFile moves to dCommon
and records named node transforms for the modules' attach points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:57 -05:00

64 lines
2.0 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
#include <glm/glm.hpp>
#include "UgcBricks.h"
namespace NifFile {
struct Model;
}
/**
* Brick models as triangle meshes: LXFML parts, the mesh built from them (opaque and transparent bricks apart, with
* the material colors as vertex colors) and the mesh of a client .nif. Pure apart from BrickLibrary's file reads.
*/
namespace UgcModel {
// A brick in a model: its primitive, materials (one per geometry part) and where it is
struct Part {
uint32_t designId{};
std::vector<uint32_t> materials;
glm::mat4 transform{ 1.0f };
};
// The parts of an LXFML 5 (Bricks/Brick/Part with bones) or 4 (Scene/Model/Group/Part with axis angles) model.
// Empty with `error` set when it can't be read.
std::vector<Part> ParseLxfml(std::string_view lxfml, std::string& error);
struct Mesh {
std::vector<glm::vec3> positions;
std::vector<glm::vec3> normals;
std::vector<glm::vec4> colors; // sRGB, 0..1, alpha is opacity
std::vector<uint32_t> indices;
size_t TriangleCount() const { return indices.size() / 3; }
bool Empty() const { return indices.empty(); }
void Append(const Mesh& other);
void Transform(const glm::mat4& transform);
};
struct Model {
Mesh opaque;
Mesh transparent;
std::vector<uint32_t> missingDesigns; // designs without geometry in the client, skipped
size_t bricks{};
bool Empty() const { return opaque.Empty() && transparent.Empty(); }
// The bounds of every vertex; false when there are none
bool Bounds(glm::vec3& min, glm::vec3& max) const;
};
// The mesh of a model's parts
Model Build(const std::vector<Part>& parts, UgcBricks::BrickLibrary& library);
// A client .nif's meshes as one model (vertex colors times material color; transparent when blended)
Model FromNif(const NifFile::Model& nif);
// Splits a mesh into pieces the .nif format can hold (at most `maxVertices` vertices and `maxTriangles` triangles)
std::vector<Mesh> Split(const Mesh& mesh, size_t maxVertices = 65535, size_t maxTriangles = 65535);
}