Last active
October 5, 2024 15:07
-
-
Save jasonswearingen/953b0561aaf503ad41038527dcedb4b2 to your computer and use it in GitHub Desktop.
PhotoBooth: Render Images of 3D objects in-game.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//This code utilizes some of my private godot c# scafolding, so unfortunately you can't just copy/paste it. | |
// but the workflow is general to godot 4.3, so just follow the _Setup() workflow and then the workflow to use it is | |
// - AddToStage() | |
// - TakePicture() | |
// - ClearStage() | |
/// <summary> | |
/// PhotoBooth class represents a virtual photo booth for capturing images of 3D scenes. | |
/// It uses a separate scene with a Viewport to render the content, allowing snapshots to be taken with different configurations. | |
/// </summary> | |
using env; | |
using Godot; | |
using lib.Base; | |
/// <summary> | |
/// A class representing a photo booth for capturing 3D scenes. | |
/// </summary> | |
public partial class PhotoBooth : NN_Node_Base | |
{ | |
/// <summary> | |
/// Reference to a SubViewport used to render the scene | |
/// </summary> | |
private SubViewport subViewport; | |
/// <summary> | |
/// Reference to the main stage of the photo booth scene | |
/// </summary> | |
private Node3D stage; | |
/// <summary> | |
/// Called when the node is added to the scene. | |
/// Initializes the PhotoBooth by setting up the viewport and camera. | |
/// </summary> | |
protected override void NN_Ready() | |
{ | |
base.NN_Ready(); | |
_Setup(); | |
} | |
/// <summary> | |
/// Sets up the PhotoBooth by creating the viewport, the stage, and adding necessary components like the camera. | |
/// </summary> | |
private void _Setup() | |
{ | |
// Create a root node for the photo booth content | |
var photoBooth = new Node3D(); | |
_AddChild(photoBooth); | |
// Create a SubViewport with specific settings | |
subViewport = new SubViewport() | |
{ | |
Size = new Vector2I(512, 512), // Set the render size of the viewport | |
TransparentBg = true, // Enable transparency | |
OwnWorld3D = true, // Use an independent 3D world | |
RenderTargetUpdateMode = SubViewport.UpdateMode.Once, // Update the viewport only once | |
}; | |
subViewport._AddChild(new PhotoBoothEnvironment()); // Add environment settings to the viewport | |
// Create a new stage Node3D to hold the 3D objects being captured | |
stage = new Node3D(); | |
subViewport._AddChild(stage); | |
// Create a new Camera3D to be used in the separate rendering scene | |
Camera3D camera = new Camera3D | |
{ | |
Position = Vector3.Back, // Position the camera to see the content properly | |
Current = true, // Set this camera to be active in the viewport | |
}; | |
// Add the camera to the viewport | |
subViewport._AddChild(camera); | |
// Add the viewport to the root photo booth node | |
photoBooth._AddChild(subViewport); | |
} | |
/// <summary> | |
/// Adds a Node3D object to the stage at the given transform. | |
/// The node should not already be in the scene; duplication is required if it is. | |
/// </summary> | |
/// <param name="node">The Node3D to be added to the stage.</param> | |
/// <param name="xform">The Transform3D specifying the location and rotation of the node.</param> | |
public void AddToStage(Node3D node, Transform3D xform) | |
{ | |
__.Assert(node.IsInsideTree() is false, "Cannot take picture of an object already in the scene. Duplicate it first."); | |
node.Transform = xform * node.Transform; | |
stage.AddChild(node); | |
} | |
/// <summary> | |
/// Adds a Node3D object to the stage with the option to automatically adjust focus. | |
/// </summary> | |
/// <typeparam name="TNode">The type of Node3D being added.</typeparam> | |
/// <param name="node">The Node3D to be added to the stage.</param> | |
/// <param name="xform">The Transform3D specifying the location and rotation of the node.</param> | |
/// <param name="autoFocus">If true, automatically adjusts the transform to focus on the node's center.</param> | |
public void AddToStage<TNode>(TNode node, Transform3D xform, bool autoFocus) where TNode : Node3D, IAabb | |
{ | |
if (autoFocus) | |
{ | |
var aabb = node.GetLocalAabb()._Transformed(xform); // Calculate transformed AABB | |
var center = aabb.GetCenter(); // Get the center of the AABB | |
xform.Origin -= center; // Adjust origin to center the object | |
} | |
AddToStage(node, xform); | |
} | |
/// <summary> | |
/// Clears all the content from the stage. | |
/// </summary> | |
public void ClearStage() | |
{ | |
stage.QueueFree(); // Free the current stage | |
stage = new Node3D(); // Create a new empty stage | |
subViewport._AddChild(stage); // Add the new stage to the viewport | |
} | |
/// <summary> | |
/// Takes a picture of the current state of the viewport and returns it as a Texture2D. | |
/// </summary> | |
/// <returns>A Texture2D representing the rendered image of the viewport.</returns> | |
public Texture2D TakePicture() | |
{ | |
subViewport.RenderTargetUpdateMode = SubViewport.UpdateMode.Once; // Set the viewport to update once to capture the current frame | |
return subViewport.GetTexture(); // Return the rendered texture | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//just a helper to setup the environment and lighting | |
using Godot; | |
using lib.Base; | |
using Environment = Godot.Environment; | |
namespace env; | |
public partial class PhotoBoothEnvironment : NN_Node_Base | |
{ | |
protected override void NN_Ready() | |
{ | |
base.NN_Ready(); | |
var worldEnv = new WorldEnvironment() | |
{ | |
Environment = new() | |
{ | |
BackgroundMode = Environment.BGMode.Color, | |
BackgroundColor = Colors.Beige, | |
//GlowEnabled = true, | |
//GlowBlendMode = Environment.GlowBlendModeEnum.Additive, | |
//AdjustmentEnabled = true, | |
//AdjustmentBrightness = 1.45f, | |
//AdjustmentContrast = 1.08f, | |
} | |
}; | |
_AddChild(worldEnv); | |
//ambient light | |
//extra light from same dir as sun, but no shadows | |
var ambientLight = new DirectionalLight3D() | |
{ | |
LightColor = Colors.DarkGray, | |
RotationDegrees = new(-60, 150, 0), | |
LightEnergy = 0.2f, | |
LightSpecular = 0f, | |
LightIndirectEnergy = 0f, | |
//ShadowEnabled = true, | |
LightVolumetricFogEnergy = 0, | |
//LightNegative = true, | |
}; | |
this._AddChild(ambientLight); | |
//also from opposite direction | |
var ambientLight2 = ambientLight.Duplicate() as DirectionalLight3D; | |
ambientLight2.RotationDegrees *= -1; | |
this._AddChild(ambientLight2); | |
//and again, rotated 180' around y axis | |
var ambientLight3 = ambientLight2.Duplicate() as DirectionalLight3D; | |
ambientLight3.RotateY(_PI); | |
this._AddChild(ambientLight3); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment