Przeglądaj źródła

Merge pull request #633 from simonferquel/master

Fbx Exporter : support for mesh instancing
davrous 10 lat temu
rodzic
commit
257a457c83

+ 11 - 0
Exporters/FBX/BabylonFbxNative/BabylonMesh.cpp

@@ -383,6 +383,12 @@ web::json::value BabylonMesh::toJson()
 		}
 		jobj[L"pivotMatrix"] = jpivot;
 	}
+
+	auto jinstances = web::json::value::array();
+	for (auto& instance : _instances) {
+		jinstances[jinstances.size()] = instance.toJson();
+	}
+	jobj[L"instances"] = jinstances;
 	return jobj;
 }
 
@@ -867,6 +873,11 @@ BabylonMesh::BabylonMesh(BabylonMesh && moved) :
 {
 }
 
+void BabylonMesh::addInstance(BabylonNode * node)
+{
+	_instances.emplace_back(node);
+}
+
 BabylonMesh::~BabylonMesh()
 {
 }

+ 1 - 0
Exporters/FBX/BabylonFbxNative/BabylonMesh.h

@@ -123,6 +123,7 @@ public:
 	BabylonMesh(BabylonNode* node);
 	BabylonMesh(const BabylonMesh&) = default;
 	BabylonMesh(BabylonMesh&& moved);
+	void addInstance(BabylonNode* node);
 	virtual ~BabylonMesh();
 };
 

+ 56 - 34
Exporters/FBX/BabylonFbxNative/BabylonScene.cpp

@@ -76,7 +76,8 @@ BabylonScene::BabylonScene(BabylonNode & rootNode, bool skipEmptyNodes) :
 	_ambientColor(0, 0, 0),
 	_gravity(0, 0, -.9f)
 {
-	exploreNodes(rootNode, skipEmptyNodes);
+	std::map<FbxMesh*, size_t> meshInstanceMap;
+	exploreNodes(rootNode, skipEmptyNodes, meshInstanceMap);
 	if (_cameras.size() == 0) {
 		babylon_boundingbox bbox(rootNode.fbxNode()->GetScene());
 		auto cam = buildCameraFromBoundingBox(bbox);
@@ -151,8 +152,20 @@ void fixupTextureCoordinateIndices(BabylonMaterial& mat, BabylonMesh& mesh) {
 		}
 	}
 }
+bool isAlreadyInstanciatedMesh(FbxNode* node, std::map<FbxMesh*, size_t>& meshInstanceMap, size_t* oIndex) {
+	auto mesh = node->GetMesh();
+	if (!mesh) {
+		return false;
+	}
+	auto found = meshInstanceMap.find(mesh);
+	if (found == meshInstanceMap.end()) {
+		return false;
+	}
 
-void BabylonScene::exploreNodes(BabylonNode & node, bool skipEmptyNodes)
+	*oIndex = found->second;
+	return true;
+}
+void BabylonScene::exploreNodes(BabylonNode & node, bool skipEmptyNodes, std::map<FbxMesh*, size_t>& meshInstanceMap)
 {
 	if (node.nodeType() == BabylonNodeType::Skeleton && node.hasOnlySkeletonDescendants()) {
 		return;
@@ -167,44 +180,53 @@ void BabylonScene::exploreNodes(BabylonNode & node, bool skipEmptyNodes)
 	case BabylonNodeType::Mesh:
 	case BabylonNodeType::Skeleton:
 	{
+		size_t instanceOwnerIndex;
+		if (isAlreadyInstanciatedMesh(node.fbxNode(), meshInstanceMap, &instanceOwnerIndex)) {
+			_meshes[instanceOwnerIndex].addInstance(&node);
+		}
+		else {
+			BabylonMesh mesh(&node);
 
-		BabylonMesh mesh(&node);
-
-		auto matCount = node.fbxNode()->GetMaterialCount();
-		BabylonMultiMaterial multiMat;
-		for (auto i = 0; i < matCount; ++i) {
-			auto mat = node.fbxNode()->GetMaterial(i);
-			if (mat) {
-
-				auto id = getMaterialId(mat);
-				auto existing = std::find_if(_materials.begin(), _materials.end(), [id](const BabylonMaterial& e) {
-					return e.id == id;
-				});
-				if (existing == _materials.end()) {
-					auto babMat = BabylonMaterial(mat);
-					fixupTextureCoordinateIndices(babMat, mesh);
-					_materials.push_back(std::move(babMat));
-				}
+			auto matCount = node.fbxNode()->GetMaterialCount();
+			BabylonMultiMaterial multiMat;
+			for (auto i = 0; i < matCount; ++i) {
+				auto mat = node.fbxNode()->GetMaterial(i);
+				if (mat) {
+
+					auto id = getMaterialId(mat);
+					auto existing = std::find_if(_materials.begin(), _materials.end(), [id](const BabylonMaterial& e) {
+						return e.id == id;
+					});
+					if (existing == _materials.end()) {
+						auto babMat = BabylonMaterial(mat);
+						fixupTextureCoordinateIndices(babMat, mesh);
+						_materials.push_back(std::move(babMat));
+					}
+
+					multiMat.materials.push_back(id);
 
-				multiMat.materials.push_back(id);
+				}
+			}
 
+			if (mesh.associatedSkeleton) {
+				mesh.associatedSkeleton->id = static_cast<int>(_skeletons.size() + 1);
+				mesh.skeletonId(static_cast<int>(_skeletons.size() + 1));
+				_skeletons.push_back(mesh.associatedSkeleton);
 			}
-		}
+			if (multiMat.materials.size() > 0) {
 
-		if (mesh.associatedSkeleton) {
-			mesh.associatedSkeleton->id = static_cast<int>(_skeletons.size() + 1);
-			mesh.skeletonId(static_cast<int>(_skeletons.size() + 1));
-			_skeletons.push_back(mesh.associatedSkeleton);
-		}
-		if (multiMat.materials.size() > 0) {
+				multiMat.id = mesh.id();
+				multiMat.name = mesh.name();
+				mesh.materialId(multiMat.id);
+				_multiMaterials.push_back(std::move(multiMat));
+			}
 
-			multiMat.id = mesh.id();
-			multiMat.name = mesh.name();
-			mesh.materialId(multiMat.id);
-			_multiMaterials.push_back(std::move(multiMat));
+			auto fbxMesh = node.fbxNode()->GetMesh();
+			if (fbxMesh) {
+				meshInstanceMap[fbxMesh] = _meshes.size();
+			}
+			_meshes.push_back(std::move(mesh));
 		}
-
-		_meshes.push_back(std::move(mesh));
 	}
 	break;
 	case BabylonNodeType::Camera:
@@ -230,7 +252,7 @@ void BabylonScene::exploreNodes(BabylonNode & node, bool skipEmptyNodes)
 
 
 	for (auto& child : node.children()) {
-		exploreNodes(child, skipEmptyNodes);
+		exploreNodes(child, skipEmptyNodes, meshInstanceMap);
 	}
 
 }

+ 1 - 1
Exporters/FBX/BabylonFbxNative/BabylonScene.h

@@ -89,6 +89,6 @@ public:
 	BabylonScene(BabylonScene&& moved);
 	~BabylonScene();
 private:
-	void exploreNodes(BabylonNode& node, bool skipEmptyNodes);
+	void exploreNodes(BabylonNode& node, bool skipEmptyNodes, std::map<FbxMesh*, size_t>& meshInstanceMap);
 };
 

BIN
Exporters/FBX/Redist/redist_x86/FbxExporter.exe


BIN
Exporters/FBX/Redist/babylon fbx tools x86.zip


BIN
Exporters/FBX/Redist/redist_x64/FbxExporter.exe


BIN
Exporters/FBX/Redist/redist_x64/FbxRerouteSkeleton.exe


BIN
Exporters/FBX/Redist/redist_x64/cpprest120_2_6.dll


BIN
Exporters/FBX/Redist/redist_x86/FbxRerouteSkeleton.exe


BIN
Exporters/FBX/Redist/redist_x86/cpprest120_2_6.dll


+ 2 - 1
Exporters/FBX/readme.md

@@ -3,7 +3,8 @@ FBX Exporter produces a babylon scene file from an FBX asset
 ##Supported features
 - cameras
 - lights (including shadowcasting info)
-- meshes (with or without associated skeletons, no support for instancing yet)
+- meshes (with or without associated skeletons)
+- mesh instancing (make sure "preserve instances" is checked when exporting from 3DS Max)
 - bone hierarchies
 - materials (with any number of textures)
 - animations (transforms / opacities / hierarchical) except on materials / textures