Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,21 @@ SET(SDL_BUILD_DIR "${GLARE_CORE_LIBS_ENV}/SDL/sdl_build" CACHE FILEPATH "SDL b

MESSAGE("SDL_BUILD_DIR: ${SDL_BUILD_DIR}")

SET(USE_SDL OFF CACHE BOOL "Use SDL for client UI instead of Qt")
MESSAGE("USE_SDL (instead of Qt): ${USE_SDL}")


# External library directory (apart from llvm, openssl and Qt)
SET(USE_SDL OFF CACHE BOOL "Use SDL for client UI instead of Qt")
MESSAGE("USE_SDL (instead of Qt): ${USE_SDL}")

SET(XR_SUPPORT OFF CACHE BOOL "Enable experimental XR/OpenXR integration for native clients")
MESSAGE("XR_SUPPORT: ${XR_SUPPORT}")

set(default_openxr_sdk_dir "")
if(EXISTS "${GLARE_CORE_LIBS_ENV}/OpenXR-SDK-1.1.57/install/cmake/OpenXRConfig.cmake")
set(default_openxr_sdk_dir "${GLARE_CORE_LIBS_ENV}/OpenXR-SDK-1.1.57/install")
endif()
SET(OPENXR_SDK_DIR "${default_openxr_sdk_dir}" CACHE PATH "Path to installed OpenXR SDK root (contains include/, lib/, cmake/)")
MESSAGE("OPENXR_SDK_DIR: ${OPENXR_SDK_DIR}")


# External library directory (apart from llvm, openssl and Qt)
set(imathdir "${GLARE_CORE_TRUNK_DIR_ENV}/Imath")
set(openexrdir "${GLARE_CORE_TRUNK_DIR_ENV}/OpenEXR")
set(zlibdir "${GLARE_CORE_TRUNK_DIR_ENV}/zlib")
Expand Down Expand Up @@ -299,7 +309,7 @@ ADD_SUBDIRECTORY(server server)

#ADD_SUBDIRECTORY(lightmapper_bot lightmapper_bot)

#ADD_SUBDIRECTORY(screenshot_bot screenshot_bot)
ADD_SUBDIRECTORY(screenshot_bot screenshot_bot)

#ADD_SUBDIRECTORY(${SUBSTRATA_PRIVATE_REPO_PATH}/eth_bot eth_bot)

Expand Down
116 changes: 27 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,99 +1,37 @@
# ❄️ Metasiberia - metaverse from Siberia ❄️

Substrata is an open-source metaverse, developed by Glare Technologies Limited, see https://substrata.info/.
[![C++](https://img.shields.io/badge/C%2B%2B-20-00599C?logo=cplusplus&logoColor=white)](https://isocpp.org/)
[![Build](https://img.shields.io/badge/build-CMake-064F8C?logo=cmake&logoColor=white)](https://cmake.org/)
[![UI](https://img.shields.io/badge/UI-Qt%205%20%7C%20Qt%206%20%7C%20SDL-41CD52)](https://www.qt.io/)
[![Platforms](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20Android-1F6FEB)](https://github.com/shipilovden/sub-metasiberia)
[![Windows CI](https://img.shields.io/github/actions/workflow/status/shipilovden/sub-metasiberia/windows-main-ci.yml?branch=master&label=Windows%20CI)](https://github.com/shipilovden/sub-metasiberia/actions/workflows/windows-main-ci.yml)
[![Linux Configure](https://img.shields.io/github/actions/workflow/status/shipilovden/sub-metasiberia/cmake-configure-linux.yml?branch=master&label=Linux%20Configure)](https://github.com/shipilovden/sub-metasiberia/actions/workflows/cmake-configure-linux.yml)
[![License](https://img.shields.io/github/license/shipilovden/sub-metasiberia)](LICENCE)
[![Stars](https://img.shields.io/github/stars/shipilovden/sub-metasiberia?style=social)](https://github.com/shipilovden/sub-metasiberia/stargazers)
[![Admin](https://img.shields.io/website?url=https%3A%2F%2Fvr.metasiberia.com%2F&up_message=online&down_message=offline&label=admin&logo=googlechrome)](https://vr.metasiberia.com/)
[![Signup](https://img.shields.io/website?url=https%3A%2F%2Fvr.metasiberia.com%2Fsignup&up_message=online&down_message=offline&label=signup&logo=googlechrome)](https://vr.metasiberia.com/signup)
[![Website](https://img.shields.io/website?url=https%3A%2F%2Fmetasiberia.com%2F&up_message=online&down_message=offline&label=website&logo=googlechrome)](https://metasiberia.com/)

The Substrata client and server are native apps written in C++, that support Windows, Mac and Linux.
There is also an in-development web-client.
Metasiberia is a virtual world project inspired by and based on the open-source Substrata software.

You can build the Substrata client or Substrata server from this repository.
## Quick Access

![main screenshot](https://github.com/glaretechnologies/substrata/assets/30285/38a6db93-e729-4f15-8561-5c848eb5391c)
- **🛠 Admin**: [https://vr.metasiberia.com/](https://vr.metasiberia.com/)
- **📝 Signup**: [https://vr.metasiberia.com/signup](https://vr.metasiberia.com/signup)
- **🌐 Web Client**: [https://vr.metasiberia.com/webclient](https://vr.metasiberia.com/webclient)
- **🏠 Website**: [https://metasiberia.com/](https://metasiberia.com/)

![](docs/images/metasiberia.png)

## Usage examples
## Community and Support

You can always visit the main Substrata world at substrata.info - this is the server that we run, and the main gathering point for Substrata users. However you are also welcome to run your own server! You can do this using by building the server from this repository, or using one of the prebuilt binaries.
[![VK](https://img.shields.io/badge/VK-metasiberia__official-0077FF?logo=vk&logoColor=white)](https://vk.com/metasiberia_official)
[![Telegram](https://img.shields.io/badge/Telegram-metasiberia__channel-26A5E4?logo=telegram&logoColor=white)](https://t.me/metasiberia_channel)

### 3D model Visualisation
## Credits

Substrata is perfect for multi-user visualisation of 3d models. Walk around a digital twin model with your team, hosted on an on-premises server!
**Metasiberia** is inspired and based on **Substrata**.

### Education

Because Substrata is open source, you can build and run your own world for students without paying any licensing fees.

## Get Involved

We welcome contributions from people!

Chat about Substrata on the Substrata discord here: https://discord.gg/3Ds9cxyEnZ

Feel free to drop a message on the discord if you are having trouble building Substrata, or have any questions about it.



## Building

See [docs/building.txt](docs/building.txt) for build instructions.

## Features

### High performance, physically-based rendering engine

Substrata uses the Glare engine (https://github.com/glaretechnologies/glare-core), which produces realistic graphics while rendering the entire Substrata world - e.g. over 12000 objects with user-generated content at 200 fps.

* The Glare engine is designed for metaverses, in particular large numbers of varied objects.
* Automatic level of detail generation
* Streaming loading and unloading of objects without hitches as the player moves around
* Physically-based rendering
* Highly realistic sun/sky/daylight system derived from a multiple scattering ray-tracing atmospheric simulation in https://www.indigorenderer.com/
* Skeletal animation system with procedural animations and animation retargetting for sharing animation data amongst avatars with varied sizes
* Runtime texture compression for making best use of GPU memory
* GLTF, OBJ import, plus supports many image formats
* Terrain and water rendering
* Particle system for rendering dust, water splashes, smoke etc.

![boat](https://github.com/glaretechnologies/substrata/assets/30285/0dde612a-ea95-49af-bc64-07f1a7114c7f)


### Networked physics simulation

We have integrated the Jolt physics engine (https://github.com/jrouwe/JoltPhysics), and have implemented a networked physics simulation on top of it.
What that means is that multiple players can interact with objects in a world, drive vehicles, push objects etc. in a realistic way.

Physics-based vehicles: (Image links are to videos on YouTube)

<a href="https://youtu.be/-E3J8kaqolQ"><img src="https://github.com/glaretechnologies/substrata/assets/30285/18703540-58ae-4e18-bf28-635784cd6c9a" width="600"></a>

Networked physics:

<a href="https://youtu.be/_rECxiwVteY"><img src="https://github.com/glaretechnologies/substrata/assets/30285/37eaacef-0f1b-48af-a820-1dcc9c17466e" width="600"></a>


Handling lots of interactive objects:

<a href="https://youtu.be/CzGz6voUE_8?t=8"><img src="https://github.com/glaretechnologies/substrata/assets/30285/6956d5a7-33f4-4c79-947c-951a2fe3cb18" width="600"></a>


### Spatial Audio and Voice Chat

Substrata has built-in spatial audio and voice chat, without using any third-party services or servers.


### In-world building

The substrata client has controls for creating and editing objects, as well as for editing voxels

<img src="https://github.com/glaretechnologies/substrata/assets/30285/1680739d-b7ae-4e8f-9ba8-48769643e27b" width="600">


<img src="https://github.com/glaretechnologies/substrata/assets/30285/3e3fb2f5-de3a-4132-9b86-b275b89c5dbd" width="600">

You can add objects to the world from your local machine, and they will be automatically uploaded to the server and be visible to other users.

## Get help

Chat about Substrata on the Substrata discord here: https://discord.gg/3Ds9cxyEnZ

Feel free to drop a message on the discord if you are having trouble building Substrata, or have any questions about it.

Alternatively, feel free to create a github issue.
[![Original Substrata](https://img.shields.io/badge/GitHub-glaretechnologies%2Fsubstrata-181717?logo=github&logoColor=white)](https://github.com/glaretechnologies/substrata)
[![Substrata Creator](https://img.shields.io/badge/X-%40NickChapmn-000000?logo=x&logoColor=white)](https://x.com/NickChapmn)
[![Metasiberia Author](https://img.shields.io/badge/X-%40denshipilovart-000000?logo=x&logoColor=white)](https://x.com/denshipilovart)
41 changes: 24 additions & 17 deletions gui_client/AvatarGraphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ Copyright Glare Technologies Limited 2021 -
#include "AvatarGraphics.h"


#include "AnimationManager.h"
#include "PhysicsWorld.h"
#include "AnimationManager.h"
#include "AvatarGroundingUtils.h"
#include "PhysicsWorld.h"
#include "ParticleManager.h"
#include "../shared/GestureSettings.h"
#include "opengl/OpenGLEngine.h"
Expand Down Expand Up @@ -46,11 +47,12 @@ AvatarGraphics::AvatarGraphics(Avatar* avatar_)
next_eye_target_os = Vec4f(0,0,1,1);
eye_start_transition_time = -2;
eye_end_transition_time = -1;
saccade_gap = 0.5;

last_cam_rotation_time = 0;

cur_head_rot_z = 0;
saccade_gap = 0.5;

last_cam_rotation_time = 0;
avatar_eye_height_above_ground = AvatarGrounding::kDefaultAvatarEyeHeightM;

cur_head_rot_z = 0;
//cur_head_rot_quat = Quatf::identity();

turn_anim_end_time = -1;
Expand Down Expand Up @@ -158,7 +160,7 @@ void AvatarGraphics::setOverallTransform(OpenGLEngine& engine, PhysicsWorld& phy
// We will translate the position of the capsule down from eye position to the centre of the avatar.
// TODO: rotate based on current head bone and hip bone relative position?
const Vec4f last_eye_pos = getLastHeadPosition(); // Use animated head position so it works for animations with root motion.
physics_world.setNewPosition(*physics_ob, last_eye_pos - Vec4f(0, 0, EYE_HEIGHT / 2.f, 0));
physics_world.setNewPosition(*physics_ob, last_eye_pos - Vec4f(0, 0, avatar_eye_height_above_ground / 2.f, 0));
}


Expand Down Expand Up @@ -847,7 +849,7 @@ void AvatarGraphics::setOverallTransform(OpenGLEngine& engine, PhysicsWorld& phy
lowest_bone_z_os = myMin(lowest_bone_z_os, left_foot_pos_os[2]);
}

const float lowest_node_height_above_ground = lowest_bone_z_os + 1.67f - 0.03f; // Translate up by default eye height.
const float lowest_node_height_above_ground = lowest_bone_z_os + avatar_eye_height_above_ground - 0.03f;

float vertical_adjustment = 0;
if(lowest_node_height_above_ground < 0)
Expand Down Expand Up @@ -984,8 +986,8 @@ void AvatarGraphics::setOverallTransform(OpenGLEngine& engine, PhysicsWorld& phy
const float NECK_FACTOR = 0.5f; // relative to amount of head rotation and translation
//const float pitch_move_forwards_factor = head_pitch_amount * 0.0f * NECK_FACTOR;
const float neck_yaw_amount = head_yaw_amount * NECK_FACTOR;
const float neck_pitch_amount = 0.3f + head_pitch_amount * NECK_FACTOR; // Idle pose neck rotation is 0.5. A value of 0 gives a very erect posture. Use 0.3 as a compromise.
// Note that ideally we would compute the neck pitch as some fraction betweeen 0.3 and head pitch amount.
// Keep neutral XR head pose aligned with the horizon instead of baking in a constant downward neck tilt.
const float neck_pitch_amount = head_pitch_amount * NECK_FACTOR;

//Matrix4f neck_rot = Matrix4f::translationMatrix(0, 0, pitch_move_forwards_factor /*- fabs(yaw_amount) * 0.04 * NECK_FACTOR*/) *
// Matrix4f::rotationAroundZAxis(-0.2f * head_yaw_amount * NECK_FACTOR) * Matrix4f::rotationAroundYAxis(head_yaw_amount * NECK_FACTOR) * Matrix4f::rotationAroundXAxis(head_pitch_amount * NECK_FACTOR);
Expand Down Expand Up @@ -1264,12 +1266,17 @@ void AvatarGraphics::build(bool our_avatar_)
running_backwards_anim_i = findAnimation(*skinned_gl_ob, "Running Backward");
floating_anim_i = findAnimation(*skinned_gl_ob, "Floating");
flying_anim_i = findAnimation(*skinned_gl_ob, "Flying");
turn_left_anim_i = findAnimation(*skinned_gl_ob, "Left Turn");
turn_right_anim_i = findAnimation(*skinned_gl_ob, "Right Turn");

const AnimationData& anim_data = skinned_gl_ob->mesh_data->animation_data;

// root_node_i = skinned_gl_ob->mesh_data->animation_data.getNodeIndex("player_rig");
turn_left_anim_i = findAnimation(*skinned_gl_ob, "Left Turn");
turn_right_anim_i = findAnimation(*skinned_gl_ob, "Right Turn");

const AnimationData& anim_data = skinned_gl_ob->mesh_data->animation_data;
avatar_eye_height_above_ground = AvatarGrounding::computeGroundingInfo(
anim_data,
/*use_retarget_adjustment=*/true,
AvatarGrounding::kRetargetedToeBottomOffsetM
).eye_height_above_ground;

// root_node_i = skinned_gl_ob->mesh_data->animation_data.getNodeIndex("player_rig");
neck_node_i = skinned_gl_ob->mesh_data->animation_data.getNodeIndex("Neck");
head_node_i = skinned_gl_ob->mesh_data->animation_data.getNodeIndex("Head");

Expand Down
9 changes: 5 additions & 4 deletions gui_client/AvatarGraphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,11 @@ class AvatarGraphics : public ThreadSafeRefCounted
int RightHandPinky3_i;
int RightHandPinky4_i;

float upper_right_arm_len, lower_right_arm_len;
float upper_left_arm_len, lower_left_arm_len;

Avatar* avatar;
float upper_right_arm_len, lower_right_arm_len;
float upper_left_arm_len, lower_left_arm_len;
float avatar_eye_height_above_ground;

Avatar* avatar;
bool our_avatar;
Reference<PhysicsObject> physics_ob;
};
Expand Down
74 changes: 74 additions & 0 deletions gui_client/AvatarGroundingUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include <graphics/AnimationData.h>
#include <cmath>


namespace AvatarGrounding
{
static constexpr float kDefaultAvatarEyeHeightM = 1.67f;
static constexpr float kHeadNodeToEyesOffsetM = 0.076f;
static constexpr float kDefaultToeBottomOffsetM = 0.0362269469f;
static constexpr float kRetargetedToeBottomOffsetM = 0.03f;


struct GroundingInfo
{
float foot_bottom_height;
float anchor_height_from_origin;
float eye_height_above_ground;
};


inline bool tryGetNodeHeight(const AnimationData& animation_data, const char* node_name, bool use_retarget_adjustment, float& height_out)
{
const int node_i = animation_data.getNodeIndex(node_name);
if(node_i < 0)
return false;

const Vec4f node_pos = animation_data.getNodePositionModelSpace(node_i, use_retarget_adjustment);
if(!std::isfinite(node_pos[1]))
return false;

height_out = node_pos[1];
return true;
}


inline GroundingInfo computeGroundingInfo(const AnimationData& animation_data, bool use_retarget_adjustment, float toe_bottom_offset_m)
{
GroundingInfo info = {};

float toe_height = toe_bottom_offset_m;
if(tryGetNodeHeight(animation_data, "LeftToe_End", use_retarget_adjustment, toe_height))
info.foot_bottom_height = toe_height - toe_bottom_offset_m;
else
info.foot_bottom_height = 0.0f;

float left_eye_height = 0.0f;
float right_eye_height = 0.0f;
float head_height = 0.0f;
const bool has_left_eye = tryGetNodeHeight(animation_data, "LeftEye", use_retarget_adjustment, left_eye_height);
const bool has_right_eye = tryGetNodeHeight(animation_data, "RightEye", use_retarget_adjustment, right_eye_height);

if(has_left_eye && has_right_eye)
info.anchor_height_from_origin = 0.5f * (left_eye_height + right_eye_height);
else if(has_left_eye)
info.anchor_height_from_origin = left_eye_height;
else if(has_right_eye)
info.anchor_height_from_origin = right_eye_height;
else if(tryGetNodeHeight(animation_data, "Head", use_retarget_adjustment, head_height))
info.anchor_height_from_origin = head_height + kHeadNodeToEyesOffsetM;
else
info.anchor_height_from_origin = info.foot_bottom_height + kDefaultAvatarEyeHeightM;

info.eye_height_above_ground = info.anchor_height_from_origin - info.foot_bottom_height;
if(!(std::isfinite(info.eye_height_above_ground) && (info.eye_height_above_ground > 0.1f)))
{
info.eye_height_above_ground = kDefaultAvatarEyeHeightM;
info.anchor_height_from_origin = info.foot_bottom_height + info.eye_height_above_ground;
}

return info;
}
}
Loading