Files
EVEngine/src/modules/procgen/ShapeGrammar.h
T

84 lines
3.2 KiB
C++

#pragma once
#include "procgen/PointSet.h"
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
namespace eve::procgen {
/** @brief One weighted asset variant represented by a shape-grammar symbol. */
struct ShapeModuleVariant {
std::string asset;
float length = 1.f;
float weight = 1.f;
};
/**
* @brief Module-based shape grammar expanded continuously along a 3D polyline.
*
* Grammar syntax follows UE PCG's useful subset: comma-separated symbols,
* bracket groups, `*` (zero or more), `+` (one or more), and an integer repeat
* suffix. Module symbols do not contain decimal digits, which are reserved for
* exact repetition. Repetitions fill available spline length without crossing its end.
*/
class ShapeGrammar {
public:
/** @brief Remove every registered symbol and diagnostic. */
void clear();
/** @brief Register a weighted asset variant for a symbol. */
bool addModule(const std::string& symbol, const std::string& asset, float length,
float weight = 1.f);
bool removeModule(const std::string& symbol);
bool hasModule(const std::string& symbol) const;
int getModuleCount() const;
std::string getModuleSymbol(int index) const;
int getVariantCount(const std::string& symbol) const;
std::string getVariantAsset(const std::string& symbol, int index) const;
float getVariantLength(const std::string& symbol, int index) const;
/** @brief Parse and validate grammar without generating output. */
bool validate(const std::string& grammar);
/**
* @brief Expand grammar along control points.
* @param acceptIncomplete Keep a final module only when it fully fits; when false,
* generation fails if mandatory grammar cannot fit the spline.
*/
PointSet* generate(const std::string& grammar, PointSet* controlPoints, uint32_t seed,
bool acceptIncomplete);
std::string getError() const;
std::string debugReport() const;
private:
struct Element {
std::string symbol;
std::vector<Element> children;
int repeatMin = 1;
int repeatMax = 1; // -1 = fill available length
};
struct Parser {
const std::string& text;
size_t position = 0;
std::string error;
std::vector<Element> sequence(char terminator = '\0');
void skipWhitespace();
};
float elementMinLength(const Element& element) const;
float sequenceMinLength(const std::vector<Element>& sequence, size_t from = 0) const;
bool expandSequence(const std::vector<Element>& sequence, float available,
std::vector<std::string>& symbols, float& used) const;
const ShapeModuleVariant* chooseVariant(const std::string& symbol, uint32_t seed) const;
std::unordered_map<std::string, std::vector<ShapeModuleVariant>> modules_;
std::vector<std::string> moduleOrder_;
std::string error_;
int lastSymbolCount_ = 0;
float lastUsedLength_ = 0.f;
float lastSplineLength_ = 0.f;
};
} // namespace eve::procgen