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); }
// 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; } }
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); }
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); }