diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d087ce..6cd0612 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,13 +17,13 @@ jobs: shell: msys2 {0} steps: - - uses: actions/checkout@v3 - name: Clone repository + - name: Checkout repository + uses: actions/checkout@v3 with: submodules: recursive - - - uses: msys2/setup-msys2@v2 - name: Set up MSYS2 and install required packages + + - name: Set up MSYS2 and install required packages + uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.sys }} install: >- @@ -34,6 +34,7 @@ jobs: mingw-w64-${{ matrix.env }}-pugixml mingw-w64-${{ matrix.env }}-ogre3d mingw-w64-${{ matrix.env }}-qt6 + mingw-w64-${{ matrix.env }}-bullet - name: Generate Ninja build files run: cmake -G Ninja -B build -DCI=ON -DCMAKE_BUILD_TYPE=${{ matrix.configuration == 'Release' && 'MinSizeRel' || matrix.configuration }} . @@ -58,7 +59,7 @@ jobs: run: mkdir build/dist/shaders && cp -R /${{ matrix.sys }}/share/OGRE/Media/Main/* build/dist/shaders && cp -R /${{ matrix.sys }}/share/OGRE/Media/RTShaderLib/* build/dist/shaders - name: Add additional runtime dependencies - run: ldd build/dist/*.exe | grep "=> /" | awk '{print $3}' | grep "clang64" | xargs -I '{}' cp -v '{}' build/dist + run: ldd build/dist/*.exe | grep "=> /" | awk '{print $3}' | grep "${{ matrix.sys }}" | xargs -I '{}' cp -v '{}' build/dist - name: Set output variables id: vars diff --git a/CMakeLists.txt b/CMakeLists.txt index 9525950..35b9a01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,11 +16,13 @@ add_compile_options(-Wno-attributes -Wno-return-type) # Ignore warnings generate find_package(Boost REQUIRED) find_package(OGRE REQUIRED COMPONENTS Bites CONFIG) find_package(pugixml REQUIRED) +find_package(Bullet CONFIG REQUIRED) +set(RNR_BULLET_LIBRARIES LinearMath Bullet3Common BulletDynamics BulletSoftBody BulletCollision BulletInverseDynamics) add_subdirectory(Projects) if(CI AND UNIX) - file(COPY ${OGRE_MEDIA_DIR}/Main/ DESTINATION ${CMAKE_BINARY_DIR}/shaders) + file(COPY ${OGRE_MEDIA_DIR}/ShadowVolume/ DESTINATION ${CMAKE_BINARY_DIR}/shaders) file(COPY ${OGRE_MEDIA_DIR}/RTShaderLib/ DESTINATION ${CMAKE_BINARY_DIR}/shaders) file(COPY ${OGRE_PLUGIN_DIR}/ DESTINATION ${CMAKE_BINARY_DIR}/plugins) endif() \ No newline at end of file diff --git a/Content/RNR/materials/partinstanced.material b/Content/RNR/materials/partinstanced.material index 7ec2dcb..c74c162 100644 --- a/Content/RNR/materials/partinstanced.material +++ b/Content/RNR/materials/partinstanced.material @@ -16,7 +16,6 @@ material materials/partinstanced rtshader_system { - transform_stage instanced lighting_stage per_pixel } } diff --git a/Content/RNR/sky/null_plainsky512_bk.jpg b/Content/RNR/sky/null_plainsky512_bk.jpg index a01c9ef..9e042e2 100644 Binary files a/Content/RNR/sky/null_plainsky512_bk.jpg and b/Content/RNR/sky/null_plainsky512_bk.jpg differ diff --git a/Content/RNR/sky/null_plainsky512_dn.jpg b/Content/RNR/sky/null_plainsky512_dn.jpg index 75f16c1..e9281a2 100644 Binary files a/Content/RNR/sky/null_plainsky512_dn.jpg and b/Content/RNR/sky/null_plainsky512_dn.jpg differ diff --git a/Content/RNR/sky/null_plainsky512_ft.jpg b/Content/RNR/sky/null_plainsky512_ft.jpg index 253cb31..c20ed21 100644 Binary files a/Content/RNR/sky/null_plainsky512_ft.jpg and b/Content/RNR/sky/null_plainsky512_ft.jpg differ diff --git a/Content/RNR/sky/null_plainsky512_lf.jpg b/Content/RNR/sky/null_plainsky512_lf.jpg index aa3626b..12a95c1 100644 Binary files a/Content/RNR/sky/null_plainsky512_lf.jpg and b/Content/RNR/sky/null_plainsky512_lf.jpg differ diff --git a/Content/RNR/sky/null_plainsky512_rt.jpg b/Content/RNR/sky/null_plainsky512_rt.jpg index 45b5071..ef1c45d 100644 Binary files a/Content/RNR/sky/null_plainsky512_rt.jpg and b/Content/RNR/sky/null_plainsky512_rt.jpg differ diff --git a/Content/RNR/sky/null_plainsky512_up.jpg b/Content/RNR/sky/null_plainsky512_up.jpg index 49940da..efff81e 100644 Binary files a/Content/RNR/sky/null_plainsky512_up.jpg and b/Content/RNR/sky/null_plainsky512_up.jpg differ diff --git a/Projects/Client/Common/Header/OgreWidget.hpp b/Projects/Client/Common/Header/OgreWidget.hpp index 7acd8db..c4bc911 100644 --- a/Projects/Client/Common/Header/OgreWidget.hpp +++ b/Projects/Client/Common/Header/OgreWidget.hpp @@ -34,6 +34,7 @@ namespace RNR Ogre::SceneManager* ogreSceneManager; Ogre::Camera* ogreCamera; Ogre::Viewport* ogreViewport; + Ogre::Light* ogreSceneLight; Ogre::RTShader::ShaderGenerator* ogreShaderGen; QCursor cursor; diff --git a/Projects/Client/Common/Source/OgreWidget.cpp b/Projects/Client/Common/Source/OgreWidget.cpp index 911fa75..f98dabe 100644 --- a/Projects/Client/Common/Source/OgreWidget.cpp +++ b/Projects/Client/Common/Source/OgreWidget.cpp @@ -1,11 +1,13 @@ #include #include +#include #include #include #include #include #include +#include #ifdef __unix__ #include @@ -70,6 +72,11 @@ namespace RNR if(Ogre::RTShader::ShaderGenerator::initialize()) { ogreShaderGen = Ogre::RTShader::ShaderGenerator::getSingletonPtr(); + + if (!std::filesystem::is_directory("ShaderCache") || !std::filesystem::exists("ShaderCache")) { + std::filesystem::create_directory("ShaderCache"); + } + ogreShaderGen->setShaderCachePath("ShaderCache/"); ogreShaderGen->addSceneManager(ogreSceneManager); @@ -90,19 +97,19 @@ namespace RNR pFont->setTrueTypeSize(16); pFont->load(); - ogreSceneManager->setShadowTechnique(Ogre::ShadowTechnique::SHADOWTYPE_STENCIL_ADDITIVE); + ogreSceneManager->setShadowTechnique(Ogre::ShadowTechnique::SHADOWTYPE_STENCIL_MODULATIVE); ogreSceneManager->setShadowFarDistance(500.f); - Ogre::Light* light = ogreSceneManager->createLight("SunLight"); + ogreSceneLight = ogreSceneManager->createLight("SunLight"); Ogre::SceneNode* lightNode = ogreSceneManager->getRootSceneNode()->createChildSceneNode(); lightNode->setPosition(0, 10, 15); lightNode->setDirection(-0.25, -0.5, -0.5); - lightNode->attachObject(light); + lightNode->attachObject(ogreSceneLight); - light->setCastShadows(true); - light->setDiffuseColour(0.9, 0.9, 1.0); - light->setSpecularColour(1.0, 1.0, 1.0); - light->setType(Ogre::Light::LT_DIRECTIONAL); + ogreSceneLight->setCastShadows(true); + ogreSceneLight->setDiffuseColour(0.9, 0.9, 1.0); + ogreSceneLight->setSpecularColour(1.0, 1.0, 1.0); + ogreSceneLight->setType(Ogre::Light::LT_DIRECTIONAL); Ogre::MaterialManager::getSingletonPtr()->reloadAll(); Ogre::MaterialManager::getSingletonPtr()->load("sky/null_plainsky512", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); @@ -126,6 +133,21 @@ namespace RNR ogreCamera->getParentSceneNode()->setPosition(cam->getCFrame().getPosition()); ogreCamera->getParentSceneNode()->setOrientation(Ogre::Quaternion(cam->getCFrame().getRotation())); } + + RNR::Lighting* lighting = (RNR::Lighting*)world->getDatamodel()->getService("Lighting"); + if(lighting) + { + Ogre::Vector3 clearColor = lighting->getClearColor(); + ogreViewport->setBackgroundColour(Ogre::ColourValue(clearColor.x, clearColor.y, clearColor.z)); + Ogre::Vector3 shadowColor = lighting->getShadowColor(); + ogreSceneManager->setShadowColour(Ogre::ColourValue(shadowColor.x, shadowColor.y, shadowColor.z)); + Ogre::Vector3 topAmbient = lighting->getTopAmbient(); + ogreSceneLight->setDiffuseColour(Ogre::ColourValue(topAmbient.x, topAmbient.y, topAmbient.z)); + Ogre::Vector3 bottomAmbient = lighting->getBottomAmbient(); + ogreSceneManager->setAmbientLight(Ogre::ColourValue(bottomAmbient.x, bottomAmbient.y, bottomAmbient.z)); + Ogre::Vector3 spotLight = lighting->getSpotLight(); + ogreSceneLight->setSpecularColour(Ogre::ColourValue(spotLight.x, spotLight.y, spotLight.z)); + } if(isVisible()) ogreRoot->renderOneFrame(this->delta); @@ -198,7 +220,11 @@ namespace RNR void OgreWidget::closeEvent(QCloseEvent* event) { + delete world; + ogreWindow->destroy(); + ogreRoot->destroySceneManager(ogreSceneManager); + ogreRoot->shutdown(); } QPaintEngine* OgreWidget::paintEngine() const diff --git a/Projects/Client/Player/Source/main.cpp b/Projects/Client/Player/Source/main.cpp index e11bae9..93cca8d 100644 --- a/Projects/Client/Player/Source/main.cpp +++ b/Projects/Client/Player/Source/main.cpp @@ -7,10 +7,6 @@ int main(int argc, char** argv) { - if (!std::filesystem::is_directory("ShaderCache") || !std::filesystem::exists("ShaderCache")) { - std::filesystem::create_directory("ShaderCache"); - } - QApplication app(argc, argv); MainWindow window = MainWindow(); diff --git a/Projects/Client/Studio/Source/MainWindow.cpp b/Projects/Client/Studio/Source/MainWindow.cpp index 41163b6..b0a7e52 100644 --- a/Projects/Client/Studio/Source/MainWindow.cpp +++ b/Projects/Client/Studio/Source/MainWindow.cpp @@ -51,7 +51,7 @@ void MainWindow::widgetItemPrepare(QTreeWidgetItem* item, RNR::Instance* instanc { QString icon_path; icon_path = "content/textures/studio/icons/"; - icon_path += instance->getClassName(); + icon_path += instance->getExplorerIcon(); icon_path += ".png"; QIcon icon; if(QFile::exists(icon_path)) @@ -138,7 +138,10 @@ void MainWindow::playSolo() RNR::Players* players = (RNR::Players*)this->ogreWidget->world->getDatamodel()->getService("Players"); RNR::Player* player = players->createLocalPlayer(0); + if(!player) + return; player->setName(QInputDialog::getText(this, "Player Name", "Enter your player name").toLocal8Bit().data()); + player->loadCharacter(); updateTree(ogreWidget->world->getDatamodel()); } diff --git a/Projects/Client/Studio/Source/PropertyViewer.cpp b/Projects/Client/Studio/Source/PropertyViewer.cpp index 691da6a..51f3f60 100644 --- a/Projects/Client/Studio/Source/PropertyViewer.cpp +++ b/Projects/Client/Studio/Source/PropertyViewer.cpp @@ -81,7 +81,7 @@ void PropertyViewer::view(RNR::Instance* instance) QImage image; QString icon_path; icon_path = "content/textures/studio/icons/"; - icon_path += instance_ref->getClassName(); + icon_path += instance_ref->getExplorerIcon(); icon_path += ".png"; if(QFile::exists(icon_path)) image = QImage(icon_path); diff --git a/Projects/Client/Studio/Source/main.cpp b/Projects/Client/Studio/Source/main.cpp index e2dbf5b..8c4d815 100644 --- a/Projects/Client/Studio/Source/main.cpp +++ b/Projects/Client/Studio/Source/main.cpp @@ -10,10 +10,6 @@ int main(int argc, char** argv) { - if (!std::filesystem::is_directory("ShaderCache") || !std::filesystem::exists("ShaderCache")) { - std::filesystem::create_directory("ShaderCache"); - } - QSurfaceFormat format; format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); diff --git a/Projects/Engine/CMakeLists.txt b/Projects/Engine/CMakeLists.txt index 78f0797..f436faa 100644 --- a/Projects/Engine/CMakeLists.txt +++ b/Projects/Engine/CMakeLists.txt @@ -16,6 +16,7 @@ add_library(Engine STATIC Header/App/V8/DataModel/ForceField.hpp Header/App/V8/DataModel/PartInstance.hpp Header/App/V8/DataModel/Light.hpp + Header/App/V8/DataModel/Lighting.hpp Header/App/V8/DataModel/FaceInstance.hpp Header/App/V8/DataModel/RunService.hpp Header/App/V8/DataModel/Workspace.hpp @@ -48,6 +49,7 @@ add_library(Engine STATIC Source/App/V8/DataModel/ForceField.cpp Source/App/V8/DataModel/PartInstance.cpp Source/App/V8/DataModel/Light.cpp + Source/App/V8/DataModel/Lighting.cpp Source/App/V8/DataModel/FaceInstance.cpp Source/App/V8/DataModel/RunService.cpp Source/App/V8/DataModel/Workspace.cpp @@ -65,5 +67,7 @@ add_library(Engine STATIC Source/Rendering/Adorn.cpp ) -target_include_directories(Engine PUBLIC ${BOOST_INCLUDE_DIRS} Header/) -target_link_libraries(Engine PUBLIC ${BOOST_LIBRARIES} pugixml OgreBites Luau.Analysis Luau.Ast Luau.Compiler Luau.VM) \ No newline at end of file +target_include_directories(Engine PUBLIC ${Boost_INCLUDE_DIRS} ${BULLET_ROOT_DIR}/${BULLET_INCLUDE_DIR} Header/) +target_compile_definitions(Engine PUBLIC ${BULLET_DEFINITIONS}) +target_link_directories(Engine PUBLIC ${Bullet_DIR}/${BULLET_ROOT_DIR}/${BULLET_LIBRARY_DIRS}) +target_link_libraries(Engine PUBLIC ${Boost_LIBRARIES} ${RNR_BULLET_LIBRARIES} pugixml OgreBites Luau.Analysis Luau.Ast Luau.Compiler Luau.VM) \ No newline at end of file diff --git a/Projects/Engine/Header/App/Humanoid/Humanoid.hpp b/Projects/Engine/Header/App/Humanoid/Humanoid.hpp index 197acff..555f146 100644 --- a/Projects/Engine/Header/App/Humanoid/Humanoid.hpp +++ b/Projects/Engine/Header/App/Humanoid/Humanoid.hpp @@ -20,6 +20,7 @@ namespace RNR PartInstance* getTorso(); PartInstance* getHead(); + void inputFrame(float dx, float dy); void createHealthBar(); float getHealth() { return m_health; }; diff --git a/Projects/Engine/Header/App/InputManager.hpp b/Projects/Engine/Header/App/InputManager.hpp index 8274d1c..d4dc866 100644 --- a/Projects/Engine/Header/App/InputManager.hpp +++ b/Projects/Engine/Header/App/InputManager.hpp @@ -15,6 +15,8 @@ namespace RNR { World* m_world; std::vector scancodes_down; + float m_mouseDX; + float m_mouseDY; protected: MouseState state; public: diff --git a/Projects/Engine/Header/App/V8/DataModel/Camera.hpp b/Projects/Engine/Header/App/V8/DataModel/Camera.hpp index 0b66cf0..4da20ea 100644 --- a/Projects/Engine/Header/App/V8/DataModel/Camera.hpp +++ b/Projects/Engine/Header/App/V8/DataModel/Camera.hpp @@ -13,6 +13,7 @@ namespace RNR private: CoordinateFrame m_cframe; CoordinateFrame m_focus; + Ogre::Radian m_yaw; virtual void deserializeProperty(char* prop_name, pugi::xml_node prop); virtual void addProperties(std::vector& properties); public: @@ -24,6 +25,7 @@ namespace RNR virtual std::string getClassName() { return "Camera"; } CoordinateFrame& getCFrame() { return m_cframe; }; CoordinateFrame& getFocus() { return m_focus; } + Ogre::Radian getYaw() { return m_yaw; } void setCFrame(CoordinateFrame cframe) { m_cframe = cframe; }; void setFocus(CoordinateFrame focus) { m_focus = focus; } bool zoom(float distance); diff --git a/Projects/Engine/Header/App/V8/DataModel/Lighting.hpp b/Projects/Engine/Header/App/V8/DataModel/Lighting.hpp new file mode 100644 index 0000000..b4ce5a5 --- /dev/null +++ b/Projects/Engine/Header/App/V8/DataModel/Lighting.hpp @@ -0,0 +1,36 @@ +#pragma once +#include + +namespace RNR +{ + class Lighting : public Instance + { + float m_brightness; + float m_geographicLatitude; + + Ogre::Vector3 m_bottomAmbient; + Ogre::Vector3 m_topAmbient; + Ogre::Vector3 m_spotLight; + Ogre::Vector3 m_shadowColor; + Ogre::Vector3 m_clearColor; + + virtual void addProperties(std::vector& properties); + virtual void deserializeProperty(char* prop_name, pugi::xml_node prop); + public: + Lighting(); + + void setBottomAmbient(Ogre::Vector3 color) { m_bottomAmbient = color; } + Ogre::Vector3 getBottomAmbient() { return m_bottomAmbient; } + void setTopAmbient(Ogre::Vector3 color) { m_topAmbient = color; } + Ogre::Vector3 getTopAmbient() { return m_topAmbient; } + void setSpotLight(Ogre::Vector3 color) { m_spotLight = color; } + Ogre::Vector3 getSpotLight() { return m_spotLight; } + void setShadowColor(Ogre::Vector3 color) { m_shadowColor = color; } + Ogre::Vector3 getShadowColor() { return m_shadowColor; } + void setClearColor(Ogre::Vector3 color) { m_clearColor = color; } + Ogre::Vector3 getClearColor() { return m_clearColor; } + + virtual std::string getClassName() { return "Lighting"; } + virtual std::string getExplorerIcon() { return "PointLight"; } + }; +} \ No newline at end of file diff --git a/Projects/Engine/Header/App/V8/DataModel/Workspace.hpp b/Projects/Engine/Header/App/V8/DataModel/Workspace.hpp index 792e7ab..3670e45 100644 --- a/Projects/Engine/Header/App/V8/DataModel/Workspace.hpp +++ b/Projects/Engine/Header/App/V8/DataModel/Workspace.hpp @@ -13,8 +13,10 @@ namespace RNR enum WorkspaceBatchingMode { + BATCH_DONT, BATCH_INSTANCED, BATCH_STATIC_GEOMETRY, + BATCH_DYNAMIC_RENDERABLE, }; class Workspace : public ModelInstance diff --git a/Projects/Engine/Header/App/V8/Tree/Instance.hpp b/Projects/Engine/Header/App/V8/Tree/Instance.hpp index 23f10c6..f261e40 100644 --- a/Projects/Engine/Header/App/V8/Tree/Instance.hpp +++ b/Projects/Engine/Header/App/V8/Tree/Instance.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -36,7 +37,7 @@ namespace RNR public: Instance(); - ~Instance(); + virtual ~Instance(); virtual std::vector getProperties(); void deserializeXmlProperty(pugi::xml_node prop); // TODO: eventually replace this with a method that uses getProperties @@ -61,6 +62,7 @@ namespace RNR RNR::Instance* getParent() { return this->m_parent; }; std::string getName() { return this->m_name; }; virtual std::string getClassName() { return "Instance"; } + virtual std::string getExplorerIcon() { return getClassName(); } void setParent(RNR::Instance* newParent); void setName(std::string name); diff --git a/Projects/Engine/Header/App/V8/Tree/PVInstance.hpp b/Projects/Engine/Header/App/V8/Tree/PVInstance.hpp index 94508b1..9f0739b 100644 --- a/Projects/Engine/Header/App/V8/Tree/PVInstance.hpp +++ b/Projects/Engine/Header/App/V8/Tree/PVInstance.hpp @@ -15,6 +15,7 @@ namespace RNR CoordinateFrame& getCFrame() { return m_cframe; }; void setCFrame(CoordinateFrame cframe) { m_cframe = cframe; }; + Ogre::Vector3 relativePositionTo(PVInstance* point); virtual std::string getClassName() { return "PVInstance"; } Ogre::Vector3 getPosition() { return m_cframe.getPosition(); } diff --git a/Projects/Engine/Header/App/V8/World/World.hpp b/Projects/Engine/Header/App/V8/World/World.hpp index ff2e87f..0537c08 100644 --- a/Projects/Engine/Header/App/V8/World/World.hpp +++ b/Projects/Engine/Header/App/V8/World/World.hpp @@ -11,6 +11,9 @@ #include #include #include +#include "LinearMath/btVector3.h" +#include "btBulletDynamicsCommon.h" + namespace RNR { @@ -28,6 +31,7 @@ namespace RNR private: std::map m_refs; std::stack m_undeserialized; + btDiscreteDynamicsWorld* m_dynamicsWorld; DataModel* m_datamodel; Workspace* m_workspace; RunService* m_runService; @@ -52,6 +56,7 @@ namespace RNR double step(float timestep); void update(); + btDiscreteDynamicsWorld* getDynamicsWorld() { return m_dynamicsWorld; } float getLastDelta() { return m_lastDelta; } DataModel* getDatamodel() { return m_datamodel; } void setInputManager(IInputManager* inputManager) { m_inputManager = inputManager; } diff --git a/Projects/Engine/Header/Helpers/Name.hpp b/Projects/Engine/Header/Helpers/Name.hpp index c14cb99..4d22e9c 100644 --- a/Projects/Engine/Header/Helpers/Name.hpp +++ b/Projects/Engine/Header/Helpers/Name.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace RNR { diff --git a/Projects/Engine/Header/Helpers/XML.hpp b/Projects/Engine/Header/Helpers/XML.hpp index 4ca425a..fa2a366 100644 --- a/Projects/Engine/Header/Helpers/XML.hpp +++ b/Projects/Engine/Header/Helpers/XML.hpp @@ -10,6 +10,7 @@ namespace RNR { public: static Ogre::Vector3 getVector3(pugi::xml_node node); + static Ogre::Vector3 getColor(pugi::xml_node node); static CoordinateFrame getCFrame(pugi::xml_node node); }; } \ No newline at end of file diff --git a/Projects/Engine/Header/Network/Player.hpp b/Projects/Engine/Header/Network/Player.hpp index 6354492..a8cdc1d 100644 --- a/Projects/Engine/Header/Network/Player.hpp +++ b/Projects/Engine/Header/Network/Player.hpp @@ -10,10 +10,13 @@ namespace RNR int m_userId; public: Player(); + + void initLocalPlayer(); + void loadCharacter(); ModelInstance* getCharacter() { return m_character; } void setCharacter(ModelInstance* model) { m_character = model; } virtual std::string getClassName() { return "Player"; } - virtual void addProperties(std::vector& properties); + virtual void addProperties(std::vector& properties); }; } \ No newline at end of file diff --git a/Projects/Engine/Source/App/GUI/TopMenuBar.cpp b/Projects/Engine/Source/App/GUI/TopMenuBar.cpp index 59164a8..3457d27 100644 --- a/Projects/Engine/Source/App/GUI/TopMenuBar.cpp +++ b/Projects/Engine/Source/App/GUI/TopMenuBar.cpp @@ -115,6 +115,7 @@ namespace RNR void TopMenuBar::frame() { Workspace* workspace = m_world->getWorkspace(); + btDiscreteDynamicsWorld* dynamicsWorld = m_world->getDynamicsWorld(); char debugtext[512]; char render_debugtext[255]; @@ -127,7 +128,11 @@ namespace RNR snprintf(render_debugtext, 255, "using BATCH_STATIC_GEOMETRY\nGeom Regions: %i", workspace->m_geom->getRegions().size()); break; } - snprintf(debugtext, 512, "Render\nLast DT = %f\n%s\n",m_world->getLastDelta(),render_debugtext,m_world->getOgreSceneManager()); + snprintf(debugtext, 512, "Render\nLast DT = %f\n%s\n\nPhysics\n%i objects, %i constraints", + m_world->getLastDelta(), + render_debugtext, + m_world->getOgreSceneManager(), + dynamicsWorld->getNumCollisionObjects(), dynamicsWorld->getNumConstraints()); m_debugText->setCaption(debugtext); Players* players = (Players*)m_world->getDatamodel()->getService("Players"); diff --git a/Projects/Engine/Source/App/Humanoid/Humanoid.cpp b/Projects/Engine/Source/App/Humanoid/Humanoid.cpp index cf7c50d..e708461 100644 --- a/Projects/Engine/Source/App/Humanoid/Humanoid.cpp +++ b/Projects/Engine/Source/App/Humanoid/Humanoid.cpp @@ -1,11 +1,15 @@ #include #include +#include namespace RNR { Humanoid::Humanoid() { - // + setName("Humanoid"); + + m_maxHealth = 100.f; + m_health = 100.f; } Humanoid::~Humanoid() @@ -42,13 +46,38 @@ namespace RNR } if(getNode()) world->getOgreSceneManager()->destroySceneNode(getNode()); + setNode(world->getOgreSceneManager()->getRootSceneNode()->createChildSceneNode()); Ogre::BillboardSet* healthBarSet = world->getOgreSceneManager()->createBillboardSet("HumanoidHealth" + getParent()->getName()); - Ogre::Billboard* healthBarBillboard = healthBarSet->createBillboard(Ogre::Vector3(100, 0, 200)); - getNode()->attachObject(healthBarSet); - getNode()->setPosition(getHead()->getPosition()); + healthBarSet->setBillboardType(Ogre::BillboardType::BBT_PERPENDICULAR_COMMON); + healthBarSet->setDebugDisplayEnabled(true); + //Ogre::Billboard* healthBarBillboard = healthBarSet->createBillboard(Ogre::Vector3(0, 2, 0), Ogre::ColourValue(1, 0, 0, 1)); + Ogre::Billboard* healthBarBillboardFilled = healthBarSet->createBillboard(Ogre::Vector3(0, 2, 0), Ogre::ColourValue(0, 1, 0, 1)); + float healthBarScale = 0.5f; + //healthBarBillboard->setDimensions(5 * healthBarScale, 1 * healthBarScale); + healthBarBillboardFilled->setDimensions((m_health / m_maxHealth) * 5.f * healthBarScale, 1 * healthBarScale); + healthBarSet->setCastShadows(true); - printf("Humanoid::createHealthBar: WIP"); + Ogre::BillboardSet* nameBarSet = world->getOgreSceneManager()->createBillboardSet("HumanoidName" + getParent()->getName()); + nameBarSet->setDebugDisplayEnabled(true); + Ogre::FontPtr comic = Ogre::FontManager::getSingletonPtr()->getByName("ComicSans"); + if(!comic) + printf("Humanoid::createHealthBar: comic == NULL\n"); + comic->putText(nameBarSet, getParent()->getName(), 1); + int num_billboards = nameBarSet->getNumBillboards(); + for(int i = 0; i < num_billboards; i++) + { + Ogre::Billboard* chara = nameBarSet->getBillboard(i); + Ogre::Vector3 chara_pos = chara->getPosition(); + chara_pos.y += 2 + healthBarScale; + chara->setPosition(chara_pos); + } + getNode()->attachObject(healthBarSet); + getNode()->attachObject(nameBarSet); + getNode()->setPosition(getHead()->getPosition()); + getNode()->setOrientation(getHead()->getRotation()); + + printf("Humanoid::createHealthBar: WIP\n"); } void Humanoid::deserializeProperty(char* prop_name, pugi::xml_node prop) @@ -63,6 +92,58 @@ namespace RNR } } + void Humanoid::inputFrame(float dx, float dy) + { + IInputManager* inputManager = world->getInputManager(); + PartInstance* torso = getTorso(); + Camera* camera = world->getWorkspace()->getCurrentCamera(); + if(!torso) + { + printf("Humanoid::inputFrame: no torso\n"); + return; + } + if(inputManager) + { + Ogre::Matrix3 camera_rotation; + camera_rotation.FromEulerAnglesYXZ(camera->getYaw(), Ogre::Radian(0), Ogre::Radian(0)); // we only want yaw because otherwise the movement target will go through the ground/in the air + Ogre::Quaternion direction = torso->getRotation(); + Ogre::Quaternion new_direction = Ogre::Quaternion::nlerp(0.5f, direction, camera_rotation); + float forward = 0; + + if(inputManager->isKeyDown('W')) + forward = 16; + + Ogre::Vector3 move = Ogre::Vector3(0, 0, -forward); + move = direction * move; + move *= world->getLastDelta(); + + bool move_valid = true; + + // TODO: collision checking + + if(!move_valid) + return; + + for(auto& child : *getParent()->getChildren()) + { + PartInstance* bp = dynamic_cast(child); + if(bp) + { + Ogre::Vector3 bp_p = bp->getPosition(); + bp_p += move; + bp->getCFrame().setPosition(bp_p); + Ogre::Matrix3 new_rotation_matrix; + new_direction.ToRotationMatrix(new_rotation_matrix); + bp->getCFrame().setRotation(new_rotation_matrix); + bp->updateMatrix(); + world->getWorkspace()->setDirty(); + } + } + + camera->getCFrame().setPosition(camera->getCFrame().getPosition() + move); + } + } + void Humanoid::addProperties(std::vector& properties) { ReflectionProperty _properties[] = { diff --git a/Projects/Engine/Source/App/InputManager.cpp b/Projects/Engine/Source/App/InputManager.cpp index 383e192..28c6b1b 100644 --- a/Projects/Engine/Source/App/InputManager.cpp +++ b/Projects/Engine/Source/App/InputManager.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace RNR @@ -9,8 +10,7 @@ namespace RNR state.mouse_primary = false; state.mouse_secondary = false; state.mouse_middle = false; - state.mouse_scroll = 0; - } + state.mouse_scroll = 0; } void IInputManager::keyDown(int scancode) { @@ -43,14 +43,14 @@ namespace RNR { if(m_world) { - float xd = x * m_world->getLastDelta(); - float yd = y * m_world->getLastDelta(); + m_mouseDX = x * m_world->getLastDelta(); + m_mouseDY = y * m_world->getLastDelta(); Workspace* workspace = m_world->getWorkspace(); Camera* camera = workspace->getCurrentCamera(); if(camera && state.mouse_secondary) { - camera->cameraFrame(xd, yd); + camera->cameraFrame(m_mouseDX, m_mouseDY); resetMouse(); } @@ -59,10 +59,29 @@ namespace RNR void IInputManager::frame() { - Workspace* workspace = m_world->getWorkspace(); - Camera* camera = workspace->getCurrentCamera(); - if(camera) - camera->cameraFrame(0, 0, false); // update camera position + Players* players = (Players*)m_world->getDatamodel()->getService("Players"); + Player* localPlayer = players->getLocalPlayer(); + if(localPlayer) + { + ModelInstance* character = localPlayer->getCharacter(); + if(character) + { + Humanoid* humanoid = (Humanoid*)character->findFirstChildOfType("Humanoid"); + if(humanoid) + { + humanoid->inputFrame(m_mouseDX, m_mouseDY); + } + } + } + else + { + Workspace* workspace = m_world->getWorkspace(); + Camera* camera = workspace->getCurrentCamera(); + if(camera) + camera->cameraFrame(0, 0, false); // update camera position + } + m_mouseDX = 0; + m_mouseDY = 0; } void IInputManager::mousePrimaryState(bool down) diff --git a/Projects/Engine/Source/App/Script/ReflectionProperty.cpp b/Projects/Engine/Source/App/Script/ReflectionProperty.cpp index 6342ee6..ced6aa2 100644 --- a/Projects/Engine/Source/App/Script/ReflectionProperty.cpp +++ b/Projects/Engine/Source/App/Script/ReflectionProperty.cpp @@ -3,7 +3,6 @@ #include #include #include -#include namespace RNR { diff --git a/Projects/Engine/Source/App/V8/DataModel/Camera.cpp b/Projects/Engine/Source/App/V8/DataModel/Camera.cpp index a782974..f600adf 100644 --- a/Projects/Engine/Source/App/V8/DataModel/Camera.cpp +++ b/Projects/Engine/Source/App/V8/DataModel/Camera.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include namespace RNR @@ -40,6 +41,7 @@ namespace RNR pitch = old_pitch + pitch; yaw = old_yaw - yaw; + m_yaw = yaw; Ogre::Matrix3 rotation; rotation.FromEulerAnglesYXZ(yaw, pitch, Ogre::Radian(0)); diff --git a/Projects/Engine/Source/App/V8/DataModel/Lighting.cpp b/Projects/Engine/Source/App/V8/DataModel/Lighting.cpp new file mode 100644 index 0000000..27bf0bc --- /dev/null +++ b/Projects/Engine/Source/App/V8/DataModel/Lighting.cpp @@ -0,0 +1,67 @@ +#include +#include + +namespace RNR +{ + Lighting::Lighting() + { + setName("Lighting"); + setClearColor(Ogre::Vector3(255.f/255.f,255.f/255.f,255.f/255.f)); + setShadowColor(Ogre::Vector3(127.f/255.f,127.f/255.f,127.f/255.f)); + setBottomAmbient(Ogre::Vector3(122.f/255.f,134.f/255.f,120.f/255.f)); + setTopAmbient(Ogre::Vector3(209.f/255.f,208.f/255.f,217.f/255.f)); + setSpotLight(Ogre::Vector3(191.f/255.f,191.f/255.f,191.f/255.f)); + } + + void Lighting::deserializeProperty(char* prop_name, pugi::xml_node node) + { + if(prop_name == std::string("ClearColor")) + { + setClearColor(XML::getColor(node)); + } + else if(prop_name == std::string("ShadowColor")) + { + setShadowColor(XML::getColor(node)); + } + else if(prop_name == std::string("SpotLightV9")) + { + setSpotLight(XML::getColor(node)); + } + else if(prop_name == std::string("BottomAmbientV9")) + { + setBottomAmbient(XML::getColor(node)); + } + else if(prop_name == std::string("TopAmbientV9")) + { + setTopAmbient(XML::getColor(node)); + } + } + + void Lighting::addProperties(std::vector& properties) + { + ReflectionProperty _properties[] = { + { this, std::string("BottomAmbientV9"), std::string(""), + ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3, + REFLECTION_GETTER(Lighting* instance = (Lighting*)object; return &instance->m_bottomAmbient; ), + REFLECTION_SETTER(Lighting* instance = (Lighting*)object; instance->setBottomAmbient(*(Ogre::Vector3*)value); ) }, + { this, std::string("TopAmbientV9"), std::string(""), + ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3, + REFLECTION_GETTER(Lighting* instance = (Lighting*)object; return &instance->m_topAmbient; ), + REFLECTION_SETTER(Lighting* instance = (Lighting*)object; instance->setTopAmbient(*(Ogre::Vector3*)value); ) }, + { this, std::string("ClearColor"), std::string(""), + ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3, + REFLECTION_GETTER(Lighting* instance = (Lighting*)object; return &instance->m_clearColor; ), + REFLECTION_SETTER(Lighting* instance = (Lighting*)object; instance->setClearColor(*(Ogre::Vector3*)value); ) }, + { this, std::string("ShadowColor"), std::string(""), + ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3, + REFLECTION_GETTER(Lighting* instance = (Lighting*)object; return &instance->m_shadowColor; ), + REFLECTION_SETTER(Lighting* instance = (Lighting*)object; instance->setShadowColor(*(Ogre::Vector3*)value); ) }, + { this, std::string("SpotLightV9"), std::string(""), + ACCESS_NONE, OPERATION_READWRITE, PROPERTY_VECTOR3, + REFLECTION_GETTER(Lighting* instance = (Lighting*)object; return &instance->m_spotLight; ), + REFLECTION_SETTER(Lighting* instance = (Lighting*)object; instance->setSpotLight(*(Ogre::Vector3*)value); ) }, + }; + + properties.insert(properties.end(), _properties, _properties+(sizeof(_properties)/sizeof(ReflectionProperty))); + } +} \ No newline at end of file diff --git a/Projects/Engine/Source/App/V8/DataModel/Workspace.cpp b/Projects/Engine/Source/App/V8/DataModel/Workspace.cpp index b314c73..1ad8055 100644 --- a/Projects/Engine/Source/App/V8/DataModel/Workspace.cpp +++ b/Projects/Engine/Source/App/V8/DataModel/Workspace.cpp @@ -8,7 +8,7 @@ namespace RNR Workspace::Workspace() : ModelInstance() { setName("Workspace"); - m_batchMode = BATCH_STATIC_GEOMETRY; + m_batchMode = BATCH_DONT; m_worldspawn = world->getOgreSceneManager()->getRootSceneNode()->createChildSceneNode(); @@ -19,9 +19,11 @@ namespace RNR break; case BATCH_STATIC_GEOMETRY: m_geom = world->getOgreSceneManager()->createStaticGeometry("workspaceGeom"); - m_geom->setRegionDimensions(Ogre::Vector3(512,512,512)); + m_geom->setRegionDimensions(Ogre::Vector3(2048,2048,2048)); m_geom->setCastShadows(true); break; + case BATCH_DONT: + break; } } @@ -36,11 +38,12 @@ namespace RNR case BATCH_INSTANCED: { Ogre::Entity* childEntity = (Ogre::Entity*)childAdded->getObject(); - Ogre::InstancedEntity* replica = m_instanceManager->createInstancedEntity(childEntity->getSubEntity(0)->getMaterialName()); + Ogre::InstancedEntity* replica = m_instanceManager->createInstancedEntity("materials/partinstanced"); replica->setPosition(part->getPosition()); replica->setOrientation(part->getCFrame().getRotation()); replica->setScale(part->getSize()); childAdded->setObject(replica); + child_node->setVisible(false); } break; case BATCH_STATIC_GEOMETRY: @@ -48,10 +51,12 @@ namespace RNR part->getPosition(), part->getCFrame().getRotation(), part->getSize()); + child_node->setVisible(false); + m_geomDirty = true; + break; + case BATCH_DONT: break; } - child_node->setVisible(false); - m_geomDirty = true; } } diff --git a/Projects/Engine/Source/App/V8/Tree/Instance.cpp b/Projects/Engine/Source/App/V8/Tree/Instance.cpp index c3680ca..49e90b0 100644 --- a/Projects/Engine/Source/App/V8/Tree/Instance.cpp +++ b/Projects/Engine/Source/App/V8/Tree/Instance.cpp @@ -15,6 +15,17 @@ namespace RNR Instance::~Instance() { setParent(NULL); + if(getNode()) + { + getNode()->removeAndDestroyAllChildren(); + delete getNode(); + } + if(getObject()) + { + if(getObject()->getParentNode() != 0) + getObject()->detachFromParent(); + delete getObject(); + } } std::vector Instance::getProperties() @@ -47,6 +58,8 @@ namespace RNR pugi::xml_attribute prop_name = prop.attribute("name"); if(prop_name.as_string() == std::string("Name")) setName(prop.text().as_string()); + else if(prop_name.as_string() == std::string("archivable")) + m_archivable = prop.text().as_bool(); else deserializeProperty((char*)prop_name.as_string(), prop); } diff --git a/Projects/Engine/Source/App/V8/Tree/PVInstance.cpp b/Projects/Engine/Source/App/V8/Tree/PVInstance.cpp index f9502f2..c006885 100644 --- a/Projects/Engine/Source/App/V8/Tree/PVInstance.cpp +++ b/Projects/Engine/Source/App/V8/Tree/PVInstance.cpp @@ -6,7 +6,7 @@ namespace RNR { PVInstance::PVInstance() : m_cframe(), Instance() { - + } void PVInstance::deserializeProperty(char* prop_name, pugi::xml_node node) @@ -28,4 +28,9 @@ namespace RNR properties.insert(properties.end(), _properties, _properties+(sizeof(_properties)/sizeof(ReflectionProperty))); } + + Ogre::Vector3 PVInstance::relativePositionTo(PVInstance* point) + { + return point->getPosition() - getPosition(); + } } \ No newline at end of file diff --git a/Projects/Engine/Source/App/V8/World/World.cpp b/Projects/Engine/Source/App/V8/World/World.cpp index 38bc843..85b7ee3 100644 --- a/Projects/Engine/Source/App/V8/World/World.cpp +++ b/Projects/Engine/Source/App/V8/World/World.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,13 @@ namespace RNR { Instance::setWorld(this); + btDefaultCollisionConfiguration* collisionConfiguration = new btDefaultCollisionConfiguration(); + btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration); + btBroadphaseInterface* overlappingPairCache = new btDbvtBroadphase(); + btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver; + m_dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration); + m_dynamicsWorld->setGravity(btVector3(0, -10, 0)); + m_inputManager = 0; m_instanceFactory = new InstanceFactory(); @@ -27,6 +35,7 @@ namespace RNR m_instanceFactory->registerInstance("RunService", InstanceFactory::instanceBuilder); m_instanceFactory->registerInstance("Players", InstanceFactory::instanceBuilder); m_instanceFactory->registerInstance("Player", InstanceFactory::instanceBuilder); + m_instanceFactory->registerInstance("Lighting", InstanceFactory::instanceBuilder); m_ogreRoot = ogre; m_ogreSceneManager = ogreSceneManager; @@ -109,13 +118,14 @@ namespace RNR WorldUndeserialized s = m_undeserialized.top(); m_undeserialized.pop(); + s.instance->setParent(s.parent); + pugi::xml_node props = s.node.child("Properties"); for(pugi::xml_node prop : props.children()) { s.instance->deserializeXmlProperty(prop); } - s.instance->setParent(s.parent); if(s.instance->getClassName() == "Model") { ModelInstance* m = (ModelInstance*)s.instance; @@ -140,7 +150,10 @@ namespace RNR double World::step(float timestep) { if(m_runService && m_runService->getRunning()) + { m_runService->step(timestep); + m_dynamicsWorld->stepSimulation(timestep); + } m_lastDelta = timestep; return 0.0; } diff --git a/Projects/Engine/Source/Helpers/XML.cpp b/Projects/Engine/Source/Helpers/XML.cpp index cde148b..0bd77ab 100644 --- a/Projects/Engine/Source/Helpers/XML.cpp +++ b/Projects/Engine/Source/Helpers/XML.cpp @@ -11,6 +11,16 @@ namespace RNR ); } + Ogre::Vector3 XML::getColor(pugi::xml_node node) + { + Ogre::Vector3 rgb; + unsigned int hex = node.text().as_uint(); + rgb.x = ((hex >> 16) & 0xff) / 255.0; + rgb.y = ((hex >> 8) & 0xff) / 255.0; + rgb.z = ((hex) & 0xff) / 255.0; + return rgb; + } + CoordinateFrame XML::getCFrame(pugi::xml_node node) { CoordinateFrame cframe; diff --git a/Projects/Engine/Source/Network/Player.cpp b/Projects/Engine/Source/Network/Player.cpp index 352b7fe..1a01c5c 100644 --- a/Projects/Engine/Source/Network/Player.cpp +++ b/Projects/Engine/Source/Network/Player.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace RNR @@ -22,4 +23,38 @@ namespace RNR properties.insert(properties.end(), _properties, _properties+(sizeof(_properties)/sizeof(ReflectionProperty))); } + + void Player::initLocalPlayer() + { + + } + + void Player::loadCharacter() + { + m_character = new ModelInstance(); + m_character->setName(getName()); + + PartInstance* head = new PartInstance(); + head->setName("Head"); + head->setSize(Ogre::Vector3(2, 1, 1)); + head->setBrickColor(24); + head->getCFrame().setPosition(Ogre::Vector3(0, 1.5, 0)); + head->updateMatrix(); + head->setParent(m_character); + + PartInstance* torso = new PartInstance(); + torso->setName("Torso"); + torso->setSize(Ogre::Vector3(2, 2, 1)); + torso->setBrickColor(23); + torso->getCFrame().setPosition(Ogre::Vector3(0, 0, 0)); + torso->updateMatrix(); + torso->setParent(m_character); + + Humanoid* character_humanoid = new Humanoid(); + character_humanoid->setParent(m_character); + m_character->setParent(world->getWorkspace()); + + Camera* player_camera = world->getWorkspace()->getCurrentCamera(); + player_camera->setCFrame(CoordinateFrame()); + } } diff --git a/Projects/Engine/Source/Network/Players.cpp b/Projects/Engine/Source/Network/Players.cpp index 186610b..ec7bf77 100644 --- a/Projects/Engine/Source/Network/Players.cpp +++ b/Projects/Engine/Source/Network/Players.cpp @@ -12,11 +12,12 @@ namespace RNR { if(m_localPlayer) { - printf("Players::createLocalPlayer: attempt to create another local player\n", userId); - return m_localPlayer; + printf("Players::createLocalPlayer: attempt to create another local player %i\n", userId); + return 0; } printf("Players::createLocalPlayer: created player %i\n", userId); m_localPlayer = new Player(); + m_localPlayer->initLocalPlayer(); return m_localPlayer; } diff --git a/README.md b/README.md index fc79773..a0872a0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # RNR [![GitHub CI Status](https://img.shields.io/github/actions/workflow/status/lrre-foss/rnr/build.yml?branch=trunk&label=builds)](https://github.com/lrre-foss/rnr/actions) +[![Discord](https://img.shields.io/discord/1130992923329175552?style=social&logo=discord)](https://discord.gg/2tj4TREby3) [![Star](https://img.shields.io/github/stars/lrre-foss/RNR?style=social)](https://github.com/lrre-foss/RNR/stargazers) -[Discord](https://discord.gg/89WagAUr) -RNR (**R**NR's **N**ot **R**oblox) is a project that aims to recreate the look and feel of classic Roblox with new features while remaining partially compatible with clients from that era. It is built upon an engine that closely resembles Roblox's own at the time, referencing disassemblies of legacy client binaries. +RNR (**R**NR's **N**ot **R**oblox) is a project that aims to recreate the look and feel of classic Roblox with new features while remaining fully compatible with clients from that era. It is built upon an engine that closely resembles Roblox's own at the time, referencing disassemblies of legacy client binaries. Interested in contributing? [Feel free to make a pull request!](https://github.com/lrre-foss/RNR/pulls) @@ -13,6 +13,7 @@ There are several goals that RNR seeks to accomplish, them being; - Easy-to-use (simple CLI options to launch and host games, as well as a level editor with a modern UI) - Fully compatible with Roblox versions up to 0.3.744.0 (dated April 2008) in areas such as hosting, joining, levels, etc. - Incorporates all the various facets of the Roblox engine, plus a little bit extra (e.g. a network replication whitelist, fancy shader support, etc.) +- Made using clean-room reverse engineering - Uses Roblox's [Luau](https://luau-lang.org/) as its scripting language while remaining fully compatible with classic Roblox scripts written using Lua 5.1 - As free and open-source as possible (with client code licensed under the GPL and the engine itself being released into the public domain, void of any copyright) - Patching all the security vulnerabilities and fixing bugs/inefficiencies that legacy Roblox clients had