//------------------------------------------------------------------------- bool ExportMeshToFile(GeneratedColliderData generatedColliderData) { ColladaExporter colladaWriter = new ColladaExporter(); ColladaExporter.GeometryNode rootGeometryNode = new ColladaExporter.GeometryNode(); Vector3[] jointVertices = null; int[] jointIndices = null; JoinVertexGroups(generatedColliderData.mColliderRegions, out jointVertices, out jointIndices); if (jointVertices == null || jointVertices.Length == 0) { CreateDummyTriangleToCreateAValidColladaFile(out jointVertices, out jointIndices); } rootGeometryNode.mName = "Collider"; rootGeometryNode.mAreVerticesLeftHanded = true; rootGeometryNode.mVertices = jointVertices; rootGeometryNode.mTriangleIndices = jointIndices; rootGeometryNode.mGenerateNormals = true; float scaleX = GetOutputScaleX(true); float scaleY = GetOutputScaleY(); colladaWriter.mVertexScaleAfterInitialRotation.x = scaleX; // the mesh is imported in a way that we end up correct this way. colladaWriter.mVertexScaleAfterInitialRotation.y = scaleY; colladaWriter.mVertexScaleAfterSecondRotation = Vector3.one; float atlasFrameRotation = mAtlasFrameRotation; if (mRegionIndependentParameters.CustomTex != null) { colladaWriter.mVertexScaleAfterInitialRotation.Scale(GetCustomImageScale()); atlasFrameRotation = 0.0f; } // In order to rotate well, we need to compensate for the gameobject's // transform.scale that is applied automatically after all of our transforms. Vector3 automaticallyAppliedScale = this.transform.localScale; Vector3 rotationCompensationScaleBefore = new Vector3(automaticallyAppliedScale.x, automaticallyAppliedScale.y, 1.0f); Vector3 rotationCompensationScaleAfter = new Vector3(1.0f / automaticallyAppliedScale.x, 1.0f / automaticallyAppliedScale.y, 1.0f); colladaWriter.mVertexScaleAfterInitialRotation.Scale(rotationCompensationScaleBefore); colladaWriter.mVertexScaleAfterSecondRotation.Scale(rotationCompensationScaleAfter); colladaWriter.mVertexOffset.x = -mOutlineOffset.x -mRegionIndependentParameters.CustomOffset.x; colladaWriter.mVertexOffset.y = mOutlineOffset.y + mRegionIndependentParameters.CustomOffset.y; colladaWriter.mVertexOffset.z = -mOutlineOffset.z -mRegionIndependentParameters.CustomOffset.z; colladaWriter.mVertexTransformationCenter = new Vector3(0, 0, 0); colladaWriter.mVertexInitialRotationQuaternion = Quaternion.Euler(0, 0, -atlasFrameRotation); colladaWriter.mVertexSecondRotationQuaternion = Quaternion.Euler(0, 0, -mRegionIndependentParameters.CustomRotation); System.IO.Directory.CreateDirectory(mColliderMeshDirectory); colladaWriter.ExportTriangleMeshToFile(ActiveFrameFullColliderMeshPath(), rootGeometryNode); return true; }
//------------------------------------------------------------------------- bool GenerateUnreducedColliderMesh(ref GeneratedColliderData generatedColliderData) { UpdateFromOldVersionForBackwardsCompatibility(); // just in case the texture has changed. InitTextureParams(); #if UNITY_4_3_AND_LATER if (mHasUnity43SpriteAnimatorComponent) { EnsureHasUnity43AnimatedColliderComponents(); EnsureDataIsPreparedForMultipleColliders(); } #endif if (mHasSmoothMovesAnimBoneColliderComponent || mHasSmoothMovesBoneAnimationParent == InitState.Yes) { EnsureSmoothMovesBoneAnimHasRestoreComponent(mSmoothMovesBoneAnimation); } if (UsedTexture == null) { return false; } UpdateColliderMeshFilename(); if (generatedColliderData.mOutlineAlgorithm == null) { generatedColliderData.mOutlineAlgorithm = new PolygonOutlineFromImageFrontend(); } PolygonOutlineFromImageFrontend outlineAlgorithm = generatedColliderData.mOutlineAlgorithm; bool useImageRegion = false; int regionX = 0; int regionY = 0; int regionWidth = UsedTexture.width; int regionHeight = UsedTexture.height; float unroundedRegionX = 0.0f; float unroundedRegionY = 0.0f; float unroundedWidth = 1.0f; float unroundedHeight = 1.0f; float pixelCutawayLeft = 0.0f; float pixelCutawayRight = 0.0f; float pixelCutawayBottom = 0.0f; float pixelCutawayTop = 0.0f; if (mRegionIndependentParameters.IsCustomAtlasRegionUsed || mIsAtlasUsed) { useImageRegion = true; if (mRegionIndependentParameters.IsCustomAtlasRegionUsed) { unroundedRegionX = mRegionIndependentParameters.CustomAtlasFramePositionInPixels.x; unroundedRegionY = mRegionIndependentParameters.CustomAtlasFramePositionInPixels.y; unroundedWidth = mRegionIndependentParameters.CustomAtlasFrameSizeInPixels.x; unroundedHeight = mRegionIndependentParameters.CustomAtlasFrameSizeInPixels.y; } else if (mIsAtlasUsed) { // mRegionIndependentParameters.IsCustomAtlasRegionUsed has priority over mIsAtlasUsed. unroundedRegionX = mAtlasFramePositionInPixels.x; unroundedRegionY = mAtlasFramePositionInPixels.y; unroundedWidth = mAtlasFrameSizeInPixels.x; unroundedHeight = mAtlasFrameSizeInPixels.y; } regionX = Mathf.FloorToInt(unroundedRegionX); regionY = Mathf.FloorToInt(unroundedRegionY); float unroundedEndX = unroundedRegionX + unroundedWidth; float unroundedEndY = unroundedRegionY + unroundedHeight; int endX = Mathf.CeilToInt(unroundedEndX); int endY = Mathf.CeilToInt(unroundedEndY); regionWidth = endX - regionX; regionHeight = endY - regionY; pixelCutawayLeft = unroundedRegionX - regionX; pixelCutawayRight = endX - unroundedEndX; pixelCutawayBottom = endY - unroundedEndY; pixelCutawayTop = unroundedRegionY - regionY; } if (regionWidth == 0 || regionHeight == 0) { Debug.LogError("Error: Encountered image width or height of 0. Stopping collider generation."); return false; } bool wasSuccessful = outlineAlgorithm.BinaryAlphaThresholdImageFromTexture(out generatedColliderData.mBinaryImage, UsedTexture, mRegionIndependentParameters.AlphaOpaqueThreshold, useImageRegion, regionX, regionY, regionWidth, regionHeight); if (!wasSuccessful) { Debug.LogError(outlineAlgorithm.LastError); return false; } IslandDetector.Region[] islands; IslandDetector.Region[] seaRegions; /*bool anyIslandsFound =*/ CalculateIslandStartingPoints(generatedColliderData.mBinaryImage, out islands, out seaRegions); /*if (!anyIslandsFound) { // we now tolerate if there is no island pixel, one could still enable the single sea-region. return false; }*/ SetupColliderRegions(out generatedColliderData.mColliderRegions, islands, seaRegions); CopyOldColliderRegionParametersForBackwardsCompatibility(); SetupColliderRegionParameters(ref mIslandRegionParameters, ref mSeaRegionParameters, mRegionIndependentParameters.DefaultMaxPointCount, islands, seaRegions); CopyOldPointCountParameterToFirstIslandForBackwardsCompatibility(ref mIslandRegionParameters, ref mMaxPointCount); bool ccwVertexOrder = IsOutlineInCCWOrderNecessary(); outlineAlgorithm.RegionPixelCutawayLeft = pixelCutawayLeft; outlineAlgorithm.RegionPixelCutawayRight = pixelCutawayRight; outlineAlgorithm.RegionPixelCutawayBottom = pixelCutawayBottom; outlineAlgorithm.RegionPixelCutawayTop = pixelCutawayTop; outlineAlgorithm.NormalizeResultToCutRegion = true; CalculateUnreducedOutlineForAllColliderRegions(ref generatedColliderData.mColliderRegions, ref outlineAlgorithm, mRegionIndependentParameters, mIslandRegionParameters, mSeaRegionParameters, generatedColliderData.mBinaryImage, ccwVertexOrder); return true; }
//------------------------------------------------------------------------- bool ReduceAndStoreColliderMesh(ref GeneratedColliderData generatedColliderData) { UpdateFromOldVersionForBackwardsCompatibility(); if (generatedColliderData.mColliderRegions == null || generatedColliderData.mColliderRegions.Length == 0) { return false; // needs to be calculated before calling this method! } if (generatedColliderData.mOutlineAlgorithm == null) { generatedColliderData.mOutlineAlgorithm = new PolygonOutlineFromImageFrontend(); } PolygonOutlineFromImageFrontend outlineAlgorithm = generatedColliderData.mOutlineAlgorithm; bool needsTriangleFenceUpdate = this.mRegionIndependentParameters.HasThicknessChanged; this.mRegionIndependentParameters.HasThicknessChanged = false; ColliderRegionData[] colliderRegions = generatedColliderData.mColliderRegions; int islandIndex = 0; int seaRegionIndex = 0; bool anyRegionWithVerticesFound = false; for (int count = 0; count < colliderRegions.Length; ++count) { ColliderRegionData region = colliderRegions[count]; bool isIslandRegion = region.mRegionIsIsland; ColliderRegionParameters regionParameters = isIslandRegion ? mIslandRegionParameters[islandIndex] : mSeaRegionParameters[seaRegionIndex]; if (isIslandRegion) { ++islandIndex; } else { ++seaRegionIndex; } if (region.mIntermediateOutlineVertices == null) { region.mResultVertices = null; region.mResultTriangleIndices = null; continue; } anyRegionWithVerticesFound = true; bool needsRegionReduction = true; if (region.mReducedOutlineVertices != null && region.mReducedOutlineVertices.Count != 0 && !regionParameters.RegionUpdateCalculationNeeded) { needsRegionReduction = false; } outlineAlgorithm.VertexReductionDistanceTolerance = this.mRegionIndependentParameters.VertexReductionDistanceTolerance; outlineAlgorithm.MaxPointCount = regionParameters.MaxPointCount; // TODO: replace this with a joint-convex hull implementation, just a workaround for now. bool allRegionsConvex = mRegionIndependentParameters.Convex; outlineAlgorithm.Convex = allRegionsConvex ? true : regionParameters.Convex; outlineAlgorithm.XOffsetNormalized = -0.5f;//-this.transform.localScale.x / 2.0f; outlineAlgorithm.YOffsetNormalized = -0.5f;//-this.transform.localScale.y / 2.0f; outlineAlgorithm.Thickness = this.mRegionIndependentParameters.Thickness; if (needsRegionReduction) { region.mReducedOutlineVertices = outlineAlgorithm.ReduceOutline(region.mIntermediateOutlineVertices, region.mOutlineVertexOrderIsCCW); } regionParameters.RegionUpdateCalculationNeeded = false; if (needsRegionReduction || needsTriangleFenceUpdate) { Vector3[] vertices; int[] triangleIndices; outlineAlgorithm.TriangleFenceFromOutline(out vertices, out triangleIndices, region.mReducedOutlineVertices, false); region.mResultVertices = vertices; region.mResultTriangleIndices = triangleIndices; } } if (!anyRegionWithVerticesFound) { return false; // we cannot/don't want to store a collada file with no vertices and import it back in, unfortunately. } bool needsToWriteMeshColliderFile = true; #if UNITY_4_3_AND_LATER needsToWriteMeshColliderFile = (mRegionIndependentParameters.TargetColliderType == TargetColliderType.MeshCollider); #endif if (needsToWriteMeshColliderFile) { return ExportMeshToFile(generatedColliderData); } else { return true; // done already } }
//------------------------------------------------------------------------- void EnsureDataIsPreparedForMultipleColliders() { int oldSize = mGeneratedColliderData.Length; int desiredSize = mRegionIndependentParameters.NumCollidersNeeded; if (oldSize != desiredSize) { Array.Resize(ref mGeneratedColliderData, desiredSize); for (int newIndexToInit = oldSize; newIndexToInit < desiredSize; ++newIndexToInit) { mGeneratedColliderData[newIndexToInit] = new GeneratedColliderData(); } } }