AirControl  1.3.0
Open Source, Modular, and Extensible Flight Simulator For Deep Learning Research
AC_Airplane_CameraController.cs
1 using System.Collections;
2 using System.Collections.Generic;
3 using UnityEngine;
4 using UnityEngine.Rendering;
5 using Communicator;
6 using System.IO;
7 using Commons;
8 
9 namespace AirControl
10 {
14  public class AC_Airplane_CameraController : MonoBehaviour
15  {
16  #region Variables
17  [Header("Camera Controller Properties")]
18  public AC_BaseAirplane_Input input;
19  [SerializeField]
20  private List<Camera> cameras = new List<Camera>();
21  private Transform airplane;
22  public List<CapturePass> CapturePassList = new List<CapturePass>();
23  public int startCameraIndex =1;
24  private int curentCameraIndex=1;
25  private int currentCaprtureCamera=1;
26  [Header("Shader Setup")]
27  public Shader uberReplacementShader;
28  public Shader opticalFlowShader;
29  public float opticalFlowSensitivity = 1.0f;
30 
31  [Header("Save Image Capture")]
32  public bool saveImage = true;
33  public bool saveIdSegmentation = true;
34  public bool saveLayerSegmentation = true;
35  public bool saveDepth = true;
36  public bool saveNormals = true;
37  public bool saveOpticalFlow=true;
38  // camera pass configuration
39  public CapturePass[] capturePasses = new CapturePass[] {
40  new CapturePass() { name = "_img" },
41  new CapturePass() { name = "_id", supportsAntialiasing = false },
42  new CapturePass() { name = "_layer", supportsAntialiasing = false },
43  new CapturePass() { name = "_depth" },
44  new CapturePass() { name = "_normals" },
45  new CapturePass() { name = "_flow", supportsAntialiasing = false, needsRescale = true } // (see issue with Motion Vectors in @KNOWN ISSUES)
46  };
47  // Capture pass struct
48  public struct CapturePass
49  {
50  // configuration
51  public string name;
52  public bool supportsAntialiasing;
53  public bool needsRescale;
54  public CapturePass(string name_) { name = name_; supportsAntialiasing = true; needsRescale = false; camera = null; }
55 
56  // impl
57  public Camera camera;
58  };
59 
60  // cached materials
61  private Material opticalFlowMaterial;
62  // Type of capture
63  public enum ReplacementMode
64  {
65  ObjectId = 0,
66  CatergoryId = 1,
67  DepthCompressed = 2,
68  DepthMultichannel = 3,
69  Normals = 4
70  };
71 
72  #endregion
73 
74  #region Builtin Methods
75  // Start is called before the first frame update
76 
77 
78  void Start()
79  {
80  input = GameObject.Find(CommonFunctions.ActiveAirplane).GetComponent<AC_BaseAirplane_Input>();
81  //Adding cameras
82  cameras.Add(GameObject.Find(CommonFunctions.ActiveAirplane+"/FollowCamera").GetComponent<Camera>());
83  cameras.Add(GameObject.Find(CommonFunctions.ActiveAirplane+"/CockpitCamera").GetComponent<Camera>());
84 
85 
86  if (startCameraIndex >=0 && startCameraIndex < cameras.Count)
87  {
88  DisableAllCameras();
89  cameras[startCameraIndex].enabled = true;
90  cameras[startCameraIndex].GetComponent<AudioListener>().enabled = true;
91  startCameraIndex = curentCameraIndex;
92  }
93  CreateCamera();
94  }
95 
99  public void CreateCamera()
100  {
101  for (int q = 0; q < capturePasses.Length; q++)
102  {
103  capturePasses[q].camera = CreateHiddenCamera(capturePasses[q].name, cameras[currentCaprtureCamera].transform);
104  CapturePassList.Add(capturePasses[q]);
105  }
106 
107  }
114  private Camera CreateHiddenCamera(string name, Transform parentCamera)
115  {
116  var go = new GameObject(name, typeof(Camera));
117  go.hideFlags = HideFlags.HideAndDontSave;
118  go.transform.SetParent(parentCamera, false);
119  var newCamera = go.GetComponent<Camera>();
120  newCamera.enabled = false;
121  return newCamera;
122  }
123 
124 
125  // Update is called once per frame
126  void Update()
127  {
128  //switching camera as per the database
129  #region IOSwitch
130  // Keeping Get connection in the update loop is essential to avoid the lag
131  int activeCamera = StaticCameraSchema.ActiveCamera;
132  int captureCamera = StaticCameraSchema.CaptureCamera;
133  int captureType = StaticCameraSchema.CaptureType;
134  bool isCapture = StaticCameraSchema.IsCapture;
135  int captureWidth = StaticCameraSchema.CaptureWidth;
136  int captureHeight = StaticCameraSchema.CaptureHeight;
137  bool isActive = StaticCameraSchema.IsActive;
138  if (isActive)
139  {
140  curentCameraIndex = activeCamera;
141  selectCamera(activeCamera);
142  CreateCamera();
143  string logString = System.String.Format("Active scene camera - {0} Capture camera - {1} Width - {2} Height - {3}: ",curentCameraIndex, currentCaprtureCamera, captureWidth, captureHeight);
144  StaticLogger.Log = logString;
145  Debug.unityLogger.Log(logString);
146  StaticCameraSchema.IsActive = false;
147  }
148  // These statements were removed as these were causing delay in the scrrenshot by one frame
149  // Capture will be continuous, to send the capture to client is defined by `isCapture` defined in Outputhandle
150  // if (isCapture)
151  // {
152  //screen capture
153  CapturePass pass = CapturePassList[captureType];
154  pass.camera.enabled =true;
155  ScreenToBytes(pass.camera, cameras[curentCameraIndex] , captureWidth, captureHeight, pass.supportsAntialiasing, pass.needsRescale, ref StaticOutputSchema.ScreenCapture);
156  //if the camera is changed then disable all active capture camera
157  if(currentCaprtureCamera != captureCamera)
158  {
159  ResetAllCaptureCam(capturePasses);
160  currentCaprtureCamera = captureCamera;
161  string logString = System.String.Format("Active scene camera - {0} Capture camera - {1} Width - {2} Height - {3}: ",curentCameraIndex, currentCaprtureCamera, captureWidth, captureHeight);
162  StaticLogger.Log = logString;
163  Debug.unityLogger.Log(logString);
164  OnCameraChange(cameras[currentCaprtureCamera]);// 1 indicate the outside camera
165  // OnSceneChange();
166  }
167 
168 
169  // }
170 
171  #endregion
172  // Manual Camera switch with key c
173  if(input.CameraSwitch){
174  SwitchCamera();
175  }
176  // run through all camera and save screenshot - for testing
177  // if (Input.GetKeyDown(KeyCode.Return))
178  // {
179  // int i = 0;
180  // foreach(CapturePass pass in CapturePassList)
181  // {
182  // pass.camera.enabled =true;
183  // Save(pass.camera, cameras[0] ,"Sunil_"+ i + pass.name + ".png", 250, 200, pass.supportsAntialiasing, pass.needsRescale);
184  // i++;
185  // pass.camera.enabled =false;
186  // OnCameraChange(cameras[0]);
187  // OnSceneChange();
188  // }
189  // foreach(CapturePass pass in CapturePassList)
190  // {
191  // pass.camera.enabled =true;
192  // Save(pass.camera, cameras[1] ,"Sunil_"+ i + pass.name + ".png", 250, 200, pass.supportsAntialiasing, pass.needsRescale);
193  // i++;
194  // pass.camera.enabled =false;
195  // OnCameraChange(cameras[1]);
196  // OnSceneChange();
197  // }
198  // }
199 
200  }
207  void ResetAllCaptureCam(CapturePass[] CapturePassList){
208  foreach(CapturePass pass in CapturePassList)
209  {
210  pass.camera.enabled =false;
211  }
212  }
213  #endregion
214 
215  #region Custom Methods
216  public void ScreenToBytes(Camera cam, Camera mainCamera, int width, int height, bool supportsAntialiasing, bool needsRescale, ref byte[] screencapture)
227  {
228  if (width <= 0 || height <= 0)
229  {
230  width = Screen.width;
231  height = Screen.height;
232  }
233 
234  var depth = 8;
235  var format = RenderTextureFormat.Default;
236  var readWrite = RenderTextureReadWrite.Default;
237  var antiAliasing = (supportsAntialiasing) ? Mathf.Max(1, QualitySettings.antiAliasing) : 1;
238 
239  var finalRT =
240  RenderTexture.GetTemporary(width, height, depth, format, readWrite, antiAliasing);
241  var renderRT = (!needsRescale) ? finalRT :
242  RenderTexture.GetTemporary(mainCamera.pixelWidth, mainCamera.pixelHeight, depth, format, readWrite, antiAliasing);
243  var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
244 
245  var prevActiveRT = RenderTexture.active;
246  var prevCameraRT = cam.targetTexture;
247 
248  // render to offscreen texture (readonly from CPU side)
249  RenderTexture.active = renderRT;
250  cam.targetTexture = renderRT;
251 
252  cam.Render();
253 
254  if (needsRescale)
255  {
256  // blit to rescale (see issue with Motion Vectors in @KNOWN ISSUES)
257  RenderTexture.active = finalRT;
258  Graphics.Blit(renderRT, finalRT);
259  RenderTexture.ReleaseTemporary(renderRT);
260  }
261 
262  // read offsreen texture contents into the CPU readable texture
263  tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
264  tex.Apply();
265 
266  // encode texture into PNG
267  screencapture = tex.EncodeToPNG();
268 
269  }
270 
281 
282  private void Save(Camera cam, Camera mainCamera, string filename, int width, int height, bool supportsAntialiasing, bool needsRescale)
283  {
284  var depth = 24;
285  var format = RenderTextureFormat.Default;
286  var readWrite = RenderTextureReadWrite.Default;
287  var antiAliasing = (supportsAntialiasing) ? Mathf.Max(1, QualitySettings.antiAliasing) : 1;
288 
289  var finalRT =
290  RenderTexture.GetTemporary(width, height, depth, format, readWrite, antiAliasing);
291  var renderRT = (!needsRescale) ? finalRT :
292  RenderTexture.GetTemporary(mainCamera.pixelWidth, mainCamera.pixelHeight, depth, format, readWrite, antiAliasing);
293  var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
294 
295  var prevActiveRT = RenderTexture.active;
296  var prevCameraRT = cam.targetTexture;
297 
298  // render to offscreen texture (readonly from CPU side)
299  RenderTexture.active = renderRT;
300  cam.targetTexture = renderRT;
301 
302  cam.Render();
303 
304  if (needsRescale)
305  {
306  // blit to rescale (see issue with Motion Vectors in @KNOWN ISSUES)
307  RenderTexture.active = finalRT;
308  Graphics.Blit(renderRT, finalRT);
309  RenderTexture.ReleaseTemporary(renderRT);
310  }
311 
312  // read offsreen texture contents into the CPU readable texture
313  tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
314  tex.Apply();
315 
316  // encode texture into PNG
317  var bytes = tex.EncodeToPNG();
318  File.WriteAllBytes(filename, bytes);
319 
320  // restore state and cleanup
321  cam.targetTexture = prevCameraRT;
322  RenderTexture.active = prevActiveRT;
323 
324  Object.Destroy(tex);
325  RenderTexture.ReleaseTemporary(finalRT);
326  }
327 
331  protected virtual void SwitchCamera()
332  {
333  // chnage the camera index as we chaneg the camera
334  DisableAllCameras();
335  curentCameraIndex++;
336  //circular index
337  if (curentCameraIndex >= cameras.Count){
338  curentCameraIndex = 0;
339  }
340  cameras[curentCameraIndex].enabled = true;
341  cameras[curentCameraIndex].GetComponent<AudioListener>().enabled = true;
342  }
343 
348  public void selectCamera(int cameraId)
349  {
350  DisableAllCameras();
351 
352  if (cameraId <= cameras.Count){
353  cameras[cameraId].enabled = true;
354  cameras[cameraId].GetComponent<AudioListener>().enabled = true;
355  curentCameraIndex = cameraId;
356  }
357  }
358 
362  void DisableAllCameras()
363  {
364  if(cameras.Count > 0)
365  {
366  foreach(Camera cam in cameras)
367  {
368  cam.enabled = false;
369  cam.GetComponent<AudioListener>().enabled = false;
370  }
371  }
372  }
373 
381  static private void SetupCameraWithReplacementShader(Camera cam, Shader shader, ReplacementMode mode, Color clearColor)
382  {
383  var cb = new CommandBuffer();
384  cb.SetGlobalFloat("_OutputMode", (int)mode); // @TODO: CommandBuffer is missing SetGlobalInt() method
385  cam.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cb);
386  cam.AddCommandBuffer(CameraEvent.BeforeFinalPass, cb);
387  cam.SetReplacementShader(shader, "");
388  cam.backgroundColor = clearColor;
389  cam.clearFlags = CameraClearFlags.SolidColor;
390  cam.allowHDR = false;
391  cam.allowMSAA = false;
392  }
397  public void OnSceneChange()
398  {
399  var renderers = Object.FindObjectsOfType<Renderer>();
400  var mpb = new MaterialPropertyBlock();
401  foreach (var r in renderers)
402  {
403  var id = r.gameObject.GetInstanceID();
404  var layer = r.gameObject.layer;
405  var tag = r.gameObject.tag;
406 
407  mpb.SetColor("_ObjectColor", ColorEncoding.EncodeIDAsColor(id));
408  mpb.SetColor("_CategoryColor", ColorEncoding.EncodeLayerAsColor(layer));
409  r.SetPropertyBlock(mpb);
410  }
411  }
416  public void OnCameraChange(Camera activeCamera)
417  {
418  int targetDisplay = 1;
419  foreach (var pass in capturePasses)
420  {
421  if (pass.camera == activeCamera)
422  continue;
423 
424  // cleanup capturing camera
425  pass.camera.RemoveAllCommandBuffers();
426 
427  // copy all "main" camera parameters into capturing camera
428  pass.camera.CopyFrom(activeCamera);
429 
430  // set targetDisplay here since it gets overriden by CopyFrom()
431  pass.camera.targetDisplay = targetDisplay++;
432  }
433 
434  // cache materials and setup material properties
435  if (!opticalFlowMaterial || opticalFlowMaterial.shader != opticalFlowShader)
436  opticalFlowMaterial = new Material(opticalFlowShader);
437  opticalFlowMaterial.SetFloat("_Sensitivity", opticalFlowSensitivity);
438 
439  // setup command buffers and replacement shaders
440  SetupCameraWithReplacementShader(capturePasses[1].camera, uberReplacementShader, ReplacementMode.ObjectId, Color.black);
441  SetupCameraWithReplacementShader(capturePasses[2].camera, uberReplacementShader, ReplacementMode.CatergoryId, Color.black);
442  SetupCameraWithReplacementShader(capturePasses[3].camera, uberReplacementShader, ReplacementMode.DepthCompressed, Color.white);
443  SetupCameraWithReplacementShader(capturePasses[4].camera, uberReplacementShader, ReplacementMode.Normals, Color.black);
444  SetupCameraWithPostShader(capturePasses[5].camera, opticalFlowMaterial, DepthTextureMode.Depth | DepthTextureMode.MotionVectors);
445  }
446 
453  static private void SetupCameraWithPostShader(Camera cam, Material material, DepthTextureMode depthTextureMode = DepthTextureMode.None)
454  {
455  var cb = new CommandBuffer();
456  cb.Blit(null, BuiltinRenderTextureType.CurrentActive, material);
457  cam.AddCommandBuffer(CameraEvent.AfterEverything, cb);
458  cam.depthTextureMode = depthTextureMode;
459  }
460 
461  #endregion
462  }
463 
464 }
AirControl.AC_BaseAirplane_Input
Base class to listen for keyboard Inputs
Definition: AC_BaseAirplane_Input.cs:12
AirControl.AC_Airplane_CameraController.CreateCamera
void CreateCamera()
Create hidden capture camera for each schene camera
Definition: AC_Airplane_CameraController.cs:99
AirControl.AC_Airplane_CameraController
Camera switching and capture functionality
Definition: AC_Airplane_CameraController.cs:14
AirControl.ColorEncoding
Capture camera settings
Definition: ColorEncoding.cs:7
AirControl.AC_Airplane_CameraController.OnSceneChange
void OnSceneChange()
Deprecated Not used with API This fucntion was used to switch the screen manually
Definition: AC_Airplane_CameraController.cs:397
AirControl.AC_Airplane_CameraController.OnCameraChange
void OnCameraChange(Camera activeCamera)
Refresh capture camera when active scene camera switch happens
Definition: AC_Airplane_CameraController.cs:416
AirControl.AC_Airplane_CameraController.CapturePass
Definition: AC_Airplane_CameraController.cs:48
AirControl.AC_Airplane_CameraController.SwitchCamera
virtual void SwitchCamera()
Switch camera
Definition: AC_Airplane_CameraController.cs:331
AirControl
Definition: AirplaneSelector.cs:8
AirControl.AC_Airplane_CameraController.ScreenToBytes
void ScreenToBytes(Camera cam, Camera mainCamera, int width, int height, bool supportsAntialiasing, bool needsRescale, ref byte[] screencapture)
Capture and return the screen as byte array
Definition: AC_Airplane_CameraController.cs:226
XCharts.SerieSymbolType.Rect
@ Rect
正方形。可通过设置itemStyle的cornerRadius变成圆角矩形。
Communicator
Definition: InputHandle.cs:10
AirControl.AC_Airplane_CameraController.selectCamera
void selectCamera(int cameraId)
select scene cameras
Definition: AC_Airplane_CameraController.cs:348
Commons
Definition: AirplaneProperties.cs:14