| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- // zlib open source license
- //
- // Copyright (c) 2017 to 2019 David Forsgren Piuva
- //
- // This software is provided 'as-is', without any express or implied
- // warranty. In no event will the authors be held liable for any damages
- // arising from the use of this software.
- //
- // Permission is granted to anyone to use this software for any purpose,
- // including commercial applications, and to alter it and redistribute it
- // freely, subject to the following restrictions:
- //
- // 1. The origin of this software must not be misrepresented; you must not
- // claim that you wrote the original software. If you use this software
- // in a product, an acknowledgment in the product documentation would be
- // appreciated but is not required.
- //
- // 2. Altered source versions must be plainly marked as such, and must not be
- // misrepresented as being the original software.
- //
- // 3. This notice may not be removed or altered from any source
- // distribution.
- #include "../../../../api/modelAPI.h"
- #include "../Model.h" // TODO: Only use the public API
- using namespace dsr;
- struct Vertex_DMF1 {
- FVector3D position;
- FVector4D texCoord;
- FVector4D color;
- Vertex_DMF1() : position(FVector3D(0.0f)), texCoord(FVector4D(0.0f)), color(FVector4D(1.0f)) {}
- };
- struct Triangle_DMF1 {
- Vertex_DMF1 vertices[3];
- Triangle_DMF1() {}
- };
- struct Part_DMF1 {
- String textures[16];
- String shaderZero;
- int minDetailLevel = 0, maxDetailLevel = 2;
- List<Triangle_DMF1> triangles;
- String name;
- Part_DMF1() {}
- void addEmptyTriangle() {
- this->triangles.push(Triangle_DMF1());
- }
- Triangle_DMF1* getLastTriangle() {
- if (this->triangles.length() > 0) {
- return &(triangles[this->triangles.length() - 1]);
- } else {
- return nullptr;
- }
- }
- };
- struct Model_DMF1 {
- Filter filter = Filter::Solid;
- List<Part_DMF1> parts;
- Model_DMF1() {}
- Part_DMF1* getLastPart() {
- if (this->parts.length() > 0) {
- return &(parts[this->parts.length() - 1]);
- } else {
- return nullptr;
- }
- }
- void addEmptyPart() {
- this->parts.push(Part_DMF1());
- }
- };
- #define LoopForward(min, var, max) for(var = min; var <= max; var++)
- #define PARSER_NOINDEX if (index != 0) { printText("This version of the engine does not have an index for the property ", propertyName, ".\n"); }
- #define PROPERTY_MATCH(NAME) (string_caseInsensitiveMatch(propertyName, U ## #NAME))
- #define CONTENT_MATCH(NAME) (string_caseInsensitiveMatch(content, U ## #NAME))
- static const DsrChar tab = 9;
- static const DsrChar space = 32;
- static const DsrChar lineFeed = 10;
- static const DsrChar carriageReturn = 13;
- static const int ParserState_WaitForStatement = 0; // NameSpace -> WaitForStatement, Identifier -> WaitForIndexOrProperty
- static const int ParserState_WaitForIndexOrProperty = 1; // index -> WaitForProperty, Property -> WaitForStatement
- static const int ParserState_WaitForProperty = 2; // Property -> WaitForStatement
- static const int ParserSpace_Main = 0;
- static const int ParserSpace_Part = 1;
- static const int ParserSpace_Triangle = 2;
- static const int ParserSpace_Bone = 3;
- static const int ParserSpace_Shape = 4;
- static const int ParserSpace_Point = 5;
- static const int ParserSpace_Unhandled = 6;
- struct ParserState {
- Model_DMF1 *model;
- int parserState, parserSpace, propertyIndex;
- String lastPropertyName;
- explicit ParserState(Model_DMF1 *model) :
- model(model),
- parserState(ParserState_WaitForStatement),
- parserSpace(ParserSpace_Main),
- propertyIndex(0) {}
- };
- // Precondition: value >= 0.0
- static int roundIndex(double value) {
- return (int)round(value);
- }
- static void setProperty(ParserState &state, const String &propertyName, int index, const ReadableString &content) {
- float value = (float)string_toDouble(content);
- if (state.parserSpace == ParserSpace_Main) {
- if PROPERTY_MATCH(FilterType) {
- PARSER_NOINDEX
- if CONTENT_MATCH(Alpha) {
- state.model->filter = Filter::Alpha;
- } else { // None
- state.model->filter = Filter::Solid;
- }
- } else if PROPERTY_MATCH(CullingType) {
- PARSER_NOINDEX
- if CONTENT_MATCH(AABB) {
- // TODO: Use bounding box culling on state.model
- } else if CONTENT_MATCH(Radius) {
- // TODO: Use centered sphere culling on state.model
- } else { // None
- // TODO: Use no culling on state.model
- }
- } else if PROPERTY_MATCH(BoundMultiplier) {
- PARSER_NOINDEX
- // TODO: Use bound multiplier exactly like in the other engine
- //state.model->ModelProperties.BoundMultiplier = value;
- }
- } else if (state.parserSpace == ParserSpace_Part) {
- Part_DMF1* lastPart = state.model->getLastPart();
- if (!lastPart) {
- printText("Failed to find the last part!\n");
- } else if PROPERTY_MATCH(Name) {
- PARSER_NOINDEX
- lastPart->name = content;
- } else if PROPERTY_MATCH(Texture) {
- if (index < 0 || index >= 16) {
- printText("Texture index ", index, " is out of bound 0..15\n");
- } else {
- lastPart->textures[index] = content;
- }
- } else if PROPERTY_MATCH(Shader) {
- if (index == 0) {
- lastPart->shaderZero = content;
- }
- } else if PROPERTY_MATCH(TextureOverride) {
- // TODO: Set the texture override channel
- } else if PROPERTY_MATCH(MinDetailLevel) {
- lastPart->minDetailLevel = roundIndex(value);
- } else if PROPERTY_MATCH(MaxDetailLevel) {
- lastPart->maxDetailLevel = roundIndex(value);
- }
- } else if (state.parserSpace == ParserSpace_Triangle) {
- Part_DMF1* lastPart = state.model->getLastPart();
- if (!lastPart) {
- printText("Failed to find the last part!\n");
- } else {
- Triangle_DMF1 *lastTriangle = lastPart->getLastTriangle();
- if (!lastTriangle) {
- printText("Cannot define vertex data after failing to create a triangle!\n");
- } else if (index < 0 || index > 2) {
- printText("Triangle vertex index ", index, " is out of bound 0..2!\n");
- } else {
- if PROPERTY_MATCH(X) {
- lastTriangle->vertices[index].position.x = value;
- } else if PROPERTY_MATCH(Y) {
- lastTriangle->vertices[index].position.y = value;
- } else if PROPERTY_MATCH(Z) {
- lastTriangle->vertices[index].position.z = value;
- } else if PROPERTY_MATCH(CR) {
- lastTriangle->vertices[index].color.x = value;
- } else if PROPERTY_MATCH(CG) {
- lastTriangle->vertices[index].color.y = value;
- } else if PROPERTY_MATCH(CB) {
- lastTriangle->vertices[index].color.z = value;
- } else if PROPERTY_MATCH(CA) {
- lastTriangle->vertices[index].color.w = value;
- } else if PROPERTY_MATCH(U1) {
- lastTriangle->vertices[index].texCoord.x = value;
- } else if PROPERTY_MATCH(V1) {
- lastTriangle->vertices[index].texCoord.y = value;
- } else if PROPERTY_MATCH(U2) {
- lastTriangle->vertices[index].texCoord.z = value;
- } else if PROPERTY_MATCH(V2) {
- lastTriangle->vertices[index].texCoord.w = value;
- }
- }
- }
- }
- }
- static void changeNamespace(ParserState &state, const String &newNamespace) {
- if (string_caseInsensitiveMatch(newNamespace, U"Part")) {
- // Create a new part
- state.model->addEmptyPart();
- state.parserSpace = ParserSpace_Part;
- } else if (string_caseInsensitiveMatch(newNamespace, U"Triangle")) {
- if (state.parserSpace == ParserSpace_Part || state.parserSpace == ParserSpace_Triangle) {
- // Create a new triangle
- state.model->getLastPart()->addEmptyTriangle();
- state.parserSpace = ParserSpace_Triangle;
- } else {
- printText("Triangles must be created as members of a part!\n");
- }
- } else if (string_caseInsensitiveMatch(newNamespace, U"Bone")) {
- state.parserSpace = ParserSpace_Bone; // A bone for animation
- } else if (string_caseInsensitiveMatch(newNamespace, U"Shape")) {
- state.parserSpace = ParserSpace_Shape; // A physical shape.
- } else if (string_caseInsensitiveMatch(newNamespace, U"Point")) {
- state.parserSpace = ParserSpace_Point; // A physical point in a convex hull.
- } else {
- state.parserSpace = ParserSpace_Unhandled;
- }
- }
- // Start and end are in base zero
- // End is an inclusive index
- static void readToken(ParserState &state, const String &fileContent, int start, int end) {
- if (end >= start) {
- if (fileContent[start] == U'(' && fileContent[end] == U')') {
- // Property
- if (state.parserState == ParserState_WaitForProperty || state.parserState == ParserState_WaitForIndexOrProperty) {
- setProperty(state, state.lastPropertyName, state.propertyIndex, string_inclusiveRange(fileContent, start + 1, end - 1));
- state.parserState = ParserState_WaitForStatement;
- state.propertyIndex = 0; // Reset index for the next property
- } else {
- printText("Unexpected property!\n");
- }
- } else if (fileContent[start] == U'[' && fileContent[end] == U']') {
- // Index
- if (state.parserState == ParserState_WaitForIndexOrProperty) {
- state.propertyIndex = roundIndex(string_toDouble(string_inclusiveRange(fileContent, start + 1, end - 1)));
- } else {
- printText("Unexpected index!\n");
- }
- } else if (fileContent[start] == U'<' && fileContent[end] == U'>') {
- // Namespace
- if (state.parserState == ParserState_WaitForStatement) {
- if (end - start > 258) {
- printText("Name of namespace is too long!\n");
- } else {
- // Change namespace and create things
- changeNamespace(state, string_inclusiveRange(fileContent, start + 1, end - 1));
- }
- } else {
- printText("Change of namespace before finishing the last statement!\n");
- }
- } else {
- // Identifier
- if (state.parserState == ParserState_WaitForStatement) {
- // Global property
- if (end - start > 258) {
- printText("Name of property is too long!\n");
- } else {
- state.lastPropertyName = string_inclusiveRange(fileContent, start, end);
- state.parserState = ParserState_WaitForIndexOrProperty;
- }
- }
- }
- }
- }
- // Parses a simplified native representation of the model as a syntax tree that can later be converted into a real model
- static Model_DMF1 loadNative_DMF1(const String &fileContent) {
- Model_DMF1 resultModel;
- ParserState state(&resultModel);
- if (string_length(fileContent) < 4 || (fileContent[0] != 'D' || fileContent[1] != 'M' || fileContent[2] != 'F' || fileContent[3] != '1')) {
- printText("The file does not start with \"DMF1\"!\n");
- return resultModel;
- }
- int tokenStart = 4; // Everything before this will no longer be used
- int readIndex = 4;
- // Scan the string and send tokens to the state machine
- DsrChar firstCharOfToken = U'\0';
- for (readIndex = tokenStart; readIndex < string_length(fileContent); readIndex++) {
- DsrChar curChar = fileContent[readIndex];
- if (firstCharOfToken == U'\0' && (curChar == tab || curChar == space || curChar == lineFeed || curChar == carriageReturn)) {
- // Finish the current token and don't save this character
- readToken(state, fileContent, tokenStart, readIndex - 1);
- tokenStart = readIndex + 1;
- } else if (curChar == '<' || curChar == '(' || curChar == '[') {
- // Finish the last token and save this character
- readToken(state, fileContent, tokenStart, readIndex-1);
- tokenStart = readIndex;
- firstCharOfToken = curChar;
- } else if (firstCharOfToken == '<' && curChar == '>') {
- // Use this token
- readToken(state, fileContent, tokenStart, readIndex);
- tokenStart = readIndex + 1;
- firstCharOfToken = U'\0';
- } else if (firstCharOfToken == '(' && curChar == ')') {
- // Use this token
- readToken(state, fileContent, tokenStart, readIndex);
- tokenStart = readIndex + 1;
- firstCharOfToken = U'\0';
- } else if (firstCharOfToken == '[' && curChar == ']') {
- // Use this token
- readToken(state, fileContent, tokenStart, readIndex);
- tokenStart = readIndex + 1;
- firstCharOfToken = U'\0';
- }
- }
- readToken(state, fileContent, tokenStart, readIndex - 1);
- if (state.parserState != ParserState_WaitForStatement) {
- printText("The last statement in the model was not finished.\n");
- }
- return resultModel;
- }
- static Model convertFromDMF1(const Model_DMF1 &nativeModel, ResourcePool &pool, int detailLevel) {
- Model result = model_create();
- // Convert all parts from the native representation
- for (int inputPartIndex = 0; inputPartIndex < nativeModel.parts.length(); inputPartIndex++) {
- const Part_DMF1 *inputPart = &(nativeModel.parts[inputPartIndex]);
- if (detailLevel >= inputPart->minDetailLevel && detailLevel <= inputPart->maxDetailLevel) {
- int part = result->addEmptyPart(inputPart->name);
- if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_0Tex")) {
- // Color
- } else if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_1Tex")) {
- // Diffuse
- result->setDiffuseMapByName(pool, inputPart->textures[0], part);
- } else if (string_caseInsensitiveMatch(inputPart->shaderZero, U"M_Diffuse_2Tex")) {
- // Diffuse and light
- result->setDiffuseMapByName(pool, inputPart->textures[0], part);
- result->setLightMapByName(pool, inputPart->textures[1], part);
- } else {
- printText("The shader ", inputPart->shaderZero, " is not supported. Use M_Diffuse_0Tex, M_Diffuse_1Tex or M_Diffuse_2Tex.\n");
- }
- for (int inputTriangleIndex = 0; inputTriangleIndex < inputPart->triangles.length(); inputTriangleIndex++) {
- const Triangle_DMF1 *inputTriangle = &(inputPart->triangles[inputTriangleIndex]);
- const float threshold = 0.00001f;
- int posIndexA = result->addPointIfNeeded(inputTriangle->vertices[0].position, threshold);
- int posIndexB = result->addPointIfNeeded(inputTriangle->vertices[1].position, threshold);
- int posIndexC = result->addPointIfNeeded(inputTriangle->vertices[2].position, threshold);
- VertexData dataA(inputTriangle->vertices[0].texCoord, inputTriangle->vertices[0].color);
- VertexData dataB(inputTriangle->vertices[1].texCoord, inputTriangle->vertices[1].color);
- VertexData dataC(inputTriangle->vertices[2].texCoord, inputTriangle->vertices[2].color);
- Polygon polygon(Vertex(posIndexA, dataA), Vertex(posIndexB, dataB), Vertex(posIndexC, dataC));
- result->addPolygon(polygon, part);
- }
- }
- }
- return result;
- }
- Model dsr::importFromContent_DMF1(const String &fileContent, ResourcePool &pool, int detailLevel) {
- // Load the raw data
- Model_DMF1 nativeModel = loadNative_DMF1(fileContent);
- // Construct a model while loading resources
- return convertFromDMF1(nativeModel, pool, detailLevel);
- }
|