diff --git a/src/Doc/Steps/db.md b/src/Doc/Steps/db.md index 179dbce..a1b1b38 100644 --- a/src/Doc/Steps/db.md +++ b/src/Doc/Steps/db.md @@ -1,36 +1,213 @@ # Database Generation -## Save Camera view -In the runtime, the main camera view will be automatically saved in the path you specify. The camera view will be saved as a .png file with the date and time as the file name. -There is a parameter where you can set the number of screenshots you want to save per room. The default value is 5. +After the room is generated, the data is collected in the ```DatabaseGenerator.cs``` script. Each steps will be detailed in the following sections. -## Camera parameters +## Camera placement -The camera can be adjusted with the following parameters: +First, all the empty nodes are retrieved from the room's quad tree. The camera is the placed randomly in one of them, and a random rotation is applied according to the settings. We then ensure that the camera is not placed too close to the walls, the ceiling or to props to avoid clipping issues. This is done by creating a collider sphere around it and checking for collisions. -- **Field of view**: The field of view of the camera. The default value is 90. -- **ISO**: The ISO of the camera. -- **Focus distance**: The shutter speed of the camera. -- **Aperture**: The aperture of the camera. The default value is 5.6. -- **Camera rotation**: Define the maximum rotation of the camera in degrees. By default, the camera will on all axes. -## Take a screenshot +## Save Camera view -To take a screenshot of the camera view, you can use the following code: +Secondly, a screenshot is generated using this custom method in ```CameraScreenshot.cs``` which allow to save the camera view while ignoring the UI. ```Csharp -using UnityEngine; -using CameraScreenshot; +private IEnumerator Capture() + { + // Create a RenderTexture to save the camera view + RenderTexture rt = new RenderTexture(imageWidth, imageHeight, 24); + cameraToCapture.targetTexture = rt; -public class CameraScreenshotExample : MonoBehaviour -{ - [SerializeField] private CameraScreenshot cameraScreenshot; - void Start() + // Create a 2D texture to save the screenshot + Texture2D screenShot = new Texture2D(imageWidth, imageHeight, TextureFormat.RGB24, false); + cameraToCapture.Render(); + + // Activate the RenderTexture and read the pixels + RenderTexture.active = rt; + screenShot.ReadPixels(new Rect(0, 0, imageWidth, imageHeight), 0, 0); + screenShot.Apply(); + + // Reset the camera and RenderTexture + cameraToCapture.targetTexture = null; + RenderTexture.active = null; + Destroy(rt); + + // Save the screenshot + byte[] bytes = screenShot.EncodeToPNG(); + File.WriteAllBytes(savePath, bytes); + yield return null; + } +``` + +## Data collection + +Finally, the data about each openings in the image is collected : + +#### Distance + +The distance from the camera is calculated by substrating the camera position from the opening position. + +#### Angle + +The quaternion angle between the camera and the opening is calculated using the ```Quaternion.LookRotation()``` method. + +#### Dimensions + +The dimensions of the opening are calculated by using the ```Bounds.size``` property of the opening's collider. + +#### Bounding boxes + +##### Full bounding box + +The bounding box is the 2D rectangle that contains the opening. It is calculated by using the ```Bounds.min``` and ```Bounds.max``` properties of the opening's collider in the ```Openings.cs``` script. + +##### Visibility bounding box + +The visibility bounding box is the 2D rectangle that contains only the visible part of the opening. It is calculated by casting numerous rays from the camera to the opening and finding the intersection points. The bounding box is then calculated using the intersection points in the ```Openings.cs``` script. + +````Csharp +public BoundingBox2D GetVisibilityBoundingBox() + { + gameObject.TryGetComponent(out BoxCollider openingBounds); + _width = RoomsGenerator.GetOpeningWidth(openingBounds.size); + _height = openingBounds.size.y; + int minX = Screen.width + 1; + int maxX = -1; + int minY = Screen.height + 1; + int maxY = -1; + + float widthStep = _width / Mathf.Sqrt(NumberOfPoints); + float heightStep = _height / Mathf.Sqrt(NumberOfPoints); + + for (float x = -_width / 2f + widthStep / 2; x < _width / 2f; x += widthStep) + { + for (float y = -_height / 2f + heightStep / 2; y <= _height / 2f; y += heightStep) + { + var thisTransform = transform; + Vector3 positionOffset = thisTransform.right * x + thisTransform.up * y; + Vector3 aimPoint = GetCenter() + positionOffset; + if (IsPointVisible(aimPoint) && IsPointOnScreen(aimPoint)) + { + Vector3 screenPoint = _mainCamera.WorldToScreenPoint(aimPoint); + minX = (int)Mathf.Min(minX, screenPoint.x); + maxX = (int)Mathf.Max(maxX, screenPoint.x); + minY = (int)Mathf.Min(minY, screenPoint.y); + maxY = (int)Mathf.Max(maxY, screenPoint.y); + } + } + } + // 640 * 360 is the minimum resolution + int screenShotWidth = 640 * MainMenuController.PresetData.Resolution; + int screenShotHeight = 360 * MainMenuController.PresetData.Resolution; + + // Scale coordinates to screenshot size + minX = (int)(minX * screenShotWidth / Screen.width); + maxX = (int)(maxX * screenShotWidth / Screen.width); + minY = (int)(minY * screenShotHeight / Screen.height); + maxY = (int)(maxY * screenShotHeight / Screen.height); + + + return new BoundingBox2D(new Vector2Int(minX, minY), maxX - minX, maxY - minY); + } + + private bool IsPointVisible(Vector3 aimPoint) { - cameraScreenshot.savePath = Application.dataPath + "/yourPath"; - cameraScreenshot.CaptureScreenshot(); + GameObject mainCamera = _mainCamera!.gameObject; + Vector3 aimPointDirection = aimPoint - mainCamera.transform.position; + + if (Physics.Raycast(mainCamera.transform.position, aimPointDirection, out var hit, float.MaxValue)) + { + if (hit.collider.gameObject == gameObject || hit.collider.gameObject.transform.parent == transform) + return true; + } + + return false; + } + + // Check if a point is on the screen, i.e. in the camera's view frustum + private bool IsPointOnScreen(Vector3 point) + { + Vector3 screenPoint = _mainCamera!.WorldToViewportPoint(point); + return screenPoint.x is > 0 and < 1 && screenPoint.y is > 0 and < 1 && screenPoint.z > 0; + } +```` + +#### Visibility ratio + +The visibility ratio is the ratio of the visibility bounding box area over the full bounding box area. The data is only kept if the visibility ratio is different from 0, to avoid collecting data from openings that are not visible in the image. + +## Save data + +The screenshot is then saved, along with a matching JSON file containing the collected data about openings, the seed and the camera information. The JSON file is structured as follows: + +```json +{ + "SeedsData": { + "DatabaseSeed": 522732214, + "RoomsSeed": -320021519, + "OpeningsSeed": 723920686, + "ObjectsSeed": 1119647337 + }, + "CameraData": { + "FieldOfView": 52.2338448, + "NearClipPlane": 0.3, + "FarClipPlane": 1000.0, + "ViewportRectX": 0.0, + "ViewportRectY": 0.0, + "ViewportRectWidth": 1920, + "ViewportRectHeight": 1080, + "Depth": -1.0, + "IsOrthographic": false + }, + "ScreenshotData": { + "OpeningsData": [ + { + "Type": "Window", + "Dimensions": { + "Height": 1.603815, + "Width": 1.13412368, + "Thickness": 0.10204263 + }, + "DistanceToCamera": 7.12805271, + "RotationQuaternionFromCamera": { + "w": 0.457306623, + "x": -0.004237673, + "y": 0.8892608, + "z": 0.008240416 + }, + "OpenessDegree": 0.6515185, + "VisibilityRatio": 0.9289916, + "BoundingBox": { + "Origin": [ + 1118, + 454 + ], + "Dimension": [ + 118, + 205 + ] + }, + "VisibilityBoundingBox": { + "Origin": [ + 1120, + 458 + ], + "Dimension": [ + 116, + 200 + ] + } + }, + ], + "CameraRotation": { + "w": 0.5645235, + "x": 0.0, + "y": 0.825417, + "z": 0.0 } + } } ``` +A JSON is also created for each room, containing the seeds, the room's dimensions, the generation's time and the placement data. \ No newline at end of file diff --git a/src/Doc/Steps/props.md b/src/Doc/Steps/props.md index 2532189..3960853 100644 --- a/src/Doc/Steps/props.md +++ b/src/Doc/Steps/props.md @@ -1,6 +1,6 @@ # Props placement -### Quad tree +## Quad tree The props are placed in the room using a quad tree : the space is divided into four equal nodes, which represent rectangles in the room. The props are placed one by one randomly in one of the biggest empty nodes, namely the empty nodes with the lowest depth. The node chosen is then divided into four nodes once again. The process is repeated until all the props are placed inside the room. This ensures that the props have the highest chance of being placed without overlapping with other props, thus limiting the number of tries needed to place them. @@ -10,7 +10,7 @@ The props are placed in the room using a quad tree : the space is divided into f Figure: 3 steps of the quad tree division process for prop placement.

-### Optimal nodes +## Optimal nodes For certain types of furnitures, the node choice is not fully random and follow some rules. This allows to place the furniture in a more realistic way. Thus, to generate other kind of rooms, you would have to add new types of furniture and new sets of rules. @@ -20,7 +20,7 @@ Cuurently there are 2 main rules : They are implemented in the ```QuadTreeNode.cs``` script. -### "Spawner" props +## Spawner props Some props place other props around them when they are instantiated. This helps having a coherent placement, while still having a random aspect. The props instancianting other props are the following : @@ -28,7 +28,7 @@ Some props place other props around them when they are instantiated. This helps - **Tables** : They place a random amount of chairs around them, and make them face the table. - **Desk** : They place a random armchair in front of them. -### Add a prop +## Add a prop To create a new prop prefab, the game object needs some mandatory components : - **A mesh** : The visual representation of the prop. diff --git a/src/Doc/build.md b/src/Doc/build.md index d446e6e..e4c1733 100644 --- a/src/Doc/build.md +++ b/src/Doc/build.md @@ -11,4 +11,4 @@ Then, you can build the project by going to `File > Build Settings` and selectin Windows is the only build target that has been tested, but building for other platforms should work as well using the correct [Unity plugins](https://docs.unity3d.com/560/Documentation/Manual/PluginsForDesktop.html). -> :warning: The .exe will not work if not associated with the other files created by Unity. +> Note that the .exe will not work if not associated with the other files created by Unity. diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 405e713..6767838 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -10,7 +10,7 @@ - [Generation steps](./Doc/./Steps/steps_menu.md) - [Room generation](./Doc/./Steps/room.md) - [Props placement](./Doc/./Steps/props.md) - - [Data recovery](./Doc/./Steps/room.md) + - [Data recovery](./Doc/./Steps/db.md) # Testing diff --git a/src/intro.md b/src/intro.md index 402f4ef..2352ce5 100644 --- a/src/intro.md +++ b/src/intro.md @@ -114,8 +114,8 @@ Each screenshot is matched with a Json containing info about the room, the seeds } ``` -## Open source +## Work on ISGT -ISGT is fully open-source, you can fork the GitHub repository and start helping us improve the tool. +You can fork the [GitHub repository](https://github.com/numediart/ISGT) and start helping us improve the tool. -You can find the complete technical documentation [here](./Doc/doc-menu.md). \ No newline at end of file +You can find the technical documentation [here](./Doc/doc-menu.md). \ No newline at end of file