/// <summary> /// Instantiates a mesh object inside cpp plugin and stores /// a reference to it in this class /// </summary> private void SendSkinnedMesh() { // The plugin will want to modify the vertex buffer -- on many platforms // for that to work we have to mark mesh as "dynamic" (which makes the buffers CPU writable -- // by default they are immutable and only GPU-readable). mesh.MarkDynamic(); var vertices = mesh.vertices; // Vector3[] var faces = mesh.triangles.Clone() as int[]; // int[] var nativeWeights = mesh.GetAllBoneWeights(); // NativeArray<BoneWeight1> var boneCounts = mesh.GetBonesPerVertex().ToArray(); // byte[] Debug.Assert(boneCounts.Length == vertices.Length, "Bone per vertex array has different length from vertex array"); // copy because original struct is not interop-friendly int largestBoneIndex = 0; Bone[] weights = new Bone[nativeWeights.Length]; for (int i = 0; i < weights.Length; i++) { var native = nativeWeights[i]; weights[i] = new Bone(native.boneIndex, native.weight); largestBoneIndex = native.boneIndex > largestBoneIndex ? native.boneIndex : largestBoneIndex; } Debug.Log($"Largest bone index in mesh {mesh.name}: {largestBoneIndex}"); GCHandle gcVertices = GCHandle.Alloc(vertices, GCHandleType.Pinned); GCHandle gcFaces = GCHandle.Alloc(faces, GCHandleType.Pinned); GCHandle gcWeights = GCHandle.Alloc(weights, GCHandleType.Pinned); GCHandle gcBoneCounts = GCHandle.Alloc(boneCounts, GCHandleType.Pinned); var cppMesh = NativeInterface.CreateMesh(gcVertices.AddrOfPinnedObject(), mesh.vertexCount, gcFaces.AddrOfPinnedObject(), faces.Length / 3, gcWeights.AddrOfPinnedObject(), gcBoneCounts.AddrOfPinnedObject(), largestBoneIndex + 1); gcVertices.Free(); gcFaces.Free(); gcWeights.Free(); gcBoneCounts.Free(); string errorMessage = ExtractFailureMessage(NativeInterface.HasFailedMeshConstruction(cppMesh)); if (errorMessage.Equals("")) { this._cppMesh = cppMesh; } else { NativeInterface.DestroyMesh(cppMesh); throw new Exception(errorMessage); } // send pointer to the mesh buffer // if (mesh.vertexBufferCount > 1) // Debug.LogWarning("There are more than one vertex buffer: " + mesh.vertexBufferCount); // NativeInterface.SetMeshVertexBuffer(this._cppMesh, mesh.GetNativeVertexBufferPtr(0)); }
/// <summary> /// To be called on FixedUpdate every frame /// </summary> public void Animate(Vector4[] rotations, Vector3[] translations) { GCHandle gcRotations = GCHandle.Alloc(rotations, GCHandleType.Pinned); GCHandle gcTranslations = GCHandle.Alloc(translations, GCHandleType.Pinned); // results var deformed = new Vector3[this.GetRestVertexCount()]; deformed.Initialize(); GCHandle gcDeformed = GCHandle.Alloc(deformed, GCHandleType.Pinned); NativeInterface.Animate(this._cppMesh, gcRotations.AddrOfPinnedObject(), gcTranslations.AddrOfPinnedObject(), gcDeformed.AddrOfPinnedObject()); gcRotations.Free(); gcTranslations.Free(); gcDeformed.Free(); string errorMessage = ExtractFailureMessage(NativeInterface.AnimationError(this._cppMesh)); if (!errorMessage.Equals("")) { NativeInterface.DestroyMesh(this._cppMesh); throw new Exception(errorMessage); } this.mesh.SetVertices(deformed); }
/// <summary> /// Serialize the entire mesh on disk, including vertices, /// faces, weights and centers of rotation (if available) /// </summary> /// <param name="path">A folder for where to store the files</param> public void Serialize(string path) { NativeInterface.SerializeMesh(this._cppMesh, Path.Combine(path, this.mesh.name)); var error = ExtractFailureMessage(NativeInterface.SerializationError(this._cppMesh)); if (!error.Equals("")) { NativeInterface.DestroyMesh(this._cppMesh); throw new Exception(error); } }
/// <summary> /// Read a previously serialized file with centers of rotation /// </summary> /// <param name="path"></param> public void ReadCenters(string path) { NativeInterface.ReadCenters(this._cppMesh, path); var error = ExtractFailureMessage(NativeInterface.SerializationError(this._cppMesh)); if (!error.Equals("")) { NativeInterface.DestroyMesh(this._cppMesh); throw new Exception(error); } }
// public int GetSubdividedFaceCount() => NativeInterface.GetSubdividedFaceCount(this._cppMesh); // public int GetSubdividedVertexCount() => NativeInterface.GetSubdividedVertexCount(this._cppMesh); /// <summary> /// Get the number of center of rotations in the mesh. /// It will computed it if the number is not defined. /// </summary> /// <returns></returns> public int GetCenterCount() { var count = NativeInterface.GetCenterCount(this._cppMesh); var error = ExtractFailureMessage(NativeInterface.HasFailedGettingCentersOfRotation(_cppMesh)); if (error.Equals("")) { return(count); } else { NativeInterface.DestroyMesh(this._cppMesh); throw new Exception(error); } }