Ejemplo n.º 1
0
        public static bool UnpackCommonPart(string name, bool lightmapped, int lod)
        {
            string lodDir    = lod == 0 ? lod0Dir : lod == 1 ? lod1Dir : lod2Dir;
            string extension = lod <= 1 ? ".fbx" : ".prefab";

            if (File.Exists(Path.Combine(geometryPath, commonPartsDir, lightmapped ? lightmappedDir : "", lodDir, name + extension)))
            {
                return(true);
            }

            if (lod <= 1)
            {
                OpenDB();

                var entry = commonPartsZipArchive.GetEntry(name + ".fbx");
                if (entry != null)
                {
                    var fileName = entry.Name;
                    var filePath = Path.Combine(geometryPath, commonPartsDir, lightmapped ? lightmappedDir : "", lodDir, fileName);

                    var directoryName = Path.GetDirectoryName(filePath);
                    if (directoryName.Length > 0)
                    {
                        Directory.CreateDirectory(directoryName);
                    }

                    var zipStream  = entry.Open();
                    var fileStream = File.Create(filePath);
                    zipStream.CopyTo(fileStream);
                    fileStream.Dispose();
                    zipStream.Dispose();
#if UNITY_EDITOR
                    AssetDatabase.ImportAsset(filePath);
#endif
                    return(true);
                }
            }
            else
            {
                // Generate LOD 2.
                var knob            = name.StartsWith("knob");
                var tube            = name.StartsWith("tube");
                var hollow          = knob ? name.EndsWith("C") : tube;
                var tubeOrPinHeight = name.Contains("_01_") ? 0.21f : name.Contains("_02_") ? 0.85f : 2.73f;
                var height          = knob ? 0.178f : tubeOrPinHeight;
                var radius          = knob ? 0.25f : tube ? 0.3377f : 0.1575f;
                PartUtility.CreateCommonPartLod2(name, height, radius, knob, hollow, radius - 0.08f, lightmapped);

                return(true);
            }

            return(false);
        }
Ejemplo n.º 2
0
        private static void InstantiateCommonParts <T>(Part part, List <T> partsList, Transform parent, bool lightmapped, int lod) where T : CommonPart
        {
            int count = parent.childCount;

            // Instantiate common parts using locators.
            for (int i = 0; i < count; i++)
            {
                var commonPartLocation = parent.GetChild(i);
                var name = Regex.Split(commonPartLocation.name, "(_[0-9]+ 1)");

                GameObject commonPartToInstantiate = null;

                var commonPartAvailable = PartUtility.UnpackCommonPart(name[0], lightmapped);
                if (commonPartAvailable)
                {
                    commonPartToInstantiate = PartUtility.LoadCommonPart(name[0], lightmapped, lod);
                }

                if (commonPartToInstantiate == null)
                {
                    Debug.LogError("Missing Common Part -> " + name[0]);
                    continue;
                }

                var commonPartGO = Object.Instantiate(commonPartToInstantiate);
                commonPartGO.name = commonPartToInstantiate.name;

                var commonPartComponent = commonPartGO.AddComponent <T>();
                commonPartComponent.part = part;

                // Set position and rotation.
                commonPartGO.transform.position = commonPartLocation.position;
                commonPartGO.transform.rotation = commonPartLocation.rotation;

                commonPartGO.transform.SetParent(parent, true);

                partsList.Add(commonPartComponent);
            }
            // Remove locators.
            for (int i = 0; i < count; i++)
            {
                Object.DestroyImmediate(parent.GetChild(0).gameObject);
            }
        }
Ejemplo n.º 3
0
        void ProcessCommon(GameObject go, bool genLightmapUV, int lod)
        {
            MeshFilter[] mfs = go.GetComponentsInChildren <MeshFilter>();
            foreach (var mf in mfs)
            {
                if (mf && mf.sharedMesh)
                {
                    MeshTool mt = new MeshTool(mf.sharedMesh);

                    var normalMappedLogo = go.name.StartsWith("knob") && !go.name.EndsWith("C");

                    // Generate lowest possible LOD.
                    var bounds = mf.sharedMesh.bounds;
                    var knob   = go.name.StartsWith("knob");
                    var hollow = knob ? go.name.EndsWith("C") : go.name.StartsWith("tube");
                    PartUtility.CreateCommonPartLod2(go.name, bounds.size.y, bounds.extents.x, knob, hollow, bounds.extents.x - 0.08f, genLightmapUV);

                    if (lod == 0)
                    {
                        mt.GenerateChamfer(1.0f / mf.transform.localScale.x);
                    }

                    if (normalMappedLogo)
                    {
                        mt.GenerateKnobNormalMapUVs();
                    }
                    else
                    {
                        mt.ClearNormalMapUVs();
                    }

                    mt.ApplyTo(mf.sharedMesh, normalMappedLogo, genLightmapUV);

                    EditorUtility.SetDirty(mf.sharedMesh);

                    System.GC.Collect();
                }
            }
        }
Ejemplo n.º 4
0
        public static void ProcessModelGroup(ModelGroup group, ref Vector2Int vertCount, ref Vector2Int triCount, ref Vector2Int meshCount, ref Vector2Int boxColliderCount)
        {
            bool collapseMesh = true;
            bool collapseCols = group.importSettings.colliders;

            // Keep track of how many of the removable meshes are left for each part.
            var partMeshCount = new Dictionary <Part, int>();
            // Keep track of how many of the removable surface meshes (shell, colourChangeSurface) are left for each non-legacy part.
            var partSurfaceMeshCount = new Dictionary <Part, int>();

            // TODO: Move orphaned colliders to group root

            var progress = 0;

            if (collapseMesh)
            {
                //    Debug.Log("Collapsing Mesh for "+group.name);

                // Optimization settings
                bool doSort = group.optimizations.HasFlag(ModelGroup.Optimizations.SortFrontToBack);
                bool canRemoveTubesAndPins        = group.optimizations.HasFlag(ModelGroup.Optimizations.RemoveTubesAndPins);
                bool canRemoveKnobs               = group.optimizations.HasFlag(ModelGroup.Optimizations.RemoveKnobs);
                bool canRemoveCompletelyInvisible = group.optimizations.HasFlag(ModelGroup.Optimizations.RemoveInvisible);
                bool cullBackfaces = group.optimizations.HasFlag(ModelGroup.Optimizations.BackfaceCulling);

                bool randomizeNormals = group.randomizeNormals;

                // Split into different meshes depending on whether they have knobs or are transparent.
                // 0 = No knobs = Fast track to avoid normal mapping when it isn't required
                // 1 = Knobs = Slow track needs normal mapping
                // 2 = Transparent No Knobs = Fast track to avoid normal mapping when it isn't required.
                // 3 = Transparent Knobs = Slow track since we will not render transparent bricks during visibility tests.

                MeshHelper[] meshHelpers = new MeshHelper[] { new MeshHelper(), new MeshHelper(), new MeshHelper(), new MeshHelper() };

                Matrix4x4 groupMatrix    = group.transform.localToWorldMatrix;
                Matrix4x4 groupMatrixInv = groupMatrix.inverse;

                // Collect all parts.
                var parts = group.GetComponentsInChildren <Part>();

                // Collect relevant part mesh renderers along with mesh type information and a randomized rotation for each part.
                List <PartMeshRenderer> mrs = new List <PartMeshRenderer>();

                foreach (var part in parts)
                {
                    if (progress++ % 200 == 0)
                    {
                        EditorUtility.DisplayProgressBar("Processing", "Collecting part renderers.", ((float)progress / parts.Length) * 0.05f);
                    }

                    partMeshCount[part] = 0;

                    if (part.legacy)
                    {
                        // Legacy parts only have unstructured meshes.
                        foreach (var renderer in part.GetComponentsInChildren <MeshRenderer>(true))
                        {
                            var partMeshRenderer = new PartMeshRenderer()
                            {
                                part       = part,
                                rendererer = renderer,
                                type       = MeshType.Legacy
                            };
                            mrs.Add(partMeshRenderer);
                            partMeshCount[part]++;
                        }
                    }
                    else
                    {
                        partSurfaceMeshCount[part] = 0;

                        foreach (var renderer in part.GetComponentsInChildren <MeshRenderer>(true))
                        {
                            var parentName = renderer.transform.parent.name;

                            // First check if it is a decoration surface since we will not include them in the processing.
                            if (parentName == "DecorationSurfaces")
                            {
                                continue;
                            }

                            var partMeshRenderer = new PartMeshRenderer()
                            {
                                part       = part,
                                rendererer = renderer.GetComponent <MeshRenderer>()
                            };
                            if (parentName == "Knobs") // Is it a knob?
                            {
                                partMeshRenderer.type = MeshType.Knob;
                            }
                            else if (parentName == "Tubes") // Is it a tube?
                            {
                                partMeshRenderer.type = MeshType.Tube;
                            }
                            else if (parentName == "ColourChangeSurfaces") // Is it a colour change surface?
                            {
                                partMeshRenderer.type = MeshType.ColourChange;
                                partSurfaceMeshCount[part]++;
                            }
                            else // It must be the shell.
                            {
                                partMeshRenderer.type = MeshType.Shell;
                                partSurfaceMeshCount[part]++;
                            }

                            mrs.Add(partMeshRenderer);
                            partMeshCount[part]++;
                        }
                    }
                }

                Vector3 camPos = Vector3.zero;
                Vector3 camDir = Vector3.zero;

                if (group.views.Count == 0 && Camera.main)
                {
                    camPos = Camera.main.transform.position;
                    camDir = Camera.main.transform.forward;
                }
                else if (group.views.Count > 0)
                {
                    camPos = group.views[0].position;
                    camDir = group.views[0].rotation * Vector3.forward;
                }
                else
                {
                    doSort = false;
                    Debug.LogError("No views specified for front-to-back geometry sorting. Disabling!");
                }

                if (doSort)
                {
                    Vector3 sortDir;
                    if (group.views.Count == 0)
                    {
                        sortDir = camDir;
                    }
                    else
                    {
                        sortDir = group.views[0].rotation * Vector3.forward;
                    }

                    mrs.Sort(delegate(PartMeshRenderer a, PartMeshRenderer b)
                    {
                        float dA = Vector3.Dot(a.rendererer.bounds.center, sortDir);
                        float dB = Vector3.Dot(b.rendererer.bounds.center, sortDir);
                        return(dA.CompareTo(dB));
                    });
                }


                // Multiple passes
                // - Collect all meshes
                // - Remove original meshfilters and renderers
                // - Determine which Optimization level can be used (for legacy meshes only) or if mesh can be discarded completely
                // - Remove backfaces
                // - Build combined mesh

                // Collect meshes
                List <MeshInstance> instances = new List <MeshInstance>();

                for (int i = 0; i < mrs.Count; ++i)
                {
                    if (i % 200 == 0)
                    {
                        EditorUtility.DisplayProgressBar("Processing", "Collecting part meshes.", 0.05f + (float)i / mrs.Count * 0.05f);
                    }

                    MeshFilter mf = mrs[i].rendererer.GetComponent <MeshFilter>();
                    if (mf)
                    {
                        if (mrs[i].rendererer.enabled && mrs[i].rendererer.gameObject.activeInHierarchy)
                        {
                            Mesh source = mf.sharedMesh;
                            if (source)
                            {
                                meshCount.x++;
                                triCount.x  += (int)source.GetIndexCount(0) / 3;
                                vertCount.x += source.vertexCount;

                                Material material = mrs[i].rendererer.sharedMaterial;

                                MeshInstance c = new MeshInstance();
                                c.mesh        = source;
                                c.matrix      = groupMatrixInv * mf.transform.localToWorldMatrix;
                                c.worldMatrix = mf.transform.localToWorldMatrix;
                                c.material    = material;
                                c.transparent = material && material.color.a < 1.0;
                                c.part        = mrs[i].part;
                                c.type        = mrs[i].type;
                                c.up          = mf.transform.up;
                                c.bounds      = mrs[i].rendererer.bounds;
                                instances.Add(c);
                            }
                        }
                        Object.DestroyImmediate(mf);
                    }
                    Object.DestroyImmediate(mrs[i].rendererer);
                }

                List <Vector3>   viewPositions      = new List <Vector3>();
                List <Vector3>   viewDirections     = new List <Vector3>();
                List <Matrix4x4> viewMatrices       = new List <Matrix4x4>();
                List <Matrix4x4> projectionMatrices = new List <Matrix4x4>();
                List <Plane[]>   viewFrustums       = new List <Plane[]>();

                if (group.views.Count == 0 && Camera.main)
                {
                    viewMatrices.Add(Camera.main.worldToCameraMatrix);

                    if (Camera.main.orthographic)
                    {
                        viewDirections.Add(camDir);
                    }
                    else
                    {
                        viewPositions.Add(camPos);
                    }

                    viewFrustums.Add(GeometryUtility.CalculateFrustumPlanes(Camera.main));

                    projectionMatrices.Add(Camera.main.projectionMatrix);
                }
                else if (group.views.Count > 0)
                {
                    for (int i = 0; i < group.views.Count; ++i)
                    {
                        var view       = group.views[i];
                        var viewMatrix = Matrix4x4.TRS(view.position, view.rotation, Vector3.one).inverse;

                        // Invert Z for metal/openGL
                        if (SystemInfo.usesReversedZBuffer)
                        {
                            viewMatrix.SetRow(2, -viewMatrix.GetRow(2));
                        }

                        viewMatrices.Add(viewMatrix);

                        Matrix4x4 projectionMatrix;
                        Plane[]   frustumPlanes;
                        if (view.perspective)
                        {
                            projectionMatrix = Matrix4x4.Perspective(
                                view.fov,
                                view.aspect,
                                view.minRange,
                                view.maxRange
                                );

                            viewPositions.Add(view.position);

                            frustumPlanes = MathUtils.GetFrustumPlanesPerspective(view.position, view.rotation, view.fov, view.aspect, view.minRange, view.maxRange);
                        }
                        else
                        {
                            projectionMatrix = Matrix4x4.Ortho(
                                -view.size * view.aspect,
                                view.size * view.aspect,
                                -view.size,
                                view.size,
                                view.minRange,
                                view.maxRange
                                );

                            viewDirections.Add(view.rotation * Vector3.forward);

                            frustumPlanes = MathUtils.GetFrustumPlanesOrtho(view.position, view.rotation, view.size, view.aspect, view.minRange, view.maxRange);
                        }

                        projectionMatrices.Add(projectionMatrix);

                        viewFrustums.Add(frustumPlanes);
                    }
                }
                else
                {
                    if (canRemoveTubesAndPins || canRemoveKnobs || canRemoveCompletelyInvisible || cullBackfaces)
                    {
                        Debug.LogError("No views specified for backface culling and geometry removal. Disabling!");
                        canRemoveTubesAndPins        = false;
                        canRemoveKnobs               = false;
                        canRemoveCompletelyInvisible = false;
                        cullBackfaces = false;
                    }
                }

                if (instances.Count > 16777215)
                {
                    Debug.LogError($"Group {group.groupName} contains too many meshes. Some meshes will not be optimized correctly. Please split the group into multiple groups.");
                }

                AnalyzeMeshes(viewMatrices, projectionMatrices, viewPositions, viewDirections, viewFrustums, instances, partMeshCount, partSurfaceMeshCount, canRemoveTubesAndPins, canRemoveKnobs, canRemoveCompletelyInvisible, group.importSettings.lod);

                // Combine instances to a single mesh
                for (int i = 0; i < instances.Count; ++i)
                {
                    if (i % 200 == 0)
                    {
                        EditorUtility.DisplayProgressBar("Processing", "Combining part meshes.", 0.2f + (float)i / instances.Count * 0.05f);
                    }

                    Mesh source = instances[i].mesh;
                    if (source == null)
                    {
                        continue;
                    }

                    Matrix4x4 matrix   = instances[i].matrix;
                    Material  material = instances[i].material;
                    int       subMesh  = instances[i].transparent ? (instances[i].type == MeshType.Knob ? 3 : 2) : instances[i].pixelKnobCount >= knobNormalMapThreshold ? 1 : 0;

                    MeshHelper mh = new MeshHelper(source);
                    mh.Transform(matrix);

                    // Cull backfaces
                    if (cullBackfaces)
                    {
                        for (int t = 0; t < mh.triangles.Count; t += 3)
                        {
                            int     v0     = mh.triangles[t];
                            int     v1     = mh.triangles[t + 1];
                            int     v2     = mh.triangles[t + 2];
                            Vector3 triCen = groupMatrix.MultiplyPoint((mh.vertices[v0] + mh.vertices[v1] + mh.vertices[v2]) / 3);
                            Vector3 triNor = groupMatrix.MultiplyVector(mh.normals[v0] + mh.normals[v1] + mh.normals[v2]);//.normalized;

                            bool anyInView = false;
                            for (int v = 0; v < viewPositions.Count; ++v)
                            {
                                anyInView |= (Vector3.Dot(viewPositions[v] - triCen, triNor) >= 0);
                            }
                            for (int v = 0; v < viewDirections.Count; ++v)
                            {
                                anyInView |= (Vector3.Dot(viewDirections[v], triNor) <= 0);
                            }
                            if (!anyInView)
                            {
                                mh.triangles[t]     = -1;
                                mh.triangles[t + 1] = -1;
                                mh.triangles[t + 2] = -1;
                            }
                        }
                        mh.triangles.RemoveAll((obj) => obj < 0);

                        mh.RemoveUnusedVertices();
                    }

                    // culled completely?
                    if (mh.vertices.Count == 0)
                    {
                        partMeshCount[instances[i].part]--;
                        continue;
                    }

                    meshCount.y++;
                    triCount.y  += mh.triangles.Count / 3;
                    vertCount.y += mh.vertices.Count;

                    // Store color in vertices
                    mh.SetColor(material);

                    // Randomize normals
                    if (randomizeNormals)
                    {
                        mh.AddNormalNoise();
                    }

                    meshHelpers[subMesh].Combine(mh);

                    mh = null;
                }

                EditorUtility.DisplayProgressBar("Processing", "Building new meshes.", 0.3f);

                for (int i = 0; i < meshHelpers.Length; ++i)
                {
                    if (meshHelpers[i].vertices.Count > 0)
                    {
                        GameObject target = group.gameObject;
                        if (i > 0)
                        {
                            target = new GameObject(group.name + "_subMesh" + i);
                            Undo.RegisterCreatedObjectUndo(target, "Create sub mesh");
                            target.transform.SetParent(group.transform, false);
                        }

                        MeshFilter   mf = Undo.AddComponent <MeshFilter>(target);
                        MeshRenderer mr = Undo.AddComponent <MeshRenderer>(target);

                        Mesh m = new Mesh();
                        meshHelpers[i].ToMesh(m, group.importSettings.lightmapped);

                        // Need tangents?
                        if (i > 0)
                        {
                            m.RecalculateTangents();
                        }

                        // Make static.
                        target.isStatic = true;
                        if (group.importSettings.lightmapped)
                        {
                            mr.receiveGI = ReceiveGI.Lightmaps;
                        }
                        else
                        {
                            mr.receiveGI = ReceiveGI.LightProbes;
                        }

                        mf.sharedMesh = m;
                        switch (i)
                        {
                        case 0:
                        {
                            PartUtility.StoreOptimizedMesh(m, group.parentName + "_" + group.groupName + "_Optimized.asset");
                            mr.sharedMaterial = AssetDatabase.LoadAssetAtPath <Material>("Packages/com.unity.lego.modelimporter/Materials/LEGO_VertexColor.mat");
                            break;
                        }

                        case 1:
                        {
                            PartUtility.StoreOptimizedMesh(m, group.parentName + "_" + group.groupName + "_Optimized_NormalMap.asset");
                            mr.sharedMaterial = AssetDatabase.LoadAssetAtPath <Material>("Packages/com.unity.lego.modelimporter/Materials/LEGO_VertexColor_NormalMap.mat");
                            break;
                        }

                        case 2:
                        {
                            PartUtility.StoreOptimizedMesh(m, group.parentName + "_" + group.groupName + "_Optimized_Transparent.asset");
                            mr.sharedMaterial = AssetDatabase.LoadAssetAtPath <Material>("Packages/com.unity.lego.modelimporter/Materials/LEGO_VertexColor_Transparent.mat");
                            break;
                        }

                        case 3:
                        {
                            PartUtility.StoreOptimizedMesh(m, group.parentName + "_" + group.groupName + "_Optimized_Transparent_NormalMap.asset");
                            mr.sharedMaterial = AssetDatabase.LoadAssetAtPath <Material>("Packages/com.unity.lego.modelimporter/Materials/LEGO_VertexColor_Transparent_NormalMap.mat");
                            break;
                        }
                        }
                    }
                }
            }

            // Remove decoration surfaces for non-legacy parts that have no surface meshes left.
            progress = 0;
            foreach (var entry in partSurfaceMeshCount)
            {
                if (progress++ % 200 == 0)
                {
                    EditorUtility.DisplayProgressBar("Processing", "Removing unneeded decorations.", 0.3f + (float)progress / partSurfaceMeshCount.Count * 0.25f);
                }

                if (!entry.Key.legacy && entry.Value == 0)
                {
                    var decorationSurfaces = entry.Key.transform.Find("DecorationSurfaces");
                    if (decorationSurfaces)
                    {
                        Undo.DestroyObjectImmediate(decorationSurfaces.gameObject);
                    }
                }
            }

            // Remove parts that have no meshes left.
            progress = 0;
            foreach (var entry in partMeshCount)
            {
                if (progress++ % 200 == 0)
                {
                    EditorUtility.DisplayProgressBar("Processing", "Removing empty parts.", 0.55f + (float)progress / partMeshCount.Count * 0.25f);
                }

                if (entry.Value == 0)
                {
                    entry.Key.brick.parts.Remove(entry.Key);

                    // If no parts are left, remove the brick.
                    if (entry.Key.brick.parts.Count == 0)
                    {
                        Undo.DestroyObjectImmediate(entry.Key.brick.gameObject);
                    }
                    else
                    {
                        Undo.DestroyObjectImmediate(entry.Key.gameObject);
                    }
                }
            }

            // Collapse remaining box colliders.
            if (collapseCols)
            {
                // FIXME Move to proper constant.
                var colliderSizeBias = 0.02f;
                var epsilon          = 0.001f;

                var allColliders = group.GetComponentsInChildren <BoxCollider>();
                // Filter out colliders that are part of connectivity features.
                var partColliders = allColliders.Where((c) => c.gameObject.layer != LayerMask.NameToLayer(Connection.connectivityReceptorLayerName) && c.gameObject.layer != LayerMask.NameToLayer(Connection.connectivityConnectorLayerName)).ToArray();
                boxColliderCount.x = partColliders.Length;
                boxColliderCount.y = partColliders.Length;
                bool[] colDeleted       = new bool[partColliders.Length];
                var    collapseHappened = true;
                var    iterationCount   = 0;
                while (collapseHappened)
                {
                    collapseHappened = false;
                    iterationCount++;
                    for (var i = 0; i < partColliders.Length; ++i)
                    {
                        if (i % 200 == 0)
                        {
                            EditorUtility.DisplayProgressBar("Processing", "Collapsing colliders - iteration " + iterationCount + ".", 0.8f + (float)i / partColliders.Length * 0.05f);
                        }

                        if (colDeleted[i])
                        {
                            continue;
                        }

                        for (var j = i + 1; j < partColliders.Length; ++j)
                        {
                            if (colDeleted[j])
                            {
                                continue;
                            }

                            var colliderA = partColliders[i];
                            var colliderB = partColliders[j];

                            // Check that spaces match up.
                            var colliderBRotationInALocalSpace = Quaternion.Inverse(colliderA.transform.rotation) * colliderB.transform.rotation;
                            var euler = colliderBRotationInALocalSpace.eulerAngles;
                            if (Mathf.Abs(Mathf.Round(euler.x / 90.0f) - euler.x / 90.0f) < epsilon && Mathf.Abs(Mathf.Round(euler.y / 90.0f) - euler.y / 90.0f) < epsilon && Mathf.Abs(Mathf.Round(euler.z / 90.0f) - euler.z / 90.0f) < epsilon)
                            {
                                // Check that centers match up.
                                var colliderBCenterInColliderALocalSpace = colliderA.transform.InverseTransformPoint(colliderB.transform.TransformPoint(colliderB.center));

                                var centerDiff            = colliderBCenterInColliderALocalSpace - colliderA.center;
                                var axisToMatch           = centerDiff.MajorAxis();
                                var centerProjectedToAxis = centerDiff.SnapToMajorAxis() * centerDiff.magnitude;
                                if ((centerDiff - centerProjectedToAxis).sqrMagnitude < 0.01f)
                                {
                                    //Debug.Log(GetGameObjectPath(colliderA.gameObject) + colliderA.transform.GetSiblingIndex() + "\n" + GetGameObjectPath(colliderB.gameObject) + colliderB.transform.GetSiblingIndex());
                                    //Debug.Log("Matched centers on " + axisToMatch);
                                    // Check that size match up.
                                    var colliderBSizeInColliderALocalSpace = colliderA.transform.InverseTransformVector(colliderB.transform.TransformVector(colliderB.size)).Abs();
                                    //Debug.Log("Collider b size in collider a local space: " + colliderBSizeInColliderALocalSpace);

                                    var otherAxis1 = (axisToMatch + 1) % 3;
                                    var otherAxis2 = (axisToMatch + 2) % 3;
                                    if (Mathf.Abs(centerDiff.magnitude - (colliderA.size[axisToMatch] + colliderBSizeInColliderALocalSpace[axisToMatch] + 2.0f * colliderSizeBias) * 0.5f) < epsilon)
                                    {
                                        //Debug.Log("Matched on axis length");
                                        if (Mathf.Abs(colliderA.size[otherAxis1] - colliderBSizeInColliderALocalSpace[otherAxis1]) < epsilon && Mathf.Abs(colliderA.size[otherAxis2] - colliderBSizeInColliderALocalSpace[otherAxis2]) < epsilon)
                                        {
                                            //Debug.Log("Matched other axes");
                                            // Merge collider B into collider A.
                                            colliderA.center += centerDiff.normalized * (colliderBSizeInColliderALocalSpace[axisToMatch] + colliderSizeBias) * 0.5f;
                                            var newSize = colliderA.size;
                                            newSize[axisToMatch] += colliderBSizeInColliderALocalSpace[axisToMatch] + colliderSizeBias;
                                            colliderA.size        = newSize;

                                            // Update part and destroy collider game object. Empty parent Colliders game objects will be removed during part clean-up.
                                            var part = colliderB.GetComponentInParent <Part>();
                                            part.colliders.Remove(colliderB);
                                            Undo.DestroyObjectImmediate(colliderB.gameObject);

                                            colDeleted[j] = true;

                                            boxColliderCount.y--;

                                            // Note that a change was made, and run over colliders again when done.
                                            collapseHappened = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (collapseMesh || collapseCols)
            {
                // Collect all remaining parts and clean them up.
                progress = 0;
                var parts = group.GetComponentsInChildren <Part>();
                foreach (var part in parts)
                {
                    if (progress++ % 200 == 0)
                    {
                        EditorUtility.DisplayProgressBar("Processing", "Cleaning up remaining parts.", 0.85f + (float)progress / parts.Length * 0.15f);
                    }
                    CleanupPartGeometryTransforms(part);
                }
            }

            EditorUtility.ClearProgressBar();
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Instantiate game objects for each brick in an LXFML-file
        /// </summary>
        /// <param name="lxfml">The LXFML-file</param>
        /// <param name="colliders">Add colliders to part</param>
        /// <param name="connectivity">Add connectivity to part</param>
        /// <param name="isStatic">Make the part static</param>
        /// <param name="lightmapped">Instantiate meshes with or without lightmap UVs</param>
        /// <param name="randomizeRotation">Slightly rotate rotation of part</param>
        /// <param name="preferLegacy">Choose legacy meshes if available</param>
        /// <param name="lod">Instantiate meshes of a certain LOD</param>
        /// <param name="resultBricks">Dictionary that contains brick component, using refID as key</param>
        /// <param name="groupNumber">If non-negative, only instantiate bricks from the specified group number</param>
        public static void InstantiateModelBricks(LXFMLDoc lxfml, DictionaryIntToModelGroupImportSettings importSettings, ref Dictionary <int, Brick> resultBricks, int groupNumber = -1)
        {
            for (var i = 0; i < lxfml.bricks.Count; ++i)
            {
                if (i % 200 == 0)
                {
                    EditorUtility.DisplayProgressBar("Importing", "Creating bricks.", ((float)i / lxfml.bricks.Count) * 0.7f);
                }

                var brick = lxfml.bricks[i];

                var group = FindGroup(lxfml, brick);

                // Discard bricks from other groups if group number is specified.
                if (groupNumber >= 0 && group != null && group.number != groupNumber)
                {
                    continue;
                }

                // Determine whether or not to be static and to generate light map UVs.
                var brickStatic      = (group != null ? importSettings[group.number].isStatic : false);
                var brickLightmapped = brickStatic && (group != null ? importSettings[group.number].lightmapped : false);
                var brickLod         = (group != null ? importSettings[group.number].lod : 0);

                var brickGO   = new GameObject(brick.designId, typeof(Brick));
                var brickComp = brickGO.GetComponent <Brick>();
                Undo.RegisterCreatedObjectUndo(brickGO, "Brick");

                foreach (var part in brick.parts)
                {
                    GameObject partToInstantiate = null;

                    var partExistenceResult = PartUtility.UnpackPart(part.partDesignId, brickLightmapped, group != null ? importSettings[group.number].preferLegacy : false, brickLod);

                    if (partExistenceResult.existence != PartUtility.PartExistence.None)
                    {
                        // FIXME Make a note of changed design ids.
                        partToInstantiate = PartUtility.LoadPart(partExistenceResult.designID, brickLightmapped, partExistenceResult.existence == PartUtility.PartExistence.Legacy, brickLod);
                    }

                    if (partToInstantiate == null)
                    {
                        Debug.LogError("Missing part FBX -> " + partExistenceResult.designID);
                        continue;
                    }
                    var partGO = Object.Instantiate(partToInstantiate);
                    partGO.name = partToInstantiate.name;

                    // Assign legacy, material IDs and set up references.
                    var partComp = partGO.AddComponent <Part>();
                    partComp.designID = Convert.ToInt32(part.partDesignId);
                    partComp.legacy   = partExistenceResult.existence == PartUtility.PartExistence.Legacy;
                    foreach (var material in part.materials)
                    {
                        partComp.materialIDs.Add(material.colorId);
                    }
                    partComp.brick = brickComp;
                    brickComp.parts.Add(partComp);


                    if (partExistenceResult.existence == PartUtility.PartExistence.New)
                    {
                        // FIXME Handle normal mapped model.
                        InstantiateKnobsAndTubes(partComp, brickLightmapped, brickLod);
                    }

                    // Create collider and connectivity information.
                    var brickColliders    = (group != null ? importSettings[group.number].colliders : false);
                    var brickConnectivity = brickColliders && (group != null ? importSettings[group.number].connectivity : false);

                    if (brickColliders)
                    {
                        GameObject collidersToInstantiate = null;

                        var collidersAvailable = PartUtility.UnpackCollidersForPart(partExistenceResult.designID);
                        if (collidersAvailable)
                        {
                            collidersToInstantiate = PartUtility.LoadCollidersPrefab(partExistenceResult.designID);
                        }

                        if (collidersToInstantiate == null && partExistenceResult.existence != PartUtility.PartExistence.Legacy)
                        {
                            Debug.LogError("Missing part collider information -> " + partExistenceResult.designID);
                        }

                        if (collidersToInstantiate)
                        {
                            var collidersGO = Object.Instantiate(collidersToInstantiate);
                            collidersGO.name = "Colliders";
                            collidersGO.transform.SetParent(partGO.transform, false);
                            var colliderComps = collidersGO.GetComponentsInChildren <Collider>();
                            partComp.colliders.AddRange(colliderComps);
                        }
                    }

                    if (brickConnectivity)
                    {
                        GameObject connectivityToInstantiate = null;

                        var connectivityAvailable = PartUtility.UnpackConnectivityForPart(partExistenceResult.designID);
                        if (connectivityAvailable)
                        {
                            connectivityToInstantiate = PartUtility.LoadConnectivityPrefab(partExistenceResult.designID);
                        }

                        if (connectivityToInstantiate == null && partExistenceResult.existence != PartUtility.PartExistence.Legacy)
                        {
                            Debug.LogError("Missing part connectivity information -> " + partExistenceResult.designID);
                        }

                        if (connectivityToInstantiate)
                        {
                            var connectivityGO = Object.Instantiate(connectivityToInstantiate);
                            connectivityGO.name = "Connectivity";
                            connectivityGO.transform.SetParent(partGO.transform, false);
                            var connectivityComp = connectivityGO.GetComponent <Connectivity>();
                            partComp.connectivity = connectivityComp;
                            brickComp.totalBounds.Encapsulate(connectivityComp.extents);
                            connectivityComp.part = partComp;

                            foreach (var field in connectivityComp.connectionFields)
                            {
                                foreach (var connection in field.connections)
                                {
                                    MatchConnectionWithKnob(connection, partComp.knobs);
                                    MatchConnectionWithTubes(connection, partComp.tubes);
                                }
                            }
                        }
                    }

                    SetMaterials(partComp, part.materials, partExistenceResult.existence == PartUtility.PartExistence.Legacy);
                    SetDecorations(partComp, part.decorations, partExistenceResult.existence == PartUtility.PartExistence.Legacy);

                    SetStaticAndGIParams(partGO, brickStatic, brickLightmapped, true);

                    // Set Position & Rotation
                    SetPositionRotation(partGO, part);

                    if (group != null ? importSettings[group.number].randomizeRotation : false)
                    {
                        RandomizeRotation(partComp, brickConnectivity);
                    }

                    // If first part, place brick at same position.
                    if (brickGO.transform.childCount == 0)
                    {
                        brickGO.transform.position   = partGO.transform.position;
                        brickGO.transform.rotation   = partGO.transform.rotation;
                        brickGO.transform.localScale = Vector3.one;
                    }
                    partGO.transform.SetParent(brickGO.transform, true);

                    if (!brickConnectivity)
                    {
                        var worldBounds = ComputeBounds(partGO.transform);
                        worldBounds.SetMinMax(brickComp.transform.InverseTransformPoint(worldBounds.min), brickComp.transform.InverseTransformPoint(worldBounds.max));
                        brickComp.totalBounds.Encapsulate(worldBounds);
                    }
                }

                // If all parts were missing, discard brick.
                if (brickGO.transform.childCount == 0)
                {
                    Undo.DestroyObjectImmediate(brickGO);
                    continue;
                }

                SetStaticAndGIParams(brickGO, brickStatic, brickLightmapped);

                // Assign uuid
                brickComp.designID = Convert.ToInt32(brick.designId);
                brickComp.uuid     = brick.uuid;

                // Add LEGOAsset component.
                brickGO.AddComponent <LEGOAsset>();

                resultBricks[brick.refId] = brickComp;
            }
        }
Ejemplo n.º 6
0
        private static void AddPartToBrick(Brick brick, string ldrawID, Matrix4x4 transformation, string materialID, string fullName)
        {
            // Report if material is missing.
            var materialExistence = MaterialUtility.CheckIfMaterialExists(materialID);

            if (materialExistence == MaterialUtility.MaterialExistence.None)
            {
                missingMaterials.Add($"Brick ID {ldrawID}\tMaterial ID {materialID}");
            }
            else if (materialExistence == MaterialUtility.MaterialExistence.Legacy)
            {
                legacyMaterials.Add($"Brick ID {ldrawID}\tMaterial ID {materialID}");
            }

            // 1. Peel off anything from 'p' onwards and check if it includes a number. (Pattern constant + pattern sequential #)
            var patternSplit = Regex.Split(ldrawID, "(p[a-z]*[0-9]*)");

            // 2. If something was peeled off, make a note of it. We cannot map the pattern sequential id to a decoration imageId and we also don't know the surfaceName.
            if (patternSplit.Length > 1)
            {
                //Debug.Log("PATTERN: " + patternSplit[0] + patternSplit[1]);
                missingDecorations.Add($"Brick ID {fullName}");
            }

            // 3. Peel off anything from 'd' onwards and check if it includes a number. (Sticker + sticker number)
            var stickerSplit = Regex.Split(patternSplit[0], "(d[0-9]+)");

            // 4. If something was peeled off, make a note of it.
            if (stickerSplit.Length > 1)
            {
                //Debug.Log("STICKER: " + stickerSplit[0] + stickerSplit[1]);
                missingStickers.Add($"Brick ID {fullName}");
            }

            // 5. Reassemble remaining id + ".dat" and use as ldrawID.
            var designID = stickerSplit[0];

            ldrawID = designID + ".dat";

            // Apply mapping to another designID.
            if (LDrawBrickToLEGOBrick.ContainsKey(ldrawID))
            {
                designID = LDrawBrickToLEGOBrick[ldrawID];
            }

            // 6. If a mesh does not exist with exact designID, make a note of it and try to peel off any trailing letters.
            var partExistenceResult = PartUtility.CheckIfPartExists(designID);

            if (partExistenceResult.existence == PartUtility.PartExistence.None)
            {
                var versionSplit = Regex.Split(designID, "([a-z]+)");
                designID = versionSplit[0];

                // 6b. If there was something to peel off, look again with new designID.
                if (versionSplit.Length > 1)
                {
                    partExistenceResult = PartUtility.CheckIfPartExists(designID);
                    if (partExistenceResult.existence == PartUtility.PartExistence.None)
                    {
                        // Missing part.
                        missingParts.Add($"Brick ID {fullName}");
                    }
                    else
                    {
                        // Changed part.
                        changedParts.Add($"Brick ID {fullName}\tChanges to {designID}");

                        // Legacy part.
                        if (partExistenceResult.existence == PartUtility.PartExistence.Legacy)
                        {
                            // FIXME Check if colliders and connectivity info are available.
                            legacyParts.Add($"Brick ID {fullName}");
                        }
                    }
                }
                else
                {
                    // Missing part.
                    missingParts.Add($"Brick ID {fullName}");
                }
            }
            else if (partExistenceResult.existence == PartUtility.PartExistence.Legacy)
            {
                // Legacy part.
                // FIXME Check if colliders and connectivity info are available.
                legacyParts.Add($"Brick ID {fullName}");
            }

            // Reconstruct potentially changed ldrawID.
            ldrawID = designID + ".dat";

            // Apply transformation for ldrawID.
            if (LDrawBrickToTransformation.ContainsKey(ldrawID))
            {
                transformation *= LDrawBrickToTransformation[ldrawID];
            }
            else if (partExistenceResult.existence != PartUtility.PartExistence.None)
            {
                missingTransformations.Add($"Brick ID {fullName}");
            }

            var part = new Part()
            {
                transformation = transformation,
                designID       = designID,
                materialID     = materialID
            };

            brick.parts.Add(part);

            // Assign design ID of part to brick. This is incorrect for multi-part bricks but we do not know the correct design ID.
            brick.designID = designID;
        }
 public static void ConvertVersion_0_To_1(Connectivity connectivity)
 {
     Debug.Log($"Updating Connectivity 0 -> 1 on {connectivity.name}");
     PartUtility.UnpackConnectivityForPart(connectivity.name, true);
 }
        private static bool UpdateConnections(Brick[] bricks)
        {
            var updated = false;

            foreach (var brick in bricks)
            {
                if (PrefabUtility.IsPartOfPrefabInstance(brick))
                {
                    continue;
                }

                foreach (var part in brick.parts)
                {
                    var designID = part.designID.ToString();
                    if (!PartUtility.CheckIfConnectivityForPartIsUnpacked(designID))
                    {
                        PartUtility.UnpackConnectivityForPart(designID);
                    }

                    var connectivity = part.connectivity;
                    if (!connectivity)
                    {
                        // Unsupported or legacy.
                        continue;
                    }

                    if (connectivity.version == currentVersion)
                    {
                        // Already up to date.
                        continue;
                    }

                    var connectivityToInstantiate = PartUtility.LoadConnectivityPrefab(designID);
                    if (connectivityToInstantiate)
                    {
                        GameObject.DestroyImmediate(connectivity.gameObject, true);
                        var connectivityGO = UnityEngine.Object.Instantiate(connectivityToInstantiate);
                        connectivityGO.name = "Connectivity";
                        connectivityGO.transform.SetParent(part.transform, false);
                        var connectivityComp = connectivityGO.GetComponent <Connectivity>();
                        part.connectivity = connectivityComp;
                        part.brick.totalBounds.Encapsulate(connectivityComp.extents);
                        connectivityComp.part = part;

                        updated = true;

                        foreach (var tube in part.tubes)
                        {
                            tube.connections.Clear();
                            tube.field = null;
                        }

                        foreach (var knob in part.knobs)
                        {
                            knob.field           = null;
                            knob.connectionIndex = -1;
                        }

                        foreach (var field in connectivityComp.connectionFields)
                        {
                            foreach (var connection in field.connections)
                            {
                                ModelImporter.MatchConnectionWithKnob(connection, part.knobs);
                                ModelImporter.MatchConnectionWithTubes(connection, part.tubes);
                            }
                        }
                    }
                }
            }
            return(updated);
        }