Exemplo n.º 1
0
        private FaceDescriptor makeFaceDescriptor(VoxelTypeID typeId, Direction originalDirection, LightValue lightValue, VoxelRotation rotation = default)
        {
            if (typeId == VoxelTypeID.AIR_ID)
            {
                return(nullFace);
            }

            var faceDirection = originalDirection;

            if (!rotation.isBlank)
            {
                ///Face direction needs to be the direction index of the face currently pointing in the original
                ///direction. Therefore it is the direction such that applying the rotation gives the original direction.
                faceDirection = directionRotator.GetDirectionBeforeRotation(originalDirection, rotation);
            }

            FaceDescriptor faceDescriptor = new FaceDescriptor()
            {
                typeId        = typeId,
                faceDirection = faceDirection,
                rotation      = rotation,
                lightValue    = lightValue
            };

            return(faceDescriptor);
        }
Exemplo n.º 2
0
        public FaceDescriptor GetFace(BoxFace face)
        {
            var r = new FaceDescriptor
            {
                VertexCount = 4,
                IndexCount  = 6
            };

            // Todo: Put the underlying vertex data in the right order and this is just some multiplies.
            switch (face)
            {
            case BoxFace.Top:
                r.IndexOffset  = 12;
                r.VertexOffset = 8;
                return(r);

            case BoxFace.Bottom:
                r.IndexOffset  = 18;
                r.VertexOffset = 12;
                return(r);

            case BoxFace.Left:
                r.IndexOffset  = 24;
                r.VertexOffset = 16;
                return(r);

            case BoxFace.Right:
                r.IndexOffset  = 30;
                r.VertexOffset = 20;
                return(r);

            case BoxFace.Front:
                r.IndexOffset  = 0;
                r.VertexOffset = 0;
                return(r);

            case BoxFace.Back:
                r.IndexOffset  = 6;
                r.VertexOffset = 4;
                return(r);
            }

            r.IndexOffset  = 0;
            r.VertexOffset = 0;
            return(r);
        }
Exemplo n.º 3
0
        private bool IncludeFace(FaceDescriptor thisFace, FaceDescriptor oppositeFace)
        {
            if (thisFace.typeId == oppositeFace.typeId)
            {
                return(false);//Dont include faces between voxels of the same type
            }

            if (oppositeFace.typeId == VoxelTypeID.AIR_ID)
            {
                //Include the face if the opposite voxel is air
                return(true);
            }

            var meshID      = data.meshDatabase.voxelTypeToMeshTypeMap[oppositeFace.typeId];
            var meshRange   = data.meshDatabase.meshTypeRanges[meshID];
            var faceIsSolid = data.meshDatabase.isFaceSolid[meshRange.start + (int)oppositeFace.faceDirection];

            //Exclude this face if opposite face is solid
            return(!faceIsSolid);
        }
Exemplo n.º 4
0
 public DoLater(FaceDescriptor currentMaskValue,
                int3 workingCoordinates,
                byte primaryAxis,
                byte secondaryAxis,
                byte tertiaryAxis,
                int width,
                int height,
                bool flip,
                bool flipNormals)
 {
     this.currentMaskValue   = currentMaskValue;
     this.workingCoordinates = workingCoordinates;
     this.primaryAxis        = primaryAxis;
     this.secondaryAxis      = secondaryAxis;
     this.tertiaryAxis       = tertiaryAxis;
     this.width       = width;
     this.height      = height;
     this.flip        = flip;
     this.flipNormals = flipNormals;
 }
Exemplo n.º 5
0
        public void Execute()
        {
            if (data.dimensions.x != data.dimensions.y || data.dimensions.y != data.dimensions.z)
            {
                throw new ArgumentException($"Greedy meshing does not support non-cubic chunk dimensions. " +
                                            $"x,y,z of chunk dimensions must be identical to use greedy meshing.");
            }

            //initialise locals
            nullFace   = default;
            dxdy       = data.dimensions.x * data.dimensions.y;
            runTracker = new MaterialRunTracker();

            //Initialise rotated voxels map from list
            for (int i = 0; i < data.rotatedVoxels.Length; i++)
            {
                var item = data.rotatedVoxels[i];
                rotatedVoxelsMap.Add(item.flatIndex, item.rotation);
            }

            int size = data.dimensions.x;

            //Sweep over 3-axes
            for (byte axis = 0; axis < 3; axis++)
            {
                //Secondary axis is used for width, tertiary axis is used for height
                byte secondaryAxis;
                byte tertiaryAxis;

                switch (axis)
                {
                case 0:    //x axis primary
                    secondaryAxis = 2;
                    tertiaryAxis  = 1;
                    break;

                case 1:    //y axis primary
                    secondaryAxis = 0;
                    tertiaryAxis  = 2;
                    break;

                case 2:    //z axis primary
                    secondaryAxis = 0;
                    tertiaryAxis  = 1;
                    break;

                default:
                    throw new Exception("axis not valid");
                }


                int3 workingCoordinates = new int3();
                int3 axisVector         = new int3();

                axisVector[axis] = 1;

                //Compute the direction we are currently meshing
                //Direction currentMeshDirection;
                Direction positiveAxisDirection;
                Direction negativeAxisDirection;
                if (axis == 0)
                {
                    positiveAxisDirection = Direction.east;
                    negativeAxisDirection = Direction.west;
                }
                else if (axis == 1)
                {
                    positiveAxisDirection = Direction.up;
                    negativeAxisDirection = Direction.down;
                }
                else
                {
                    positiveAxisDirection = Direction.north;
                    negativeAxisDirection = Direction.south;
                }


                //Sweep through planes in the direction of the current axis
                for (workingCoordinates[axis] = -1; workingCoordinates[axis] < size;)
                {
                    int maskIndex = 0;
                    // Compute the mask for this plane
                    ///NOTE assumes and requires that both masks are clear (full of nullface)
                    ///before this point
                    for (workingCoordinates[tertiaryAxis] = 0; workingCoordinates[tertiaryAxis] < size; ++workingCoordinates[tertiaryAxis])
                    {
                        for (workingCoordinates[secondaryAxis] = 0; workingCoordinates[secondaryAxis] < size; ++workingCoordinates[secondaryAxis], ++maskIndex)
                        {
                            //Face in the positive axis direction
                            bool           positiveFaceInThisChunk = (workingCoordinates[axis] >= 0);
                            FaceDescriptor positiveFace            = positiveFaceInThisChunk ? maskData(workingCoordinates, positiveAxisDirection, workingCoordinates + axisVector)
                                : GetFaceInNeighbour(workingCoordinates, negativeAxisDirection, positiveAxisDirection, axis, workingCoordinates + axisVector);

                            //Face in the negative axis direction
                            bool           negativeFaceInThisChunk = (workingCoordinates[axis] < size - 1);
                            FaceDescriptor negativeFace            = negativeFaceInThisChunk ? maskData(workingCoordinates + axisVector, negativeAxisDirection, workingCoordinates)
                                : GetFaceInNeighbour(workingCoordinates + axisVector, positiveAxisDirection, negativeAxisDirection, axis, workingCoordinates);


                            if (positiveFaceInThisChunk && IncludeFace(positiveFace, negativeFace))
                            {
                                maskPositive[maskIndex] = positiveFace;
                            }

                            if (negativeFaceInThisChunk && IncludeFace(negativeFace, positiveFace))
                            {
                                maskNegative[maskIndex] = negativeFace;
                            }
                        }
                    }

                    // Step forward one slice along the axis
                    ++workingCoordinates[axis];

                    //Run meshing twice, once in the positive direction, then again in the negative direction
                    for (int maskSelector = 0; maskSelector <= 1; maskSelector++)
                    {
                        bool isPositive  = (maskSelector == 0) ? true : false;
                        var  currentMask = (isPositive) ? maskPositive : maskNegative;

                        ///The X and Y axes need to flip quads for negative faces,
                        ///the z axis needs to flip quads for positive faces
                        bool flip = (axis != 2) ? !isPositive : isPositive;
                        //Normals must be flipped for negative faces
                        bool flipNormals = !isPositive;

                        // Generate mesh for current mask using lexicographic ordering
                        maskIndex = 0;
                        for (int j = 0; j < size; ++j)
                        {
                            for (int i = 0; i < size;)
                            {
                                var currentMaskValue = currentMask[maskIndex];

                                // Compute width as maximal width such that the mask value does not change
                                int width;
                                for (width = 1; currentMaskValue.Equals(currentMask[maskIndex + width]) && i + width < size; ++width)
                                {
                                }

                                // Compute height
                                int  height;
                                bool heightDone = false;
                                // Increase height up to no more than the size
                                for (height = 1; j + height < size; ++height)
                                {
                                    //Check that the mask value does not change along the row
                                    for (int k = 0; k < width; ++k)
                                    {
                                        if (!currentMaskValue.Equals(currentMask[maskIndex + k + height * size]))
                                        {
                                            //If the mask value has changed, we can go no further
                                            heightDone = true;
                                            break;
                                        }
                                    }

                                    if (heightDone)
                                    {
                                        break;
                                    }
                                }


                                if (!currentMaskValue.Equals(nullFace))
                                {
                                    workingCoordinates[secondaryAxis] = i;
                                    workingCoordinates[tertiaryAxis]  = j;

                                    if (data.voxelTypeDatabase.voxelTypeToIsPassableMap[currentMaskValue.typeId])
                                    {
                                        nonCollidableQueue.Add(new DoLater(currentMaskValue, workingCoordinates,
                                                                           axis, secondaryAxis, tertiaryAxis, width, height, flip, flipNormals));
                                    }
                                    else
                                    {
                                        ProcessSection(currentMaskValue, workingCoordinates,
                                                       axis, secondaryAxis, tertiaryAxis, width, height, flip, flipNormals);
                                    }
                                }

                                /// Zero-out mask for this section
                                /// This is necessary to prevent the same area being meshed multiple times,
                                /// and also to ensure the mask is clear for the next pass
                                for (int l = 0; l < height; ++l)
                                {
                                    for (int k = 0; k < width; ++k)
                                    {
                                        currentMask[maskIndex + k + l * size] = nullFace;
                                    }
                                }
                                // Increment counters and continue
                                i += width; maskIndex += width;
                            }
                        }
                    }
                }
            }

            //Record length of collidable mesh section
            data.collisionSubmesh.Record(data.vertices.Length, data.allTriangleIndices.Length, data.materialRuns.Length);
            //End collidable run
            runTracker.EndRun(data.materialRuns, data.allTriangleIndices);

            //Process non collidable sections
            for (int j = 0; j < nonCollidableQueue.Length; j++)
            {
                var item = nonCollidableQueue[j];
                ProcessSection(item.currentMaskValue, item.workingCoordinates,
                               item.primaryAxis, item.secondaryAxis, item.tertiaryAxis, item.width, item.height, item.flip, item.flipNormals);
            }

            //End non collidable run
            runTracker.EndRun(data.materialRuns, data.allTriangleIndices);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Compute the properties of the quad with given parameters,
        /// and add it to the mesh.
        /// </summary>
        /// <param name="currentMaskValue"></param>
        /// <param name="workingCoordinates"></param>
        /// <param name="du"></param>
        /// <param name="dv"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="flip"></param>
        private void ProcessSection(FaceDescriptor currentMaskValue,
                                    int3 workingCoordinates,
                                    byte primaryAxis,
                                    byte secondaryAxis,
                                    byte tertiaryAxis,
                                    int width,
                                    int height,
                                    bool flip,
                                    bool flipNormals)
        {
            // Add quad if mask value not null
            var meshID         = data.meshDatabase.voxelTypeToMeshTypeMap[currentMaskValue.typeId];
            var meshRange      = data.meshDatabase.meshTypeRanges[meshID];
            var usedNodesSlice = data.meshDatabase.nodesUsedByFaces[meshRange.start + (int)currentMaskValue.faceDirection];
            var uvStart        = data.voxelTypeDatabase.voxelTypeToZIndicesRangeMap[currentMaskValue.typeId].start;
            var uvZ            = data.voxelTypeDatabase.zIndicesPerFace[uvStart + (int)currentMaskValue.faceDirection];


            var materialId = data.meshDatabase.voxelTypeToMaterialIDMap[currentMaskValue.typeId];

            //Update material runs
            runTracker.Update(materialId, data.materialRuns, data.allTriangleIndices);

            //int3 du = new int3();
            //int3 dv = new int3();

            //du[secondaryAxis] = width;
            //dv[tertiaryAxis] = height;

            //float3 bl = workingCoordinates;
            //float3 tr = workingCoordinates + du + dv;
            //float3 br = workingCoordinates + du;
            //float3 tl = workingCoordinates + dv;

            Node nodebl = data.meshDatabase.allMeshNodes[usedNodesSlice.start];
            Node nodetr = data.meshDatabase.allMeshNodes[usedNodesSlice.start + 1];
            Node nodebr = data.meshDatabase.allMeshNodes[usedNodesSlice.start + 2];
            Node nodetl = data.meshDatabase.allMeshNodes[usedNodesSlice.start + 3];

            //Apply rotation
            if (!currentMaskValue.rotation.isBlank)
            {
                var rotationQuat = directionRotator.GetRotationQuat(currentMaskValue.rotation);
                nodebl = adjustForRotation(nodebl, rotationQuat);
                nodetr = adjustForRotation(nodetr, rotationQuat);
                nodebr = adjustForRotation(nodebr, rotationQuat);
                nodetl = adjustForRotation(nodetl, rotationQuat);
            }


            int3 scale = new int3();

            scale[secondaryAxis] = width;
            scale[tertiaryAxis]  = height;
            //Apply scaling
            nodebl.vertex *= scale;
            nodetr.vertex *= scale;
            nodebr.vertex *= scale;
            nodetl.vertex *= scale;
            //Apply translation
            nodebl.vertex += workingCoordinates;
            nodetr.vertex += workingCoordinates;
            nodebr.vertex += workingCoordinates;
            nodetl.vertex += workingCoordinates;


            int3 normal = new int3();

            normal[primaryAxis] = 1;

            ///NOTE for negative faces, the UVs are already flipped in the face definition
            ///Therefore, flip just the vertices and normals when necessary
            //if (flip)
            //{
            //    Swap(ref nodebl, ref nodebr);
            //    Swap(ref nodetr, ref nodetl);
            //}
            if (flipNormals)
            {
                normal *= -1;
            }

            nodebl.normal = normal;
            nodetr.normal = normal;
            nodebr.normal = normal;
            nodetl.normal = normal;

            //nodebl.vertex = bl;
            //nodetr.vertex = tr;
            //nodebr.vertex = br;
            //nodetl.vertex = tl;

            var  secondaryDif = math.lengthsq(nodetl.vertex - nodebl.vertex);
            var  tertiaryDif  = math.lengthsq(nodebr.vertex - nodebl.vertex);
            int2 uvScale      = new int2(width, height);

            ///Calculate the correct uv scale based on which axis is longer
            ///(if they are equal it doesn't matter).
            ///This is needed because it is difficult to track the correct orientation
            ///of width and height after rotation
            if (!currentMaskValue.rotation.isBlank)
            {
                if (width > height)
                {
                    if (secondaryDif > tertiaryDif)
                    {
                        uvScale = new int2(height, width);
                    }
                }
                else
                {
                    if (secondaryDif < tertiaryDif)
                    {
                        uvScale = new int2(height, width);
                    }
                }
            }

            //Scale the UVs
            //nodebl.uv *= uvScale;//Don't need to scale the bottom left, as its UV should be 0,0
            nodetr.uv *= uvScale;
            nodebr.uv *= uvScale;
            nodetl.uv *= uvScale;

            bool makeBackface = data.meshDatabase.meshIdToIncludeBackfacesMap[meshID];

            Color lightForFace = new Color();

            if (data.includeLighting)
            {
                lightForFace = currentMaskValue.lightValue.ToVertexColour();
            }

            AddQuad(nodebl, nodetr, nodebr, nodetl, uvZ, lightForFace, makeBackface);
        }