public static NPVoxModel TransformSocket(NPVoxModel sourceModel, string socketName, Matrix4x4 transformMatrix, NPVoxModel reuse = null) { NPVoxModel transformedModel = null; transformedModel = NPVoxModel.NewInstance(sourceModel, reuse); transformedModel.CopyOver(sourceModel); NPVoxSocket[] sockets = new NPVoxSocket[sourceModel.Sockets.Length]; for (int i = 0; i < sockets.Length; i++) { NPVoxSocket socket = sourceModel.Sockets[i]; if (socket.Name == socketName) { // transform anchor Vector3 saveOriginalAnchor = NPVoxCoordUtil.ToVector(socket.Anchor); Vector3 saveTargetAnchor = transformMatrix.MultiplyPoint(saveOriginalAnchor); socket.Anchor = sourceModel.Clamp(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; } sockets[i] = socket; } transformedModel.Sockets = sockets; return(transformedModel); }
override protected NPVoxModel CreateProduct(NPVoxModel reuse = null) { if (Input == null) { return(NPVoxModel.NewInvalidInstance(reuse, "No Input Setup"));; } NPVoxModel model = ((NPVoxIModelFactory)Input).GetProduct() as NPVoxModel; NPVoxModel newModel = NPVoxModel.NewInstance(model, NPVoxCoord.ZERO, reuse); newModel.CopyOver(model); bool addVoxelGroups = addVoxelGroup || newModel.HasVoxelGroups(); byte theVoxelGroup = (byte)255; if (addVoxelGroups) { if (!newModel.HasVoxelGroups()) { newModel.InitVoxelGroups(); foreach (NPVoxCoord coord in newModel.EnumerateVoxels()) { newModel.SetVoxelGroup(coord, 0); } theVoxelGroup = 1; newModel.NumVoxelGroups = 2; } else { theVoxelGroup = newModel.NumVoxelGroups; newModel.NumVoxelGroups++; } } Vector3 direction1 = rotation1 * Vector3.forward; Vector3 direction2 = rotation2 * Vector3.forward; bool[] usedColors = NPVoxModelUtils.GetUsedColors(newModel); Color32[] colorTable = newModel.Colortable; Vector3 currentP1 = NPVoxCoordUtil.ToVector(startCoord1); Vector3 currentP2 = NPVoxCoordUtil.ToVector(startCoord2); for (int i = 0; i < numSteps; i++) { byte color = NPVoxModelUtils.FindUnusedColor(ref usedColors); colorTable[color] = Color32.Lerp(startColor, endColor, (float)i / (float)numSteps); NPVoxGeomUtil.DrawLine(newModel, currentP1, currentP2, color, theVoxelGroup); currentP1 += direction1 * stepSize1; currentP2 += direction2 * stepSize2; } newModel.Colortable = colorTable; newModel.RecalculateNumVoxels(); // return(newModel); }
public static NPVoxModel MatrixTransformSocket(NPVoxModel sourceModel, string socketName, Matrix4x4 matrix, Vector3 pivot, NPVoxModel reuse = null) { NPVoxSocket socket = sourceModel.GetSocketByName(socketName); Vector3 pivotPoint = NPVoxCoordUtil.ToVector(socket.Anchor) + pivot; Matrix4x4 transformMatrix = (Matrix4x4.TRS(pivotPoint, Quaternion.identity, Vector3.one) * matrix) * Matrix4x4.TRS(-pivotPoint, Quaternion.identity, Vector3.one); return(TransformSocket(sourceModel, socketName, transformMatrix, reuse)); }
override public Vector3 GetPivot() { NPVoxIModelFactory modelFactory = (Input as NPVoxIModelFactory); if (modelFactory != null) { NPVoxSocket socket = modelFactory.GetProduct().GetSocketByName(SocketName); return(this.PivotOffset + NPVoxCoordUtil.ToVector(socket.Anchor)); } else { return(Vector3.zero); } }
override public void SetPivot(Vector3 pivot) { NPVoxIModelFactory modelFactory = (Input as NPVoxIModelFactory); if (modelFactory != null) { NPVoxSocket socket = modelFactory.GetProduct().GetSocketByName(SocketName); this.PivotOffset = pivot - NPVoxCoordUtil.ToVector(socket.Anchor); } else { this.PivotOffset = Vector3.zero; } }
public static NPVoxCoord GetNearbyCoord(NPVoxModel model, Vector3 saveCoord, ResolveConflictMethodType resolveConflictMethod) { NPVoxCoord favoriteCoord = NPVoxCoordUtil.ToCoord(saveCoord); if (model.HasVoxelFast(favoriteCoord)) { NPVoxBox box = new NPVoxBox(favoriteCoord - NPVoxCoord.ONE, favoriteCoord + NPVoxCoord.ONE); favoriteCoord = NPVoxCoord.INVALID; float nearestDistance = 9999f; int favoriteEnclosingVoxelCount = -1; foreach (NPVoxCoord currentTestCoord in box.Enumerate()) { if (model.IsInside(currentTestCoord) && !model.HasVoxelFast(currentTestCoord)) { if (resolveConflictMethod == ResolveConflictMethodType.CLOSEST) { float distance = Vector3.Distance(NPVoxCoordUtil.ToVector(currentTestCoord), saveCoord); if (distance < nearestDistance) { nearestDistance = distance; favoriteCoord = currentTestCoord; } } else { int enclosingVoxelCount = 0; NPVoxBox enclosingBoxCheck = new NPVoxBox(currentTestCoord - NPVoxCoord.ONE, currentTestCoord + NPVoxCoord.ONE); foreach (NPVoxCoord enclosingTestCoord in enclosingBoxCheck.Enumerate()) { if (model.IsInside(currentTestCoord) && model.HasVoxelFast(currentTestCoord)) { enclosingVoxelCount++; } } if (enclosingVoxelCount > favoriteEnclosingVoxelCount) { enclosingVoxelCount = favoriteEnclosingVoxelCount; favoriteCoord = currentTestCoord; } } } } } return(favoriteCoord); }
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); }
public static NPVoxBox DrawBoxSelection(NPVoxToUnity npVoxToUnity, NPVoxBox box, bool editable = true) { Vector3 leftDownBack = npVoxToUnity.ToUnityPosition(box.LeftDownBack) - npVoxToUnity.VoxeSize * 0.5f; Vector3 rightUpForward = npVoxToUnity.ToUnityPosition(box.RightUpForward) + npVoxToUnity.VoxeSize * 0.5f; Vector3 rightDownBack = new Vector3(rightUpForward.x, leftDownBack.y, leftDownBack.z); Vector3 leftUpBack = new Vector3(leftDownBack.x, rightUpForward.y, leftDownBack.z); Vector3 rightUpBack = new Vector3(rightUpForward.x, rightUpForward.y, leftDownBack.z); Vector3 leftDownForward = new Vector3(leftDownBack.x, leftDownBack.y, rightUpForward.z); Vector3 rightDownForward = new Vector3(rightUpForward.x, leftDownBack.y, rightUpForward.z); Vector3 leftUpForward = new Vector3(leftDownBack.x, rightUpForward.y, rightUpForward.z); Handles.DrawDottedLine(leftDownBack, rightDownBack, 1f); Handles.DrawDottedLine(leftDownBack, leftUpBack, 1f); Handles.DrawDottedLine(leftUpBack, rightUpBack, 1f); Handles.DrawDottedLine(rightDownBack, rightUpBack, 1f); Handles.DrawDottedLine(leftDownForward, rightDownForward, 1f); Handles.DrawDottedLine(leftDownForward, leftUpForward, 1f); Handles.DrawDottedLine(leftUpForward, rightUpForward, 1f); Handles.DrawDottedLine(rightDownForward, rightUpForward, 1f); Handles.DrawDottedLine(leftDownBack, leftDownForward, 1f); Handles.DrawDottedLine(rightDownBack, rightDownForward, 1f); Handles.DrawDottedLine(leftUpBack, leftUpForward, 1f); Handles.DrawDottedLine(rightUpBack, rightUpForward, 1f); if (!editable) { return(box); } NPVoxBox newBox = new NPVoxBox(box.LeftDownBack, box.RightUpForward); if (SceneView.currentDrawingSceneView.camera.orthographic) { NPVoxCoord oldCoord; NPVoxCoord newCoord; oldCoord = box.LeftDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(leftDownBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftDownBack = newCoord; } oldCoord = box.RightDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(rightDownBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightDownBack = newCoord; } oldCoord = box.LeftUpBack; newCoord = NPVoxHandles.DrawVoxelHandle(leftUpBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftUpBack = newCoord; } oldCoord = box.RightUpBack; newCoord = NPVoxHandles.DrawVoxelHandle(rightUpBack, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightUpBack = newCoord; } oldCoord = box.LeftDownForward; newCoord = NPVoxHandles.DrawVoxelHandle(leftDownForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftDownForward = newCoord; } oldCoord = box.RightDownForward; newCoord = NPVoxHandles.DrawVoxelHandle(rightDownForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightDownForward = newCoord; } oldCoord = box.LeftUpForward; newCoord = NPVoxHandles.DrawVoxelHandle(leftUpForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.LeftUpForward = newCoord; } oldCoord = box.RightUpForward; newCoord = NPVoxHandles.DrawVoxelHandle(rightUpForward, oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.RightUpForward = newCoord; } // center mover oldCoord = box.LeftDownBack; newCoord = NPVoxHandles.DrawVoxelHandle(npVoxToUnity.ToUnityPosition(box.SaveCenter), oldCoord, npVoxToUnity); if (!newCoord.Equals(oldCoord)) { newBox.SaveCenter += NPVoxCoordUtil.ToVector(newCoord - oldCoord); } } else { sbyte oldPos; sbyte newPos; Vector3 handlePos; handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z); oldPos = box.Back; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.forward); if (oldPos != newPos) { newBox.Back = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, rightUpForward.z); oldPos = box.Forward; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.forward); if (oldPos != newPos) { newBox.Forward = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, leftDownBack.y, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Down; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.up); if (oldPos != newPos) { newBox.Down = newPos; } handlePos = new Vector3(leftDownBack.x + (rightUpForward.x - leftDownBack.x) / 2, rightUpForward.y, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Up; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.up); if (oldPos != newPos) { newBox.Up = newPos; } handlePos = new Vector3(leftDownBack.x, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Left; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.right); if (oldPos != newPos) { newBox.Left = newPos; } handlePos = new Vector3(rightUpForward.x, leftDownBack.y + (rightUpForward.y - leftDownBack.y) / 2, leftDownBack.z + (rightUpForward.z - leftDownBack.z) / 2); oldPos = box.Right; newPos = NPVoxHandles.DrawAxisHandle(handlePos, oldPos, npVoxToUnity, Vector3.right); if (oldPos != newPos) { newBox.Right = newPos; } } return(newBox); }
public override void Process(NPVoxModel model, NPVoxMeshData tempdata, Vector3[] inNormals, ref Vector3[] outNormals) { // calculate normals based on present neighbour voxels Vector3 voxelNormal = Vector3.zero; if (!tempdata.isHidden) { foreach (NPVoxCoord offset in voxelNormalNeighbours.Enumerate()) { NPVoxCoord checkCoord = tempdata.voxCoord + offset; checkCoord = model.LoopCoord(checkCoord, tempdata.loop); if (!model.HasVoxel(checkCoord)) { voxelNormal += NPVoxCoordUtil.ToVector(offset); } } voxelNormal.Normalize(); } else { voxelNormal = tempdata.voxelCenter.normalized; } for (int t = 0; t < tempdata.numVertices; t++) { Vector3 normal = Vector3.zero; switch (m_normalMode) { case NPVoxNormalMode.VOXEL: normal = voxelNormal; normal = Vector3.zero; if (tempdata.vertexPositionOffsets[t].x < 0.0f) { if (tempdata.hasLeft && !tempdata.hasForward && !tempdata.hasBack && !tempdata.hasUp && !tempdata.hasDown) { normal.x = -1; } else { normal.x = voxelNormal.x; } } else if (tempdata.vertexPositionOffsets[t].x > 0.0f) { if (tempdata.hasRight && !tempdata.hasForward && !tempdata.hasBack && !tempdata.hasUp && !tempdata.hasDown) { normal.x = 1; } else { normal.x = voxelNormal.x; } } if (tempdata.vertexPositionOffsets[t].y < 0.0f) { if (tempdata.hasUp && !tempdata.hasForward && !tempdata.hasBack && !tempdata.hasLeft && !tempdata.hasRight) { normal.y = -1; } else { normal.y = voxelNormal.y; } } else if (tempdata.vertexPositionOffsets[t].y > 0.0f) { if (tempdata.hasDown && !tempdata.hasForward && !tempdata.hasBack && !tempdata.hasLeft && !tempdata.hasRight) { normal.y = +1; } else { normal.y = voxelNormal.y; } } if (tempdata.vertexPositionOffsets[t].z < 0.0f) { if (tempdata.hasBack && !tempdata.hasLeft && !tempdata.hasRight && !tempdata.hasUp && !tempdata.hasDown) { normal.z = -1; } else { normal.z = voxelNormal.z; } } else if (tempdata.vertexPositionOffsets[t].z > 0.0f) { if (tempdata.hasForward && !tempdata.hasLeft && !tempdata.hasRight && !tempdata.hasUp && !tempdata.hasDown) { normal.z = +1; } else { normal.z = voxelNormal.z; } } if (Mathf.Abs(normal.x) < 0.1f && Mathf.Abs(normal.y) < 0.1f && Mathf.Abs(normal.z) < 0.1f) { // we would like to have full color when we are a stand-alone voxel, however there is no way to do so right now, so we just // fallback to the centoid normal normal = tempdata.voxelCenter; } normal.Normalize(); break; case NPVoxNormalMode.SMOOTH: normal = Vector3.zero; for (float xx = -0.5f; xx < 1.0f; xx += 1f) { for (float yy = -.5f; yy < 1; yy += 1) { for (float zz = -.5f; zz < 1; zz += 1) { sbyte xCoord = ( sbyte )Mathf.Round(tempdata.vertexPositionOffsets[t].x + xx); sbyte yCoord = ( sbyte )Mathf.Round(tempdata.vertexPositionOffsets[t].y + yy); sbyte zCoord = ( sbyte )Mathf.Round(tempdata.vertexPositionOffsets[t].z + zz); if (!model.HasVoxel(tempdata.voxCoord + new NPVoxCoord(( sbyte )xCoord, ( sbyte )yCoord, ( sbyte )zCoord))) { normal += new Vector3( xx, yy, zz ); } } } } normal.Normalize(); break; case NPVoxNormalMode.FORWARD: normal = Vector3.forward; break; case NPVoxNormalMode.BACK: normal = Vector3.back; break; case NPVoxNormalMode.UP: normal = Vector3.up; break; case NPVoxNormalMode.DOWN: normal = Vector3.down; break; case NPVoxNormalMode.LEFT: normal = Vector3.left; break; case NPVoxNormalMode.RIGHT: normal = Vector3.right; break; } outNormals[tempdata.vertexIndexOffsetBegin + t] = normal; } }
public static void CreateMesh( NPVoxModel model, Mesh mesh, Vector3 cubeSize, Vector3 NormalVariance, int NormalVarianceSeed = 0, NPVoxOptimization optimization = NPVoxOptimization.OFF, NPVoxNormalMode NormalMode = NPVoxNormalMode.SMOOTH, int BloodColorIndex = 0, NPVoxFaces loop = null, NPVoxFaces cutout = null, NPVoxFaces include = null, int MinVertexGroups = 1, NPVoxNormalMode[] NormalModePerVoxelGroup = null ) { bool hasVoxelGroups = model.HasVoxelGroups(); var vertices = new Vector3[model.NumVoxels * 8]; byte vertexGroupCount = model.NumVoxelGroups; var triangles = new int[vertexGroupCount, model.NumVoxels * 36]; var normals = new Vector3[model.NumVoxels * 8]; var tangents = new Vector4[model.NumVoxels * 8]; var colors = new Color[model.NumVoxels * 8]; int currentVertexIndex = 0; var currentTriangleIndex = new int[vertexGroupCount]; for (int i = 0; i < vertexGroupCount; i++) { currentTriangleIndex[i] = 0; } UnityEngine.Random.InitState(NormalVarianceSeed); if (loop == null) { loop = new NPVoxFaces(); } if (include == null) { include = new NPVoxFaces(1, 1, 1, 1, 1, 1); } NPVoxBox voxelsToInclude = model.BoundingBox; Vector3 cutoutOffset = Vector3.zero; if (cutout != null) { Vector3 originalCenter = voxelsToInclude.SaveCenter; voxelsToInclude.Left = (sbyte)Mathf.Abs(cutout.Left); voxelsToInclude.Down = (sbyte)Mathf.Abs(cutout.Down); voxelsToInclude.Back = (sbyte)Mathf.Abs(cutout.Back); voxelsToInclude.Right = (sbyte)(voxelsToInclude.Right - (sbyte)Mathf.Abs(cutout.Right)); voxelsToInclude.Up = (sbyte)(voxelsToInclude.Up - (sbyte)Mathf.Abs(cutout.Up)); voxelsToInclude.Forward = (sbyte)(voxelsToInclude.Forward - (sbyte)Mathf.Abs(cutout.Forward)); cutoutOffset = Vector3.Scale(originalCenter - voxelsToInclude.SaveCenter, cubeSize); } NPVoxToUnity npVoxToUnity = new NPVoxToUnity(model, cubeSize); Vector3 size = new Vector3( voxelsToInclude.Size.X * cubeSize.x, voxelsToInclude.Size.Y * cubeSize.y, voxelsToInclude.Size.Z * cubeSize.z ); NPVoxBox voxelNormalNeighbours = new NPVoxBox(new NPVoxCoord(-1, -1, -1), new NPVoxCoord(1, 1, 1)); NPVoxNormalMode normalMode = NormalMode; foreach (NPVoxCoord voxCoord in voxelsToInclude.Enumerate()) { if (model.HasVoxel(voxCoord)) { Vector3 voxelCenter = npVoxToUnity.ToUnityPosition(voxCoord) + cutoutOffset; int vertexGroupIndex = 0; if (hasVoxelGroups) { vertexGroupIndex = model.GetVoxelGroup(voxCoord); } if (NormalModePerVoxelGroup != null && NormalModePerVoxelGroup.Length > vertexGroupIndex) { normalMode = NormalModePerVoxelGroup[vertexGroupIndex]; } else { normalMode = NormalMode; } // do we have this side bool hasLeft = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.LEFT, loop)); bool hasRight = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.RIGHT, loop)); bool hasDown = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.DOWN, loop)); bool hasUp = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.UP, loop)); bool hasForward = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.FORWARD, loop)); bool hasBack = !model.HasVoxel(model.LoopCoord(voxCoord + NPVoxCoord.BACK, loop)); // do we actually want to include this side in our mesh // NOTE: cutout < 0 means we still render the mesh even though it is cutout // cutout > 0 means we don't render the mesh when cutout bool includeLeft = (hasLeft || (cutout.Left < 0 && voxCoord.X == voxelsToInclude.Left)) && include.Left == 1; bool includeRight = (hasRight || (cutout.Right < 0 && voxCoord.X == voxelsToInclude.Right)) && include.Right == 1; bool includeUp = (hasUp || (cutout.Up < 0 && voxCoord.Y == voxelsToInclude.Up)) && include.Up == 1; bool includeDown = (hasDown || (cutout.Down < 0 && voxCoord.Y == voxelsToInclude.Down)) && include.Down == 1; bool includeBack = (hasBack || (cutout.Back < 0 && voxCoord.Z == voxelsToInclude.Back)) && include.Back == 1; bool includeForward = (hasForward || (cutout.Forward < 0 && voxCoord.Z == voxelsToInclude.Forward)) && include.Forward == 1; bool isHidden = !hasForward && !hasBack && !hasLeft && !hasRight && !hasUp && !hasDown; if (isHidden && optimization == NPVoxOptimization.PER_VOXEL) { continue; } if (isHidden && BloodColorIndex > 0) { model.SetVoxel(voxCoord, (byte)BloodColorIndex); // WTF WTF WTF?!? we should not modify the MODEL in here !!!! } Color color = model.GetColor(voxCoord); // prepare cube vertices int numVertices = 0; int[] vertexIndexOffsets = new int[8]; Vector3[] vertexPositionOffsets = new Vector3[8]; if (optimization != NPVoxOptimization.PER_FACE || includeBack || includeLeft || includeDown) { vertexIndexOffsets[0] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[0]] = new Vector3(-0.5f, -0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeBack || includeRight || includeDown) { vertexIndexOffsets[1] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[1]] = new Vector3(0.5f, -0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeBack || includeLeft || includeUp) { vertexIndexOffsets[2] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[2]] = new Vector3(-0.5f, 0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeBack || includeRight || includeUp) { vertexIndexOffsets[3] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[3]] = new Vector3(0.5f, 0.5f, -0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeForward || includeLeft || includeDown) { vertexIndexOffsets[4] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[4]] = new Vector3(-0.5f, -0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeForward || includeRight || includeDown) { vertexIndexOffsets[5] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[5]] = new Vector3(0.5f, -0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeForward || includeLeft || includeUp) { vertexIndexOffsets[6] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[6]] = new Vector3(-0.5f, 0.5f, 0.5f); } if (optimization != NPVoxOptimization.PER_FACE || includeForward || includeRight || includeUp) { vertexIndexOffsets[7] = numVertices++; vertexPositionOffsets[vertexIndexOffsets[7]] = new Vector3(0.5f, 0.5f, 0.5f); } // add cube faces int i = currentTriangleIndex[vertexGroupIndex]; // back if (optimization != NPVoxOptimization.PER_FACE || includeBack) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[0]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[2]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[1]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[2]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[3]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[1]; } // Forward if (optimization != NPVoxOptimization.PER_FACE || includeForward) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[6]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[4]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[5]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[7]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[6]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[5]; } // right if (optimization != NPVoxOptimization.PER_FACE || includeRight) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[1]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[3]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[5]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[3]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[7]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[5]; } // left if (optimization != NPVoxOptimization.PER_FACE || includeLeft) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[0]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[4]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[2]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[2]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[4]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[6]; } // up if (optimization != NPVoxOptimization.PER_FACE || includeUp) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[2]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[6]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[3]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[3]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[6]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[7]; } // down if (optimization != NPVoxOptimization.PER_FACE || includeDown) { triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[0]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[1]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[4]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[1]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[5]; triangles[vertexGroupIndex, i++] = currentVertexIndex + vertexIndexOffsets[4]; } // TODO create some kind of strategy pattern for the normal calculation, else this here is becomming a mess ... Vector3 variance = Vector3.zero; if (NormalVariance.x != 0 || NormalVariance.y != 0 || NormalVariance.z != 0) { variance.x = -NormalVariance.x * 0.5f + 2 * UnityEngine.Random.value * NormalVariance.x; variance.y = -NormalVariance.y * 0.5f + 2 * UnityEngine.Random.value * NormalVariance.y; variance.z = -NormalVariance.z * 0.5f + 2 * UnityEngine.Random.value * NormalVariance.z; } // calculate normals based on present neighbour voxels Vector3 voxelNormal = Vector3.zero; if (!isHidden) { foreach (NPVoxCoord offset in voxelNormalNeighbours.Enumerate()) { NPVoxCoord checkCoord = voxCoord + offset; checkCoord = model.LoopCoord(checkCoord, loop); if (!model.HasVoxel(checkCoord)) { voxelNormal += NPVoxCoordUtil.ToVector(offset); } } voxelNormal.Normalize(); } else { voxelNormal = voxelCenter.normalized; } // Normals for (int t = 0; t < numVertices; t++) { Vector3 normal = Vector3.zero; switch (normalMode) { case NPVoxNormalMode.VOXEL: normal = voxelNormal; normal = Vector3.zero; if (vertexPositionOffsets[t].x < 0.0f) { if (hasLeft && !hasForward && !hasBack && !hasUp && !hasDown) { normal.x = -1; } else { normal.x = voxelNormal.x; } } else if (vertexPositionOffsets[t].x > 0.0f) { if (hasRight && !hasForward && !hasBack && !hasUp && !hasDown) { normal.x = 1; } else { normal.x = voxelNormal.x; } } if (vertexPositionOffsets[t].y < 0.0f) { if (hasUp && !hasForward && !hasBack && !hasLeft && !hasRight) { normal.y = -1; } else { normal.y = voxelNormal.y; } } else if (vertexPositionOffsets[t].y > 0.0f) { if (hasDown && !hasForward && !hasBack && !hasLeft && !hasRight) { normal.y = +1; } else { normal.y = voxelNormal.y; } } if (vertexPositionOffsets[t].z < 0.0f) { if (hasBack && !hasLeft && !hasRight && !hasUp && !hasDown) { normal.z = -1; } else { normal.z = voxelNormal.z; } } else if (vertexPositionOffsets[t].z > 0.0f) { if (hasForward && !hasLeft && !hasRight && !hasUp && !hasDown) { normal.z = +1; } else { normal.z = voxelNormal.z; } } if (Mathf.Abs(normal.x) < 0.1f && Mathf.Abs(normal.y) < 0.1f && Mathf.Abs(normal.z) < 0.1f) { // we would like to have full color when we are a stand-alone voxel, however there is no way to do so right now, so we just // fallback to the centoid normal normal = voxelCenter; } normal.Normalize(); break; case NPVoxNormalMode.SMOOTH: normal = Vector3.zero; for (float xx = -0.5f; xx < 1.0f; xx += 1f) { for (float yy = -.5f; yy < 1; yy += 1) { for (float zz = -.5f; zz < 1; zz += 1) { sbyte xCoord = (sbyte)Mathf.Round(vertexPositionOffsets[t].x + xx); sbyte yCoord = (sbyte)Mathf.Round(vertexPositionOffsets[t].y + yy); sbyte zCoord = (sbyte)Mathf.Round(vertexPositionOffsets[t].z + zz); if (!model.HasVoxel(voxCoord + new NPVoxCoord((sbyte)xCoord, (sbyte)yCoord, (sbyte)zCoord))) { normal += new Vector3( xx, yy, zz ); } } } } normal.Normalize(); break; case NPVoxNormalMode.FORWARD: normal = Vector3.forward; break; case NPVoxNormalMode.BACK: normal = Vector3.back; break; case NPVoxNormalMode.UP: normal = Vector3.up; break; case NPVoxNormalMode.DOWN: normal = Vector3.down; break; case NPVoxNormalMode.LEFT: normal = Vector3.left; break; case NPVoxNormalMode.RIGHT: normal = Vector3.right; break; } normals[currentVertexIndex + t] = (normal + variance).normalized; // store voxel center for shader usage Vector4 tangent = new Vector4(); tangent.x = voxelCenter.x; tangent.y = voxelCenter.y; tangent.z = voxelCenter.z; // encode model size tangent.w = ((voxelsToInclude.Size.X & 0x7F) << 14) | ((voxelsToInclude.Size.Y & 0x7F) << 7) | (voxelsToInclude.Size.Z & 0x7F); tangents[currentVertexIndex + t] = tangent; } // UVs for (int t = 0; t < numVertices; t++) { colors[currentVertexIndex + t] = color; } // translate & scale vertices to voxel position for (int t = 0; t < numVertices; t++) { vertices[currentVertexIndex + t] = voxelCenter + Vector3.Scale(vertexPositionOffsets[t], cubeSize); } currentTriangleIndex[vertexGroupIndex] = i; currentVertexIndex += numVertices; } } // shrink arrays as needed if (optimization != NPVoxOptimization.OFF) { Array.Resize(ref vertices, currentVertexIndex); Array.Resize(ref normals, currentVertexIndex); Array.Resize(ref tangents, currentVertexIndex); Array.Resize(ref colors, currentVertexIndex); } mesh.vertices = vertices; if (hasVoxelGroups) { mesh.subMeshCount = Math.Max(vertexGroupCount, MinVertexGroups); for (int i = 0; i < vertexGroupCount; i++) { int numberOfTrianglesForVertexGroup = currentTriangleIndex[i]; int[] trianglesForVertexGroup = new int[numberOfTrianglesForVertexGroup]; for (int j = 0; j < numberOfTrianglesForVertexGroup; j++) { trianglesForVertexGroup[j] = triangles[i, j]; } mesh.SetTriangles(trianglesForVertexGroup, i); } } else { int numberOfTrianglesForVertexGroup = currentTriangleIndex[0]; int[] trianglesForVertexGroup = new int[numberOfTrianglesForVertexGroup]; Buffer.BlockCopy(triangles, 0, trianglesForVertexGroup, 0, numberOfTrianglesForVertexGroup * sizeof(int)); if (MinVertexGroups < 2) { mesh.triangles = trianglesForVertexGroup; } else { mesh.subMeshCount = MinVertexGroups; mesh.SetTriangles(trianglesForVertexGroup, 0); } } mesh.normals = normals; mesh.tangents = tangents; mesh.colors = colors; mesh.bounds = new Bounds(Vector3.zero, size); if (NormalMode == NPVoxNormalMode.AUTO) { mesh.RecalculateNormals(); } }