Compare commits
12 commits
e5f543ef4a
...
621ae30464
Author | SHA1 | Date | |
---|---|---|---|
621ae30464 | |||
12ef549a12 | |||
811d377669 | |||
2aae16744e | |||
f5727f3b03 | |||
49dfeec18c | |||
879d92f145 | |||
fc1c4a06f1 | |||
66f7f0bea6 | |||
deb72660da | |||
590ecbe5ec | |||
47d720f438 |
57 changed files with 1318 additions and 407 deletions
|
@ -4,8 +4,10 @@ find_package(Clang REQUIRED)
|
|||
add_executable(autogen
|
||||
src/main.cpp
|
||||
src/util.cpp
|
||||
src/analysis.cpp
|
||||
src/codegen.cpp
|
||||
src/object/analysis.cpp
|
||||
src/object/codegen.cpp
|
||||
src/data/analysis.cpp
|
||||
src/data/codegen.cpp
|
||||
)
|
||||
|
||||
set_target_properties(autogen PROPERTIES OUTPUT_NAME "autogen")
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "analysis.h"
|
||||
#include <fstream>
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, ClassAnalysis& state);
|
216
autogen/src/data/analysis.cpp
Normal file
216
autogen/src/data/analysis.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
#include "analysis.h"
|
||||
#include "../util.h"
|
||||
#include <cctype>
|
||||
#include <clang-c/CXFile.h>
|
||||
#include <clang-c/CXSourceLocation.h>
|
||||
#include <clang-c/Index.h>
|
||||
#include <cstdio>
|
||||
#include <optional>
|
||||
|
||||
using namespace data;
|
||||
|
||||
static std::string toStaticName(std::string orig) {
|
||||
bool isSnakeCase = orig.find('_') == -1;
|
||||
|
||||
std::string newName = "";
|
||||
int wordStart = 0;
|
||||
for (char c : orig) {
|
||||
if (c == '_') {
|
||||
wordStart = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wordStart == 1)
|
||||
newName += std::toupper(c);
|
||||
else if (wordStart == 0)
|
||||
newName += std::tolower(c);
|
||||
else if (wordStart == 2)
|
||||
newName += c;
|
||||
|
||||
if (c >= 'a' && c <= 'z')
|
||||
wordStart = 2;
|
||||
else
|
||||
wordStart = 0;
|
||||
}
|
||||
|
||||
newName[0] = std::tolower(newName[0]);
|
||||
return newName;
|
||||
}
|
||||
|
||||
// Constructors are stored the same way as static functions, but with the name "new"
|
||||
static void processConstructor(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_data_ctor");
|
||||
if (!propertyDef) return;
|
||||
|
||||
MethodAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur));
|
||||
CXType retType = clang_getCursorResultType(cur);
|
||||
|
||||
anly.name = result["name"];
|
||||
anly.functionName = "__ctor";
|
||||
anly.returnType = state->name;
|
||||
|
||||
// if name field is not provided, use new
|
||||
if (anly.name == "") {
|
||||
anly.name = "new";
|
||||
}
|
||||
|
||||
// Populate parameter list
|
||||
// https://stackoverflow.com/a/45867090/16255372
|
||||
|
||||
for (int i = 0; i < clang_Cursor_getNumArguments(cur); i++) {
|
||||
CXCursor paramCur = clang_Cursor_getArgument(cur, i);
|
||||
|
||||
std::string paramName = x_clang_toString(clang_getCursorDisplayName(paramCur));
|
||||
std::string paramType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(paramCur)));
|
||||
|
||||
MethodParameter param;
|
||||
param.name = paramName;
|
||||
param.type = paramType;
|
||||
|
||||
anly.parameters.push_back(param);
|
||||
}
|
||||
|
||||
state->staticMethods.push_back(anly);
|
||||
}
|
||||
|
||||
static void processMethod(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_data_method");
|
||||
if (!propertyDef) return;
|
||||
|
||||
MethodAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur));
|
||||
CXType retType = clang_getCursorResultType(cur);
|
||||
|
||||
bool isStatic = clang_CXXMethod_isStatic(cur);
|
||||
|
||||
anly.name = result["name"];
|
||||
anly.functionName = symbolName;
|
||||
anly.returnType = x_clang_toString(clang_getTypeSpelling(retType));
|
||||
|
||||
// if name field is not provided, use fieldName instead, but capitalize the first character
|
||||
if (anly.name == "") {
|
||||
anly.name = symbolName;
|
||||
if (!isStatic)
|
||||
anly.name[0] = std::toupper(anly.name[0]);
|
||||
else
|
||||
anly.name[0] = std::tolower(anly.name[0]);
|
||||
}
|
||||
|
||||
// Populate parameter list
|
||||
// https://stackoverflow.com/a/45867090/16255372
|
||||
|
||||
for (int i = 0; i < clang_Cursor_getNumArguments(cur); i++) {
|
||||
CXCursor paramCur = clang_Cursor_getArgument(cur, i);
|
||||
|
||||
std::string paramName = x_clang_toString(clang_getCursorDisplayName(paramCur));
|
||||
std::string paramType = x_clang_toString(clang_getTypeSpelling(clang_getCursorType(paramCur)));
|
||||
|
||||
MethodParameter param;
|
||||
param.name = paramName;
|
||||
param.type = paramType;
|
||||
|
||||
anly.parameters.push_back(param);
|
||||
}
|
||||
|
||||
// If it's a static method, push it into the library instead
|
||||
if (clang_CXXMethod_isStatic(cur))
|
||||
state->staticMethods.push_back(anly);
|
||||
else
|
||||
state->methods.push_back(anly);
|
||||
}
|
||||
|
||||
// This processes both methods and fields
|
||||
static void processProperty(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_data_prop");
|
||||
if (!propertyDef) return;
|
||||
|
||||
PropertyAnalysis anly;
|
||||
|
||||
auto result = parseAnnotationString(propertyDef.value());
|
||||
std::string symbolName = x_clang_toString(clang_getCursorSpelling(cur));
|
||||
CXType retType = clang_getCursorResultType(cur);
|
||||
|
||||
bool isStatic = clang_getCursorKind(cur) == CXCursor_VarDecl || clang_CXXMethod_isStatic(cur);
|
||||
|
||||
anly.name = result["name"];
|
||||
anly.backingSymbol = symbolName;
|
||||
anly.valueType = x_clang_toString(clang_getTypeSpelling(retType));
|
||||
anly.backingType = clang_getCursorKind(cur) == CXCursor_CXXMethod ? PropertyBackingType::Method : PropertyBackingType::Field;
|
||||
|
||||
// if name field is not provided, use fieldName instead, but capitalize the first character
|
||||
if (anly.name == "") {
|
||||
anly.name = symbolName;
|
||||
if (!isStatic)
|
||||
anly.name[0] = std::toupper(anly.name[0]);
|
||||
else
|
||||
anly.name = toStaticName(anly.name);
|
||||
}
|
||||
|
||||
// If it's a static property, push it into the library instead
|
||||
if (isStatic)
|
||||
state->staticProperties.push_back(anly);
|
||||
else
|
||||
state->properties.push_back(anly);
|
||||
}
|
||||
|
||||
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||
ClassAnalysis anly;
|
||||
|
||||
std::string propertyDef = findAnnotation(cur, "OB::def_data").value();
|
||||
auto result = parseAnnotationString(propertyDef);
|
||||
|
||||
anly.name = className;
|
||||
anly.serializedName = result["name"];
|
||||
anly.hasFromString = result.count("from_string") > 0;
|
||||
|
||||
if (anly.serializedName == "")
|
||||
anly.serializedName = className;
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
|
||||
if (kind == CXCursor_Constructor) {
|
||||
processConstructor(cur, &anly);
|
||||
}
|
||||
|
||||
if (kind == CXCursor_CXXMethod || kind == CXCursor_FieldDecl || kind == CXCursor_VarDecl) {
|
||||
processProperty(cur, &anly);
|
||||
}
|
||||
|
||||
if (kind == CXCursor_CXXMethod) {
|
||||
processMethod(cur, &anly);
|
||||
}
|
||||
|
||||
return CXChildVisit_Continue;
|
||||
});
|
||||
|
||||
state->classes[className] = anly;
|
||||
}
|
||||
|
||||
bool data::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) {
|
||||
// Search for classes
|
||||
x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
if (kind == CXCursor_Namespace) return CXChildVisit_Recurse;
|
||||
if (kind != CXCursor_ClassDecl) return CXChildVisit_Continue;
|
||||
|
||||
CXSourceLocation loc = clang_getCursorLocation(cur);
|
||||
if (!clang_Location_isFromMainFile(loc)) return CXChildVisit_Continue; // This class is not from this header. Skip
|
||||
|
||||
std::string className = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
// Forward-decls can slip through the cracks, this prevents that, but also allows us to filter non-instance classes in the src/objects directory
|
||||
if (!findAnnotation(cur, "OB::def_data")) return CXChildVisit_Continue; // Class is not "primary" declaration/is not instance, skip
|
||||
if (state->classes.count(className) > 0) return CXChildVisit_Continue; // Class has already been analyzed, skip...
|
||||
|
||||
processClass(cur, state, className, srcRoot);
|
||||
|
||||
return CXChildVisit_Continue;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
51
autogen/src/data/analysis.h
Normal file
51
autogen/src/data/analysis.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace data {
|
||||
|
||||
enum PropertyBackingType {
|
||||
Method,
|
||||
Field
|
||||
};
|
||||
|
||||
struct PropertyAnalysis {
|
||||
std::string name;
|
||||
std::string backingSymbol;
|
||||
PropertyBackingType backingType;
|
||||
std::string valueType;
|
||||
};
|
||||
|
||||
struct MethodParameter {
|
||||
std::string name;
|
||||
std::string type;
|
||||
};
|
||||
|
||||
struct MethodAnalysis {
|
||||
std::string name;
|
||||
std::string functionName;
|
||||
std::string returnType;
|
||||
std::vector<MethodParameter> parameters;
|
||||
};
|
||||
|
||||
struct ClassAnalysis {
|
||||
std::string name;
|
||||
std::string serializedName;
|
||||
std::string headerPath;
|
||||
bool hasFromString = false;
|
||||
std::vector<PropertyAnalysis> properties;
|
||||
std::vector<MethodAnalysis> methods;
|
||||
std::vector<PropertyAnalysis> staticProperties;
|
||||
std::vector<MethodAnalysis> staticMethods;
|
||||
};
|
||||
|
||||
struct AnalysisState {
|
||||
std::map<std::string, ClassAnalysis> classes;
|
||||
};
|
||||
|
||||
bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
|
||||
|
||||
}
|
436
autogen/src/data/codegen.cpp
Normal file
436
autogen/src/data/codegen.cpp
Normal file
|
@ -0,0 +1,436 @@
|
|||
#include "codegen.h"
|
||||
#include "analysis.h"
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
using namespace data;
|
||||
|
||||
static std::map<std::string, std::string> MAPPED_TYPE = {
|
||||
{ "bool", "Data::Bool" },
|
||||
{ "int", "Data::Int" },
|
||||
{ "float", "Data::Float" },
|
||||
{ "std::string", "Data::String" },
|
||||
{ "glm::vec3", "Vector3" },
|
||||
};
|
||||
|
||||
static std::map<std::string, std::string> LUA_CHECK_FUNCS = {
|
||||
{ "bool", "lua_toboolean" },
|
||||
{ "int", "luaL_checkinteger" },
|
||||
{ "float", "luaL_checknumber" },
|
||||
{ "std::string", "luaL_checkstring" },
|
||||
};
|
||||
|
||||
static std::map<std::string, std::string> LUA_TEST_FUNCS = {
|
||||
{ "bool", "lua_isboolean" },
|
||||
{ "int", "lua_isinteger" },
|
||||
{ "float", "lua_isnumber" },
|
||||
{ "std::string", "lua_isstring" },
|
||||
};
|
||||
|
||||
static std::string getLuaMethodFqn(std::string className, std::string methodName) {
|
||||
return "__lua_impl__" + className + "__" + methodName;
|
||||
}
|
||||
|
||||
static std::string getMtName(std::string type) {
|
||||
if (type.starts_with("Data::"))
|
||||
return "__mt_" + type.substr(6);
|
||||
return "__mt_" + type;
|
||||
}
|
||||
|
||||
static void writeLuaGetArgument(std::ofstream& out, std::string type, int narg, bool member) {
|
||||
std::string varname = "arg" + std::to_string(narg);
|
||||
narg += 1; // Arguments start at 1
|
||||
|
||||
std::string checkFunc = LUA_CHECK_FUNCS[type];
|
||||
if (checkFunc != "") {
|
||||
out << " " << type << " " << varname << " = " << checkFunc << "(L, " << std::to_string(member ? narg + 1: narg) << ");\n";
|
||||
} else {
|
||||
std::string udataname = getMtName(type);
|
||||
out << " " << type << " " << varname << " = *(" << type << "*)luaL_checkudata(L, " << std::to_string(member ? narg + 1 : narg) << ", \"" << udataname << "\");\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an expression which tests that the given argument by index matches a certain type
|
||||
static void writeLuaTestArgument(std::ofstream& out, std::string type, int narg, bool member) {
|
||||
std::string varname = "arg" + std::to_string(narg);
|
||||
narg += 1; // Arguments start at 1
|
||||
|
||||
std::string testFunc = LUA_TEST_FUNCS[type];
|
||||
if (testFunc != "") {
|
||||
// Arguments start at 1
|
||||
out << testFunc << "(L, " << std::to_string(member ? narg + 1 : narg) << ")";
|
||||
} else {
|
||||
std::string udataname = getMtName(type);
|
||||
out << "luaL_testudata(L, " << std::to_string(member ? narg + 1 : narg) << ", \"" << udataname << "\")";
|
||||
}
|
||||
}
|
||||
|
||||
static void writeLuaMethodImpls(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
// Collect all method names to account for overloaded functions
|
||||
std::map<std::string, std::vector<MethodAnalysis>> methods;
|
||||
std::map<std::string, std::vector<MethodAnalysis>> staticMethods;
|
||||
|
||||
for (MethodAnalysis method : state.methods) {
|
||||
methods[method.name].push_back(method);
|
||||
}
|
||||
|
||||
for (MethodAnalysis method : state.staticMethods) {
|
||||
staticMethods[method.name].push_back(method);
|
||||
}
|
||||
|
||||
for (auto& [name, methodImpls] : methods) {
|
||||
std::string methodFqn = getLuaMethodFqn(state.name, name);
|
||||
out << "static int " << methodFqn << "(lua_State* L) {\n"
|
||||
" auto this__ = (Data::Base*)lua_touserdata(L, 1);\n"
|
||||
" if (&this__->GetType() != &" << fqn << "::TYPE) return luaL_typerror(L, 0, \"" << state.name << "\");\n"
|
||||
" " << fqn << "* this_ = (" << fqn << "*)this__;\n\n"
|
||||
" int n = lua_gettop(L);\n";
|
||||
out << " ";
|
||||
|
||||
// Support multiple overloads of the same function
|
||||
bool first = true;
|
||||
for (MethodAnalysis methodImpl : methodImpls) {
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
|
||||
// Check to see if the arguments possibly match this implementation's parameter types
|
||||
out << "if (";
|
||||
|
||||
// Check number of arguments
|
||||
out << "n == " << std::to_string(methodImpl.parameters.size() + 1); // Account for first argument as 'this'
|
||||
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
out << " && ";
|
||||
writeLuaTestArgument(out, methodImpl.parameters[i].type, i, true);
|
||||
}
|
||||
|
||||
out << ") {\n"; // End if condition, start if body
|
||||
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
writeLuaGetArgument(out, methodImpl.parameters[i].type, i, true);
|
||||
}
|
||||
|
||||
// Store result
|
||||
if (methodImpl.returnType != "void")
|
||||
out << " " << methodImpl.returnType << " result = ";
|
||||
else
|
||||
out << " ";
|
||||
|
||||
// Call function
|
||||
out << "this_->" << methodImpl.functionName << "(";
|
||||
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
std::string varname = "arg" + std::to_string(i);
|
||||
if (i != 0) out << ", ";
|
||||
out << varname;
|
||||
}
|
||||
|
||||
out << ");\n"; // End function call
|
||||
|
||||
// Return result
|
||||
if (methodImpl.returnType != "void") {
|
||||
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||
if (mappedType == "")
|
||||
out << " result.PushLuaValue(L);\n";
|
||||
else
|
||||
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||
}
|
||||
|
||||
if (methodImpl.returnType == "void")
|
||||
out << " return 0;\n";
|
||||
else
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
// No function implementation matched
|
||||
out << "\n\n return luaL_error(L, \"No definition of function '%s' in %s matches these argument types\", \"" << name << "\", \"" << state.name << "\");\n";
|
||||
|
||||
out << "}\n\n"; // End function
|
||||
}
|
||||
|
||||
for (auto& [name, methodImpls] : staticMethods) {
|
||||
std::string methodFqn = getLuaMethodFqn(state.name, name);
|
||||
out << "static int " << methodFqn << "(lua_State* L) {\n"
|
||||
" int n = lua_gettop(L);\n";
|
||||
out << " ";
|
||||
|
||||
// Support multiple overloads of the same function
|
||||
bool first = true;
|
||||
for (MethodAnalysis methodImpl : methodImpls) {
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
|
||||
// Check to see if the arguments possibly match this implementation's parameter types
|
||||
out << "if (";
|
||||
|
||||
// Check number of arguments
|
||||
out << "n == " << std::to_string(methodImpl.parameters.size());
|
||||
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
out << " && ";
|
||||
writeLuaTestArgument(out, methodImpl.parameters[i].type, i, false);
|
||||
}
|
||||
|
||||
out << ") {\n"; // End if condition, start if body
|
||||
|
||||
// Get the arguments
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
writeLuaGetArgument(out, methodImpl.parameters[i].type, i, false);
|
||||
}
|
||||
|
||||
// Store result
|
||||
if (methodImpl.returnType != "void")
|
||||
out << " " << methodImpl.returnType << " result = ";
|
||||
else
|
||||
out << " ";
|
||||
|
||||
// Call function
|
||||
if (methodImpl.functionName == "__ctor")
|
||||
out << fqn << "(";
|
||||
else
|
||||
out << fqn << "::" << methodImpl.functionName << "(";
|
||||
|
||||
for (int i = 0; i < methodImpl.parameters.size(); i++) {
|
||||
std::string varname = "arg" + std::to_string(i);
|
||||
if (i != 0) out << ", ";
|
||||
out << varname;
|
||||
}
|
||||
|
||||
out << ");\n"; // End function call
|
||||
|
||||
// Return result
|
||||
if (methodImpl.returnType != "void") {
|
||||
std::string mappedType = MAPPED_TYPE[methodImpl.returnType];
|
||||
if (mappedType == "")
|
||||
out << " result.PushLuaValue(L);\n";
|
||||
else
|
||||
out << " " << mappedType << "(result).PushLuaValue(L);\n";
|
||||
}
|
||||
|
||||
if (methodImpl.returnType == "void")
|
||||
out << " return 0;\n";
|
||||
else
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
// No function implementation matched
|
||||
out << "\n\n return luaL_error(L, \"No definition of function '%s' in %s matches these argument types\", \"" << name << "\", \"" << state.name << "\");\n";
|
||||
|
||||
out << "}\n\n"; // End function
|
||||
}
|
||||
}
|
||||
|
||||
static void writeLuaValueGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
out << "static int data_gc(lua_State*);\n"
|
||||
"static int data_index(lua_State*);\n"
|
||||
"static int data_tostring(lua_State*);\n"
|
||||
"static const struct luaL_Reg metatable [] = {\n"
|
||||
" {\"__gc\", data_gc},\n"
|
||||
" {\"__index\", data_index},\n"
|
||||
" {\"__tostring\", data_tostring},\n"
|
||||
" {NULL, NULL} /* end of array */\n"
|
||||
"};\n\n";
|
||||
|
||||
out << "void Data::" << state.name << "::PushLuaValue(lua_State* L) const {\n"
|
||||
" int n = lua_gettop(L);\n"
|
||||
|
||||
// " // I'm torn... should this be Data::Variant, or Data::Base?\n"
|
||||
// " // If I ever decouple typing from Data::Base, I'll switch it to variant,\n"
|
||||
// " // otherwise, it doesn't make much sense to represent it as one\n"
|
||||
" " << fqn << "* userdata = (" << fqn << "*)lua_newuserdata(L, sizeof(" << fqn << "));\n"
|
||||
" new(userdata) " << fqn << "(*this);\n"
|
||||
|
||||
" // Create the library's metatable\n"
|
||||
" luaL_newmetatable(L, \"__mt_" << state.name << "\");\n"
|
||||
" luaL_register(L, NULL, metatable);\n"
|
||||
|
||||
" lua_setmetatable(L, n+1);\n"
|
||||
"}\n\n";
|
||||
|
||||
|
||||
out << "result<Data::Variant, LuaCastError> Data::" << state.name << "::FromLuaValue(lua_State* L, int idx) {\n"
|
||||
" " << fqn << "* userdata = (" << fqn << "*) luaL_testudata(L, idx, \"" << getMtName(state.name) << "\");\n"
|
||||
" if (userdata == nullptr)\n"
|
||||
" return LuaCastError(lua_typename(L, idx), \"" << state.name << "\");\n"
|
||||
" return Data::Variant(*userdata);\n"
|
||||
"}\n\n";
|
||||
|
||||
// Indexing methods and properties
|
||||
|
||||
out << "static int data_index(lua_State* L) {\n"
|
||||
" auto this__ = (Data::Base*)lua_touserdata(L, 1);\n"
|
||||
" if (&this__->GetType() != &" << fqn << "::TYPE) return luaL_typerror(L, 0, \"" << state.name << "\");\n"
|
||||
" " << fqn << "* this_ = (" << fqn << "*)this__;\n"
|
||||
"\n"
|
||||
" std::string key(lua_tostring(L, 2));\n"
|
||||
" lua_pop(L, 2);\n"
|
||||
"\n";
|
||||
|
||||
out << " ";
|
||||
|
||||
bool first = true;
|
||||
for (PropertyAnalysis prop : state.properties) {
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
|
||||
out << "if (key == \"" << prop.name << "\") {\n";
|
||||
|
||||
std::string type = MAPPED_TYPE[prop.valueType];
|
||||
if (type == "") type = prop.valueType;
|
||||
|
||||
std::string valueExpr;
|
||||
if (prop.backingType == PropertyBackingType::Field)
|
||||
valueExpr = "this_->" + prop.backingSymbol;
|
||||
else if (prop.backingType == PropertyBackingType::Method)
|
||||
valueExpr = "this_->" + prop.backingSymbol + "()";
|
||||
|
||||
// This largely depends on the type
|
||||
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
std::map<std::string, bool> accountedMethods;
|
||||
for (MethodAnalysis method : state.methods) {
|
||||
if (accountedMethods[method.name]) continue;
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
accountedMethods[method.name] = true;
|
||||
|
||||
out << "if (key == \"" << method.name << "\") {\n";
|
||||
out << " lua_pushcfunction(L, " << getLuaMethodFqn(state.name, method.name) << ");\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
out << "\n\n"
|
||||
" return luaL_error(L, \"%s is not a valid member of %s\\n\", key.c_str(), \"" << state.name << "\");\n"
|
||||
"}\n\n";
|
||||
|
||||
// ToString
|
||||
|
||||
out << "\nint data_tostring(lua_State* L) {\n"
|
||||
" auto this_ = (" << fqn << "*)lua_touserdata(L, 1);\n"
|
||||
" lua_pushstring(L, std::string(this_->ToString()).c_str());\n"
|
||||
" return 1;\n"
|
||||
"}\n\n";
|
||||
|
||||
// Destructor
|
||||
|
||||
out << "\nint data_gc(lua_State* L) {\n"
|
||||
" auto this_ = (" << fqn << "*)lua_touserdata(L, 1);\n"
|
||||
" delete this_;\n"
|
||||
" return 0;\n"
|
||||
"}\n\n";
|
||||
}
|
||||
|
||||
static void writeLuaLibraryGenerator(std::ofstream& out, ClassAnalysis& state) {
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
out << "static int lib_index(lua_State*);\n"
|
||||
"static const struct luaL_Reg lib_metatable [] = {\n"
|
||||
" {\"__index\", lib_index},\n"
|
||||
" {NULL, NULL} /* end of array */\n"
|
||||
"};\n\n";
|
||||
|
||||
out << "void Data::" << state.name << "::PushLuaLibrary(lua_State* L) {\n"
|
||||
" lua_getglobal(L, \"_G\");\n"
|
||||
" lua_pushstring(L, \"" << state.name << "\");\n"
|
||||
"\n"
|
||||
" lua_newuserdata(L, 0);\n"
|
||||
"\n"
|
||||
" // Create the library's metatable\n"
|
||||
" luaL_newmetatable(L, \"__mt_lib_" << state.name << "\");\n"
|
||||
" luaL_register(L, NULL, lib_metatable);\n"
|
||||
" lua_setmetatable(L, -2);\n"
|
||||
"\n"
|
||||
" lua_rawset(L, -3);\n"
|
||||
" lua_pop(L, 1);\n"
|
||||
"}\n\n";
|
||||
|
||||
// Indexing methods and properties
|
||||
|
||||
out << "static int lib_index(lua_State* L) {\n"
|
||||
" std::string key(lua_tostring(L, 2));\n"
|
||||
" lua_pop(L, 2);\n"
|
||||
"\n";
|
||||
|
||||
out << " ";
|
||||
|
||||
bool first = true;
|
||||
for (PropertyAnalysis prop : state.staticProperties) {
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
|
||||
out << "if (key == \"" << prop.name << "\") {\n";
|
||||
|
||||
std::string type = MAPPED_TYPE[prop.valueType];
|
||||
if (type == "") type = prop.valueType;
|
||||
|
||||
std::string valueExpr;
|
||||
if (prop.backingType == PropertyBackingType::Field)
|
||||
valueExpr = fqn + "::" + prop.backingSymbol;
|
||||
else if (prop.backingType == PropertyBackingType::Method)
|
||||
valueExpr = fqn + "::" + prop.backingSymbol + "()";
|
||||
|
||||
out << " " << type << "(" << valueExpr << ").PushLuaValue(L);\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
std::map<std::string, bool> accountedMethods;
|
||||
for (MethodAnalysis method : state.staticMethods) {
|
||||
if (accountedMethods[method.name]) continue;
|
||||
if (!first) out << " else ";
|
||||
first = false;
|
||||
accountedMethods[method.name] = true;
|
||||
|
||||
out << "if (key == \"" << method.name << "\") {\n";
|
||||
out << " lua_pushcfunction(L, " << getLuaMethodFqn(state.name, method.name) << ");\n";
|
||||
out << " return 1;\n";
|
||||
|
||||
out << " }";
|
||||
}
|
||||
|
||||
out << "\n\n"
|
||||
" return luaL_error(L, \"%s is not a valid member of %s\\n\", key.c_str(), \"" << state.name << "\");\n"
|
||||
"}\n\n";
|
||||
}
|
||||
|
||||
void data::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||
std::string fqn = "Data::" + state.name;
|
||||
|
||||
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||
out << "#include \"" << headerPath << "\"\n\n";
|
||||
out << "#include \"datatypes/meta.h\"\n";
|
||||
out << "#include <pugixml.hpp>\n";
|
||||
out << "#include \"lua.h\"\n\n";
|
||||
out << "const Data::TypeInfo " << fqn << "::TYPE = {\n"
|
||||
<< " .name = \"" << state.serializedName << "\",\n"
|
||||
<< " .deserializer = &" << fqn << "::Deserialize,\n";
|
||||
if (state.hasFromString) out << " .fromString = &" << fqn << "::FromString,\n";
|
||||
out << " .fromLuaValue = &" << fqn << "::FromLuaValue,\n"
|
||||
<< "};\n\n";
|
||||
|
||||
out << "const Data::TypeInfo& " << fqn << "::GetType() const {\n"
|
||||
<< " return TYPE;\n"
|
||||
<< "};\n\n";
|
||||
|
||||
writeLuaMethodImpls(out, state);
|
||||
writeLuaValueGenerator(out, state);
|
||||
writeLuaLibraryGenerator(out, state);
|
||||
}
|
10
autogen/src/data/codegen.h
Normal file
10
autogen/src/data/codegen.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "analysis.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace data {
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
|
||||
|
||||
}
|
|
@ -6,37 +6,89 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include "analysis.h"
|
||||
#include "codegen.h"
|
||||
#include "object/analysis.h"
|
||||
#include "object/codegen.h"
|
||||
#include "data/analysis.h"
|
||||
#include "data/codegen.h"
|
||||
|
||||
// namespace data {
|
||||
// #include "data/analysis.h"
|
||||
// #include "data/codegen.h"
|
||||
// }
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// https://clang.llvm.org/docs/LibClang.html
|
||||
int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
|
||||
const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 };
|
||||
// THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL
|
||||
// https://stackoverflow.com/a/59206378/16255372
|
||||
CXIndex index = clang_createIndex(0, 0);
|
||||
CXTranslationUnit unit = clang_parseTranslationUnit(
|
||||
index,
|
||||
srcPath.c_str(), cargs, 5,
|
||||
nullptr, 0,
|
||||
CXTranslationUnit_None);
|
||||
|
||||
if (!unit) {
|
||||
fprintf(stderr, "Failed to parse file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print errors
|
||||
int ndiags = clang_getNumDiagnostics(unit);
|
||||
for (int i = 0; i < ndiags; i++) {
|
||||
CXDiagnostic diag = clang_getDiagnostic(unit, i);
|
||||
CXString str = clang_formatDiagnostic(diag, 0);
|
||||
fprintf(stderr, "diag: %s\n", clang_getCString(str));
|
||||
|
||||
clang_disposeString(str);
|
||||
clang_disposeDiagnostic(diag);
|
||||
}
|
||||
|
||||
CXCursor cursor = clang_getTranslationUnitCursor(unit);
|
||||
|
||||
object::AnalysisState objectAnlyState;
|
||||
data::AnalysisState dataAnlyState;
|
||||
|
||||
fs::path relpath = fs::relative(srcPath, srcRoot);
|
||||
printf("[AUTOGEN] Processing file %s...\n", relpath.c_str());
|
||||
object::analyzeClasses(cursor, srcRoot, &objectAnlyState);
|
||||
data::analyzeClasses(cursor, srcRoot, &dataAnlyState);
|
||||
|
||||
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
|
||||
|
||||
printf("[AUTOGEN] Generating file %s...\n", relpath.c_str());
|
||||
std::ofstream outStream(outPath);
|
||||
|
||||
if (!objectAnlyState.classes.empty() || !dataAnlyState.classes.empty()) {
|
||||
outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n";
|
||||
outStream << "// This file was automatically generated by autogen, and should not be edited manually //\n";
|
||||
outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n\n";
|
||||
}
|
||||
|
||||
for (auto& [_, clazz] : objectAnlyState.classes) {
|
||||
object::writeCodeForClass(outStream, relpath, clazz);
|
||||
}
|
||||
|
||||
for (auto& [_, clazz] : dataAnlyState.classes) {
|
||||
data::writeCodeForClass(outStream, relpath, clazz);
|
||||
}
|
||||
|
||||
outStream.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: autogen <src-root> <src-file> <out-dir>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
AnalysisState state;
|
||||
|
||||
fs::path srcRoot = argv[1];
|
||||
fs::path srcPath = argv[2];
|
||||
fs::path outPath = argv[3];
|
||||
|
||||
fs::path relpath = fs::relative(srcPath, srcRoot);
|
||||
printf("[AUTOGEN] Processing file %s...\n", relpath.c_str());
|
||||
analyzeClasses(srcPath, srcRoot, &state);
|
||||
|
||||
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
|
||||
|
||||
printf("[AUTOGEN] Generating file %s...\n", relpath.c_str());
|
||||
std::ofstream outStream(outPath);
|
||||
|
||||
for (auto& [_, clazz] : state.classes) {
|
||||
writeCodeForClass(outStream, clazz);
|
||||
}
|
||||
|
||||
outStream.close();
|
||||
|
||||
return 0;
|
||||
return processHeader(srcRoot, srcPath, outPath);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#include "analysis.h"
|
||||
#include "util.h"
|
||||
#include "../util.h"
|
||||
#include <clang-c/CXFile.h>
|
||||
#include <clang-c/CXSourceLocation.h>
|
||||
#include <clang-c/Index.h>
|
||||
|
@ -7,77 +7,11 @@
|
|||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
using namespace object;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Very simple parser
|
||||
// Example format:
|
||||
// name="Hello!", world=Test, read_only
|
||||
// Result:
|
||||
// "name": "Hello!", "world": "Test", "read_only": ""
|
||||
std::map<std::string, std::string> parseAnnotationString(std::string src) {
|
||||
std::map<std::string, std::string> result;
|
||||
|
||||
std::string currentIdent = "";
|
||||
std::string currentValue = "";
|
||||
int stage = 0;
|
||||
bool quoted = false;
|
||||
|
||||
int i = 0;
|
||||
for (; i < src.length(); i++) {
|
||||
if (src[i] == ' ' && (stage != 2 || !quoted)) continue; // Ignore spaces if not in stage 2 and quoted
|
||||
if (src[i] == ',' && stage == 0) continue; // Let empty commas slip by
|
||||
if (stage < 2 && (src[i] >= 'a' && src[i] <= 'z' || src[i] >= 'A' && src[i] <= 'Z' || src[i] >= '0' && src[i] <= '9' || src[i] == '_')) {
|
||||
currentIdent += src[i];
|
||||
stage = 1;
|
||||
continue;
|
||||
}
|
||||
if (stage == 1 && src[i] == '=') { // What follows is a value
|
||||
stage = 2;
|
||||
continue;
|
||||
}
|
||||
if (stage == 1 && src[i] == ',') { // Value-less key
|
||||
stage = 0;
|
||||
result[currentIdent] = "";
|
||||
currentIdent = "";
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && quoted && src[i] == '"') { // Close a quoted string
|
||||
quoted = false;
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && !quoted && src[i] == '"') { // Start a quoted string
|
||||
quoted = true;
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && !quoted && (src[i] == ' ' || src[i] == ',')) { // Terminate the string
|
||||
stage = 0;
|
||||
result[currentIdent] = currentValue;
|
||||
currentIdent = "";
|
||||
currentValue = "";
|
||||
continue;
|
||||
}
|
||||
if (stage == 2) { // Otherwise if in stage 2, simply add the character
|
||||
currentValue += src[i];
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "Unexpected symbol: %c at index %d\n", src[i], i);
|
||||
fprintf(stderr, "\t%s\n", src.c_str());
|
||||
fprintf(stderr, "\t%s^\n", i > 0 ? std::string(i, '~').c_str() : "");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Add the remaining value
|
||||
if (stage == 1) {
|
||||
result[currentIdent] = "";
|
||||
} else if (stage == 2) {
|
||||
result[currentIdent] = currentValue;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool findInstanceAnnotation(CXCursor cur) {
|
||||
static bool findInstanceAnnotation(CXCursor cur) {
|
||||
bool found = false;
|
||||
// Look for annotations in the class itself
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
|
@ -93,38 +27,7 @@ bool findInstanceAnnotation(CXCursor cur) {
|
|||
return found;
|
||||
}
|
||||
|
||||
std::optional<std::string> findAnnotation(CXCursor cur, std::string annotationName) {
|
||||
std::optional<std::string> ret = std::nullopt;
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
if (kind != CXCursor_AnnotateAttr) return CXChildVisit_Continue;
|
||||
|
||||
std::string annString = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
if (annString != annotationName) return CXChildVisit_Continue;
|
||||
|
||||
// Look inside for a StringLiteral
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
// if (kind != CXCursor_StringLiteral) return CXChildVisit_Recurse;
|
||||
// String literals cannot be parsed as CXCursor_StringLiteral. I don't know why.
|
||||
if (kind != CXCursor_UnexposedExpr) return CXChildVisit_Recurse;
|
||||
|
||||
// https://stackoverflow.com/a/63859988/16255372
|
||||
auto res = clang_Cursor_Evaluate(cur);
|
||||
ret = clang_EvalResult_getAsStr(res);
|
||||
clang_EvalResult_dispose(res);
|
||||
|
||||
return CXChildVisit_Break;
|
||||
});
|
||||
|
||||
return CXChildVisit_Break;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string findBaseClass(CXCursor cur) {
|
||||
static std::string findBaseClass(CXCursor cur) {
|
||||
std::string baseClass = "";
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
|
@ -137,8 +40,8 @@ std::string findBaseClass(CXCursor cur) {
|
|||
return baseClass;
|
||||
}
|
||||
|
||||
std::string currentCategory = "";
|
||||
void processField(CXCursor cur, ClassAnalysis* state) {
|
||||
static std::string currentCategory = "";
|
||||
static void processField(CXCursor cur, ClassAnalysis* state) {
|
||||
std::optional<std::string> propertyDef = findAnnotation(cur, "OB::def_prop");
|
||||
if (!propertyDef) return;
|
||||
|
||||
|
@ -216,7 +119,7 @@ void processField(CXCursor cur, ClassAnalysis* state) {
|
|||
};
|
||||
}
|
||||
|
||||
void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||
static void processClass(CXCursor cur, AnalysisState* state, std::string className, std::string srcRoot) {
|
||||
ClassAnalysis anly;
|
||||
|
||||
// Find base class
|
||||
|
@ -264,36 +167,7 @@ void processClass(CXCursor cur, AnalysisState* state, std::string className, std
|
|||
}
|
||||
|
||||
// https://clang.llvm.org/docs/LibClang.html
|
||||
bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state) {
|
||||
const char* cargs[] = { "-x", "c++", "-I", srcRoot.c_str(), "-D__AUTOGEN__", 0 };
|
||||
// THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL
|
||||
// https://stackoverflow.com/a/59206378/16255372
|
||||
CXIndex index = clang_createIndex(0, 0);
|
||||
CXTranslationUnit unit = clang_parseTranslationUnit(
|
||||
index,
|
||||
path.c_str(), cargs, 5,
|
||||
nullptr, 0,
|
||||
CXTranslationUnit_None);
|
||||
|
||||
if (!unit) {
|
||||
fprintf(stderr, "Failed to parse file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print errors
|
||||
int ndiags = clang_getNumDiagnostics(unit);
|
||||
for (int i = 0; i < ndiags; i++) {
|
||||
CXDiagnostic diag = clang_getDiagnostic(unit, i);
|
||||
CXString str = clang_formatDiagnostic(diag, 0);
|
||||
fprintf(stderr, "diag: %s\n", clang_getCString(str));
|
||||
|
||||
clang_disposeString(str);
|
||||
clang_disposeDiagnostic(diag);
|
||||
}
|
||||
|
||||
CXCursor cursor = clang_getTranslationUnitCursor(unit);
|
||||
|
||||
bool flag = false;
|
||||
bool object::analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state) {
|
||||
// Search for classes
|
||||
x_clang_visitChildren(cursor, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
|
@ -1,9 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace object {
|
||||
|
||||
enum ClassFlags {
|
||||
ClassFlag_NotCreatable = 1<<0,
|
||||
ClassFlag_Service = 1<<1,
|
||||
|
@ -56,4 +59,6 @@ struct AnalysisState {
|
|||
std::map<std::string, ClassAnalysis> classes;
|
||||
};
|
||||
|
||||
bool analyzeClasses(std::string path, std::string srcRoot, AnalysisState* state);
|
||||
bool analyzeClasses(CXCursor cursor, std::string srcRoot, AnalysisState* state);
|
||||
|
||||
}
|
|
@ -4,15 +4,18 @@
|
|||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
std::map<std::string, std::string> CATEGORY_STR = {
|
||||
using namespace object;
|
||||
|
||||
static std::map<std::string, std::string> CATEGORY_STR = {
|
||||
{ "APPEARANCE", "PROP_CATEGORY_APPEARENCE" },
|
||||
{ "DATA", "PROP_CATEGORY_DATA" },
|
||||
{ "BEHAVIOR", "PROP_CATEGORY_BEHAVIOR" },
|
||||
{ "PART", "PROP_CATEGORY_PART" },
|
||||
{ "SURFACE", "PROP_CATEGORY_SURFACE" },
|
||||
{ "SURFACE_INPUT", "PROP_CATEGORY_SURFACE_INPUT" },
|
||||
};
|
||||
|
||||
std::map<std::string, std::string> MAPPED_TYPE = {
|
||||
static std::map<std::string, std::string> MAPPED_TYPE = {
|
||||
{ "bool", "Data::Bool" },
|
||||
{ "int", "Data::Int" },
|
||||
{ "float", "Data::Float" },
|
||||
|
@ -20,11 +23,11 @@ std::map<std::string, std::string> MAPPED_TYPE = {
|
|||
{ "glm::vec3", "Vector3" },
|
||||
};
|
||||
|
||||
std::map<std::string, std::monostate> ENUM_TYPES = {
|
||||
static std::map<std::string, std::monostate> ENUM_TYPES = {
|
||||
{ "SurfaceType", std::monostate() }
|
||||
};
|
||||
|
||||
std::string parseWeakPtr(std::string weakPtrType) {
|
||||
static std::string parseWeakPtr(std::string weakPtrType) {
|
||||
if (!weakPtrType.starts_with("std::weak_ptr")) return "";
|
||||
|
||||
int pos0 = weakPtrType.find("<");
|
||||
|
@ -34,7 +37,7 @@ std::string parseWeakPtr(std::string weakPtrType) {
|
|||
return subtype;
|
||||
}
|
||||
|
||||
std::string castFromVariant(std::string valueStr, std::string fieldType) {
|
||||
static std::string castFromVariant(std::string valueStr, std::string fieldType) {
|
||||
// Manual exception for now, enums will get their own system eventually
|
||||
if (fieldType == "SurfaceType") {
|
||||
return "(SurfaceType)(int)" + valueStr + ".get<Data::Int>()";
|
||||
|
@ -44,7 +47,7 @@ std::string castFromVariant(std::string valueStr, std::string fieldType) {
|
|||
return valueStr + ".get<" + (!mappedType.empty() ? mappedType : fieldType) + ">()";
|
||||
}
|
||||
|
||||
std::string castToVariant(std::string valueStr, std::string fieldType) {
|
||||
static std::string castToVariant(std::string valueStr, std::string fieldType) {
|
||||
// Manual exception for now, enums will get their own system eventually
|
||||
if (fieldType == "SurfaceType") {
|
||||
return "Data::Int((int)" + valueStr + ")";
|
||||
|
@ -63,7 +66,7 @@ std::string castToVariant(std::string valueStr, std::string fieldType) {
|
|||
return valueStr;
|
||||
}
|
||||
|
||||
void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
static void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "fallible<MemberNotFound, AssignToReadOnlyMember> " << state.name << "::InternalSetPropertyValue(std::string name, Data::Variant value) {";
|
||||
|
||||
out << "\n ";
|
||||
|
@ -104,7 +107,7 @@ void writePropertySetHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "\n};\n\n";
|
||||
}
|
||||
|
||||
void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
static void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "void " << state.name << "::InternalUpdateProperty(std::string name) {";
|
||||
|
||||
out << "\n ";
|
||||
|
@ -124,7 +127,7 @@ void writePropertyUpdateHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "\n};\n\n";
|
||||
}
|
||||
|
||||
void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
static void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "result<Data::Variant, MemberNotFound> " << state.name << "::InternalGetPropertyValue(std::string name) {";
|
||||
|
||||
out << "\n ";
|
||||
|
@ -150,7 +153,7 @@ void writePropertyGetHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "\n};\n\n";
|
||||
}
|
||||
|
||||
void writePropertiesList(std::ofstream& out, ClassAnalysis state) {
|
||||
static void writePropertiesList(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "std::vector<std::string> " << state.name << "::InternalGetProperties() {\n";
|
||||
out << " std::vector<std::string> properties = " << state.baseClass << "::InternalGetProperties();\n";
|
||||
|
||||
|
@ -163,7 +166,7 @@ void writePropertiesList(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "};\n\n";
|
||||
}
|
||||
|
||||
void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
static void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
||||
out << "result<PropertyMeta, MemberNotFound> " << state.name << "::InternalGetPropertyMeta(std::string name) {";
|
||||
|
||||
out << "\n ";
|
||||
|
@ -203,7 +206,7 @@ void writePropertyMetaHandler(std::ofstream& out, ClassAnalysis state) {
|
|||
out << "\n};\n\n";
|
||||
}
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) {
|
||||
void object::writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state) {
|
||||
std::string strFlags;
|
||||
if (state.flags & ClassFlag_NotCreatable)
|
||||
strFlags += " | INSTANCE_NOTCREATABLE";
|
||||
|
@ -214,16 +217,13 @@ void writeCodeForClass(std::ofstream& out, ClassAnalysis& state) {
|
|||
if (!strFlags.empty()) strFlags = strFlags.substr(3); // Remove leading pipe
|
||||
else strFlags = "0"; // 0 == No option
|
||||
|
||||
out << "/////////////////////////////////////////////////////////////////////////////////////////\n";
|
||||
out << "// This file was automatically generated by autogen, and should not be edited manually //\n";
|
||||
out << "/////////////////////////////////////////////////////////////////////////////////////////\n\n";
|
||||
|
||||
std::string constructorStr;
|
||||
if (state.abstract) constructorStr = "nullptr";
|
||||
else constructorStr = "&" + state.name + "::Create";
|
||||
|
||||
out << "#define __AUTOGEN_EXTRA_INCLUDES__\n";
|
||||
out << "#include \"" << state.headerPath << "\"\n\n";
|
||||
out << "#include \"datatypes/meta.h\"\n\n";
|
||||
out << "const InstanceType " << state.name << "::TYPE = {\n"
|
||||
<< " .super = &" << state.baseClass << "::TYPE,\n"
|
||||
<< " .className = \"" << state.name << "\",\n"
|
10
autogen/src/object/codegen.h
Normal file
10
autogen/src/object/codegen.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "analysis.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace object {
|
||||
|
||||
void writeCodeForClass(std::ofstream& out, std::string headerPath, ClassAnalysis& state);
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "util.h"
|
||||
#include <clang-c/CXString.h>
|
||||
#include <optional>
|
||||
|
||||
static CXChildVisitResult _visitorFunc(CXCursor cursor, CXCursor parent, CXClientData client_data) {
|
||||
X_CXCursorVisitor* func = (X_CXCursorVisitor*)client_data;
|
||||
|
@ -14,4 +15,97 @@ std::string x_clang_toString(CXString string) {
|
|||
std::string str(clang_getCString(string));
|
||||
clang_disposeString(string);
|
||||
return str;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> parseAnnotationString(std::string src) {
|
||||
std::map<std::string, std::string> result;
|
||||
|
||||
std::string currentIdent = "";
|
||||
std::string currentValue = "";
|
||||
int stage = 0;
|
||||
bool quoted = false;
|
||||
|
||||
int i = 0;
|
||||
for (; i < src.length(); i++) {
|
||||
if (src[i] == ' ' && (stage != 2 || !quoted)) continue; // Ignore spaces if not in stage 2 and quoted
|
||||
if (src[i] == ',' && stage == 0) continue; // Let empty commas slip by
|
||||
if (stage < 2 && (src[i] >= 'a' && src[i] <= 'z' || src[i] >= 'A' && src[i] <= 'Z' || src[i] >= '0' && src[i] <= '9' || src[i] == '_')) {
|
||||
currentIdent += src[i];
|
||||
stage = 1;
|
||||
continue;
|
||||
}
|
||||
if (stage == 1 && src[i] == '=') { // What follows is a value
|
||||
stage = 2;
|
||||
continue;
|
||||
}
|
||||
if (stage == 1 && src[i] == ',') { // Value-less key
|
||||
stage = 0;
|
||||
result[currentIdent] = "";
|
||||
currentIdent = "";
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && quoted && src[i] == '"') { // Close a quoted string
|
||||
quoted = false;
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && !quoted && src[i] == '"') { // Start a quoted string
|
||||
quoted = true;
|
||||
continue;
|
||||
}
|
||||
if (stage == 2 && !quoted && (src[i] == ' ' || src[i] == ',')) { // Terminate the string
|
||||
stage = 0;
|
||||
result[currentIdent] = currentValue;
|
||||
currentIdent = "";
|
||||
currentValue = "";
|
||||
continue;
|
||||
}
|
||||
if (stage == 2) { // Otherwise if in stage 2, simply add the character
|
||||
currentValue += src[i];
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "Unexpected symbol: %c at index %d\n", src[i], i);
|
||||
fprintf(stderr, "\t%s\n", src.c_str());
|
||||
fprintf(stderr, "\t%s^\n", i > 0 ? std::string(i, '~').c_str() : "");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Add the remaining value
|
||||
if (stage == 1) {
|
||||
result[currentIdent] = "";
|
||||
} else if (stage == 2) {
|
||||
result[currentIdent] = currentValue;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> findAnnotation(CXCursor cur, std::string annotationName) {
|
||||
std::optional<std::string> ret = std::nullopt;
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
if (kind != CXCursor_AnnotateAttr) return CXChildVisit_Continue;
|
||||
|
||||
std::string annString = x_clang_toString(clang_getCursorDisplayName(cur));
|
||||
if (annString != annotationName) return CXChildVisit_Continue;
|
||||
|
||||
// Look inside for a StringLiteral
|
||||
|
||||
x_clang_visitChildren(cur, [&](CXCursor cur, CXCursor parent) {
|
||||
CXCursorKind kind = clang_getCursorKind(cur);
|
||||
// if (kind != CXCursor_StringLiteral) return CXChildVisit_Recurse;
|
||||
// String literals cannot be parsed as CXCursor_StringLiteral. I don't know why.
|
||||
if (kind != CXCursor_UnexposedExpr) return CXChildVisit_Recurse;
|
||||
|
||||
// https://stackoverflow.com/a/63859988/16255372
|
||||
auto res = clang_Cursor_Evaluate(cur);
|
||||
ret = clang_EvalResult_getAsStr(res);
|
||||
clang_EvalResult_dispose(res);
|
||||
|
||||
return CXChildVisit_Break;
|
||||
});
|
||||
|
||||
return CXChildVisit_Break;
|
||||
});
|
||||
return ret;
|
||||
}
|
|
@ -2,10 +2,21 @@
|
|||
|
||||
#include <clang-c/Index.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
typedef std::function<CXChildVisitResult(CXCursor cursor, CXCursor parent)> X_CXCursorVisitor;
|
||||
|
||||
unsigned x_clang_visitChildren(CXCursor parent, X_CXCursorVisitor visitor);
|
||||
|
||||
std::string x_clang_toString(CXString string);
|
||||
std::string x_clang_toString(CXString string);
|
||||
|
||||
// Very simple parser
|
||||
// Example format:
|
||||
// name="Hello!", world=Test, read_only
|
||||
// Result:
|
||||
// "name": "Hello!", "world": "Test", "read_only": ""
|
||||
std::map<std::string, std::string> parseAnnotationString(std::string src);
|
||||
|
||||
std::optional<std::string> findAnnotation(CXCursor cur, std::string annotationName);
|
|
@ -16,12 +16,12 @@ find_package(PkgConfig REQUIRED)
|
|||
pkg_check_modules(LUAJIT REQUIRED luajit)
|
||||
|
||||
# Run autogen
|
||||
file(GLOB_RECURSE AUTOGEN_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src/objects" "src/objects/*.h")
|
||||
file(GLOB_RECURSE AUTOGEN_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src" "src/objects/*.h" "src/datatypes/*.h")
|
||||
|
||||
# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Custom%20Commands.html
|
||||
foreach (SRC ${AUTOGEN_SOURCES})
|
||||
string(REGEX REPLACE "[.]h$" ".cpp" OUT_SRC_NAME ${SRC})
|
||||
set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/objects/${SRC}")
|
||||
set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/${SRC}")
|
||||
set(OUT_PATH "${CMAKE_BINARY_DIR}/generated/${OUT_SRC_NAME}")
|
||||
|
||||
add_custom_command(
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// TEMPORARY COMMON DATA FOR DIFFERENT INTERNAL COMPONENTS
|
||||
|
||||
#include "objects/datamodel.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "common.h"
|
||||
#include <memory>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "objects/base/instance.h"
|
||||
#include "objects/handles.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "camera.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
|
33
core/src/datatypes/annotation.h
Normal file
33
core/src/datatypes/annotation.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
// Markers for the autogen engine to generate getters, setters, lua, etc.
|
||||
|
||||
// Base macros
|
||||
#ifdef __AUTOGEN__
|
||||
#define def_data(...) clang::annotate("OB::def_data", #__VA_ARGS__)
|
||||
#define def_data_prop(...) clang::annotate("OB::def_data_prop", #__VA_ARGS__)
|
||||
#define def_data_method(...) clang::annotate("OB::def_data_method", #__VA_ARGS__)
|
||||
#define def_data_ctor(...) clang::annotate("OB::def_data_ctor", #__VA_ARGS__)
|
||||
#else
|
||||
#define def_data(...)
|
||||
#define def_data_prop(...)
|
||||
#define def_data_method(...)
|
||||
#define def_data_ctor(...)
|
||||
#define def_data_op(...)
|
||||
#endif
|
||||
|
||||
// Helper macros
|
||||
#define DEF_DATA [[ def_data() ]] // Data declaration
|
||||
#define DEF_DATA_(...) [[ def_data(__VA_ARGS__) ]]
|
||||
#define DEF_DATA_PROP [[ def_data_prop() ]] // Property. Getter or field
|
||||
#define DEF_DATA_METHOD [[ def_data_method() ]] // Method
|
||||
#define DEF_DATA_CTOR [[ def_data_ctor() ]] // Constructor (i.e. .new)
|
||||
#define DEF_DATA_OP [[ def_data_op() ]] // Operator (e.g. +, -, *)
|
||||
|
||||
#define AUTOGEN_PREAMBLE_DATA \
|
||||
public: \
|
||||
virtual const TypeInfo& GetType() const override; \
|
||||
static const TypeInfo TYPE; \
|
||||
virtual void PushLuaValue(lua_State*) const override; \
|
||||
static result<Data::Variant, LuaCastError> FromLuaValue(lua_State*, int idx); \
|
||||
private:
|
|
@ -3,6 +3,7 @@
|
|||
#include "meta.h"
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <pugixml.hpp>
|
||||
#include "lua.h"
|
||||
|
||||
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
#include <string>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <pugixml.hpp>
|
||||
#include "error/result.h"
|
||||
#include "error/data.h"
|
||||
|
||||
extern "C" { typedef struct lua_State lua_State; }
|
||||
|
||||
namespace pugi { class xml_node; };
|
||||
|
||||
#define DEF_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE) class CLASS_NAME : public Data::Base { \
|
||||
const WRAPPED_TYPE value; \
|
||||
WRAPPED_TYPE value; \
|
||||
public: \
|
||||
CLASS_NAME(WRAPPED_TYPE); \
|
||||
~CLASS_NAME(); \
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
#include "datatypes/meta.h"
|
||||
#include <pugixml.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
// #include "meta.h" // IWYU pragma: keep
|
||||
|
@ -56,12 +58,6 @@ Data::CFrame::CFrame(Vector3 position, Vector3 lookAt, Vector3 up)
|
|||
}
|
||||
|
||||
Data::CFrame::~CFrame() = default;
|
||||
const Data::TypeInfo Data::CFrame::TYPE = {
|
||||
.name = "CoordinateFrame",
|
||||
.deserializer = &Data::CFrame::Deserialize,
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::CFrame::GetType() const { return Vector3::TYPE; };
|
||||
|
||||
const Data::String Data::CFrame::ToString() const {
|
||||
return std::to_string(X()) + ", " + std::to_string(Y()) + ", " + std::to_string(Z());
|
||||
|
@ -149,9 +145,4 @@ Data::Variant Data::CFrame::Deserialize(pugi::xml_node node) {
|
|||
node.child("R21").text().as_float(),
|
||||
node.child("R22").text().as_float()
|
||||
);
|
||||
}
|
||||
|
||||
void Data::CFrame::PushLuaValue(lua_State* L) const {
|
||||
// TODO:
|
||||
panic();
|
||||
}
|
|
@ -1,17 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "datatypes/annotation.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include <glm/ext/quaternion_float.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
#include <reactphysics3d/reactphysics3d.h>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
namespace reactphysics3d { class Transform; };
|
||||
|
||||
namespace Data {
|
||||
class CFrame : Base {
|
||||
class DEF_DATA_(name="CoordinateFrame") CFrame : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
|
||||
glm::vec3 translation;
|
||||
glm::mat3 rotation;
|
||||
|
||||
|
@ -20,50 +21,48 @@ namespace Data {
|
|||
// CFrame(float x, float y, float z);
|
||||
// CFrame(const glm::vec3&);
|
||||
// CFrame(const rp::Vector3&);
|
||||
CFrame();
|
||||
CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
||||
CFrame(const rp::Transform&);
|
||||
DEF_DATA_CTOR CFrame();
|
||||
DEF_DATA_CTOR CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22);
|
||||
DEF_DATA_CTOR CFrame(Vector3 , Vector3 lookAt, Vector3 up = Vector3(0, 1, 0));
|
||||
CFrame(const reactphysics3d::Transform&);
|
||||
CFrame(Vector3 position, glm::quat quat);
|
||||
CFrame(Vector3 position, Vector3 lookAt, Vector3 up = Vector3(0, 1, 0));
|
||||
~CFrame();
|
||||
|
||||
// Same as CFrame(position, position + toward), but makes sure that up and toward are not linearly dependant
|
||||
static CFrame pointToward(Vector3 position, Vector3 toward);
|
||||
|
||||
static const CFrame IDENTITY;
|
||||
DEF_DATA_PROP static const CFrame IDENTITY;
|
||||
static const CFrame YToZ;
|
||||
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node parent) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::mat4() const;
|
||||
operator rp::Transform() const;
|
||||
operator reactphysics3d::Transform() const;
|
||||
|
||||
//inline static CFrame identity() { }
|
||||
inline Vector3 Position() const { return translation; }
|
||||
inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; }
|
||||
CFrame Inverse() const;
|
||||
inline float X() const { return translation.x; }
|
||||
inline float Y() const { return translation.y; }
|
||||
inline float Z() const { return translation.z; }
|
||||
DEF_DATA_PROP inline Vector3 Position() const { return translation; }
|
||||
DEF_DATA_PROP inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; }
|
||||
DEF_DATA_METHOD CFrame Inverse() const;
|
||||
DEF_DATA_PROP inline float X() const { return translation.x; }
|
||||
DEF_DATA_PROP inline float Y() const { return translation.y; }
|
||||
DEF_DATA_PROP inline float Z() const { return translation.z; }
|
||||
|
||||
inline Vector3 RightVector() { return glm::column(rotation, 0); }
|
||||
inline Vector3 UpVector() { return glm::column(rotation, 1); }
|
||||
inline Vector3 LookVector() { return -glm::column(rotation, 2); }
|
||||
DEF_DATA_PROP inline Vector3 RightVector() { return glm::column(rotation, 0); }
|
||||
DEF_DATA_PROP inline Vector3 UpVector() { return glm::column(rotation, 1); }
|
||||
DEF_DATA_PROP inline Vector3 LookVector() { return -glm::column(rotation, 2); }
|
||||
|
||||
Vector3 ToEulerAnglesXYZ();
|
||||
static CFrame FromEulerAnglesXYZ(Vector3);
|
||||
DEF_DATA_METHOD Vector3 ToEulerAnglesXYZ();
|
||||
DEF_DATA_METHOD static CFrame FromEulerAnglesXYZ(Vector3);
|
||||
|
||||
// Operators
|
||||
Data::CFrame operator *(Data::CFrame) const;
|
||||
Vector3 operator *(Vector3) const;
|
||||
Data::CFrame operator +(Vector3) const;
|
||||
Data::CFrame operator -(Vector3) const;
|
||||
DEF_DATA_OP Data::CFrame operator *(Data::CFrame) const;
|
||||
DEF_DATA_OP Vector3 operator *(Vector3) const;
|
||||
DEF_DATA_OP Data::CFrame operator +(Vector3) const;
|
||||
DEF_DATA_OP Data::CFrame operator -(Vector3) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
#include "color3.h"
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include "panic.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
Data::Color3::Color3(float r, float g, float b) : r(std::clamp(r, 0.f, 1.f)), g(std::clamp(g, 0.f, 1.f)), b(std::clamp(b, 0.f, 1.f)) {};
|
||||
Data::Color3::Color3(const glm::vec3& vec) : r(std::clamp(vec.x, 0.f, 1.f)), g(std::clamp(vec.y, 0.f, 1.f)), b(std::clamp(vec.z, 0.f, 1.f)) {};
|
||||
|
||||
Data::Color3::~Color3() = default;
|
||||
const Data::TypeInfo Data::Color3::TYPE = {
|
||||
.name = "Color3",
|
||||
.deserializer = &Data::Color3::Deserialize,
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::Color3::GetType() const { return Data::Color3::TYPE; };
|
||||
|
||||
const Data::String Data::Color3::ToString() const {
|
||||
return std::to_string(int(r*256)) + ", " + std::to_string(int(g*256)) + ", " + std::to_string(int(b*256));
|
||||
|
@ -45,10 +41,4 @@ void Data::Color3::Serialize(pugi::xml_node node) const {
|
|||
|
||||
Data::Variant Data::Color3::Deserialize(pugi::xml_node node) {
|
||||
return Color3::FromHex(node.text().get());
|
||||
}
|
||||
|
||||
|
||||
void Data::Color3::PushLuaValue(lua_State* L) const {
|
||||
// TODO:
|
||||
panic();
|
||||
}
|
|
@ -1,39 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include <glm/ext/quaternion_geometric.hpp>
|
||||
#include "datatypes/annotation.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <reactphysics3d/reactphysics3d.h>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
|
||||
namespace Data {
|
||||
class Color3 : Base {
|
||||
class DEF_DATA Color3 : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
|
||||
public:
|
||||
Color3(float r, float g, float b);
|
||||
DEF_DATA_CTOR Color3(float r, float g, float b);
|
||||
Color3(const glm::vec3&);
|
||||
~Color3();
|
||||
|
||||
static Color3 FromHex(std::string hex);
|
||||
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
DEF_DATA_METHOD static Color3 FromHex(std::string hex);
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
std::string ToHex() const;
|
||||
DEF_DATA_METHOD std::string ToHex() const;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::vec3() const;
|
||||
|
||||
inline float R() const { return r; }
|
||||
inline float G() const { return g; }
|
||||
inline float B() const { return b; }
|
||||
DEF_DATA_PROP inline float R() const { return r; }
|
||||
DEF_DATA_PROP inline float G() const { return g; }
|
||||
DEF_DATA_PROP inline float B() const { return b; }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "datatypes/ref.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <variant>
|
||||
|
||||
Data::String Data::Variant::ToString() const {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "objects/base/instance.h"
|
||||
#include "lua.h"
|
||||
#include "objects/base/member.h"
|
||||
#include <pugixml.hpp>
|
||||
|
||||
Data::InstanceRef::InstanceRef() {};
|
||||
Data::InstanceRef::InstanceRef(std::weak_ptr<Instance> instance) : ref(instance) {};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
class Instance;
|
||||
|
||||
namespace Data {
|
||||
class InstanceRef : Base {
|
||||
class InstanceRef : public Base {
|
||||
std::weak_ptr<Instance> ref;
|
||||
public:
|
||||
InstanceRef();
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
#include "vector.h"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <glm/ext/quaternion_geometric.hpp>
|
||||
#include <iomanip>
|
||||
#include <reactphysics3d/mathematics/Vector3.h>
|
||||
#include <string>
|
||||
#include "meta.h" // IWYU pragma: keep
|
||||
#include "panic.h"
|
||||
#include <pugixml.hpp>
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
|
||||
Data::Vector3::Vector3() : vector(glm::vec3(0, 0, 0)) {};
|
||||
Data::Vector3::Vector3(const glm::vec3& src) : vector(src) {};
|
||||
|
@ -10,13 +18,6 @@ Data::Vector3::Vector3(const rp::Vector3& src) : vector(glm::vec3(src.x, src.y,
|
|||
Data::Vector3::Vector3(float x, const float y, float z) : vector(glm::vec3(x, y, z)) {};
|
||||
|
||||
Data::Vector3::~Vector3() = default;
|
||||
const Data::TypeInfo Data::Vector3::TYPE = {
|
||||
.name = "Vector3",
|
||||
.deserializer = &Data::Vector3::Deserialize,
|
||||
.fromString = &Data::Vector3::FromString,
|
||||
};
|
||||
|
||||
const Data::TypeInfo& Data::Vector3::GetType() const { return Data::Vector3::TYPE; };
|
||||
|
||||
Data::Vector3 Data::Vector3::ZERO(0, 0, 0);
|
||||
Data::Vector3 Data::Vector3::ONE(1, 1, 1);
|
||||
|
@ -109,9 +110,4 @@ std::optional<Data::Variant> Data::Vector3::FromString(std::string string) {
|
|||
}
|
||||
|
||||
return Data::Vector3(components[0], components[1], components[2]);
|
||||
}
|
||||
|
||||
void Data::Vector3::PushLuaValue(lua_State* L) const {
|
||||
// TODO:
|
||||
panic();
|
||||
}
|
|
@ -1,61 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include <glm/ext/quaternion_geometric.hpp>
|
||||
#include "datatypes/annotation.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <reactphysics3d/reactphysics3d.h>
|
||||
#include <glm/geometric.hpp>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
namespace reactphysics3d { class Vector3; };
|
||||
|
||||
namespace Data {
|
||||
class Vector3 : Base {
|
||||
class DEF_DATA_(from_string) Vector3 : public Base {
|
||||
AUTOGEN_PREAMBLE_DATA
|
||||
glm::vec3 vector;
|
||||
|
||||
public:
|
||||
Vector3();
|
||||
Vector3(float x, float y, float z);
|
||||
DEF_DATA_CTOR Vector3();
|
||||
DEF_DATA_CTOR Vector3(float x, float y, float z);
|
||||
Vector3(const glm::vec3&);
|
||||
Vector3(const rp::Vector3&);
|
||||
Vector3(const reactphysics3d::Vector3&);
|
||||
~Vector3();
|
||||
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
static Data::Vector3 ZERO;
|
||||
static Data::Vector3 ONE;
|
||||
DEF_DATA_PROP static Data::Vector3 ZERO;
|
||||
DEF_DATA_PROP static Data::Vector3 ONE;
|
||||
|
||||
virtual const Data::String ToString() const override;
|
||||
virtual void Serialize(pugi::xml_node node) const override;
|
||||
virtual void PushLuaValue(lua_State*) const override;
|
||||
|
||||
static Data::Variant Deserialize(pugi::xml_node node);
|
||||
static std::optional<Data::Variant> FromString(std::string);
|
||||
|
||||
static void PushLuaLibrary(lua_State*);
|
||||
|
||||
operator glm::vec3() const;
|
||||
operator rp::Vector3() const;
|
||||
operator reactphysics3d::Vector3() const;
|
||||
|
||||
inline float X() const { return vector.x; }
|
||||
inline float Y() const { return vector.y; }
|
||||
inline float Z() const { return vector.z; }
|
||||
inline float Magnitude() const { return glm::length(vector); }
|
||||
inline Data::Vector3 Unit() const { return glm::normalize(vector); }
|
||||
inline Data::Vector3 Abs() const { return glm::abs(vector); }
|
||||
DEF_DATA_PROP inline float X() const { return vector.x; }
|
||||
DEF_DATA_PROP inline float Y() const { return vector.y; }
|
||||
DEF_DATA_PROP inline float Z() const { return vector.z; }
|
||||
DEF_DATA_METHOD inline float Magnitude() const { return glm::length(vector); }
|
||||
DEF_DATA_METHOD inline Data::Vector3 Unit() const { return glm::normalize(vector); }
|
||||
DEF_DATA_METHOD inline Data::Vector3 Abs() const { return glm::abs(vector); }
|
||||
|
||||
Data::Vector3 Cross(Data::Vector3) const;
|
||||
float Dot(Data::Vector3) const;
|
||||
DEF_DATA_METHOD Data::Vector3 Cross(Data::Vector3) const;
|
||||
DEF_DATA_METHOD float Dot(Data::Vector3) const;
|
||||
|
||||
// Operators
|
||||
Data::Vector3 operator *(float) const;
|
||||
Data::Vector3 operator /(float) const;
|
||||
Data::Vector3 operator *(Data::Vector3) const; // Component-wise
|
||||
Data::Vector3 operator +(Data::Vector3) const;
|
||||
Data::Vector3 operator -(Data::Vector3) const;
|
||||
Data::Vector3 operator -() const;
|
||||
DEF_DATA_OP Data::Vector3 operator *(float) const;
|
||||
DEF_DATA_OP Data::Vector3 operator /(float) const;
|
||||
DEF_DATA_OP Data::Vector3 operator *(Data::Vector3) const; // Component-wise
|
||||
DEF_DATA_OP Data::Vector3 operator +(Data::Vector3) const;
|
||||
DEF_DATA_OP Data::Vector3 operator -(Data::Vector3) const;
|
||||
DEF_DATA_OP Data::Vector3 operator -() const;
|
||||
|
||||
bool operator <(Data::Vector3) const;
|
||||
bool operator >(Data::Vector3) const;
|
||||
DEF_DATA_OP bool operator <(Data::Vector3) const;
|
||||
DEF_DATA_OP bool operator >(Data::Vector3) const;
|
||||
|
||||
bool operator ==(Data::Vector3) const;
|
||||
DEF_DATA_OP bool operator ==(Data::Vector3) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "error/instance.h"
|
||||
#include "objects/base/member.h"
|
||||
#include "objects/base/refstate.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/meta.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <pugixml.hpp>
|
||||
#include "ptr_helpers.h"
|
||||
|
||||
// Static so that this variable name is "local" to this source file
|
||||
|
@ -32,15 +34,6 @@ const InstanceType Instance::TYPE = {
|
|||
// return &TYPE_;
|
||||
// }
|
||||
|
||||
constexpr FieldCodec classNameCodec() {
|
||||
return FieldCodec {
|
||||
.write = nullptr,
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return Data::String(((const InstanceType*)source)->className);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Instance::Instance(const InstanceType* type) {
|
||||
this->name = type->className;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "error/instance.h"
|
||||
#include "error/result.h"
|
||||
|
@ -23,6 +22,8 @@ typedef std::shared_ptr<Instance>(*InstanceConstructor)();
|
|||
class DataModel;
|
||||
class Workspace;
|
||||
|
||||
namespace pugi { class xml_node; };
|
||||
|
||||
typedef int InstanceFlags;
|
||||
// This instance should only be instantiated in special circumstances (i.e. by DataModel) and should be creatable directly via any API
|
||||
const InstanceFlags INSTANCE_NOTCREATABLE = (InstanceFlags)1<<0;
|
||||
|
|
|
@ -1,48 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../datatypes/base.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "datatypes/base.h"
|
||||
#include <variant>
|
||||
|
||||
class Instance;
|
||||
|
||||
struct FieldCodec {
|
||||
void (*write)(Data::Variant source, void* destination);
|
||||
Data::Variant (*read)(void* source);
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr FieldCodec fieldCodecOf() {
|
||||
return FieldCodec {
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
*(U*)destination = (U)source.get<T>();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return T(*(U*)source);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr FieldCodec fieldCodecOf() {
|
||||
return FieldCodec {
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
*(T*)destination = source.get<T>();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return *(T*)source;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::function<void(std::string name)> memberFunctionOf(void(T::*func)(std::string), T* obj) {
|
||||
return std::bind(func, obj, std::placeholders::_1);
|
||||
}
|
||||
|
||||
typedef int PropertyFlags;
|
||||
const PropertyFlags PROP_HIDDEN = 1 << 0; // Hidden from the editor
|
||||
|
@ -56,9 +18,10 @@ enum PropertyCategory {
|
|||
PROP_CATEGORY_BEHAVIOR,
|
||||
PROP_CATEGORY_PART,
|
||||
PROP_CATEGORY_SURFACE,
|
||||
PROP_CATEGORY_SURFACE_INPUT,
|
||||
};
|
||||
|
||||
const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE;
|
||||
const int PROPERTY_CATEGORY_MAX = PROP_CATEGORY_SURFACE_INPUT;
|
||||
|
||||
struct PropertyMeta {
|
||||
const Data::TypeInfo* type;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "service.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <memory>
|
||||
|
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
// Services are top-level singletons and belong to a specific DataModel
|
||||
// They serve one specific task and can be accessed using game:GetService
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include <memory>
|
||||
|
||||
class DataModel;
|
||||
|
||||
class Service : public Instance {
|
||||
protected:
|
||||
Service(const InstanceType* type);
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
#include "objects/base/service.h"
|
||||
#include "objects/meta.h"
|
||||
#include "objects/script/serverscriptservice.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "workspace.h"
|
||||
#include "logger.h"
|
||||
#include "panic.h"
|
||||
#include <pugixml.hpp>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "../annotation.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||
#include "datatypes/cframe.h"
|
||||
|
||||
//this is necessary ebcause we use std::weak_ptr<Part> without including it in this file
|
||||
#ifdef __AUTOGEN_EXTRA_INCLUDES__
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
namespace reactphysics3d { class HingeJoint; }
|
||||
|
||||
class DEF_INST Rotate : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::HingeJoint* joint = nullptr;
|
||||
reactphysics3d::HingeJoint* joint = nullptr;
|
||||
|
||||
virtual void buildJoint() override;
|
||||
virtual void breakJoint() override;
|
||||
|
|
54
core/src/objects/joint/rotatev.cpp
Normal file
54
core/src/objects/joint/rotatev.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include "rotatev.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/part.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include <reactphysics3d/constraint/HingeJoint.h>
|
||||
|
||||
#define PI 3.14159
|
||||
|
||||
RotateV::RotateV(): JointInstance(&TYPE) {
|
||||
}
|
||||
|
||||
RotateV::~RotateV() {
|
||||
}
|
||||
static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0, 1));
|
||||
|
||||
void RotateV::buildJoint() {
|
||||
// Only if both parts are set, are not the same part, are part of a workspace, and are part of the same workspace, we build the joint
|
||||
if (part0.expired() || part1.expired() || part0.lock() == part1.lock() || !workspaceOfPart(part0.lock()) || workspaceOfPart(part0.lock()) != workspaceOfPart(part1.lock())) return;
|
||||
|
||||
// Don't build the joint if we're not part of either a workspace or JointsService
|
||||
if ((!GetParent() || GetParent().value()->GetClass() != &JointsService::TYPE) && !workspace()) return;
|
||||
|
||||
std::shared_ptr<Workspace> workspace = workspaceOfPart(part0.lock()).value();
|
||||
if (!workspace->physicsWorld) return;
|
||||
|
||||
|
||||
// Update Part1's rotation and cframe prior to creating the joint as reactphysics3d locks rotation based on how it
|
||||
// used to be rather than specifying an anchor rotation, so whatever.
|
||||
CFrame newFrame = part0.lock()->cframe * (c0 * c1.Inverse());
|
||||
part1.lock()->cframe = newFrame;
|
||||
workspace->SyncPartPhysics(part1.lock());
|
||||
// Do NOT use Abs() in this scenario. For some reason that breaks it
|
||||
rp::HingeJointInfo jointInfo(part0.lock()->rigidBody, part1.lock()->rigidBody, (part0.lock()->cframe * c0).Position(), -(part0.lock()->cframe * c0).LookVector().Unit());
|
||||
|
||||
jointInfo.isCollisionEnabled = false;
|
||||
|
||||
this->joint = dynamic_cast<rp::HingeJoint*>(workspace->physicsWorld->createJoint(jointInfo));
|
||||
jointWorkspace = workspace;
|
||||
|
||||
|
||||
// part1.lock()->rigidBody->getCollider(0)->setCollideWithMaskBits(0b10);
|
||||
// part1.lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b10);
|
||||
// part0.lock()->rigidBody->getCollider(0)->setCollideWithMaskBits(0b01);
|
||||
// part0.lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b01);
|
||||
}
|
||||
|
||||
void RotateV::breakJoint() {
|
||||
// If the joint doesn't exist, or its workspace expired (not our problem anymore), then no need to do anything
|
||||
if (!this->joint || jointWorkspace.expired() || !jointWorkspace.lock()->physicsWorld) return;
|
||||
|
||||
jointWorkspace.lock()->physicsWorld->destroyJoint(this->joint);
|
||||
this->joint = nullptr;
|
||||
}
|
22
core/src/objects/joint/rotatev.h
Normal file
22
core/src/objects/joint/rotatev.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/annotation.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
namespace reactphysics3d { class HingeJoint; }
|
||||
|
||||
class DEF_INST RotateV : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
reactphysics3d::HingeJoint* joint = nullptr;
|
||||
|
||||
virtual void buildJoint() override;
|
||||
virtual void breakJoint() override;
|
||||
public:
|
||||
RotateV();
|
||||
~RotateV();
|
||||
|
||||
static inline std::shared_ptr<RotateV> New() { return std::make_shared<RotateV>(); };
|
||||
static inline std::shared_ptr<Instance> Create() { return std::make_shared<RotateV>(); };
|
||||
};
|
|
@ -5,10 +5,12 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
namespace reactphysics3d { class FixedJoint; }
|
||||
|
||||
class DEF_INST Snap : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
reactphysics3d::FixedJoint* joint = nullptr;
|
||||
|
||||
virtual void buildJoint() override;
|
||||
virtual void breakJoint() override;
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include "objects/joint/jointinstance.h"
|
||||
#include <memory>
|
||||
|
||||
namespace reactphysics3d { class FixedJoint; }
|
||||
|
||||
class DEF_INST Weld : public JointInstance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
||||
rp::FixedJoint* joint = nullptr;
|
||||
reactphysics3d::FixedJoint* joint = nullptr;
|
||||
|
||||
virtual void buildJoint() override;
|
||||
virtual void breakJoint() override;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "jointsservice.h"
|
||||
#include "workspace.h"
|
||||
#include "datamodel.h"
|
||||
#include <memory>
|
||||
|
||||
JointsService::JointsService(): Service(&TYPE) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "meta.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
#include "objects/joint/rotate.h"
|
||||
#include "objects/joint/rotatev.h"
|
||||
#include "objects/joint/weld.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/part.h"
|
||||
#include "objects/joint/snap.h"
|
||||
|
@ -7,6 +10,7 @@
|
|||
#include "objects/script/scriptcontext.h"
|
||||
#include "objects/script/serverscriptservice.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "objects/datamodel.h"
|
||||
|
||||
std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
||||
{ "Instance", &Instance::TYPE },
|
||||
|
@ -14,6 +18,9 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
|
|||
|
||||
{ "Part", &Part::TYPE },
|
||||
{ "Snap", &Snap::TYPE },
|
||||
{ "Weld", &Weld::TYPE },
|
||||
{ "Rotate", &Rotate::TYPE },
|
||||
{ "RotateV", &RotateV::TYPE },
|
||||
{ "JointInstance", &JointInstance::TYPE },
|
||||
{ "Script", &Script::TYPE },
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "datatypes/vector.h"
|
||||
#include "objects/base/member.h"
|
||||
#include "objects/joint/rotate.h"
|
||||
#include "objects/joint/rotatev.h"
|
||||
#include "objects/joint/weld.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
|
@ -18,42 +19,6 @@
|
|||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
// template <typename T, typename U>
|
||||
// constexpr FieldCodec fieldCodecOf() {
|
||||
// return FieldCodec {
|
||||
// .write = [](Data::Variant source, void* destination) {
|
||||
// *(U*)destination = (U)source.get<T>();
|
||||
// },
|
||||
// .read = [](void* source) -> Data::Variant {
|
||||
// return T(*(U*)source);
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
constexpr FieldCodec cframePositionCodec() {
|
||||
return FieldCodec {
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
CFrame* cframe = static_cast<CFrame*>(destination);
|
||||
*cframe = cframe->Rotation() + source.get<Vector3>();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return static_cast<CFrame*>(source)->Position();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
constexpr FieldCodec cframeRotationCodec() {
|
||||
return FieldCodec {
|
||||
.write = [](Data::Variant source, void* destination) {
|
||||
CFrame* cframe = static_cast<CFrame*>(destination);
|
||||
*cframe = CFrame::FromEulerAnglesXYZ(source.get<Vector3>()) + cframe->Position();
|
||||
},
|
||||
.read = [](void* source) -> Data::Variant {
|
||||
return static_cast<CFrame*>(source)->ToEulerAnglesXYZ();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Part::Part(): Part(PartConstructParams { .size = glm::vec3(2, 1.2, 4), .color = Color3(0.639216f, 0.635294f, 0.647059f) }) {
|
||||
}
|
||||
|
||||
|
@ -93,7 +58,8 @@ void Part::onUpdated(std::string property) {
|
|||
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
|
||||
|
||||
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
|
||||
BreakJoints();
|
||||
if (property != "Anchored")
|
||||
BreakJoints();
|
||||
}
|
||||
|
||||
// Expands provided extents to fit point
|
||||
|
@ -157,6 +123,32 @@ SurfaceType Part::surfaceFromFace(NormalId face) {
|
|||
return SurfaceSmooth; // Unreachable
|
||||
}
|
||||
|
||||
float Part::GetSurfaceParamA(Vector3 face) {
|
||||
// printVec(face);
|
||||
// printf("Enum: %d\n", faceFromNormal(face));
|
||||
switch (faceFromNormal(face)) {
|
||||
case Top: return topParamA;
|
||||
case Bottom: return bottomParamA;
|
||||
case Right: return rightParamA;
|
||||
case Left: return leftParamA;
|
||||
case Front: return frontParamA;
|
||||
case Back: return backParamA;
|
||||
}
|
||||
return 0; // Unreachable
|
||||
}
|
||||
|
||||
float Part::GetSurfaceParamB(Vector3 face) {
|
||||
switch (faceFromNormal(face)) {
|
||||
case Top: return topParamB;
|
||||
case Bottom: return bottomParamB;
|
||||
case Right: return rightParamB;
|
||||
case Left: return leftParamB;
|
||||
case Front: return frontParamB;
|
||||
case Back: return backParamB;
|
||||
}
|
||||
return 0; // Unreachable
|
||||
}
|
||||
|
||||
bool Part::checkJointContinuity(std::shared_ptr<Part> otherPart) {
|
||||
// Make sure that the two parts don't depend on one another
|
||||
|
||||
|
@ -214,6 +206,8 @@ std::optional<std::shared_ptr<JointInstance>> makeJointFromSurfaces(SurfaceType
|
|||
return Snap::New();
|
||||
if (a == SurfaceHinge)
|
||||
return Rotate::New();
|
||||
if (a == SurfaceMotor)
|
||||
return RotateV::New();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -257,7 +251,7 @@ void Part::MakeJoints() {
|
|||
SurfaceType otherSurface = surfaceFromFace(faceFromNormal(otherFace));
|
||||
|
||||
// If it is a hinge, only attach if actually touching the "hinge"
|
||||
if (mySurface == SurfaceHinge && !checkSurfacesTouching(surfaceFrame, Vector3(0.4, 0.4, 0.4), myFace, otherFace, otherPart)) continue;
|
||||
if ((mySurface == SurfaceHinge || mySurface == SurfaceMotor) && !checkSurfacesTouching(surfaceFrame, Vector3(0.4, 0.4, 0.4), myFace, otherFace, otherPart)) continue;
|
||||
|
||||
// Create contacts
|
||||
// Contact always occurs at the center of Part0's surface (even if that point does not overlap both surfaces)
|
||||
|
|
|
@ -26,7 +26,7 @@ struct PartConstructParams {
|
|||
bool locked = false;
|
||||
};
|
||||
|
||||
class Snap;
|
||||
class Workspace;
|
||||
|
||||
class DEF_INST_(explorer_icon="part") Part : public Instance {
|
||||
AUTOGEN_PREAMBLE
|
||||
|
@ -46,6 +46,7 @@ protected:
|
|||
bool checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<Part> otherPart);
|
||||
|
||||
friend JointInstance;
|
||||
friend Workspace;
|
||||
|
||||
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
|
||||
void onUpdated(std::string);
|
||||
|
@ -64,7 +65,7 @@ public:
|
|||
bool selected = false;
|
||||
|
||||
DEF_PROP_CATEGORY(BEHAVIOR)
|
||||
DEF_PROP bool anchored = false;
|
||||
DEF_PROP_(on_update=onUpdated) bool anchored = false;
|
||||
DEF_PROP bool locked = false;
|
||||
|
||||
DEF_PROP_CATEGORY(SURFACE)
|
||||
|
@ -75,10 +76,27 @@ public:
|
|||
DEF_PROP SurfaceType frontSurface = SurfaceType::SurfaceSmooth;
|
||||
DEF_PROP SurfaceType backSurface = SurfaceType::SurfaceSmooth;
|
||||
|
||||
DEF_PROP_CATEGORY(SURFACE_INPUT)
|
||||
DEF_PROP float topParamA = -0.5;
|
||||
DEF_PROP float bottomParamA = -0.5;
|
||||
DEF_PROP float leftParamA = -0.5;
|
||||
DEF_PROP float rightParamA = -0.5;
|
||||
DEF_PROP float frontParamA = -0.5;
|
||||
DEF_PROP float backParamA = -0.5;
|
||||
|
||||
DEF_PROP float topParamB = 0.5;
|
||||
DEF_PROP float bottomParamB = 0.5;
|
||||
DEF_PROP float leftParamB = 0.5;
|
||||
DEF_PROP float rightParamB = 0.5;
|
||||
DEF_PROP float frontParamB = 0.5;
|
||||
DEF_PROP float backParamB = 0.5;
|
||||
|
||||
|
||||
rp::RigidBody* rigidBody = nullptr;
|
||||
|
||||
inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); }
|
||||
float GetSurfaceParamA(Vector3 face);
|
||||
float GetSurfaceParamB(Vector3 face);
|
||||
|
||||
Part();
|
||||
Part(PartConstructParams params);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "objects/base/member.h"
|
||||
#include "objects/script/scriptcontext.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "datatypes/ref.h"
|
||||
#include "lua.h"
|
||||
|
||||
Script::Script(): Instance(&TYPE) {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include "scriptcontext.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "logger.h"
|
||||
#include <cstdint>
|
||||
#include <luajit-2.1/lauxlib.h>
|
||||
#include <luajit-2.1/lua.h>
|
||||
#include <luajit-2.1/lualib.h>
|
||||
|
@ -39,6 +43,10 @@ void ScriptContext::InitService() {
|
|||
// luaopen_debug(state);
|
||||
luaopen_bit(state);
|
||||
|
||||
Data::Vector3::PushLuaLibrary(state);
|
||||
Data::CFrame::PushLuaLibrary(state);
|
||||
Data::Color3::PushLuaLibrary(state);
|
||||
|
||||
// TODO: custom os library
|
||||
|
||||
// Override print
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "serverscriptservice.h"
|
||||
#include "objects/script.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "objects/datamodel.h"
|
||||
|
||||
ServerScriptService::ServerScriptService(): Service(&TYPE) {
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include "workspace.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/joint/jointinstance.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "physics/util.h"
|
||||
#include <memory>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
|
||||
rp::PhysicsCommon* Workspace::physicsCommon = new rp::PhysicsCommon;
|
||||
|
@ -102,6 +105,16 @@ void Workspace::PhysicsStep(float deltaTime) {
|
|||
const rp::Transform& transform = part->rigidBody->getTransform();
|
||||
part->cframe = CFrame(transform);
|
||||
part->velocity = part->rigidBody->getLinearVelocity();
|
||||
|
||||
// part->rigidBody->enableGravity(true);
|
||||
for (auto& joint : part->secondaryJoints) {
|
||||
if (joint.expired() || !joint.lock()->IsA("RotateV")) continue;
|
||||
|
||||
std::shared_ptr<JointInstance> motor = joint.lock()->CastTo<JointInstance>().expect();
|
||||
float rate = motor->part0.lock()->GetSurfaceParamB(motor->c0.LookVector().Unit()) * 30;
|
||||
// part->rigidBody->enableGravity(false);
|
||||
part->rigidBody->setAngularVelocity(-(motor->part0.lock()->cframe * motor->c0).LookVector() * rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
#include "objects/annotation.h"
|
||||
#include "objects/base/service.h"
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <memory>
|
||||
#include <reactphysics3d/body/RigidBody.h>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||
|
||||
namespace rp = reactphysics3d;
|
||||
|
||||
struct RaycastResult {
|
||||
rp::Vector3 worldPoint;
|
||||
rp::Vector3 worldNormal;
|
||||
|
@ -28,6 +31,7 @@ class Part;
|
|||
class Snap;
|
||||
class Weld;
|
||||
class Rotate;
|
||||
class RotateV;
|
||||
|
||||
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
|
||||
|
||||
|
@ -41,6 +45,7 @@ class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service {
|
|||
friend Snap;
|
||||
friend Weld;
|
||||
friend Rotate;
|
||||
friend RotateV;
|
||||
protected:
|
||||
void InitService() override;
|
||||
bool initialized = false;
|
||||
|
|
|
@ -17,6 +17,7 @@ enum SurfaceType {
|
|||
SurfaceInlets = 4,
|
||||
SurfaceUniversal = 5,
|
||||
SurfaceHinge = 6,
|
||||
SurfaceMotor = 7,
|
||||
};
|
||||
|
||||
namespace Data { class Vector3; } using Data::Vector3;
|
||||
|
|
|
@ -13,7 +13,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Multimedia LinguistTools)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia LinguistTools)
|
||||
find_package(QScintilla)
|
||||
find_package(QScintilla REQUIRED)
|
||||
|
||||
set(TS_FILES editor_en_US.ts)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "physics/util.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "rendering/shader.h"
|
||||
#include "datatypes/meta.h"
|
||||
|
||||
#define PI 3.14159
|
||||
|
||||
|
|
|
@ -3,12 +3,8 @@
|
|||
#include "common.h"
|
||||
#include "logger.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/jointsservice.h"
|
||||
#include "objects/joint/snap.h"
|
||||
#include "objects/script.h"
|
||||
#include "placedocument.h"
|
||||
#include "script/scriptdocument.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <qclipboard.h>
|
||||
#include <qglobal.h>
|
||||
|
@ -18,6 +14,7 @@
|
|||
#include <qstylefactory.h>
|
||||
#include <qstylehints.h>
|
||||
#include <qmdisubwindow.h>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#ifdef _NDEBUG
|
||||
#define NDEBUG
|
||||
|
@ -145,10 +142,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
placeDocument->init();
|
||||
|
||||
ui->mdiArea->setTabsClosable(true);
|
||||
|
||||
// auto script = Script::New();
|
||||
// gWorkspace()->AddChild(script);
|
||||
// ui->mdiArea->addSubWindow(new ScriptDocument(script));
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* evt) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "panes/propertiesview.h"
|
||||
#include "common.h"
|
||||
#include "datatypes/base.h"
|
||||
#include "datatypes/meta.h"
|
||||
#include "objects/base/member.h"
|
||||
|
||||
#include <QColorDialog>
|
||||
|
@ -249,7 +250,8 @@ QStringList PROPERTY_CATEGORY_NAMES {
|
|||
"Data",
|
||||
"Behavior",
|
||||
"Part",
|
||||
"Surface"
|
||||
"Surface",
|
||||
"Surface Inputs",
|
||||
};
|
||||
|
||||
QModelIndex PropertiesView::indexAt(const QPoint &point) const {
|
||||
|
@ -330,6 +332,9 @@ void PropertiesView::setSelected(std::optional<InstanceRef> instance) {
|
|||
|
||||
// Remove child-less categories
|
||||
for (int i = 0; i <= PROPERTY_CATEGORY_MAX; i++) {
|
||||
if (i == PROP_CATEGORY_SURFACE_INPUT)
|
||||
propertyCategories[(PropertyCategory)i]->setExpanded(false);
|
||||
|
||||
if (propertyCategories[(PropertyCategory)i]->childCount() > 0) continue;
|
||||
int idx = indexOfTopLevelItem(propertyCategories[(PropertyCategory)i]);
|
||||
delete takeTopLevelItem(idx);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include "placedocument.h"
|
||||
#include "common.h"
|
||||
#include "mainglwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "objects/joint/snap.h"
|
||||
#include "objects/script.h"
|
||||
#include "rendering/surface.h"
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
@ -122,6 +124,12 @@ void PlaceDocument::init() {
|
|||
// part0->backSurface = SurfaceWeld;
|
||||
// part1->frontSurface = SurfaceWeld;
|
||||
|
||||
part0->backSurface = SurfaceHinge;
|
||||
// part0->backSurface = SurfaceHinge;
|
||||
part0->backSurface = SurfaceMotor;
|
||||
// part1->frontSurface = SurfaceHinge;
|
||||
|
||||
std::shared_ptr<Script> script = Script::New();
|
||||
gWorkspace()->AddChild(script);
|
||||
MainWindow* mainWnd = dynamic_cast<MainWindow*>(window());
|
||||
mainWnd->openScriptDocument(script);
|
||||
}
|
|
@ -1,14 +1,37 @@
|
|||
#include "scriptdocument.h"
|
||||
|
||||
#include <Qsci/qsciscintilla.h>
|
||||
#include <Qsci/qscilexer.h>
|
||||
#include <Qsci/qscilexerlua.h>
|
||||
#include <Qsci/qsciscintillabase.h>
|
||||
#include <Qsci/qscistyle.h>
|
||||
#include <map>
|
||||
#include <qboxlayout.h>
|
||||
#include <qcolor.h>
|
||||
#include <qfont.h>
|
||||
#include <qdebug.h>
|
||||
#include <qglobal.h>
|
||||
#include <qlayout.h>
|
||||
#include "objects/script.h"
|
||||
|
||||
std::map<int, const QColor> DARK_MODE_COLOR_SCHEME = {{
|
||||
{QsciLexerLua::Comment, QColor("#808080")},
|
||||
{QsciLexerLua::LineComment, QColor("#808080")},
|
||||
{QsciLexerLua::Number, QColor("#6897BB")},
|
||||
{QsciLexerLua::Keyword, QColor("#CC7832")},
|
||||
{QsciLexerLua::String, QColor("#6A8759")},
|
||||
{QsciLexerLua::Character, QColor("#6A8759")},
|
||||
{QsciLexerLua::LiteralString, QColor("#6A8759")},
|
||||
{QsciLexerLua::Preprocessor, QColor("#FF00FF")}, // Obsolete since Lua 4.0, but whatever
|
||||
{QsciLexerLua::Operator, QColor("#FFFFFF")},
|
||||
{QsciLexerLua::Identifier, QColor("#FFFFFF")},
|
||||
{QsciLexerLua::UnclosedString, QColor("#6A8759")},
|
||||
{QsciLexerLua::BasicFunctions, QColor("#CC7832")},
|
||||
{QsciLexerLua::StringTableMathsFunctions, QColor("#CC7832")},
|
||||
{QsciLexerLua::CoroutinesIOSystemFacilities, QColor("#CC7832")},
|
||||
{QsciLexerLua::Label, QColor("#FFFFFF")},
|
||||
|
||||
}};
|
||||
|
||||
ScriptDocument::ScriptDocument(std::shared_ptr<Script> script, QWidget* parent):
|
||||
script(script), QMdiSubWindow(parent) {
|
||||
|
||||
|
@ -38,6 +61,20 @@ ScriptDocument::ScriptDocument(std::shared_ptr<Script> script, QWidget* parent):
|
|||
|
||||
scintilla->setText(QString::fromStdString(script->source));
|
||||
|
||||
QsciLexerLua* lexer = new QsciLexerLua;
|
||||
lexer->setFont(font);
|
||||
scintilla->setLexer(lexer);
|
||||
|
||||
// Set color scheme
|
||||
// https://stackoverflow.com/a/26318796/16255372
|
||||
for (auto& [style, color] : DARK_MODE_COLOR_SCHEME) {
|
||||
lexer->setColor(color, style);
|
||||
}
|
||||
|
||||
// lexer->setAutoIndentStyle(QsciScintilla::AiOpening | QsciScintilla::AiMaintain | QsciScintilla::AiClosing);
|
||||
// scintilla->setAutoIndent(true);
|
||||
|
||||
|
||||
connect(scintilla, &QsciScintilla::textChanged, [this]() {
|
||||
// this-> is important here, as otherwise it will refer to the
|
||||
// parameter passed in, which will get gc'ed eventually
|
||||
|
|
5
tools/incltree.sh
Executable file
5
tools/incltree.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
(cd core/src; cinclude2dot --paths > /tmp/tree.dot)
|
||||
dot -Tsvg /tmp/tree.dot -o /tmp/tree.svg
|
||||
inkscape /tmp/tree.svg &
|
Loading…
Add table
Reference in a new issue