Camera tools
This commit is contained in:
parent
0a1852e0c9
commit
e608e2a56b
17 changed files with 384 additions and 0 deletions
8
CamToImage.meta
Normal file
8
CamToImage.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e913617f0585d954799673a0cba0b987
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
CamToImage/Runtime.meta
Normal file
8
CamToImage/Runtime.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 14ddfe4ade1ec0945b8355ace2110882
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
14
CamToImage/Runtime/CamToImage.asmdef
Normal file
14
CamToImage/Runtime/CamToImage.asmdef
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "CamToImage",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
CamToImage/Runtime/CamToImage.asmdef.meta
Normal file
7
CamToImage/Runtime/CamToImage.asmdef.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 32d7f771e78b8bf41b2d888d99ac3867
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
99
CamToImage/Runtime/CamToImage.cs
Normal file
99
CamToImage/Runtime/CamToImage.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace gay.lilyy.PlaneCam
|
||||||
|
{
|
||||||
|
[AddComponentMenu("LillithRosePup/Camera To Image")]
|
||||||
|
[RequireComponent(typeof(Camera))]
|
||||||
|
public class PlaneCam : MonoBehaviour, VRC.SDKBase.IEditorOnly
|
||||||
|
{
|
||||||
|
|
||||||
|
private const string DefaultOutputImagePath = "Output.png";
|
||||||
|
public string outputImagePath = DefaultOutputImagePath;
|
||||||
|
|
||||||
|
[Tooltip("If set to a value greater than 0, the image will be scaled so its longest side matches this value while maintaining aspect ratio")]
|
||||||
|
public int longestSide = 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[CustomEditor(typeof(PlaneCam))]
|
||||||
|
public class PlaneCamEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
base.OnInspectorGUI();
|
||||||
|
if (GUILayout.Button("Capture Image"))
|
||||||
|
{
|
||||||
|
var camera = target as PlaneCam;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CaptureImage(PlaneCam camDef) {
|
||||||
|
var camera = camDef.GetComponent<Camera>();
|
||||||
|
|
||||||
|
float aspectRatio = camera.aspect;
|
||||||
|
|
||||||
|
int cameraWidth = camera.pixelWidth;
|
||||||
|
int cameraHeight = camera.pixelHeight;
|
||||||
|
|
||||||
|
int outputWidth, outputHeight;
|
||||||
|
if (aspectRatio > 1f)
|
||||||
|
{
|
||||||
|
outputHeight = cameraHeight;
|
||||||
|
outputWidth = Mathf.RoundToInt(cameraHeight * aspectRatio);
|
||||||
|
if (outputWidth > cameraWidth)
|
||||||
|
{
|
||||||
|
outputWidth = cameraWidth;
|
||||||
|
outputHeight = Mathf.RoundToInt(cameraWidth / aspectRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outputWidth = cameraWidth;
|
||||||
|
outputHeight = Mathf.RoundToInt(cameraWidth / aspectRatio);
|
||||||
|
if (outputHeight > cameraHeight)
|
||||||
|
{
|
||||||
|
outputHeight = cameraHeight;
|
||||||
|
outputWidth = Mathf.RoundToInt(cameraHeight * aspectRatio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale to longest side if specified
|
||||||
|
if (camDef.longestSide > 0)
|
||||||
|
{
|
||||||
|
int currentLongestSide = Mathf.Max(outputWidth, outputHeight);
|
||||||
|
if (currentLongestSide != camDef.longestSide)
|
||||||
|
{
|
||||||
|
float scale = (float)camDef.longestSide / currentLongestSide;
|
||||||
|
outputWidth = Mathf.RoundToInt(outputWidth * scale);
|
||||||
|
outputHeight = Mathf.RoundToInt(outputHeight * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var renderTexture = new RenderTexture(outputWidth, outputHeight, 24);
|
||||||
|
camera.targetTexture = renderTexture;
|
||||||
|
camera.Render();
|
||||||
|
|
||||||
|
var screenshot = new Texture2D(outputWidth, outputHeight, TextureFormat.ARGB32, false);
|
||||||
|
RenderTexture.active = renderTexture;
|
||||||
|
screenshot.ReadPixels(new Rect(0, 0, outputWidth, outputHeight), 0, 0);
|
||||||
|
screenshot.Apply();
|
||||||
|
RenderTexture.active = null;
|
||||||
|
camera.targetTexture = null;
|
||||||
|
renderTexture.Release();
|
||||||
|
|
||||||
|
var bytes = screenshot.EncodeToPNG();
|
||||||
|
File.WriteAllBytes("Assets/" + camDef.outputImagePath, bytes);
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
Debug.Log($"Image captured and saved to: Assets/{camDef.outputImagePath}. Aspect Ratio: {aspectRatio:F2} ({outputWidth}x{outputHeight})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
11
CamToImage/Runtime/CamToImage.cs.meta
Normal file
11
CamToImage/Runtime/CamToImage.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 007ac06a66da7cb4b86fde9e783da04d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
PlaneCam.meta
Normal file
8
PlaneCam.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1a8dda0420e5fa43bf7d6497beec37a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
PlaneCam/Editor.meta
Normal file
8
PlaneCam/Editor.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dfb718cc85f807a4585cc3dd07098767
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
18
PlaneCam/Editor/PlaneCamEditor.asmdef
Normal file
18
PlaneCam/Editor/PlaneCamEditor.asmdef
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "PlaneCamEditor",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:a9f136790ce90f740a7142ab21ff971a"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
PlaneCam/Editor/PlaneCamEditor.asmdef.meta
Normal file
7
PlaneCam/Editor/PlaneCamEditor.asmdef.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0010e90dfaa354f4ca094b433acef847
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
121
PlaneCam/Editor/PlaneCamEditor.cs
Normal file
121
PlaneCam/Editor/PlaneCamEditor.cs
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using gay.lilyy.PlaneCam;
|
||||||
|
|
||||||
|
namespace gay.lilyy.PlaneCam.Editor
|
||||||
|
{
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class PlaneCamEditor
|
||||||
|
{
|
||||||
|
static PlaneCamEditor()
|
||||||
|
{
|
||||||
|
EditorApplication.update += UpdatePlaneCams;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdatePlaneCams()
|
||||||
|
{
|
||||||
|
PlaneCam[] planeCams = Object.FindObjectsOfType<PlaneCam>(true);
|
||||||
|
|
||||||
|
foreach (PlaneCam planeCam in planeCams)
|
||||||
|
{
|
||||||
|
if (planeCam == null || planeCam.target == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Camera cam = planeCam.GetComponent<Camera>();
|
||||||
|
if (cam == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get the bounds of the target plane
|
||||||
|
Bounds bounds = GetBounds(planeCam.target);
|
||||||
|
|
||||||
|
if (bounds.size.magnitude < 0.001f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get plane normal and center
|
||||||
|
Vector3 planeNormal = GetPlaneNormal(planeCam.target, planeCam.direction);
|
||||||
|
Vector3 planeCenter = bounds.center;
|
||||||
|
|
||||||
|
// Calculate the plane's dimensions in its local space
|
||||||
|
// Project bounds size onto the plane's local axes
|
||||||
|
Transform planeTransform = planeCam.target.transform;
|
||||||
|
Vector3 localSize = bounds.size;
|
||||||
|
|
||||||
|
// Find the two dimensions that define the plane (ignore the smallest dimension)
|
||||||
|
// This assumes the plane is flat, so one dimension should be very small
|
||||||
|
float minDim = Mathf.Min(localSize.x, localSize.y, localSize.z);
|
||||||
|
float maxDim = Mathf.Max(localSize.x, localSize.y, localSize.z);
|
||||||
|
float midDim = localSize.x + localSize.y + localSize.z - minDim - maxDim;
|
||||||
|
|
||||||
|
// Use the larger of the two plane dimensions to ensure it fills the square viewport
|
||||||
|
float planeSize = Mathf.Max(maxDim, midDim);
|
||||||
|
|
||||||
|
// Set aspect ratio to 1:1 for square viewport
|
||||||
|
cam.aspect = 1.0f;
|
||||||
|
|
||||||
|
// Calculate distance and position
|
||||||
|
if (cam.orthographic)
|
||||||
|
{
|
||||||
|
cam.orthographicSize = planeSize * 0.5f;
|
||||||
|
// Position camera perpendicular to the plane
|
||||||
|
cam.transform.position = planeCenter - planeNormal * 10f; // Distance doesn't matter for orthographic
|
||||||
|
cam.transform.LookAt(planeCenter, planeTransform.up);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For perspective camera, calculate distance based on FOV
|
||||||
|
// distance = (size/2) / tan(FOV/2)
|
||||||
|
float halfFOV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
|
||||||
|
float distance = (planeSize * 0.5f) / Mathf.Tan(halfFOV);
|
||||||
|
|
||||||
|
// Position camera perpendicular to the plane, looking at center
|
||||||
|
cam.transform.position = planeCenter - planeNormal * distance;
|
||||||
|
cam.transform.LookAt(planeCenter, planeTransform.up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bounds GetBounds(GameObject target)
|
||||||
|
{
|
||||||
|
Renderer renderer = target.GetComponent<Renderer>();
|
||||||
|
if (renderer != null)
|
||||||
|
{
|
||||||
|
return renderer.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no renderer, try to get bounds from collider
|
||||||
|
Collider collider = target.GetComponent<Collider>();
|
||||||
|
if (collider != null)
|
||||||
|
{
|
||||||
|
return collider.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: use transform scale
|
||||||
|
return new Bounds(target.transform.position, target.transform.lossyScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector3 GetPlaneNormal(GameObject target, PlaneDirection direction)
|
||||||
|
{
|
||||||
|
Transform t = target.transform;
|
||||||
|
|
||||||
|
// Use the specified direction setting
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case PlaneDirection.XPositive:
|
||||||
|
return t.right;
|
||||||
|
case PlaneDirection.XNegative:
|
||||||
|
return -t.right;
|
||||||
|
case PlaneDirection.YPositive:
|
||||||
|
return t.up;
|
||||||
|
case PlaneDirection.YNegative:
|
||||||
|
return -t.up;
|
||||||
|
case PlaneDirection.ZPositive:
|
||||||
|
return t.forward;
|
||||||
|
case PlaneDirection.ZNegative:
|
||||||
|
return -t.forward;
|
||||||
|
default:
|
||||||
|
return t.forward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
11
PlaneCam/Editor/PlaneCamEditor.cs.meta
Normal file
11
PlaneCam/Editor/PlaneCamEditor.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0e148c194888c8f4b80fbb649b2f1a02
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
PlaneCam/Runtime.meta
Normal file
8
PlaneCam/Runtime.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a6bc6863465f2e64db8b50aa3a1d067f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
24
PlaneCam/Runtime/PlaneCam.cs
Normal file
24
PlaneCam/Runtime/PlaneCam.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
namespace gay.lilyy.PlaneCam
|
||||||
|
{
|
||||||
|
public enum PlaneDirection
|
||||||
|
{
|
||||||
|
XPositive,
|
||||||
|
XNegative,
|
||||||
|
YPositive,
|
||||||
|
YNegative,
|
||||||
|
ZPositive,
|
||||||
|
ZNegative
|
||||||
|
}
|
||||||
|
|
||||||
|
[AddComponentMenu("LillithRosePup/Plane Cam")]
|
||||||
|
[RequireComponent(typeof(Camera))]
|
||||||
|
public class PlaneCam : MonoBehaviour, VRC.SDKBase.IEditorOnly
|
||||||
|
{
|
||||||
|
public GameObject target;
|
||||||
|
public PlaneDirection direction = PlaneDirection.ZPositive;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
PlaneCam/Runtime/PlaneCam.cs.meta
Normal file
11
PlaneCam/Runtime/PlaneCam.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 49ab2f404b9727b4dadf40b4aea753d0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
14
PlaneCam/Runtime/PlaneCamRuntime.asmdef
Normal file
14
PlaneCam/Runtime/PlaneCamRuntime.asmdef
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "PlaneCamRuntime",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
PlaneCam/Runtime/PlaneCamRuntime.asmdef.meta
Normal file
7
PlaneCam/Runtime/PlaneCamRuntime.asmdef.meta
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a9f136790ce90f740a7142ab21ff971a
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Loading…
Add table
Add a link
Reference in a new issue