feat: support alternative vcm models syntax
This commit is contained in:
parent
8f342fe429
commit
4333d9ab06
3
res/devtools/syntax/vcm.toml
Normal file
3
res/devtools/syntax/vcm.toml
Normal file
@ -0,0 +1,3 @@
|
||||
language = "VCM"
|
||||
extensions = ["vcm"]
|
||||
line-comment-start = "#"
|
||||
4
res/models/stairs.vcm
Normal file
4
res/models/stairs.vcm
Normal 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"
|
||||
@ -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>
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
24
test/coders/xml.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user