void PlaceOccludingObjects(
        GameObject[] objectsToPlace, Camera camera, PlacementStatics statics, NativeList <PlacedObject> placedObjects)
    {
        var textures = statics.BackgroundImages;

        if (m_OccludingObjectCache == null)
        {
            m_OccludingObjectCache = new GameObjectOneWayCache(m_ParentOccluding.transform, objectsToPlace);
        }

        m_OccludingObjectCache.ResetAllObjects();
        var occludingObjectBounds = ComputeObjectBounds(objectsToPlace);

        var materialPropertyBlock  = new MaterialPropertyBlock();
        var placedOccludingObjects = new NativeArray <PlacedObject>(placedObjects.Length, Allocator.TempJob);
        var placementRegion        = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_OccludingLayerDistance);

        using (s_ComputePlacements.Auto())
        {
            var job = new ComputeOccludingObjectPlacements()
            {
                OccludingObjectBounds   = occludingObjectBounds,
                ImageCoordinates        = placementRegion,
                Transformer             = new WorldToScreenTransformer(camera),
                PlacedForegroundObjects = placedObjects,
                RandomSeed             = m_Rand.NextUInt(),
                PlacedOccludingObjects = placedOccludingObjects,
                ScalingMin             = statics.ScalingMin,
                ScalingSize            = statics.ScalingSize
            };
            job.Schedule(placedObjects.Length, 10).Complete();
        }

        using (s_SetupObjects.Auto())
        {
            foreach (var placedOccludingObject in placedOccludingObjects)
            {
                if (placedOccludingObject.PrefabIndex < 0)
                {
                    continue;
                }

                var prefab        = objectsToPlace[placedOccludingObject.PrefabIndex];
                var objectToPlace = m_OccludingObjectCache.GetOrInstantiate(prefab);
                objectToPlace.layer = m_OccludingLayer;

                var meshRenderer = objectToPlace.GetComponentInChildren <MeshRenderer>();
                meshRenderer.GetPropertyBlock(materialPropertyBlock);
                ObjectPlacementUtilities.CreateRandomizedHue(materialPropertyBlock, statics.OccludingHueMaxOffset, ref m_Rand);
                materialPropertyBlock.SetTexture("_BaseMap", textures[m_Rand.NextInt(textures.Length)]);
                meshRenderer.SetPropertyBlock(materialPropertyBlock);

                objectToPlace.transform.localPosition = placedOccludingObject.Position;
                objectToPlace.transform.localRotation = placedOccludingObject.Rotation;
                objectToPlace.transform.localScale    = Vector3.one * placedOccludingObject.Scale;
            }
        }
        placedOccludingObjects.Dispose();
        occludingObjectBounds.Dispose();
    }
Example #2
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);
    }
    NativeList <PlacedObject> PlaceObjects(Camera camera, PlacementStatics statics, ref CurriculumState curriculumState)
    {
        var placedObjectBoundingBoxes = new NativeList <PlacedObject>(500, Allocator.TempJob);
        var objectBounds = ComputeObjectBounds(statics.ForegroundPrefabs);

        var localCurriculumState = curriculumState;
        var curriculumStatePtr   = (CurriculumState *)UnsafeUtility.AddressOf(ref localCurriculumState);
        var localRandom          = m_Rand;
        var randomPtr            = (Random *)UnsafeUtility.AddressOf(ref localRandom);
        var placementRegion      = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_ForegroundLayerDistance);

        using (s_ComputePlacements.Auto())
        {
            var computePlacementsJob = new ComputePlacementsJob()
            {
                CurriculumStatePtr     = curriculumStatePtr,
                Transformer            = new WorldToScreenTransformer(camera),
                ImageCoordinates       = placementRegion,
                ObjectBounds           = objectBounds,
                PlaceObjects           = placedObjectBoundingBoxes,
                RandomPtr              = randomPtr,
                NativePlacementStatics = new NativePlacementStatics
                {
                    ForegroundPrefabCount = statics.ForegroundPrefabs.Length,
                    MaxForegroundObjects  = statics.MaxForegroundObjectsPerFrame,
                    InPlaneRotations      = statics.InPlaneRotations,
                    OutOfPlaneRotations   = statics.OutOfPlaneRotations,
                    ScaleFactors          = statics.ScaleFactors
                }
            };
            computePlacementsJob.Run();
            curriculumState = *computePlacementsJob.CurriculumStatePtr;
            m_Rand          = *computePlacementsJob.RandomPtr;
        }

        using (s_SetupObjects.Auto())
        {
            int objectGroupIndex = 0;
            foreach (var placedObject in placedObjectBoundingBoxes)
            {
                EnsureObjectGroupsExist(statics, objectGroupIndex);
                var gameObject = m_ParentForeground.transform.GetChild(placedObject.PrefabIndex + objectGroupIndex * statics.ForegroundPrefabs.Length).gameObject;

                gameObject.transform.localRotation = placedObject.Rotation;

                gameObject.transform.localScale =
                    Vector3.one * placedObject.Scale;
                gameObject.transform.localPosition = placedObject.Position;

                ObjectPlacementUtilities.SetMeshRenderersEnabledRecursive(gameObject, true);

                if (placedObject.PrefabIndex == statics.ForegroundPrefabs.Length - 1)
                {
                    objectGroupIndex++;
                }
            }
        }

        objectBounds.Dispose();

        return(placedObjectBoundingBoxes);
    }
    NativeList <PlacedObject> PlaceObjects(Camera camera, PlacementStatics statics, NativeArray <Bounds> occludingObjectBounds, ref CurriculumState curriculumState)
    {
        var placedObjectBoundingBoxes = new NativeList <PlacedObject>(500, Allocator.TempJob);
        var objectBounds = ComputeObjectBounds(statics.ForegroundPrefabs);

        var localCurriculumState = curriculumState;
        var curriculumStatePtr   = (CurriculumState *)UnsafeUtility.AddressOf(ref localCurriculumState);
        var localRandom          = m_Rand;
        var randomPtr            = (Random *)UnsafeUtility.AddressOf(ref localRandom);
        var placementRegion      = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_ForegroundLayerDistance);

        using (s_ComputePlacements.Auto())
        {
            var computePlacementsJob = new ComputePlacementsJob()
            {
                CurriculumStatePtr     = curriculumStatePtr,
                Transformer            = new WorldToScreenTransformer(camera),
                ImageCoordinates       = placementRegion,
                ObjectBounds           = objectBounds,
                OccludingObjectBounds  = occludingObjectBounds,
                PlaceObjects           = placedObjectBoundingBoxes,
                RandomPtr              = randomPtr,
                NativePlacementStatics = new NativePlacementStatics
                {
                    ForegroundPrefabCount = statics.ForegroundPrefabs.Length,
                    MaxForegroundObjects  = statics.MaxForegroundObjectsPerFrame,
                    InPlaneRotations      = statics.InPlaneRotations,
                    OutOfPlaneRotations   = statics.OutOfPlaneRotations,
                    ScaleFactors          = statics.ScaleFactors,
                    BackgroundObjectInForegroundChance = statics.BackgroundObjectInForegroundChance,
                }
            };
            computePlacementsJob.Run();
            curriculumState = *computePlacementsJob.CurriculumStatePtr;
            m_Rand          = *computePlacementsJob.RandomPtr;
        }

        using (s_SetupObjects.Auto())
        {
            var materialPropertyBlock = new MaterialPropertyBlock();
            int objectGroupIndex      = 0;
            foreach (var placedObject in placedObjectBoundingBoxes)
            {
                GameObject gameObject;
                if (placedObject.IsOccluding)
                {
                    gameObject = m_BackgroundInForegroundObjectCache.GetOrInstantiate(statics.BackgroundPrefabs[placedObject.PrefabIndex]);

                    var meshRenderer = gameObject.GetComponentInChildren <MeshRenderer>();
                    meshRenderer.GetPropertyBlock(materialPropertyBlock);
                    ObjectPlacementUtilities.CreateRandomizedHue(materialPropertyBlock, statics.OccludingHueMaxOffset, ref m_Rand);
                    materialPropertyBlock.SetTexture("_BaseMap", statics.BackgroundImages[m_Rand.NextInt(statics.BackgroundImages.Length)]);
                    meshRenderer.SetPropertyBlock(materialPropertyBlock);
                }
                else
                {
                    EnsureObjectGroupsExist(statics, objectGroupIndex);
                    gameObject = m_ParentForeground.transform.GetChild(placedObject.PrefabIndex + objectGroupIndex * statics.ForegroundPrefabs.Length).gameObject;
                }

                gameObject.transform.localRotation = placedObject.Rotation;
                gameObject.transform.localScale    = Vector3.one * placedObject.Scale;
                gameObject.transform.localPosition = placedObject.Position;

                ObjectPlacementUtilities.SetMeshRenderersEnabledRecursive(gameObject, true);

                if (placedObject.PrefabIndex == statics.ForegroundPrefabs.Length - 1)
                {
                    objectGroupIndex++;
                }
            }
        }

        objectBounds.Dispose();

        return(placedObjectBoundingBoxes);
    }