/// <summary>Called when the renderer for this batch has changed.</summary> public void ChangeRenderer(Renderman renderer){ Renderer=renderer; RenderWithCamera(renderer.RenderLayer); // Let the mesh know that the parent changed: Mesh.ChangeParent(); InputMode mode; if(Renderer.RenderingInWorld){ mode=PowerUI.Input.WorldInputMode; }else{ mode=PowerUI.Input.Mode; } bool isPhysics=(mode==InputMode.Physics); if(isPhysics==PhysicsMode){ return; } Mesh.SetPhysicsMode(isPhysics); }
/// <summary>Creates a new UI Batch which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer that will render this batch.</param> public UIBatch(Renderman renderer){ Mesh=new DynamicMesh(this); ChangeRenderer(renderer); }
/// <summary>Creates a new UI Camera which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer that will render this camera.</param> public UICamera(Renderman renderer){ Renderer=renderer; // Create the root gameobject: Gameobject=new GameObject(); // Create camera gameobject: CameraObject=new GameObject(); // Parent the camera to the root: CameraObject.transform.parent=Gameobject.transform; // Add a camera: SourceCamera=CameraObject.AddComponent<Camera>(); // Set the clear flags: SourceCamera.clearFlags=CameraClearFlags.Depth; // Set the culling mask: SourceCamera.cullingMask=(1<<UI.Layer); // Make it forward rendered: SourceCamera.renderingPath=RenderingPath.Forward; // Setup the cameras distance: SetCameraDistance(UI.GetCameraDistance()); // Setup the field of view: SetFieldOfView(UI.GetFieldOfView()); // Parent it to the root: Gameobject.transform.parent=UI.GUINode.transform; // Call the camera creation method: UI.CameraGotCreated(SourceCamera); }
/// <summary>This locates the vertices of this block in world space to the position defined by the given box.</summary> /// <param name="block">The position of the vertices in screen coordinates.</param> /// <param name="renderer">The renderer used when rendering this block.</param> /// <param name="zIndex">The depth of the vertices.</param> private void ApplyVertices(BoxRegion block,Renderman renderer,float zIndex){ // Compute the min/max pixels: Vector3 min=renderer.PixelToWorldUnit(block.X,block.Y,zIndex); Vector3 max=renderer.PixelToWorldUnit(block.MaxX,block.MaxY,zIndex); // Get the 4 corners: VertexTopLeft=min; VertexBottomRight=max; VertexTopRight=new Vector3(max.x,min.y,min.z); VertexBottomLeft=new Vector3(min.x,max.y,min.z); }
/// <summary>Sets the vertices of this box to that specified by the given block /// but clipped to fit within a boundary. At the same time, an image is applied /// to the block and its UV coordinates are also clipped.</summary> /// <param name="boundary">The clipping boundary. The vertices will be clipped to within this.</param> /// <param name="block">The position of the vertices.</param> /// <param name="renderer">The renderer that will render this block.</param> /// <param name="zIndex">The depth of the vertices.</param> /// <param name="imgLocation">The location of the image on the meshes atlas.</param> public UVBlock SetClipped(BoxRegion boundary,BoxRegion block,Renderman renderer,float zIndex,AtlasLocation imgLocation,UVBlock uvBlock){ // Image defines how big we want the image to be in pixels on the screen. // So firstly we need to find the ratio of how scaled our image actually is: float originalHeight=block.Height; float scaleX=imgLocation.Width/block.Width; float scaleY=imgLocation.Height/originalHeight; // We'll need to clip block and make sure the image block is clipped too: float blockX=block.X; float blockY=block.Y; if(block.ClipByChecked(boundary)){ // It actually got clipped - time to do some UV clipping too. // Apply the verts: ApplyVertices(block,renderer,zIndex); block.X-=blockX; block.Y-=blockY; block.MaxX-=blockX; block.MaxY-=blockY; // Flip the gaps (the clipped and now 'missing' sections) - UV's are inverted relative to the vertices. // Bottom gap is just block.Y: float bottomGap=block.Y; // Top gap is the original height - the new maximum; write it to the bottom gap: block.Y=originalHeight-block.MaxY; // Update the top gap: block.MaxY=originalHeight-bottomGap; // Image was in terms of real screen pixels, so now we need to scale it to being in 'actual image' pixels. // From there, the region block.X*=scaleX; block.MaxX*=scaleX; block.Y*=scaleY; block.MaxY*=scaleY; if(uvBlock==null || uvBlock.Shared){ // Create the UV block: uvBlock=new UVBlock(); } // Get the new max/min values: uvBlock.MinX=imgLocation.GetU(block.X+0.2f); uvBlock.MaxX=imgLocation.GetU(block.MaxX-0.2f); uvBlock.MaxY=imgLocation.GetV(block.MaxY-0.2f); uvBlock.MinY=imgLocation.GetV(block.Y+0.2f); }else{ // Apply the verts: ApplyVertices(block,renderer,zIndex); // Globally share the UV! uvBlock=imgLocation; } return uvBlock; }
/// <summary>Sets the vertices of this box to that specified by the given block /// but clipped to fit within a boundary.</summary> /// <param name="boundary">The clipping boundary. The vertices will be clipped to within this.</param> /// <param name="block">The position of the vertices.</param> /// <param name="zIndex">The depth of the vertices.</param> public void SetClipped(BoxRegion boundary,BoxRegion block,Renderman renderer,float zIndex){ // Clipping with no image/ affect on UVs: block.ClipBy(boundary); // And just apply the result: ApplyVertices(block,renderer,zIndex); }
/// <summary>Clears all content from the UI and all WorldUI's. /// Please note that it is safer to set innerHTML to a blank string for a particular UI than calling this.</summary> public static void ClearAll(){ content=null; if(Renderer!=null){ Renderer.Destroy(); Renderer=null; document=null; } Fonts.Clear(); AtlasStacks.Clear(); Http.Clear(); SPA.Clear(); UIAnimation.Clear(); DynamicTexture.RemoveAll(); PowerUI.Input.Clear(); ScreenInfo.Clear(); WorldUI currentWorldUI=FirstWorldUI; while(currentWorldUI!=null){ currentWorldUI.Destroy(); currentWorldUI=currentWorldUI.UIAfter; } LastWorldUI=null; FirstWorldUI=null; }
/// <summary>Used internally - don't call this one. Startup the UI for use in the Editor with AOT Nitro.</summary> /// <param name="nitroAot">True if no gameobject should be generated.</param> public static void Start(bool nitroAot){ if(!Started){ Started=true; // Setup atlas stacks: AtlasStacks.Start(); // Hookup the wrench logging method: Wrench.Log.OnLog+=OnLogMessage; // Hookup the InfiniText logging method: InfiniText.Fonts.OnLog+=OnLogMessage; // Startup the tag handlers: Wrench.TagHandlers.Setup(); // Startup the file protocols (internally also starts up the CSS engine): FileProtocols.Setup(); // Setup the text/language service: if(Variables==null){ Variables=new FullVariableSet(); if(!nitroAot){ // Sign up to the variable on change event - whenever a custom var is changed, we need to refresh the screen. Variables.OnChange+=OnVariableChange; // Ensure that variables is set to whatever the default/current language is. OnLanguageChange(Wrench.Text.Language); // Sign on to the event that occurs when the language changes. Wrench.Text.OnLanguageChanged+=OnLanguageChange; // Sign on to the event that occurs when the gender changes. Wrench.Text.OnGenderChanged+=ResolveAllVariables; } } // Setup the callback queue: Callbacks.Start(); // Setup the character providers (for e.g. Emoji): CharacterProviders.Setup(); Layer=LayerMask.NameToLayer("PowerUI"); if(Layer<0){ // Invalid layer. #if UNITY_EDITOR // Create the new layer now (this will actually be a permanent change): Layer=PowerUI.LayerManager.Add(); #else throw new Exception("Error: PowerUI layer not found. Go to Edit->Project Settings->Tags and add a layer called PowerUI to fix this."+ " Don't forget to make sure it doesn't render with your main camera too!" ); #endif } // Default FPS: SetRate(DefaultRate); #if !NoNitroRuntime // Setup the compiler: NitroCode.Setup(); #endif } if(nitroAot){ return; } GUINode=GameObject.Find("#PowerUI"); if(GUINode==null){ // Not started yet. // Create the UI game object: GUINode=new GameObject(); GUINode.name="#PowerUI"; // Create the camera: CameraNode=new GameObject(); CameraNode.name="Camera"; // Create the updater: GlobalUpdater=GUINode.AddComponent<StandardUpdater>(); // Setup the camera: GUICamera=CameraNode.AddComponent<Camera>(); }else{ // Already started, but we might have updated. if(CameraNode==null){ // This can happen if the PowerUI assembly is actively reloaded (e.g. runtime updates). CameraNode=GameObject.Find("#PowerUI/Camera"); CameraTransform=CameraNode.transform; GUICamera=CameraNode.GetComponent<Camera>(); }else{ // Already started! return; } } // Hide the PowerUI layer from all cameras other than GUICamera: Camera[] cameras=Camera.allCameras; int layerMask=~(1<<UI.Layer); for(int i=0;i<cameras.Length;i++){ // Grab the camera: Camera camera=cameras[i]; // Is it the GUICamera? if(camera==GUICamera){ continue; } // Hide the UI layer from it: camera.cullingMask&=layerMask; } // Setup the transform: CameraTransform=CameraNode.transform; CameraTransform.parent=GUINode.transform; GUICamera.nearClipPlane=0.2f; GUICamera.depth=CameraDepth; GUICamera.clearFlags=CameraClearFlags.Depth; GUICamera.cullingMask=(1<<UI.Layer); GUICamera.renderingPath=RenderingPath.Forward; SetCameraDistance(60f); SetFieldOfView(60f); Renderer=new Renderman(); // Render Mesh.OutputGameObject with the GUI camera: Renderer.RenderWithCamera(UI.Layer); document=Renderer.RootDocument; document.window.top=document.window; // Some overriding default UI settings: document.html.Style.Computed.ChangeTagProperty("color",new PowerUI.Css.Value("#ffffff",PowerUI.Css.ValueType.Color)); document.html.Style.Computed.ChangeTagProperty("font-size",new PowerUI.Css.Value("14px",PowerUI.Css.ValueType.Pixels)); document.body.Style.Computed.ChangeTagProperty("overflow",new PowerUI.Css.Value("hidden hidden",PowerUI.Css.ValueType.Point)); UpdateTextDirection(); // Fire the camera event: CameraGotCreated(GUICamera); }
/// <summary>Creates a new document which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer to use when rendering this document.</param> /// <param name="parentWindow">The window that will become the parent window. Used in e.g. iframes.</param> /// <param name="aot">True if this is a Nitro AOT document (used in the Editor only).</param> public Document(Renderman renderer,Window parentWindow,bool aot):base(){ AotDocument=aot; if(!aot && DefaultStyleSheet==null){ // No default styles loaded yet. Load them now. string styleText=((TextAsset)Resources.Load("style")).text; // Have they applied any overrides? TextAsset extraStyle=Resources.Load("customStyle") as TextAsset; if(extraStyle!=null && extraStyle.text!=null){ styleText+="\n\n"+extraStyle.text; } DefaultStyleSheet=new Css.StyleSheet(this); DefaultStyleSheet.ParseCss(styleText); } #if !NoNitroRuntime // Get the default security domain: SecurityDomain=UI.DefaultSecurityDomain; #endif Renderer=renderer; window=new Window(); window.document=this; window.parent=parentWindow; if(parentWindow!=null){ window.top=parentWindow.top; }else{ window.top=window; } ActiveFonts=new Dictionary<string,DynamicFont>(); Style=new Css.StyleSheet(this); html=new Element(this,null); html.SetTag("html"); string ddbox=""; if(parentWindow==null){ // Dropdown box belongs to the top window only: ddbox="<ddbox></ddbox>"; } html.innerHTML="<body></body>"+ddbox; }
/// <summary>Creates a new document which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer to use when rendering this document.</param> /// <param name="parentWindow">The window that will become the parent window. Used in e.g. iframes.</param> public Document(Renderman renderer,Window parentWindow):this(renderer,parentWindow,false){}
/// <summary>Creates a new document which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer to use when rendering this document.</param> public Document(Renderman renderer):this(renderer,null){}
/// <summary>Destroys this UI. Note that this also occurs if the gameobject is destroyed; /// Just destroying the gameobject or a parent gameObject is all that is required.</summary> public void Destroy(){ if(Renderer==null){ return; } Renderer.Destroy(); Renderer=null; if(gameObject!=null){ GameObject.Destroy(gameObject); gameObject=null; transform=null; } // Remove it from the UI update linked list: if(UIBefore==null){ UI.FirstWorldUI=UIAfter; }else{ UIBefore.UIAfter=UIAfter; } if(UIAfter==null){ UI.LastWorldUI=UIBefore; }else{ UIAfter.UIBefore=UIBefore; } }
/// <summary>Creates a new World UI with the given pixels of space and a given name. /// The gameobjects origin sits at the middle of the UI by default. See <see cref="PowerUI.WorldUI.SetOrigin"/>. /// By default, 100 pixels are 1 world unit. See <see cref="PowerUI.WorldUI.SetResolution"/>.</summary> /// <param name="name">The name for the UI's gameobject.</param> /// <param name="widthPX">The width in pixels of this UI.</param> /// <param name="heightPX">The height in pixels of this UI.</param> public WorldUI(string name,int widthPX,int heightPX){ // Start the UI: UI.Start(); // Create the gameobject: gameObject=new GameObject(); gameObject.name=name; // Grab the name: Name=name; transform=gameObject.transform; Renderer=new Renderman(this); SetDepthResolution(0.01f); // Apply the default scale: transform.localScale=new Vector3(1/100f,1/100f,1f); document=Renderer.RootDocument; // Add it to the UI update linked list: if(UI.FirstWorldUI==null){ UI.FirstWorldUI=UI.LastWorldUI=this; }else{ UIBefore=UI.LastWorldUI; UI.LastWorldUI=UI.LastWorldUI.UIAfter=this; } SetDimensions(widthPX,heightPX); SetInputMode(PowerUI.Input.WorldInputMode); }
/// <summary>Permanently destroys this UI batch.</summary> public void Destroy(){ if(Renderer==null){ return; } if(IsolatedProperty!=null){ IsolatedProperty.Isolated=false; IsolatedProperty.OnBatchDestroy(); IsolatedProperty=null; } if(Mesh!=null){ Mesh.Destroy(); Mesh=null; } Renderer=null; }
/// <summary>Gets a batch from the pool. Null if the pool is empty.</summary> public static UIBatch Get(Renderman renderer){ if(First==null){ return null; } UIBatch result=First; First=result.BatchAfter; result.BatchAfter=null; result.Setup=false; // Show it: #if PRE_UNITY4 result.Mesh.OutputGameObject.active=true; #else result.Mesh.OutputGameObject.SetActive(true); #endif if(result.Renderer!=renderer){ result.ChangeRenderer(renderer); } return result; }