예제 #1
0
    static Vector2 ProjectBoundsVertex(
        WorldToScreenTransformer transformer, Vector3 direction, Vector3 position, Quaternion rotation, Bounds meshBounds,
        Vector3 scale, int idx, ref float zMin, ref int closestIdx)
    {
        var boundsVertex = Vector3.Scale(Vector3.Scale(direction, scale), meshBounds.extents);
        var vertex       = (rotation * boundsVertex);

        if (vertex.z < zMin)
        {
            zMin       = vertex.z;
            closestIdx = idx;
        }

        return(transformer.TransformPoint(vertex + position));
    }
예제 #2
0
    static MeshDrawInfo PlaceBackgroundObject(NativeArray <MeshInfo> meshInfos, WorldToScreenTransformer transformer,
                                              float foregroundObjectSize, Vector3 position,
                                              ref Random rand, float scaleMin, float scaleMax, int textureCount)
    {
        var meshIndex = rand.NextInt(0, meshInfos.Length);
        var meshInfo  = meshInfos[meshIndex];

        // Rotate/resize object
        var rotation    = rand.NextQuaternionRotation();
        var scaleRandom = rand.NextFloat(scaleMin, scaleMax);
        var scale       = ObjectPlacementUtilities.ComputeScaleToMatchArea(transformer, position, rotation, meshInfo.Bounds,
                                                                           scaleRandom * foregroundObjectSize);

        return(new MeshDrawInfo()
        {
            MeshIndex = meshIndex,
            Position = position,
            Rotation = rotation,
            Scale = scale * Vector3.one,
            TextureIndex = rand.NextInt(textureCount)
        });
    }
예제 #3
0
    // Update is called once per frame
    void Update()
    {
        var tfSource = SourceObject.transform;
        // NOTE: The bounds from the meshfilter mesh are for the un-transformed mesh,
        //       the mesh renderer's mesh already has the transforms (rotation and scale) applied
        var boundsSource = SourceObject.GetComponent <MeshFilter>().sharedMesh.bounds;

        var transformer = new WorldToScreenTransformer(m_Camera);

        m_ProjectedSize = ObjectPlacementUtilities.ComputeProjectedArea(
            transformer, tfSource.position, tfSource.rotation, tfSource.localScale, boundsSource);
        m_TextUI.text = $"Area: {m_ProjectedSize:F2} px^2";

        foreach (var target in TargetObjects)
        {
            var tfTarget     = target.transform;
            var boundsTarget = target.GetComponent <MeshFilter>().sharedMesh.bounds;
            var scalarTarget = ObjectPlacementUtilities.ComputeScaleToMatchArea(
                transformer, tfTarget.position, tfTarget.rotation, boundsTarget, m_ProjectedSize);
            target.transform.localScale = scalarTarget * Vector3.one;
        }
    }
예제 #4
0
        public void ComputeProjectedArea_ReturnsCorrectValues(float area, PrimitiveType primitive, Quaternion rotation)
        {
            var cameraGo = new GameObject("camera");
            var camera   = cameraGo.AddComponent <Camera>();

            camera.orthographic = true;
            var cameraViewAreaMeters = camera.orthographicSize * camera.orthographicSize * camera.aspect * 4;
            var cameraViewAreaPixels = camera.pixelHeight * camera.pixelWidth;
            var pixelsToMeters       = cameraViewAreaMeters / cameraViewAreaPixels;
            var transformer          = new WorldToScreenTransformer(camera);
            var mesh          = GetMeshForPrimitive(primitive);
            var projectedArea = ObjectPlacementUtilities.ComputeProjectedArea(
                transformer, Vector3.zero, rotation, mesh.bounds) * pixelsToMeters;

            Assert.AreApproximatelyEqual(area, projectedArea);
            var scale      = 1.5f;
            var scaledArea = ObjectPlacementUtilities.ComputeProjectedArea(
                transformer, Vector3.zero, rotation, mesh.bounds, scale) * pixelsToMeters;

            Assert.AreApproximatelyEqual(area * scale * scale, scaledArea);
            GameObject.Destroy(cameraGo);
        }
예제 #5
0
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        if (!initialized)
        {
            Initialize();
        }

        if (!initialized)
        {
            return(inputDeps);
        }

        if (m_CurriculumQuery.CalculateEntityCount() != 1)
        {
            return(inputDeps);
        }

        using (s_ResetBackgroundObjects.Auto())
        {
            objectCache.ResetAllObjects();
        }

        var entity          = m_CurriculumQuery.GetSingletonEntity();
        var curriculumState = EntityManager.GetComponentData <CurriculumState>(entity);
        var statics         = EntityManager.GetComponentObject <PlacementStatics>(entity);

        if (curriculumState.ScaleIndex >= statics.ScaleFactors.Length)
        {
            return(inputDeps);
        }

        var meshInfos = new NativeArray <MeshInfo>(statics.BackgroundPrefabs.Length, Allocator.TempJob);

        for (int i = 0; i < statics.BackgroundPrefabs.Length; i++)
        {
            // ReSharper disable once UnusedVariable
            ObjectPlacementUtilities.GetMeshAndMaterial(statics.BackgroundPrefabs[i], out var material, out var meshToDraw);
            meshInfos[i] = new MeshInfo()
            {
                Bounds = meshToDraw.bounds
            };
        }

        var foregroundObject   = statics.ForegroundPrefabs[curriculumState.PrefabIndex];
        var foregroundBounds   = ObjectPlacementUtilities.ComputeBounds(foregroundObject);
        var foregroundRotation =
            ObjectPlacementUtilities.ComposeForegroundRotation(curriculumState, statics.OutOfPlaneRotations, statics.InPlaneRotations);
        var foregroundScale = ObjectPlacementUtilities.ComputeForegroundScaling(
            foregroundBounds, statics.ScaleFactors[curriculumState.ScaleIndex]);
        var transformer = new WorldToScreenTransformer(camera);
        // NOTE: For perspective projection, size will depend on position within the viewport, but we're approximating
        //       by computing size for the center
        var foregroundSizePixels = ObjectPlacementUtilities.ComputeProjectedArea(
            transformer, m_ForegroundCenter, foregroundRotation, foregroundBounds, foregroundScale);

        var placementRegion     = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_PlacementDistance);
        var areaPlacementRegion = placementRegion.width * placementRegion.height;
        var cameraSquarePixels  = camera.pixelHeight * camera.pixelWidth;
        var foregroundSizeUnits = foregroundSizePixels * areaPlacementRegion / cameraSquarePixels;
        // Lazy approximation of how many subdivisions we need to achieve the target density
        var numCellsSqrt       = math.sqrt(m_objectDensity * areaPlacementRegion / foregroundSizeUnits);
        var numCellsHorizontal = (int)math.round(numCellsSqrt * m_aspectRatio);
        var numCellsVertical   = (int)math.round(numCellsSqrt / m_aspectRatio);
        var verticalStep       = placementRegion.height / numCellsVertical;
        var horizontalStep     = placementRegion.width / numCellsHorizontal;
        var scale0             = m_Rand.NextFloat(0.9f, 1.5f);
        var scale1             = m_Rand.NextFloat(0.9f, 1.5f);
        var scaleMin           = math.min(scale0, scale1);
        var scaleMax           = math.max(scale0, scale1);
        var cameraTf           = camera.transform;
        var placementOrigin    = new Vector3(placementRegion.x, placementRegion.y,
                                             k_PlacementDistance + cameraTf.position.z);

        DatasetCapture.ReportMetric(m_ScaleRangeMetric, $@"[ {{ ""scaleMin"": {scaleMin}, ""scaleMax"": {scaleMax} }}]");

        var meshesToDraw = new NativeArray <MeshDrawInfo>(numCellsHorizontal * numCellsVertical * numFillPasses, Allocator.TempJob);

        using (s_PlaceBackgroundObjects.Auto())
        {
            // XXX: Rather than placing a large collection and then looking for gaps, we simply assume that a sufficiently
            //      dense background will not have gaps - rendering way more objects than necessary is still substantially
            //      faster than trying to read the render texture multiple times per frame
            new PlaceBackgroundObjectsJob()
            {
                PlacementOrigin    = placementOrigin,
                Transformer        = new WorldToScreenTransformer(camera),
                NumCellsHorizontal = numCellsHorizontal,
                NumCellsVertical   = numCellsVertical,
                HorizontalStep     = horizontalStep,
                VerticalStep       = verticalStep,
                ForegroundSize     = foregroundSizePixels,
                MeshInfos          = meshInfos,
                MeshDrawInfos      = meshesToDraw,
                TextureCount       = statics.BackgroundImages.Length,
                Seed     = m_Rand.NextUInt(),
                MinScale = scaleMin,
                MaxScale = scaleMax
            }.Schedule(numFillPasses, 1, inputDeps).Complete();
        }

        using (s_DrawMeshes.Auto())
        {
            var properties = new MaterialPropertyBlock();
            foreach (var meshDrawInfo in meshesToDraw)
            {
                var prefab      = statics.BackgroundPrefabs[meshDrawInfo.MeshIndex];
                var sceneObject = objectCache.GetOrInstantiate(prefab);
                ObjectPlacementUtilities.CreateRandomizedHue(properties, backgroundHueMaxOffset, ref m_Rand);
                // ReSharper disable once Unity.PreferAddressByIdToGraphicsParams
                properties.SetTexture("_BaseMap", statics.BackgroundImages[meshDrawInfo.TextureIndex]);
                sceneObject.GetComponentInChildren <MeshRenderer>().SetPropertyBlock(properties);
                sceneObject.transform.SetPositionAndRotation(meshDrawInfo.Position, meshDrawInfo.Rotation);
                sceneObject.transform.localScale = meshDrawInfo.Scale;
            }
        }

        // We have finished drawing the meshes in the camera view, but the engine itself will call Render()
        // at the end of the frame
        meshInfos.Dispose();
        meshesToDraw.Dispose();

        var numObjectsExpected = numCellsHorizontal * numCellsVertical * numFillPasses;

        if (numObjectsExpected != objectCache.NumObjectsActive)
        {
            Debug.LogWarning($"BackgroundGenerator should have placed {numObjectsExpected} but is only using " +
                             $"{objectCache.NumObjectsActive} from the cache.");
        }

        return(inputDeps);
    }
예제 #6
0
    unsafe public static float ComputeProjectedArea(
        WorldToScreenTransformer transformer, Vector3 position, Quaternion rotation, Vector3 scale, Bounds meshBounds)
    {
        var zMin              = Single.MaxValue;
        var closestIdx        = -1;
        var projectedVertices = stackalloc Vector2[8];

        projectedVertices[0] = ProjectBoundsVertex(transformer, new Vector3(-1, -1, -1),
                                                   position, rotation, meshBounds, scale, 0, ref zMin, ref closestIdx);
        projectedVertices[1] = ProjectBoundsVertex(transformer, new Vector3(-1, -1, 1),
                                                   position, rotation, meshBounds, scale, 1, ref zMin, ref closestIdx);
        projectedVertices[2] = ProjectBoundsVertex(transformer, new Vector3(-1, 1, -1),
                                                   position, rotation, meshBounds, scale, 2, ref zMin, ref closestIdx);
        projectedVertices[3] = ProjectBoundsVertex(transformer, new Vector3(-1, 1, 1),
                                                   position, rotation, meshBounds, scale, 3, ref zMin, ref closestIdx);
        projectedVertices[4] = ProjectBoundsVertex(transformer, new Vector3(1, -1, -1),
                                                   position, rotation, meshBounds, scale, 4, ref zMin, ref closestIdx);
        projectedVertices[5] = ProjectBoundsVertex(transformer, new Vector3(1, -1, 1),
                                                   position, rotation, meshBounds, scale, 5, ref zMin, ref closestIdx);
        projectedVertices[6] = ProjectBoundsVertex(transformer, new Vector3(1, 1, -1),
                                                   position, rotation, meshBounds, scale, 6, ref zMin, ref closestIdx);
        projectedVertices[7] = ProjectBoundsVertex(transformer, new Vector3(1, 1, 1),
                                                   position, rotation, meshBounds, scale, 7, ref zMin, ref closestIdx);

        Assert.AreNotEqual(-1, closestIdx);

        var neighborMap = stackalloc int[]
        {
            // 0
            1, 2, 4,
            // 1
            0, 3, 5,
            // 2
            0, 3, 6,
            // 3
            1, 2, 7,
            // 4
            0, 5, 6,
            // 5
            1, 4, 7,
            // 6
            2, 4, 7,
            // 7
            3, 5, 6
        };

        //  Compute the projected surface area of each bounds tri facing the camera
        var closestPoint     = projectedVertices[closestIdx];
        var neighborStartIdx = closestIdx * 3;
        var totalArea        = 0f;
        var trisAdded        = 0;

        for (var i = 0; i < 2; i++)
        {
            var neighborA = projectedVertices[neighborMap[neighborStartIdx + i]];
            for (var j = i + 1; j < 3; j++)
            {
                var neighborB = projectedVertices[neighborMap[neighborStartIdx + j]];
                totalArea += ComputeAreaOfTriangle(closestPoint, neighborA, neighborB) * 2;
                trisAdded += 2;
            }
        }

        Assert.AreEqual(6, trisAdded);
        return(totalArea);
    }
예제 #7
0
 public static float ComputeProjectedArea(
     WorldToScreenTransformer transformer, Vector3 position, Quaternion rotation, Bounds meshBounds, float scale = 1f)
 {
     return(ComputeProjectedArea(transformer, position, rotation, scale * Vector3.one, meshBounds));
 }
예제 #8
0
 internal static float ComputeScaleToMatchArea(
     WorldToScreenTransformer transformer, Vector3 position, Quaternion rotation, Bounds bounds, float projectedAreaTarget)
 {
     return(math.sqrt(projectedAreaTarget / ComputeProjectedArea(transformer, position, rotation, bounds)));
 }