private NPVoxModel CreateSlicedModel(NPVoxModel source, NPVoxModel reuse) { NPVoxBox targetBox = slice.Clone(); NPVoxBox sourceBounds = source.BoundingBox; targetBox.Clamp(source.BoundingBox); NPVoxCoord origin = targetBox.LeftDownBack; NPVoxModel model = NPVoxModel.NewInstance(source, targetBox.Size, reuse); int numVoxels = 0; foreach (NPVoxCoord coord in targetBox.Enumerate()) { if (source.HasVoxel(coord)) { numVoxels++; model.SetVoxel(coord - origin, source.GetVoxel(coord)); } } model.NumVoxels = numVoxels; model.Colortable = source.Colortable; return(model); }
public static NPVoxModel Transform(NPVoxModel sourceModel, NPVoxBox affectedArea, Matrix4x4 transformMatrix, ResolveConflictMethodType resolveConflictMethod = ResolveConflictMethodType.CLOSEST, NPVoxModel reuse = null) { NPVoxBox clampedBox = sourceModel.Clamp(affectedArea); // calculate size & offset for new model NPVoxCoord size = sourceModel.Size; NPVoxCoord offset = NPVoxCoord.ZERO; { NPVoxBox parentBounds = sourceModel.BoundingBox; NPVoxBox thisBounds = parentBounds.Clone(); // transform voxels foreach (NPVoxCoord coord in clampedBox.Enumerate()) { Vector3 saveCoord = transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(coord)); NPVoxCoord newCoord = NPVoxCoordUtil.ToCoord(saveCoord); if (!sourceModel.IsInside(newCoord)) { thisBounds.EnlargeToInclude(newCoord); } } // transform sockets foreach (NPVoxSocket socket in sourceModel.Sockets) { NPVoxCoord newCoord = NPVoxCoordUtil.ToCoord(transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(socket.Anchor))); if (clampedBox.Contains(socket.Anchor) && !sourceModel.IsInside(newCoord)) { thisBounds.EnlargeToInclude(newCoord); } } CalculateResizeOffset(parentBounds, thisBounds, out offset, out size); } bool hasVoxelGroups = sourceModel.HasVoxelGroups(); NPVoxBoneModel sourceBoneModel = sourceModel as NPVoxBoneModel; bool hasBoneGropus = sourceBoneModel != null; NPVoxModel transformedModel = NPVoxModel.NewInstance(sourceModel, size, reuse); NPVoxBoneModel transformedBoneModel = transformedModel as NPVoxBoneModel; if (hasVoxelGroups) { transformedModel.InitVoxelGroups(); transformedModel.NumVoxelGroups = sourceModel.NumVoxelGroups; } if (hasBoneGropus) { transformedBoneModel.AllBones = NPVoxBone.CloneBones(sourceBoneModel.AllBones); } // 1. copy all voxels over that are not affected by the transformation transformedModel.NumVoxels = sourceModel.NumVoxels; transformedModel.Colortable = sourceModel.Colortable; foreach (NPVoxCoord coord in sourceModel.EnumerateVoxels()) { NPVoxCoord movedCoord = coord + offset; if (!clampedBox.Contains(coord)) { transformedModel.SetVoxel(movedCoord, sourceModel.GetVoxel(coord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(movedCoord, sourceModel.GetVoxelGroup(coord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(movedCoord, sourceBoneModel.GetBoneMask(coord)); } } } // 2. copy all voxels that can be tranformed without conflict, Dictionary <NPVoxCoord, Vector3> conflictVoxels = new Dictionary <NPVoxCoord, Vector3>(); foreach (NPVoxCoord sourceCoord in clampedBox.Enumerate()) { if (sourceModel.HasVoxelFast(sourceCoord)) { Vector3 saveCoord = transformMatrix.MultiplyPoint(NPVoxCoordUtil.ToVector(sourceCoord)); Vector3 targetCoordSave = saveCoord + NPVoxCoordUtil.ToVector(offset); NPVoxCoord targetCoord = NPVoxCoordUtil.ToCoord(targetCoordSave); if (!transformedModel.HasVoxelFast(targetCoord)) { transformedModel.SetVoxel(targetCoord, sourceModel.GetVoxel(sourceCoord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(targetCoord, sourceModel.GetVoxelGroup(sourceCoord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(targetCoord, sourceBoneModel.GetBoneMask(sourceCoord)); } } else { conflictVoxels[sourceCoord] = targetCoordSave; } } } // 3. try to fit in voxels that had conflicts int numberOfConflictsSolved = 0; if (resolveConflictMethod != ResolveConflictMethodType.NONE) { foreach (NPVoxCoord sourceCoord in conflictVoxels.Keys) { if (sourceModel.HasVoxelFast(sourceCoord)) { Vector3 targetSaveCoord = conflictVoxels[sourceCoord]; NPVoxCoord nearbyCoord = GetNearbyCoord(transformedModel, targetSaveCoord, resolveConflictMethod); if (!nearbyCoord.Equals(NPVoxCoord.INVALID)) { transformedModel.SetVoxel(nearbyCoord, sourceModel.GetVoxel(sourceCoord)); if (hasVoxelGroups) { transformedModel.SetVoxelGroup(nearbyCoord, sourceModel.GetVoxelGroup(sourceCoord)); } if (hasBoneGropus) { transformedBoneModel.SetBoneMask(nearbyCoord, sourceBoneModel.GetBoneMask(sourceCoord)); } numberOfConflictsSolved++; } } } if (numberOfConflictsSolved != conflictVoxels.Count) { Debug.Log(string.Format("transformation has resolved {0}/{1} conflicting voxels", numberOfConflictsSolved, conflictVoxels.Count)); } } // 4. transform all sockets NPVoxSocket[] sockets = new NPVoxSocket[sourceModel.Sockets.Length]; for (int i = 0; i < sockets.Length; i++) { NPVoxSocket socket = sourceModel.Sockets[i]; if (clampedBox.Contains(socket.Anchor)) { // transform anchor Vector3 saveOriginalAnchor = NPVoxCoordUtil.ToVector(socket.Anchor); Vector3 saveTargetAnchor = transformMatrix.MultiplyPoint(saveOriginalAnchor) + NPVoxCoordUtil.ToVector(offset); socket.Anchor = NPVoxCoordUtil.ToCoord(saveTargetAnchor); // transform Quaternion Quaternion originalRotation = Quaternion.Euler(socket.EulerAngles); Matrix4x4 rotated = (Matrix4x4.TRS(Vector3.zero, originalRotation, Vector3.one) * transformMatrix); socket.EulerAngles = Matrix4x4Util.GetRotation(rotated).eulerAngles; } else { socket.Anchor = socket.Anchor + offset; } sockets[i] = socket; } transformedModel.Sockets = sockets; // 5. count all voxels transformedModel.NumVoxels = transformedModel.NumVoxels - (conflictVoxels.Count - numberOfConflictsSolved); transformedModel.RecalculateNumVoxels(true); return(transformedModel); }
override protected Mesh CreateProduct(Mesh mesh = null) { // Debug.Log("create product"); NPVoxMeshOutput meshOutput = (Input as NPVoxMeshOutput); if (meshOutput && meshOutput.GetProduct() && TextureAtlas) { TextureAtlas.GetMaterial(SourceMaterial); NPVoxFaces includedFaces = GetIncludedFaces(); NPVoxToUnity npVoxToUnity = InputMeshFactory.GetNPVoxToUnity(); int faceCount = GetFaceCount(); NPVoxBox originalBox = InputMeshFactory.GetVoxModel().BoundingBox; NPVoxBox cutoutBox = originalBox.Clone(); NPVoxFaces cutout = InputMeshFactory.Cutout; Vector3 cutoutOffset = Vector3.zero; if (cutout != null) { Vector3 originalCenter = cutoutBox.SaveCenter; cutoutBox.Left = (sbyte)Mathf.Abs(cutout.Left); cutoutBox.Down = (sbyte)Mathf.Abs(cutout.Down); cutoutBox.Back = (sbyte)Mathf.Abs(cutout.Back); cutoutBox.Right = (sbyte)(cutoutBox.Right - (sbyte)Mathf.Abs(cutout.Right)); cutoutBox.Up = (sbyte)(cutoutBox.Up - (sbyte)Mathf.Abs(cutout.Up)); cutoutBox.Forward = (sbyte)(cutoutBox.Forward - (sbyte)Mathf.Abs(cutout.Forward)); cutoutOffset = Vector3.Scale(originalCenter - cutoutBox.SaveCenter, InputMeshFactory.VoxelSize); } // we have to be careful. Unlike cutout, which is already removed from the mesh we want to render, the inset is not yet applied and // also won't result in a "move" of the object. So it's important that we calculate a correct offset for our final mesh. NPVoxBox insetBox = cutoutBox.Clone(); Vector3 insetOffset = Vector3.zero; if (inset != null) { Vector3 cutoutCenter = cutoutBox.SaveCenter; insetBox.Left += (sbyte)Mathf.Abs(inset.Left); insetBox.Right -= (sbyte)Mathf.Abs(inset.Right); insetBox.Down += (sbyte)Mathf.Abs(inset.Down); insetBox.Up -= (sbyte)Mathf.Abs(inset.Up); insetBox.Back += (sbyte)Mathf.Abs(inset.Back); insetBox.Forward -= (sbyte)Mathf.Abs(inset.Forward); insetOffset = Vector3.Scale(cutoutCenter - insetBox.SaveCenter, InputMeshFactory.VoxelSize); } Vector3 insetCenter = insetBox.SaveCenter; if (Baked45Angle) { backSlot = CreateTexture(backSlot, includedFaces.Back != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(-45, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Back)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, ((float)insetBox.Size.Y) / Mathf.Sqrt(2))) * 0.5f, Quaternion.Euler(+45, 0, 0) ); downSlot = CreateTexture(downSlot, includedFaces.Down != 0, insetBox.Size.X, insetBox.Size.Z * 3, Quaternion.Euler(-45, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Down, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, ((float)insetBox.Size.Z) / Mathf.Sqrt(2))) * 0.5f, Quaternion.Euler(-45, 0, 0) ); leftSlot = CreateTexture(leftSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); rightSlot = CreateTexture(rightSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); upSlot = CreateTexture(upSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); forwardSlot = CreateTexture(forwardSlot, false, 0, 0, Quaternion.identity, Vector3.zero, Vector2.zero, Quaternion.identity); } else { leftSlot = CreateTexture(leftSlot, includedFaces.Left != 0, insetBox.Size.Z, insetBox.Size.Y, Quaternion.Euler(0, 90, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetBox.Left, insetCenter.y, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.Z, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); rightSlot = CreateTexture(rightSlot, includedFaces.Right != 0, insetBox.Size.Z, insetBox.Size.Y, Quaternion.Euler(0, -90, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetBox.Right, insetCenter.y, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.Z, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); downSlot = CreateTexture(downSlot, includedFaces.Down != 0, insetBox.Size.X, insetBox.Size.Z, Quaternion.Euler(-90, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Down, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Z)) * 0.5f, Quaternion.identity ); upSlot = CreateTexture(upSlot, includedFaces.Up != 0, insetBox.Size.X, insetBox.Size.Z, Quaternion.Euler(90, 0, 180), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetBox.Up, insetCenter.z)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Z)) * 0.5f, Quaternion.identity ); backSlot = CreateTexture(backSlot, includedFaces.Back != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(0, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Back)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); forwardSlot = CreateTexture(forwardSlot, includedFaces.Forward != 0, insetBox.Size.X, insetBox.Size.Y, Quaternion.Euler(-180, 0, 0), npVoxToUnity.ToUnityPosition(new Vector3(insetCenter.x, insetCenter.y, insetBox.Forward)), npVoxToUnity.ToUnityDirection(new Vector2(insetBox.Size.X, insetBox.Size.Y)) * 0.5f, Quaternion.identity ); } slotsAllocatedAtTA = TextureAtlas; if (mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); } mesh.name = "zzz Cube Simplifier Mesh"; int border = 1; var vertices = new Vector3[faceCount * 4]; var uvs = new Vector2[faceCount * 4]; var tris = new int[faceCount * 3 * 2]; var normals = new Vector3[faceCount * 4]; int v = 0; int t = 0; int v0 = 0; System.Action <Vector3, NPVoxTextureAtlas.Slot> addQuad = (Vector3 dir, NPVoxTextureAtlas.Slot theSlot) => { normals[v0] = dir; normals[v0 + 1] = dir; normals[v0 + 2] = dir; normals[v0 + 3] = dir; tris[t++] = v0; tris[t++] = v0 + 1; tris[t++] = v0 + 2; tris[t++] = v0; tris[t++] = v0 + 2; tris[t++] = v0 + 3; Vector2 uvMax = theSlot.GetUVmax(border); Vector2 uvMin = theSlot.GetUVmin(border); uvs[v0].x = uvMin.x; uvs[v0].y = uvMax.y; uvs[v0 + 1].x = uvMax.x; uvs[v0 + 1].y = uvMax.y; uvs[v0 + 2].x = uvMax.x; uvs[v0 + 2].y = uvMin.y; uvs[v0 + 3].x = uvMin.x; uvs[v0 + 3].y = uvMin.y; }; NPVoxBox bounds = insetBox; Vector3 LDB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftDownBack) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RDB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightDownBack) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LUB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftUpBack) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RUB = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightUpBack) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.back * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LDF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftDownForward) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RDF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightDownForward) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.down * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 LUF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.LeftUpForward) + (Vector3.left * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; Vector3 RUF = cutoutOffset + npVoxToUnity.ToUnityPosition(bounds.RightUpForward) + (Vector3.right * npVoxToUnity.VoxeSize.x + Vector3.up * npVoxToUnity.VoxeSize.y + Vector3.forward * npVoxToUnity.VoxeSize.z) * 0.5f; if (downSlot != null) { v0 = v; vertices[v++] = LDB; vertices[v++] = RDB; vertices[v++] = RDF; vertices[v++] = LDF; addQuad(Vector3.down, downSlot); } if (upSlot != null) { v0 = v; vertices[v++] = RUB; vertices[v++] = LUB; vertices[v++] = LUF; vertices[v++] = RUF; addQuad(Vector3.up, upSlot); } if (forwardSlot != null) { v0 = v; vertices[v++] = LDF; vertices[v++] = RDF; vertices[v++] = RUF; vertices[v++] = LUF; addQuad(Vector3.forward, forwardSlot); } if (backSlot != null) { v0 = v; vertices[v++] = LUB; vertices[v++] = RUB; vertices[v++] = RDB; vertices[v++] = LDB; addQuad(Vector3.back, backSlot); } if (leftSlot != null) { v0 = v; vertices[v++] = LUF; vertices[v++] = LUB; vertices[v++] = LDB; vertices[v++] = LDF; addQuad(Vector3.left, leftSlot); } if (rightSlot != null) { v0 = v; vertices[v++] = RUB; vertices[v++] = RUF; vertices[v++] = RDF; vertices[v++] = RDB; addQuad(Vector3.right, rightSlot); } mesh.vertices = vertices; mesh.triangles = tris; mesh.uv = uvs; mesh.RecalculateBounds(); mesh.normals = normals; TangentSolver.Solve(mesh); // mesh.bounds = new Bounds( // insetOffset, // new Vector3( // bounds.Size.X * npVoxToUnity.VoxeSize.x, // bounds.Size.Y * npVoxToUnity.VoxeSize.y, // bounds.Size.Z * npVoxToUnity.VoxeSize.z // ) // ); Mesh sourceMesh = meshOutput.GetProduct(); mesh.bounds = sourceMesh.bounds; mesh.name = "zzz Cube Mesh "; return(mesh); } else { Debug.LogWarning("No Input set up"); if (mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); } return(mesh); } }