feat: support alternative vcm models syntax

This commit is contained in:
MihailRis 2025-06-01 23:08:59 +03:00
parent 8f342fe429
commit 4333d9ab06
10 changed files with 155 additions and 28 deletions

View File

@ -0,0 +1,3 @@
language = "VCM"
extensions = ["vcm"]
line-comment-start = "#"

4
res/models/stairs.vcm Normal file
View File

@ -0,0 +1,4 @@
@box from (0,0,0) to (1,0.5,1) delete (top,south)
@box from (0,0.5,0) to (1,1,0.5) delete (bottom,south)
@rect from (0,0.5,0.5) right (1,0,0) up (0,0,0.5) texture "$2"
@rect from (0,0,0) right (1,0,0) up (0,1,0) texture "$1"

View File

@ -1,6 +0,0 @@
<model>
<box from="0,0,0" to="1,0.5,1" delete="top,south"/>
<box from="0,0.5,0" to="1,1,0.5" delete="bottom,south"/>
<rect from="0,0.5,0.5" right="1,0,0" up="0,0,0.5" texture="$2"/>
<rect from="0,0,0" right="1,0,0" up="0,1,0" texture="$1"/>
</model>

View File

@ -55,7 +55,7 @@ local function load_models_list(packs)
local registry = export.registry
for _, filename in ipairs(file.list("models")) do
local ext = file.ext(filename)
if ext == "xml" then
if ext == "xml" or ext == "vcm" then
registry[filename] = {type="model", unit=file.stem(filename)}
table.insert(export.filenames, filename)
end

View File

@ -352,21 +352,35 @@ assetload::postfunc assetload::model(
throw;
}
}
path = paths.find(file + ".xml");
if (io::exists(path)) {
auto text = io::read_string(path);
try {
auto model = vcm::parse(path.string(), text).release();
return [=](Assets* assets) {
request_textures(loader, *model);
assets->store(std::unique_ptr<model::Model>(model), name);
};
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
std::array<std::string, 2> extensions {
".xml",
".vcm"
};
path = "";
for (const auto& ext : extensions) {
auto newPath = paths.find(file + ext);
if (io::exists(newPath)) {
path = std::move(newPath);
break;
}
}
throw std::runtime_error("could not to find model " + util::quote(file));
if (path.empty()) {
throw std::runtime_error("could not to find model " + util::quote(file));
}
auto text = io::read_string(path);
try {
auto model = vcm::parse(path.string(), text).release();
return [=](Assets* assets) {
request_textures(loader, *model);
assets->store(std::unique_ptr<model::Model>(model), name);
};
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
}
}
static void read_anim_file(

View File

@ -5,6 +5,7 @@
#include "xml.hpp"
#include "util/stringutil.hpp"
#include "graphics/commons/Model.hpp"
#include "io/io.hpp"
using namespace vcm;
using namespace xml;
@ -143,12 +144,18 @@ static std::unique_ptr<model::Model> load_model(const xmlelement& root) {
std::unique_ptr<model::Model> vcm::parse(
std::string_view file, std::string_view src
) {
auto doc = xml::parse(file, src);
const auto& root = *doc->getRoot();
if (root.getTag() != "model") {
throw std::runtime_error(
"'model' tag expected as root, got '" + root.getTag() + "'"
);
try {
auto doc = io::path(std::string(file)).extension() == ".xml"
? xml::parse(file, src) : xml::parse_vcm(file, src, "model");
const auto& root = *doc->getRoot();
if (root.getTag() != "model") {
throw std::runtime_error(
"'model' tag expected as root, got '" + root.getTag() + "'"
);
}
std::cout << xml::stringify(*doc) << std::endl;
return load_model(root);
} catch (const parsing_error& err) {
throw std::runtime_error(err.errorLog());
}
return load_model(root);
}

View File

@ -355,6 +355,83 @@ std::unique_ptr<Document> xml::parse(
return parser.parse();
}
namespace {
class VcmParser : public BasicParser<char> {
public:
VcmParser(std::string_view filename, std::string_view source)
: BasicParser(filename, source) {
document = std::make_unique<Document>("1.0", "UTF-8");
hashComment = true;
}
std::string parseValue() {
char c = peek();
if (c == '"' || c == '\'') {
nextChar();
return parseString(c);
}
if (c == '(') {
nextChar();
// TODO: replace with array parsing after moving to dv::value's
std::string value = std::string(readUntil(')'));
expect(')');
return value;
}
return std::string(readUntilWhitespace());
}
void parseSubElements(Node& node) {
skipWhitespace();
while (hasNext()) {
if (peek() != '@') {
throw error("unexpected character in element");
}
nextChar();
auto subnodePtr = std::make_unique<Node>(parseName());
auto subnode = subnodePtr.get();
node.add(std::move(subnodePtr));
skipWhitespace();
while (hasNext() && peek() != '@' && peek() != '{' && peek() != '}') {
std::string attrname = parseName();
skipWhitespace();
std::string value = parseValue();
subnode->set(attrname, value);
skipWhitespace();
}
if (!hasNext()) {
break;
}
char c = peek();
if (c == '{') {
nextChar();
parseSubElements(*subnode);
expect('}');
skipWhitespace();
} else if (c == '}') {
break;
}
}
}
std::unique_ptr<Document> parse(const std::string& rootTag) {
auto root = std::make_unique<Node>(rootTag);
parseSubElements(*root);
document->setRoot(std::move(root));
return std::move(document);
}
private:
std::unique_ptr<Document> document;
};
}
std::unique_ptr<Document> xml::parse_vcm(
std::string_view filename, std::string_view source, std::string_view tag
) {
VcmParser parser(filename, source);
return parser.parse(std::string(tag));
}
inline void newline(
std::stringstream& ss, bool nice, const std::string& indentStr, int indent
) {

View File

@ -126,5 +126,9 @@ namespace xml {
std::string_view filename, std::string_view source
);
std::unique_ptr<Document> parse_vcm(
std::string_view filename, std::string_view source, std::string_view tag
);
using xmlelement = Node;
}

View File

@ -51,7 +51,7 @@ static int l_parse_model(lua::State* L) {
auto string = lua::require_lstring(L, 2);
auto name = lua::require_string(L, 3);
if (format == "xml") {
if (format == "xml" || format == "vcm") {
engine->getAssets()->store(vcm::parse(name, string), name);
} else {
throw std::runtime_error("unknown format " + util::quote(std::string(format)));

24
test/coders/xml.cpp Normal file
View File

@ -0,0 +1,24 @@
#include <gtest/gtest.h>
#include "coders/xml.hpp"
#include "coders/commons.hpp"
TEST(XML, VCM) {
std::string code = ""
"@line x1 y1 z1\n"
"# @tst ysfdrg\n"
"@box {\n"
" @rect texture \"$2\"\n"
" @utro a 53.1\n"
"}\n"
;
std::cout << code << std::endl;
try {
auto document = xml::parse_vcm("<test>", code, "test");
std::cout << xml::stringify(*document);
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw err;
}
}