Jelajahi Sumber

Added render target for game view
More work on Game window

BearishSun 10 tahun lalu
induk
melakukan
0ab825051b

+ 4 - 0
BansheeEditor/Source/BsEditorApplication.cpp

@@ -26,6 +26,7 @@
 #include "BsCoreSceneManager.h"
 #include "BsSplashScreen.h"
 #include "BsDynLib.h"
+#include "BsSceneManager.h"
 
 namespace BansheeEngine
 {
@@ -64,6 +65,9 @@ namespace BansheeEngine
 		SplashScreen::show();
 		Application::onStartUp();
 
+		// In editor we render game on a separate surface, handled in Game window
+		SceneManager::instance().setMainRenderTarget(nullptr);
+
 		loadEditorSettings();
 		mProjectSettings = bs_shared_ptr_new<ProjectSettings>();
 

+ 4 - 4
BansheeEngine/Include/BsApplication.h

@@ -43,11 +43,11 @@ namespace BansheeEngine
 		static void startUp(RENDER_WINDOW_DESC& primaryWindowDesc, RenderAPIPlugin renderAPI, RendererPlugin renderer = RendererPlugin::Default);
 
 		/**
-		 * @brief	Returns the primary viewport of the application.
-		 *
-		 * @note	e.g. player or game view.
+		 * @brief	Returns the render target that the main camera in the scene (if any) will render its view to. This
+		 * 			generally means the main game window when running standalone, or the Game viewport when running
+		 * 			in editor.
 		 */
-		ViewportPtr getPrimaryViewport() const;
+		RenderTargetPtr getMainRenderTarget() const;
 
 		/**
 		 * @brief	Returns the absolute path to the builtin managed engine assembly file.

+ 17 - 0
BansheeEngine/Include/BsSceneManager.h

@@ -97,6 +97,15 @@ namespace BansheeEngine
 		 */
 		SceneCameraData getMainCamera() const;
 
+		/**
+		 * @brief	Sets the render target that the main camera in the scene (if any) will render its view to. This
+		 * 			generally means the main game window when running standalone, or the Game viewport when running
+		 * 			in editor.
+		 *
+		 * @note	Internal method.
+		 */
+		void setMainRenderTarget(const RenderTargetPtr& rt);
+
 		/**
 		 * @brief	Notifies the scene manager that a new renderable was created.
 		 * 
@@ -162,10 +171,18 @@ namespace BansheeEngine
 		static SceneManager* instancePtr();
 
 	private:
+		/**
+		 * @brief	Callback that is triggered when the main render target size is changed.
+		 */
+		void onMainRenderTargetResized();
+
 		Map<Camera*, SceneCameraData> mCameras;
 		Map<Renderable*, SceneRenderableData> mRenderables;
 		Map<Light*, SceneLightData> mLights;
 		Vector<SceneCameraData> mMainCameras;
+		RenderTargetPtr mMainRT;
+
+		HEvent mMainRTResizedConn;
 
 		volatile static InitOnStart DoInitOnStart;
 	};

+ 3 - 1
BansheeEngine/Source/BsApplication.cpp

@@ -81,6 +81,8 @@ namespace BansheeEngine
 		Cursor::startUp();
 		Cursor::instance().setCursor(CursorType::Arrow);
 
+		SceneManager::instance().setMainRenderTarget(getPrimaryWindow());
+
 		ScriptManager::startUp();
 		loadScriptSystem();
 	}
@@ -138,7 +140,7 @@ namespace BansheeEngine
 		// Do nothing, we activate the renderer at a later stage
 	}
 
-	ViewportPtr Application::getPrimaryViewport() const
+	RenderTargetPtr Application::getMainRenderTarget() const
 	{
 		// TODO - Need a way to determine primary viewport!
 		return nullptr;

+ 1 - 1
BansheeEngine/Source/BsCamera.cpp

@@ -380,7 +380,7 @@ namespace BansheeEngine
 		}
 	}
 
-	float CameraBase::getAspectRatio(void) const
+	float CameraBase::getAspectRatio() const
 	{
 		return mAspect;
 	}

+ 38 - 1
BansheeEngine/Source/BsSceneManager.cpp

@@ -3,7 +3,7 @@
 #include "BsRenderable.h"
 #include "BsCamera.h"
 #include "BsLight.h"
-#include "BsDebug.h"
+#include "BsRenderTarget.h"
 
 namespace BansheeEngine
 {
@@ -37,15 +37,21 @@ namespace BansheeEngine
 			return entry.camera == camera;
 		});
 
+		ViewportPtr viewport = camera->getViewport();
 		if (camera->isMain())
 		{
 			if (iterFind == mMainCameras.end())
 				mMainCameras.push_back(mCameras[camera.get()]);
+
+			viewport->setTarget(mMainRT);
 		}
 		else
 		{
 			if (iterFind != mMainCameras.end())
 				mMainCameras.erase(iterFind);
+
+			if (viewport->getTarget() == mMainRT)
+				viewport->setTarget(nullptr);
 		}
 	}
 
@@ -120,6 +126,37 @@ namespace BansheeEngine
 		return SceneCameraData();
 	}
 
+	void SceneManager::setMainRenderTarget(const RenderTargetPtr& rt)
+	{
+		if (mMainRT == rt)
+			return;
+
+		mMainRTResizedConn.disconnect();
+
+		if (rt != nullptr)
+			mMainRTResizedConn = rt->onResized.connect(std::bind(&SceneManager::onMainRenderTargetResized, this));
+		
+		mMainRT = rt;
+
+		auto& rtProps = rt->getProperties();
+		float aspect = rtProps.getWidth() / (float)rtProps.getHeight();
+
+		for (auto& entry : mMainCameras)
+		{
+			entry.camera->getViewport()->setTarget(rt);
+			entry.camera->setAspectRatio(aspect);
+		}
+	}
+
+	void SceneManager::onMainRenderTargetResized()
+	{
+		auto& rtProps = mMainRT->getProperties();
+		float aspect = rtProps.getWidth() / (float)rtProps.getHeight();
+
+		for (auto& entry : mMainCameras)
+			entry.camera->setAspectRatio(aspect);
+	}
+
 	SceneManager& SceneManager::instance()
 	{
 		return static_cast<SceneManager&>(CoreSceneManager::instance());

+ 1 - 1
MBansheeEditor/AboutBox.cs

@@ -34,7 +34,7 @@ namespace BansheeEditor
 
         private void OnInitialize()
         {
-            GUILabel title = new GUILabel(new LocEdString("Banshee Engine"), EditorStyles.TitleLabel);
+            GUILabel title = new GUILabel(new LocEdString("Banshee Engine v0.2"), EditorStyles.TitleLabel);
             GUILabel subTitle = new GUILabel(new LocEdString("A modern open-source game development toolkit"), 
                 EditorStyles.LabelCentered);
             GUILabel license = new GUILabel(new LocEdString(

+ 19 - 0
MBansheeEditor/EditorApplication.cs

@@ -111,6 +111,22 @@ namespace BansheeEditor
             set { Internal_SetIsPaused(value); }
         }
 
+        /// <summary>
+        /// Render target that the main camera in the scene (if any) will render its view to. This generally means the main 
+        /// game window when running standalone, or the Game viewport when running in editor.
+        /// </summary>
+        internal static RenderTarget MainRenderTarget
+        {
+            set
+            {
+                IntPtr rtPtr = IntPtr.Zero;
+                if (value != null)
+                    rtPtr = value.GetCachedPtr();
+
+                Internal_SetMainRenderTarget(rtPtr);
+            }
+        }
+
         /// <summary>
         /// Returns the path where the script compiler is located at.
         /// </summary>
@@ -776,5 +792,8 @@ namespace BansheeEditor
         private static extern void Internal_SetIsPaused(bool value);
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_FrameStep();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMainRenderTarget(IntPtr rendertarget);
     }
 }

+ 106 - 0
MBansheeEditor/GameWindow.cs

@@ -7,6 +7,19 @@ namespace BansheeEditor
     /// </summary>
     public class GameWindow : EditorWindow
     {
+        private const int HeaderHeight = 20;
+        private readonly AspectRatio[] aspectRatios =
+        {
+            new AspectRatio(16, 9), 
+            new AspectRatio(16, 10),
+            new AspectRatio(5, 4),
+            new AspectRatio(4, 3),
+            new AspectRatio(3, 2)
+        };
+
+        private int selectedAspectRatio = 0;
+        private GUIRenderTexture renderTextureGUI;
+
         /// <summary>
         /// Opens the game window.
         /// </summary>
@@ -54,5 +67,98 @@ namespace BansheeEditor
         {
             return new LocEdString("Game");
         }
+
+        private void OnInitialize()
+        {
+            GUILayoutY mainLayout = GUI.AddLayoutY();
+
+            string[] aspectRatioTitles = new string[aspectRatios.Length + 1];
+            aspectRatioTitles[0] = "Free";
+
+            for (int i = 0; i < aspectRatios.Length; i++)
+                aspectRatioTitles[i + 1] = aspectRatios[i].width + ":" + aspectRatios[i].height;
+
+            GUIListBoxField aspectField = new GUIListBoxField(aspectRatioTitles, new LocEdString("Aspect ratio"));
+            aspectField.OnSelectionChanged += OnAspectRatioChanged;
+            
+            GUILayout buttonLayout = mainLayout.AddLayoutX();
+            buttonLayout.AddElement(aspectField);
+            buttonLayout.AddFlexibleSpace();
+
+            renderTextureGUI = new GUIRenderTexture(null);
+
+            GUILayoutY alignLayoutY = mainLayout.AddLayoutY();
+            alignLayoutY.AddFlexibleSpace();
+            GUILayoutX alignLayoutX = alignLayoutY.AddLayoutX();
+            alignLayoutX.AddFlexibleSpace();
+            alignLayoutX.AddElement(renderTextureGUI);
+            alignLayoutX.AddFlexibleSpace();
+            alignLayoutY.AddFlexibleSpace();
+
+            UpdateRenderTexture(Width, Height);
+        }
+
+        /// <summary>
+        /// Creates or rebuilds the main render texture. Should be called at least once before using the
+        /// game window. Should be called whenever the window is resized.
+        /// </summary>
+        /// <param name="width">Width of the scene render target, in pixels.</param>
+        /// <param name="height">Height of the scene render target, in pixels.</param>
+        private void UpdateRenderTexture(int width, int height)
+        {
+            width = MathEx.Max(20, width);
+            height = MathEx.Max(20, height - HeaderHeight);
+
+            if (selectedAspectRatio != 0) // 0 is free aspect
+            {
+                AspectRatio aspectRatio = aspectRatios[selectedAspectRatio - 1];
+
+                float aspectInv = aspectRatio.height/(float)aspectRatio.width;
+                height = MathEx.RoundToInt(width*aspectInv);
+            }
+
+            RenderTexture2D renderTexture = new RenderTexture2D(PixelFormat.R8G8B8A8, width, height) {Priority = 1};
+
+            EditorApplication.MainRenderTarget = renderTexture;
+            renderTextureGUI.RenderTexture = renderTexture;
+        }
+
+        /// <summary>
+        /// Triggered when the user selects a new aspect ratio from the drop down box.
+        /// </summary>
+        /// <param name="idx">Index of the aspect ratio the user selected.</param>
+        private void OnAspectRatioChanged(int idx)
+        {
+            selectedAspectRatio = idx;
+            UpdateRenderTexture(Width, Height);
+        }
+
+        /// <inheritdoc/>
+        protected override void WindowResized(int width, int height)
+        {
+            UpdateRenderTexture(width, height - HeaderHeight);
+
+            base.WindowResized(width, height);
+        }
+
+        /// <summary>
+        /// Camera aspect ratio as numerator and denominator.
+        /// </summary>
+        struct AspectRatio
+        {
+            /// <summary>
+            /// Creates a new object that holds the aspect ratio.
+            /// </summary>
+            /// <param name="width">Numerator of the aspect ratio.</param>
+            /// <param name="height">Denominator of the aspect ratio.</param>
+            public AspectRatio(int width, int height)
+            {
+                this.width = width;
+                this.height = height;
+            }
+
+            public int width;
+            public int height;
+        }
     }
 }

+ 1 - 1
MBansheeEditor/Scene/SceneWindow.cs

@@ -577,7 +577,7 @@ namespace BansheeEditor
 
         /// <summary>
         /// Creates the scene camera and updates the render texture. Should be called at least once before using the
-        /// scene view. Should be called whenver the window is resized.
+        /// scene view. Should be called whenever the window is resized.
         /// </summary>
         /// <param name="width">Width of the scene render target, in pixels.</param>
         /// <param name="height">Height of the scene render target, in pixels.</param>

+ 1 - 0
SBansheeEditor/Include/BsScriptEditorApplication.h

@@ -74,6 +74,7 @@ namespace BansheeEngine
 		static bool internal_GetIsPaused();
 		static void internal_SetIsPaused(bool value);
 		static void internal_FrameStep();
+		static void internal_SetMainRenderTarget(ScriptRenderTarget* renderTarget);
 
 		typedef void(__stdcall *OnProjectLoadedThunkDef)(MonoException**);
 		typedef void(__stdcall *OnStatusBarClickedThunkDef) (MonoException**);

+ 10 - 0
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -16,6 +16,7 @@
 #include "BsScriptManager.h"
 #include "BsGUIMenuBar.h"
 #include "BsPlayInEditorManager.h"
+#include "BsScriptRenderTarget.h"
 
 namespace BansheeEngine
 {
@@ -63,6 +64,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetIsPaused", &ScriptEditorApplication::internal_GetIsPaused);
 		metaData.scriptClass->addInternalCall("Internal_SetIsPaused", &ScriptEditorApplication::internal_SetIsPaused);
 		metaData.scriptClass->addInternalCall("Internal_FrameStep", &ScriptEditorApplication::internal_FrameStep);
+		metaData.scriptClass->addInternalCall("Internal_SetMainRenderTarget", &ScriptEditorApplication::internal_SetMainRenderTarget);
 
 		onProjectLoadedThunk = (OnProjectLoadedThunkDef)metaData.scriptClass->getMethod("Internal_OnProjectLoaded")->getThunk();
 		onStatusBarClickedThunk = (OnStatusBarClickedThunkDef)metaData.scriptClass->getMethod("Internal_OnStatusBarClicked")->getThunk();
@@ -322,4 +324,12 @@ namespace BansheeEngine
 	{
 		PlayInEditorManager::instance().frameStep();
 	}
+
+	void ScriptEditorApplication::internal_SetMainRenderTarget(ScriptRenderTarget* renderTarget)
+	{
+		if (renderTarget == nullptr)
+			SceneManager::instance().setMainRenderTarget(nullptr);
+		else
+			SceneManager::instance().setMainRenderTarget(renderTarget->getNativeValue());
+	}
 }

+ 2 - 14
SBansheeEngine/Source/BsScriptCamera.cpp

@@ -16,13 +16,7 @@ namespace BansheeEngine
 	ScriptCamera::ScriptCamera(MonoObject* managedInstance, const HSceneObject& parentSO)
 		:ScriptObject(managedInstance), mCamera(nullptr), mLastUpdateHash(0)
 	{ 
-		ViewportPtr primaryViewport = gApplication().getPrimaryViewport();
-		RenderTargetPtr target;
-
-		if (primaryViewport != nullptr) // TODO - Normally this should never be null, but right now there is no primary viewport
-			target = primaryViewport->getTarget();
-
-		mCamera = Camera::create(target);
+		mCamera = Camera::create(nullptr);
 		gSceneManager()._registerCamera(mCamera, parentSO);
 	}
 
@@ -397,15 +391,9 @@ namespace BansheeEngine
 	void ScriptCamera::internal_SetRenderTarget(ScriptCamera* instance, ScriptRenderTarget* target)
 	{
 		if (target == nullptr)
-		{
-			ViewportPtr primaryViewport = gApplication().getPrimaryViewport();
-
-			instance->mCamera->getViewport()->setTarget(primaryViewport->getTarget());
-		}
+			instance->mCamera->getViewport()->setTarget(nullptr);
 		else
-		{
 			instance->mCamera->getViewport()->setTarget(target->getNativeValue());
-		}
 	}
 
 	bool ScriptCamera::internal_GetMain(ScriptCamera* instance)

+ 11 - 7
TODO.txt

@@ -19,10 +19,6 @@ Stage 2 polish:
 
 Optional:
  - Start editor in fullscreen
- - When starting drag from hierarchy tree view it tends to select another object (can't repro)
- - Handle seems to lag behind the selected mesh
- - When resizing library window while docked, selection area appears
- - Move all the code files into subfolders so their hierarchy is similar to VS filters
  - Undo/Redo
   - CmdRecordSO records an SO and all its children but it should only record a single SO
   - CmdRecordSO should instead of recording the entire object record a diff
@@ -31,14 +27,20 @@ Optional:
   - Test & finalize undo/redo system
  - Add "focus on object" key (F) - animate it: rotate camera towards then speed towards while zooming in (+ menu entry)
  - Ortographic camera views (+ gizmo in scene view corner that shows camera orientation)
- - MenuBar - will likely need a way to mark elements as disabled when not appropriate (e.g. no "frame selected unless scene is focused")
-   - Likely use a user-provided callback to trigger when populating the menus (I already added a callback to MenuItem, just need to implement it)
- - Need to list all script components in the Components menu
  - Cursors should be replaced with better ones, or at least hot-spots fixed
  - Drag and dropping a prefab onto the scene (or hierarchy) should work the same as with meshes
  - Add tooltips to toolbar items and other buttons with icons
  - Either disable light tool icons before release or make them functional (With gizmos)
 
+ More optional:
+ - When starting drag from hierarchy tree view it tends to select another object (can't repro)
+ - Handle seems to lag behind the selected mesh
+ - When resizing library window while docked, selection area appears
+ - Move all the code files into subfolders so their hierarchy is similar to VS filters
+ - MenuBar - will likely need a way to mark elements as disabled when not appropriate (e.g. no "frame selected unless scene is focused")
+   - Likely use a user-provided callback to trigger when populating the menus (I already added a callback to MenuItem, just need to implement it)
+ - Need to list all script components in the Components menu
+
 Seriously optional:
  - Drag to select in scene view
  - Automatically generate readable inspector names and add [Name] attribute that allows custom naming
@@ -51,6 +53,8 @@ Finalizing:
  - Add copyright notices in all files & change license to GPL
  - Need to generate a proper merge of dev and preview branches
    - Use "git revert --no-commit <COMMITID>..HEAD" to reverse anything on the preview branch that was done after the branch creation, then merge
+ - Test if C++ example still works
+ - Add "example" for using the editor (possibly a video, or a set of screenshots + a data.rar with required resources)
 
 ----------------------------------------------------------------------
 Inspector persistance

+ 3 - 1
TODOExperimentation.txt

@@ -15,7 +15,7 @@ Assign ViewOrigin, PreViewTranslation, TransViewProj
  
 Next week:
  - Modify default shaders so they use deferred base pass and properly render to albedo/normal textures
- - With no light in the scene nothing renders, but it should at least render everything as black (or ambient probably)
+ - With no light in the scene nothing renders, but it should at least render everything with constant ambient (just for debug until I implement GI)
  - Get rid of old code path
 
 Later:
@@ -26,6 +26,7 @@ Later:
  - Too many depth buffers. Main render surface has one, game viewport has one and gbuffer has one
   - Disable main depth buffer by default, disable depth buffer in SceneWindow and GameWindow RT's
   - When rendering to scene target use the gbuffers depth buffer (what if their sizes don't match due to quantization?)
+  - Although having an extra depth buffer won't be the worst thing as even at full HD it's only 8MB
  - Default(dummy) shaders need to render to gbuffer
  - Finish up DefferedPointLightPass by generating cone geometry in shader
  - Modify Light so it generated adequate number of vertices required for cone geometry, without actually creating the cone
@@ -40,6 +41,7 @@ Notes:
  - R11G11B10 and R10G10B10A2 formats haven't been tested
  - Will need to ensure the code works in OpenGL (means porting shaders or building the cross compiler). I cannot delay 
    this as later it will be hard to debug when the pipeline is more complex.
+ - Consider having a debug toggle that makes the gbuffer use floating point storage, to compare quality quickly
 
 Generate different RenderableController for each set of elements
  - Will likely want to rename current LitTexRenderableController to OpaqueSomething