瀏覽代碼

Added a GUITexture class with support for tileable textures

Marko Pintera 12 年之前
父節點
當前提交
26f9639c60

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -156,6 +156,7 @@
     <ClInclude Include="Include\BsGUILayout.h" />
     <ClInclude Include="Include\BsGUILayoutY.h" />
     <ClInclude Include="Include\BsGUISpace.h" />
+    <ClInclude Include="Include\BsGUITexture.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
     <ClInclude Include="Include\BsGUIElement.h" />
     <ClInclude Include="Include\BsGUIElementStyle.h" />
@@ -196,6 +197,7 @@
     <ClCompile Include="Source\BsGUIManager.cpp" />
     <ClCompile Include="Source\BsGUIMaterialManager.cpp" />
     <ClCompile Include="Source\BsGUISkin.cpp" />
+    <ClCompile Include="Source\BsGUITexture.cpp" />
     <ClCompile Include="Source\BsGUIWidget.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrame.cpp" />
     <ClCompile Include="Source\BsImageSprite.cpp" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -144,6 +144,9 @@
     <ClInclude Include="Include\BsGUIButton.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUITexture.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -233,5 +236,8 @@
     <ClCompile Include="Source\BsGUIButton.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUITexture.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 3 - 0
BansheeEngine/Include/BsEngineGUI.h

@@ -12,14 +12,17 @@ namespace BansheeEngine
 		EngineGUI();
 
 		const GUISkin& getSkin() const { return mSkin; }
+		const SpriteTexturePtr& getWindowBgTex() const { return mWindowBgTex; }
 
 	private:
 		GUISkin mSkin;
+		SpriteTexturePtr mWindowBgTex;
 
 		static const CM::String DefaultFontPath;
 		static const UINT32 DefaultFontSize;
 
 		static const CM::String WindowFramePrimaryTexture;
+		static const CM::String WindowBackgroundTexture;
 
 		static const CM::String ButtonNormalTex;
 		static const CM::String ButtonHoverTex;

+ 63 - 0
BansheeEngine/Include/BsGUITexture.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsGUIElement.h"
+#include "BsImageSprite.h"
+
+namespace BansheeEngine
+{
+	enum class GUIImageScaleMode
+	{
+		StretchToFit,
+		ScaleToFit,
+		CropToFit,
+		RepeatToFit
+	};
+
+	class BS_EXPORT GUITexture : public GUIElement
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUITexture* create(GUIWidget& parent, const SpriteTexturePtr& texture, GUIImageScaleMode scale, const GUIElementStyle* style = nullptr);
+		static GUITexture* create(GUIWidget& parent, const SpriteTexturePtr& texture, GUIImageScaleMode scale, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style = nullptr);
+	protected:
+		~GUITexture();
+
+		/**
+		 * @copydoc GUIElement::getNumRenderElements()
+		 */
+		virtual UINT32 getNumRenderElements() const;
+
+		/**
+		 * @copydoc GUIElement::getMaterial()
+		 */
+		virtual const CM::HMaterial& getMaterial(UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::getNumQuads()
+		 */
+		virtual UINT32 getNumQuads(UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::fillBuffer()
+		 */
+		virtual void fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
+			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::updateRenderElementsInternal()
+		 */
+		virtual void updateRenderElementsInternal();
+
+		virtual UINT32 _getOptimalWidth() const;
+		virtual UINT32 _getOptimalHeight() const;
+
+	private:
+		ImageSprite* mImageSprite;
+		IMAGE_SPRITE_DESC mDesc;
+		GUIImageScaleMode mScaleMode;
+
+		GUITexture(GUIWidget& parent, const GUIElementStyle* style, const SpriteTexturePtr& texture, GUIImageScaleMode scale, const GUILayoutOptions& layoutOptions);
+	};
+}

+ 4 - 1
BansheeEngine/Include/BsImageSprite.h

@@ -2,6 +2,7 @@
 
 #include "BsPrerequisites.h"
 #include "BsSprite.h"
+#include "CmVector2.h"
 
 namespace BansheeEngine
 {
@@ -9,7 +10,7 @@ namespace BansheeEngine
 	{
 		IMAGE_SPRITE_DESC()
 			:width(0), height(0), anchor(SA_TopLeft), borderLeft(0), borderRight(0), 
-			borderTop(0), borderBottom(0)
+			borderTop(0), borderBottom(0), uvScale(1.0f, 1.0f), uvOffset(0.0f, 0.0f)
 		{ }
 
 		CM::Int2 offset;
@@ -17,6 +18,8 @@ namespace BansheeEngine
 		UINT32 height;
 		CM::Rect clipRect;
 		SpriteAnchor anchor;
+		CM::Vector2 uvScale;
+		CM::Vector2 uvOffset;
 
 		SpriteTexturePtr texture;
 		UINT32 borderLeft, borderRight, borderTop, borderBottom;

+ 8 - 3
BansheeEngine/Source/BsEngineGUI.cpp

@@ -20,6 +20,7 @@ namespace BansheeEngine
 	const UINT32 EngineGUI::DefaultFontSize = 10;
 
 	const String EngineGUI::WindowFramePrimaryTexture = "C:\\WindowFrame.psd";
+	const String EngineGUI::WindowBackgroundTexture = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\WindowBgTile.psd";
 
 	const String EngineGUI::ButtonNormalTex = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\ButtonNormal.psd";
 	const String EngineGUI::ButtonHoverTex = "C:\\Projects\\BansheeEngine\\Data\\Editor\\Skin\\ButtonHover.psd";
@@ -61,7 +62,7 @@ namespace BansheeEngine
 		windowFrameTex.waitUntilLoaded();
 
 		GUIElementStyle windowFrameStyle;
-		windowFrameStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(windowFrameTex);
+		windowFrameStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(windowFrameTex));
 		windowFrameStyle.border.left = 1;
 		windowFrameStyle.border.right = 1;
 		windowFrameStyle.border.top = 1;
@@ -76,8 +77,8 @@ namespace BansheeEngine
 		buttonHoverTex.waitUntilLoaded();
 
 		GUIElementStyle buttonStyle;
-		buttonStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(buttonNormalTex);
-		buttonStyle.hover.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(buttonHoverTex);
+		buttonStyle.normal.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(buttonNormalTex));
+		buttonStyle.hover.texture = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(buttonHoverTex));
 		buttonStyle.border.left = 5;
 		buttonStyle.border.right = 5;
 		buttonStyle.border.top = 5;
@@ -97,6 +98,10 @@ namespace BansheeEngine
 		buttonStyle.textVertAlign = TVA_Center;
 
 		mSkin.setStyle(GUIButton::getGUITypeName(), buttonStyle);
+
+		// Window background texture
+		HTexture windowBgTexture = static_resource_cast<Texture>(Importer::instance().import(WindowBackgroundTexture));
+		mWindowBgTex = cm_shared_ptr<SpriteTexture, PoolAlloc>(std::cref(windowBgTexture));
 	}
 
 }

+ 14 - 1
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -328,7 +328,20 @@ namespace BansheeEngine
 			if(child.isElement())
 			{
 				child.element->_setWidth(elementWidth);
-				child.element->_setHeight(mOptimalSizes[childIdx].y);
+
+				UINT32 elemHeight = mOptimalSizes[childIdx].y;
+				const GUILayoutOptions& layoutOptions = child.element->_getLayoutOptions();
+				if(!layoutOptions.fixedHeight)
+				{
+					elemHeight = height;
+					if(layoutOptions.minHeight > 0 && elemHeight < layoutOptions.minHeight)
+						elemHeight = layoutOptions.minHeight;
+
+					if(layoutOptions.maxHeight > 0 && elemHeight > layoutOptions.maxHeight)
+						elemHeight = layoutOptions.maxHeight;
+				}
+
+				child.element->_setHeight(elemHeight);
 
 				UINT32 yOffset = (UINT32)Math::CeilToInt((height - child.element->_getHeight()) * 0.5f);
 

+ 13 - 1
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -332,7 +332,19 @@ namespace BansheeEngine
 
 			if(child.isElement())
 			{
-				child.element->_setWidth(mOptimalSizes[childIdx].x);
+				UINT32 elemWidth = mOptimalSizes[childIdx].x;
+				const GUILayoutOptions& layoutOptions = child.element->_getLayoutOptions();
+				if(!layoutOptions.fixedWidth)
+				{
+					elemWidth = width;
+					if(layoutOptions.minWidth > 0 && elemWidth < layoutOptions.minWidth)
+						elemWidth = layoutOptions.minWidth;
+
+					if(layoutOptions.maxWidth > 0 && elemWidth > layoutOptions.maxWidth)
+						elemWidth = layoutOptions.maxWidth;
+				}
+
+				child.element->_setWidth(elemWidth);
 				child.element->_setHeight(elementHeight);
 
 				UINT32 xOffset = (UINT32)Math::CeilToInt((width - child.element->_getHeight()) * 0.5f);

+ 166 - 0
BansheeEngine/Source/BsGUITexture.cpp

@@ -0,0 +1,166 @@
+#include "BsGUITexture.h"
+#include "BsImageSprite.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsSpriteTexture.h"
+#include "BsGUILayoutOptions.h"
+#include "CmTexture.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	const String& GUITexture::getGUITypeName()
+	{
+		static String name = "Texture";
+		return name;
+	}
+
+	GUITexture::GUITexture(GUIWidget& parent, const GUIElementStyle* style, const SpriteTexturePtr& texture, 
+		GUIImageScaleMode scale, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, style, layoutOptions), mScaleMode(scale)
+	{
+		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
+
+		mDesc.texture = texture;
+	
+		mDesc.borderLeft = mStyle->border.left;
+		mDesc.borderRight = mStyle->border.right;
+		mDesc.borderTop = mStyle->border.top;
+		mDesc.borderBottom = mStyle->border.bottom;
+	}
+
+	GUITexture::~GUITexture()
+	{
+		cm_delete<PoolAlloc>(mImageSprite);
+	}
+
+	GUITexture* GUITexture::create(GUIWidget& parent, const SpriteTexturePtr& texture, 
+		GUIImageScaleMode scale, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getGUISkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITexture, PoolAlloc>()) GUITexture(parent, style, texture, scale, getDefaultLayoutOptions(style));
+	}
+
+	GUITexture* GUITexture::create(GUIWidget& parent, const SpriteTexturePtr& texture, 
+		GUIImageScaleMode scale, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getGUISkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITexture, PoolAlloc>()) GUITexture(parent, style, texture, scale, layoutOptions);
+	}
+
+	UINT32 GUITexture::getNumRenderElements() const
+	{
+		return mImageSprite->getNumRenderElements();
+	}
+
+	const HMaterial& GUITexture::getMaterial(UINT32 renderElementIdx) const
+	{
+		return mImageSprite->getMaterial(renderElementIdx);
+	}
+
+	UINT32 GUITexture::getNumQuads(UINT32 renderElementIdx) const
+	{
+		return mImageSprite->getNumQuads(renderElementIdx);
+	}
+
+	void GUITexture::updateRenderElementsInternal()
+	{		
+		mDesc.offset = mOffset;
+		mDesc.width = mWidth;
+		mDesc.height = mHeight;
+		mDesc.clipRect = mClipRect;
+
+		float optimalWidth = 0.0f;
+		float optimalHeight = 0.0f;
+		if(mDesc.texture != nullptr)
+		{
+			optimalWidth = (float)mDesc.texture->getTexture()->getWidth();
+			optimalHeight = (float)mDesc.texture->getTexture()->getHeight();
+		}
+
+		switch (mScaleMode)
+		{
+		case GUIImageScaleMode::StretchToFit:
+			mDesc.uvScale = Vector2(1.0f, 1.0f);
+			break;
+		case GUIImageScaleMode::ScaleToFit:
+			mDesc.uvScale.x = optimalWidth / mWidth;
+			mDesc.uvScale.y = optimalHeight / mHeight;
+
+			if(mDesc.uvScale.x < mDesc.uvScale.y)
+			{
+				mDesc.uvScale.x = 1.0f;
+				mDesc.uvScale.y = (mWidth * (optimalHeight / optimalWidth)) / mHeight;
+			}
+			else
+			{
+				mDesc.uvScale.x = (mHeight * (optimalWidth / optimalHeight)) / mWidth;
+				mDesc.uvScale.y = 1.0f;
+			}
+
+			break;
+		case GUIImageScaleMode::CropToFit:
+			mDesc.uvScale.x = optimalWidth / mWidth;
+			mDesc.uvScale.y = optimalHeight / mHeight;
+
+			if(mDesc.uvScale.x < mDesc.uvScale.y)
+			{
+				mDesc.uvScale.x = (mHeight * (optimalWidth / optimalHeight)) / mWidth;
+				mDesc.uvScale.y = 1.0f;
+			}
+			else
+			{
+				mDesc.uvScale.x = 1.0f;
+				mDesc.uvScale.y = (mWidth * (optimalHeight / optimalWidth)) / mHeight;
+			}
+
+			break;
+		case GUIImageScaleMode::RepeatToFit:
+			mDesc.uvScale.x = mWidth / optimalWidth;
+			mDesc.uvScale.y = mHeight / optimalHeight;
+			break;
+		default:
+			break;
+		}
+
+		mImageSprite->update(mDesc);
+		mBounds = mImageSprite->getBounds();
+	}
+
+	UINT32 GUITexture::_getOptimalWidth() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getWidth();
+		}
+
+		return 0;
+	}
+
+	UINT32 GUITexture::_getOptimalHeight() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getHeight();
+		}
+
+		return 0;
+	}
+
+	void GUITexture::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{
+		mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
+	}
+}

+ 2 - 2
BansheeEngine/Source/BsImageSprite.cpp

@@ -72,8 +72,8 @@ namespace BansheeEngine
 		}
 
 		Int2 offset = getAnchorOffset(desc.anchor, desc.width, desc.height);
-		Vector2 uvOffset(0.0f, 0.0f);
-		Vector2 uvScale(1.0f, 1.0f);
+		Vector2 uvOffset = desc.uvOffset;
+		Vector2 uvScale = desc.uvScale;
 		
 		if(useScale9Grid)
 		{

+ 5 - 1
CamelotClient/CmEditorWindow.cpp

@@ -8,6 +8,7 @@
 #include "BsGUILabel.h"
 #include "BsGUIWindowFrame.h"
 #include "BsGUIButton.h"
+#include "BsGUITexture.h"
 #include "BsGUISkin.h"
 #include "BsGUILayout.h"
 #include "BsOverlayManager.h"
@@ -64,10 +65,13 @@ namespace BansheeEditor
 
 		//GUIFixedSpace& space = otherLayout.addSpace(10); // Due to bug in MSVC compiler I need to store return value
 		//GUIFlexibleSpace& space3 = otherLayout.addFlexibleSpace();
-		otherLayout.addElement(GUIWindowFrame::create(*mGUI, GUILayoutOptions::fixed(100, 100)));
+		//otherLayout.addElement(GUIWindowFrame::create(*mGUI, GUILayoutOptions::fixed(100, 100)));
 		//GUIFixedSpace& space2 = otherLayout.addSpace(10);
 		otherLayout.addElement(GUIButton::create(*mGUI, "Test"));
 		//otherLayout.addElement(GUIWindowFrame::create(*mGUI));
+		
+		GUIArea* backgroundArea = GUIArea::create(*mGUI, 0, 0, 0, 0, 2000);
+		backgroundArea->getLayout().addElement(GUITexture::create(*mGUI, EngineGUI::instance().getWindowBgTex(), GUIImageScaleMode::RepeatToFit, GUILayoutOptions::expandableXY()));
 	}
 
 	EditorWindow::~EditorWindow()

+ 3 - 7
CamelotCore/Source/CmSceneObject.cpp

@@ -226,15 +226,11 @@ namespace CamelotFramework
 	void SceneObject::markTfrmDirty() const
 	{
 		mIsCachedLocalTfrmUpToDate = false;
+		mIsCachedWorldTfrmUpToDate = false;
 
-		if(mIsCachedWorldTfrmUpToDate) // If it's already marked as dirty, no point is recursing the hierarchy again
+		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
 		{
-			mIsCachedWorldTfrmUpToDate = false;
-
-			for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
-			{
-				(*iter)->markTfrmDirty();
-			}
+			(*iter)->markTfrmDirty();
 		}
 	}
 

+ 36 - 0
Notes.txt

@@ -45,6 +45,42 @@ Potential optimizations:
 ----------------------------------------------------------------------------------------------
 More detailed thought out system descriptions:
 
+<<<<SceneManager/SceneObject>>>>
+Two major parts:
+SceneObject update:
+ - Just takes care of updating world/local transforms
+  - Transforms are only updated when requested
+ - Marking a transform dirty will also mark it dirty in the SceneManager
+ - Only SceneObjects that have an Interactable type component (Renderer, Collider, etc.) will reside in SceneManager
+
+SceneManager maintains a list of world matrices, bounds and primitives
+ - We can query them for various collision (ray, frustum, etc.)
+ - Internally they likely use BVH or oct-tree
+ - Uses a binary-tree (unlike SceneObject hierarchy which is n-ary), which is laid out neatly in memory for quick traversal.
+   - What happens when object is removed/added? Tree keeps on growing, empty nodes have their space always reserved. Possibly add a method that can reduce tree size when enough empty nodes exist. 
+     Adding a node might increase tree size which will involve a memcpy while we increase the size.
+ - (Updating an object is SceneManager should optionally transfer its world matrix to its owner object as well)
+
+This separation should work fine, as scripts requesting transforms is unlikely to be something that done often. Or not nearly as much as frustum culling and raycasting will be.
+
+How will Physics update objects (and when?)
+ - It's unlikely there will be a massive amount of rigidbodies in the scene, so updating them should not be a huge matter of performance. 
+   Calling setTransform after physics simulation update (and before SceneManager update) should work fine.
+
+Updating children dirty whenever setPos/rot/tfrm is called is potentially slow. Can it be avoided?
+ - Then again such updates will only be done from the simulation thread, usually from scripts so its unlikely they will be many of them
+
+Do I separate SceneObject and Transform?
+ - I don' think I need to
+
+Make sure to let the user know SceneManager only gets updated after simulation, so changes to objects wont be applied right away. 
+(See that he doesn't transform something and call Raycast just so it fails)
+ - It is unlikely functionality when query results are needed right after transform will be used much. 
+   So it is acceptable to implement it like this. We might add SceneManager::forceUpdate method in case it is not acceptable.
+
+!!!BUG!!! - When I change parent I don't update individual local position/rotation/scale on scene object
+ - Also I don't have a way of setting world pos/rot directly
+
 <<<<Reducing render state changes>>>>
  - Transparent objects get sorted back to front, always
  - Opaque objects I can choose between front to back, no sort or back to front