public void Execute() { bool placedSuccessfully; do { var curriculumState = *CurriculumStatePtr; var bounds = ObjectBounds[curriculumState.PrefabIndex]; var scale = ObjectPlacementUtilities.ComputeForegroundScaling(bounds, NativePlacementStatics.ScaleFactors[curriculumState.ScaleIndex]); var rotation = ObjectPlacementUtilities.ComposeForegroundRotation(curriculumState, NativePlacementStatics.OutOfPlaneRotations, NativePlacementStatics.InPlaneRotations); var placedObject = new PlacedObject { Scale = scale, Rotation = rotation, PrefabIndex = curriculumState.PrefabIndex, }; placedSuccessfully = false; for (var i = 0; i < 100; i++) { placedObject.Position = new Vector3(RandomPtr->NextFloat(ImageCoordinates.xMin, ImageCoordinates.xMax), RandomPtr->NextFloat(ImageCoordinates.yMin, ImageCoordinates.yMax), 0f); placedObject.ProjectedArea = ObjectPlacementUtilities.ComputeProjectedArea( Transformer, placedObject.Position, rotation, bounds, scale); placedObject.BoundingBox = GetBoundingBox(bounds, placedObject.Scale, placedObject.Position, placedObject.Rotation); var cropping = CalculateCropping(placedObject.BoundingBox, ImageCoordinates); var passedOverlap = ValidateOverlap(placedObject.BoundingBox, PlaceObjects); if ((cropping <= .5 && passedOverlap)) { placedSuccessfully = true; PlaceObjects.Add(placedObject); *CurriculumStatePtr = NextCurriculumState(curriculumState, NativePlacementStatics); break; } } // If it can’t be placed within the scene due to violations of the cropping or overlap // constraints we stop processing the current foreground scene // and start with the next one. } while (placedSuccessfully && PlaceObjects.Length < NativePlacementStatics.MaxForegroundObjects && CurriculumStatePtr->ScaleIndex < NativePlacementStatics.ScaleFactors.Length); }
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); }
// Start is called before the first frame update void Start() { var outOfPlaneParent = new GameObject("Out of plane test"); var outOfPlaneRotations = ObjectPlacementUtilities.GenerateOutOfPlaneRotationCurriculum(Allocator.Temp); foreach (var rotation in outOfPlaneRotations) { var instance = Instantiate(prefab, outOfPlaneParent.transform); instance.transform.localRotation = rotation; } var inPlaneParent = new GameObject("In plane test"); var inPlaneRotations = ObjectPlacementUtilities.GenerateInPlaneRotationCurriculum(Allocator.Persistent); for (var index = 0; index < inPlaneRotations.Length; index++) { var rotation = inPlaneRotations[index]; var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.transform.localScale = new Vector3(.2f, .2f, .2f); cube.transform.Translate(Vector3.down * index + Vector3.right * 5); cube.transform.localRotation = rotation; cube.transform.parent = inPlaneParent.transform; } var combinedParent = new GameObject("Combined Test Grouped By In Plane"); combinedParent.transform.localPosition = new Vector3(0, 0, 10); for (var iInPlane = 0; iInPlane < inPlaneRotations.Length; iInPlane++) { var inPlaneCombinedParent = new GameObject("In Plane #" + iInPlane); inPlaneCombinedParent.transform.parent = combinedParent.transform; inPlaneCombinedParent.transform.localPosition = Vector3.right * (iInPlane * 2); for (var iOutOfPlane = 0; iOutOfPlane < outOfPlaneRotations.Length; iOutOfPlane++) { var rotation = ObjectPlacementUtilities.ComposeForegroundRotation(new CurriculumState() { InPlaneRotationIndex = iInPlane, OutOfPlaneRotationIndex = iOutOfPlane }, outOfPlaneRotations, inPlaneRotations); var instance = Instantiate(prefab, inPlaneCombinedParent.transform); instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = rotation; } } var combinedOutOfPlane = new GameObject("Combined Test Grouped By Out of Plane"); combinedOutOfPlane.transform.localPosition = new Vector3(0, 0, 20); for (var iOutOfPlane = 0; iOutOfPlane < outOfPlaneRotations.Length; iOutOfPlane++) { var outOfPlaneCombinedParent = new GameObject("Out of Plane #" + iOutOfPlane); outOfPlaneCombinedParent.transform.parent = combinedOutOfPlane.transform; outOfPlaneCombinedParent.transform.localPosition = Vector3.right * (iOutOfPlane * 2); for (var iInPlane = 0; iInPlane < inPlaneRotations.Length; iInPlane++) { var rotation = ObjectPlacementUtilities.ComposeForegroundRotation(new CurriculumState() { InPlaneRotationIndex = iInPlane, OutOfPlaneRotationIndex = iOutOfPlane }, outOfPlaneRotations, inPlaneRotations); var instance = Instantiate(prefab, outOfPlaneCombinedParent.transform); instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = rotation; } } var combinedHeirParent = new GameObject("Combined Test Using Heirarchy"); combinedHeirParent.transform.localPosition = new Vector3(0, 0, 40); for (var iInPlane = 0; iInPlane < inPlaneRotations.Length; iInPlane++) { var inPlaneCombinedParent = new GameObject("In Plane #" + iInPlane); inPlaneCombinedParent.transform.parent = combinedHeirParent.transform; inPlaneCombinedParent.transform.localPosition = Vector3.right * (iInPlane * 2); inPlaneCombinedParent.transform.localRotation = inPlaneRotations[iInPlane]; for (var iOutOfPlane = 0; iOutOfPlane < outOfPlaneRotations.Length; iOutOfPlane++) { var instance = Instantiate(prefab, inPlaneCombinedParent.transform); instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = outOfPlaneRotations[iOutOfPlane]; } } var combinedHeirInvParent = new GameObject("Combined Test Using Heirarchy"); combinedHeirInvParent.transform.localPosition = new Vector3(0, 0, 60); for (var iOutOfPlane = 0; iOutOfPlane < outOfPlaneRotations.Length; iOutOfPlane++) { var outOfPlaneCombinedParent = new GameObject("Out of Plane #" + iOutOfPlane); outOfPlaneCombinedParent.transform.parent = combinedHeirInvParent.transform; outOfPlaneCombinedParent.transform.localPosition = Vector3.right * (iOutOfPlane * 2); outOfPlaneCombinedParent.transform.localRotation = outOfPlaneRotations[iOutOfPlane]; for (var iInPlane = 0; iInPlane < inPlaneRotations.Length; iInPlane++) { var instance = Instantiate(prefab, outOfPlaneCombinedParent.transform); instance.transform.localPosition = Vector3.zero; instance.transform.localRotation = inPlaneRotations[iInPlane]; } } var combinedStackedParent = new GameObject("Combined Test All Stacked"); combinedStackedParent.transform.localPosition = new Vector3(0, 0, 70); for (var iInPlane = 0; iInPlane < inPlaneRotations.Length; iInPlane++) { for (var iOutOfPlane = 0; iOutOfPlane < outOfPlaneRotations.Length; iOutOfPlane++) { var rotation = ObjectPlacementUtilities.ComposeForegroundRotation(new CurriculumState() { InPlaneRotationIndex = iInPlane, OutOfPlaneRotationIndex = iOutOfPlane }, outOfPlaneRotations, inPlaneRotations); var instance = Instantiate(prefab, combinedStackedParent.transform, true); instance.name = $"InPlane: {iInPlane} OutOfPlane: {iOutOfPlane}"; instance.transform.localRotation = rotation; instance.transform.localPosition = Vector3.zero; } } outOfPlaneRotations.Dispose(); inPlaneRotations.Dispose(); }