Skip to content

DO NOT MERGE: Diff against blender 3.5 support PR #379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: blender_3.5_support2
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion .yamato/MeshSyncDCCPlugins.metafile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ platforms:
- platform_type: &platform_win
name: win
type: Unity::VM
image: package-ci/win10:v1.20.1
image: package-ci/win10:v4
flavor: b1.medium
cmd_prefix:
- platform_type: &platform_mac
Expand Down
24 changes: 11 additions & 13 deletions Documentation~/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ This allows devs to immediately see how things will look in-game while modelling

## Features

| | Maya | 3ds Max | MotionBuilder | Blender | Modo | Metasequoia |
| --------------------| -------------------- | -------------------- | -------------------- | -------------------- | -------------------- | -------------------- |
| Polygon mesh sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Camera sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Light sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Double-sided Mesh | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Negative Scale | :small_red_triangle: | :small_red_triangle: | :small_red_triangle: | :small_red_triangle: | :small_red_triangle: | |
| Multi UV | | :heavy_check_mark: | | :heavy_check_mark: | | |
| Scene Cache Export | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: | |
| Non-polygon shape | | | | | | |
| Bidirectional Sync | | | | :heavy_check_mark: | | |
| | Maya | 3ds Max | MotionBuilder | Blender |
| --------------------| -------------------- | -------------------- | -------------------- | -------------------- |
| Polygon mesh sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Camera sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Light sync | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Double-sided Mesh | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| Negative Scale | :small_red_triangle: | :small_red_triangle: | :small_red_triangle: | :small_red_triangle: |
| Multi UV | | :heavy_check_mark: | | :heavy_check_mark: |
| Scene Cache Export | :heavy_check_mark: | :heavy_check_mark: | | :heavy_check_mark: |
| Non-polygon shape | | | | |
| Bidirectional Sync | | | | :heavy_check_mark: |

### Caveats

Expand Down Expand Up @@ -75,6 +75,4 @@ Alternatively, [Manual Installation](Installation.md) is also available.
2. [3ds Max](3dsMax.md)
3. [MotionBuilder](MotionBuilder.md)
4. [Blender](Blender.md)
5. [Modo](Modo.md)
6. [Metasequoia](Metasequoia.md)

Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ struct InstancesManagerRecord
{
bool dirtyInstances = false;
bool dirtyMesh = false;
InstanceInfoPtr instances = nullptr;
TransformPtr entity = nullptr;
bool updated = false;
std::map<std::string, bool> updatedParents;
std::map<std::string, std::vector<InstanceInfoPtr>> instancesPerParent;
};

/// <summary>
Expand Down
54 changes: 46 additions & 8 deletions Plugins~/Src/MeshSyncClient/msInstancesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ namespace ms {
}
return ret;
}

vector<InstanceInfoPtr> InstancesManager::getDirtyInstances()
{
vector<InstanceInfoPtr> ret;
for (auto& p : m_records) {
InstancesManagerRecord& r = p.second;
if (r.dirtyInstances) {
ret.push_back(r.instances);
for (auto& instances : r.instancesPerParent)
{
ret.insert(ret.end(), instances.second.begin(), instances.second.end());
}
}
}
return ret;
Expand All @@ -38,6 +42,7 @@ namespace ms {
r.dirtyInstances = false;
r.dirtyMesh = false;
r.updated = false;
r.updatedParents.clear();
}

m_deleted.clear();
Expand All @@ -60,13 +65,25 @@ namespace ms {
void InstancesManager::add(InstanceInfoPtr info)
{
auto& rec = lockAndGet(info->path);

for (int i = 0; i < rec.instancesPerParent[info->parent_path].size(); ++i)
{
const auto& instance = rec.instancesPerParent[info->parent_path][i];

if (instance->parent_path == info->parent_path)
{
// Remove instance from the list, the new one will be added below:
rec.instancesPerParent[info->parent_path].erase(rec.instancesPerParent[info->parent_path].begin() + i);

if (m_always_mark_dirty || rec.instances == nullptr) {
rec.dirtyInstances = true;
break;
}
}

rec.updated = true;
rec.instances = info;
// instanceInfos need to be sent every update because it's how the server knows which instances are still alive:
rec.dirtyInstances = true;

rec.instancesPerParent[info->parent_path].push_back(info);
rec.updatedParents[info->parent_path] = true;
}

void InstancesManager::clear()
Expand All @@ -85,13 +102,34 @@ namespace ms {
{
for (auto it = m_records.begin(); it != m_records.end(); ) {
if (!it->second.updated) {
if (it->second.instances) {
m_deleted.push_back(it->second.instances->getIdentifier());
}
m_records.erase(it++);
}
else
{
// Remove records to parents that were not updated:
auto& parentMap = it->second.instancesPerParent;
auto& updatedParents = it->second.updatedParents;

for (auto parentMap_it = parentMap.cbegin(), nextParentMap_it = parentMap_it;
parentMap_it != parentMap.cend();
parentMap_it = nextParentMap_it)
{
++nextParentMap_it;

auto& instances = parentMap_it->second;

for (auto& instance : instances)
{
auto& parentPath = instance->parent_path;
if (!updatedParents[parentPath])
{
parentMap.erase(parentPath);
}
}
}

++it;
}
}
}
}
62 changes: 54 additions & 8 deletions Plugins~/Src/MeshSyncClientBlender/msblenContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,22 @@ void msblenContext::flushPendingList(msblenContextState& state, msblenContextPat
}
}

void removeExistingByPath(std::vector<ms::TransformPtr>& listToFilter, std::vector<ms::TransformPtr> sceneList)
{
for (size_t i = 0; i < listToFilter.size(); i++)
{
for (size_t j = 0; j < sceneList.size(); j++)
{
if (listToFilter[i]->path == sceneList[j]->path)
{
listToFilter.erase(listToFilter.begin() + i);
i--;
break;
}
}
}
}

void msblenContext::WaitAndKickAsyncExport()
{
m_asyncTasksController.Wait();
Expand Down Expand Up @@ -1841,14 +1857,26 @@ void msblenContext::WaitAndKickAsyncExport()

t.instanceInfos = m_instances_manager.getDirtyInstances();
t.instanceMeshes.clear();
deduplicateGeometry(m_instances_manager.getDirtyMeshes(), t.instanceMeshes, t.transforms);

auto instanceMeshes = m_instances_manager.getDirtyMeshes();

// Remove instance meshes that already exist in scene meshes:
removeExistingByPath(instanceMeshes, t.geometries);
removeExistingByPath(instanceMeshes, t.transforms);

std::vector<ms::Identifier> duplicates;

deduplicateGeometry(instanceMeshes, t.instanceMeshes, t.transforms, duplicates);
t.propertyInfos = m_property_manager.getAllProperties();
t.animations = m_animations;

t.deleted_materials = m_material_manager.getDeleted();
t.deleted_entities = m_entity_manager.getDeleted();
t.deleted_instances = m_instances_manager.getDeleted();

// Any instanced meshes that were duplicates are now in t.transforms and no longer in t.instanceMeshes so we need to mark them as deleted from instances:
t.deleted_instances.insert(t.deleted_instances.end(), duplicates.begin(), duplicates.end());

if (scale_factor != 1.0f) {
ms::ScaleConverter cv(scale_factor);
for (std::vector<std::shared_ptr<ms::Transform>>::value_type& obj : t.transforms) { cv.convert(*obj); }
Expand Down Expand Up @@ -1890,19 +1918,37 @@ void msblenContext::WaitAndKickAsyncExport()
exporter->kick();
}

void msblenContext::deduplicateGeometry(const std::vector<ms::TransformPtr>& input, std::vector<ms::TransformPtr>& geometries, std::vector<ms::TransformPtr>& transforms)
void msblenContext::deduplicateGeometry(const std::vector<ms::TransformPtr>& input,
std::vector<ms::TransformPtr>& geometries,
std::vector<ms::TransformPtr>& transforms,
std::vector<ms::Identifier>& duplicates)
{
std::unordered_map<uint64_t, std::string> cache;
for (auto& geometry : input) {
auto checksum = geometry->checksumGeom();
auto entry = cache[checksum];
if (entry.length() > 0) {
// Create a new pointer to avoid issues with change checks
// in auto sync
auto ptr = ms::Transform::create();
*ptr = *geometry;
ptr->reference = entry;
transforms.push_back(ptr);
bool found = false;
// If the transform is already in the list, update it:
for (auto& t : transforms)
{
if (t->path == geometry->path)
{
t->reference = entry;
found = true;
break;
}
}

if (!found) {
// Create a new pointer to avoid issues with change checks
// in auto sync
auto ptr = ms::Transform::create();
*ptr = *geometry;
ptr->reference = entry;
transforms.push_back(ptr);
duplicates.push_back(geometry->getIdentifier());
}
}
else {
cache[checksum] = geometry->path;
Expand Down
2 changes: 1 addition & 1 deletion Plugins~/Src/MeshSyncClientBlender/msblenContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class msblenContext {

void DoExportSceneCache(const std::vector<Object*>& nodes);
void WaitAndKickAsyncExport();
void deduplicateGeometry(const std::vector<ms::TransformPtr>& input, std::vector<ms::TransformPtr>& geometries, std::vector<ms::TransformPtr>& references);
void deduplicateGeometry(const std::vector<ms::TransformPtr>& input, std::vector<ms::TransformPtr>& geometries, std::vector<ms::TransformPtr>& references, std::vector<ms::Identifier>& duplicates);

void deduplicateGeometry(const std::vector<std::pair<ms::TransformPtr, uint64_t>>& input, std::vector<ms::TransformPtr>& geometries, std::vector<ms::TransformPtr>& references);

Expand Down
33 changes: 23 additions & 10 deletions Plugins~/Src/MeshSyncClientBlender/msblenContextGeometryNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,46 +28,60 @@ void msblenContext::exportInstances() {
blender::BlenderPyScene scene = blender::BlenderPyScene(blender::BlenderPyContext::get().scene());

std::unordered_set<std::string> scene_objects;

scene.each_objects([this, &scene_objects](Object* obj)
{
if (obj == nullptr || obj->data == nullptr)
return;

auto id = (ID*)obj->data;
auto id = static_cast<ID*>(obj->data);
scene_objects.insert(id->name + 2);
});

// Assume everything is now dirty
m_instances_state->manager.setAlwaysMarkDirty(true);


std::unordered_map<std::string, ms::TransformPtr> exportedTransforms;

m_geometryNodeUtils.each_instanced_object(
[this, &scene_objects, &exportedTransforms](blender::GeometryNodesUtils::Record& rec) {
auto obj = rec.obj;

if (!rec.from_file) {
auto settings = m_settings;
settings.BakeModifiers = false;
settings.multithreaded = false;

auto transform = exportObject(*m_instances_state, m_intermediate_paths, settings, rec.obj, false, true);

// Objects that aren't in the file should always be hidden:
transform->visibility = { false, false, false };

transform->reset();
exportedTransforms[rec.id] = transform;
}
else if (scene_objects.find(rec.name) == scene_objects.end()) {
else if (scene_objects.find(static_cast<ID*>(obj->data)->name + 2) == scene_objects.end()) {
auto settings = m_settings;
settings.multithreaded = false;
settings.BakeModifiers = false;
exportedTransforms[rec.id] = exportObject(*m_instances_state, m_default_paths, settings, rec.obj, false, true);
}
else {
exportedTransforms[rec.id] = exportObject(*m_entities_state, m_default_paths, m_settings, rec.obj, true, true);
// The object was already synced as part of the scene
auto& sceneTransform = exportObject(*m_entities_state, m_default_paths, m_settings, rec.obj, true, true);

if (sceneTransform) {
m_instances_state->manager.add(sceneTransform);
exportedTransforms[rec.id] = sceneTransform;
}
}
},
[this, &exportedTransforms](blender::GeometryNodesUtils::Record& rec) {
auto& transform = exportedTransforms[rec.id];

// If this transform was not exported (happens when the sync setting for lights, etc. is not enabled), skip it:
if(!transform)
return;

auto world_matrix = msblenEntityHandler::getWorldMatrix(rec.parent);
auto inverse = mu::invert(world_matrix);
auto& transform = exportedTransforms[rec.id];

//parent is always part of the scene
const auto& parent = exportObject(*m_entities_state, m_default_paths, m_settings, rec.parent, false);
Expand All @@ -77,8 +91,7 @@ void msblenContext::exportInstances() {
}
else {
exportInstances(transform, parent, std::move(rec.matrices), inverse, m_intermediate_paths);
}

}
});

m_geometryNodeUtils.setInstancesDirty(false);
Expand Down
Loading