ZScript "Standard Library" - Brainstorming

Post your example zscripts/ACS scripts/etc here.
Forum rules
The Projects forums are only for projects. If you are asking questions about a project, either find that project's thread, or start a thread in the General section instead.

Got a cool project idea but nothing else? Put it in the project ideas thread instead!

Projects for any Doom-based engine (especially 3DGE) are perfectly acceptable here too.

Please read the full rules for more details.
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

ZZYZX wrote:Hello so I made this https://github.com/jewalky/zgui

You can use it like so:

Code: Select all

class Doom3Element : Element
{
	TextureID mMouseCursor;
	
	override void OnCreate()
	{
		mRect = Rect.FromXYWH(0, 0, Screen.GetWidth(), Screen.GetHeight());
		mScale = 2;
	
		mMouseCursor = TexMan.CheckForTexture("graphics/D3Mouse.png", TexMan.Type_Any);
		AddChild(new('Doom3Doom').Init());
	}

	override void Draw()
	{
		Drawer d = GetDrawer();
		vector2 mouse = GetMouseXY();
		d.DrawTexture(mMouseCursor, false, mouse.x, mouse.y);
	}
}

class Doom3Menu : ListMenu
{
	Element e;

	override void Init(Menu parent, ListMenuDescriptor desc)
	{
		Super.Init(parent, desc);
		DontDim = true;
		
		e = new('Doom3Element').Init();
		e.OnCreate();
	}

	override bool OnUIEvent(UiEvent ev)
	{
		if (e) return e.OnProcessUi(ev);
		return false;
	}
	
	override bool OnInputEvent(InputEvent ev)
	{
		if (e) return e.OnProcessInput(ev);
		return false;
	}

	override void Drawer()
	{
		if (e) e.OnDraw();
	}

	override void Ticker()
	{
		if (e) e.OnTick();
	}
}
What it does: it allows you to have a WM-style hierarchy of Elements with automatic scaling, clipping, text align and wrapping.
Each Element can have a position inside the parent Element (mRect field), and scale of child elements (mScale field). The scale also affects perceived width/height of the Element (the client rect).
Topmost Element has mParent=null which means that it's using the real screen coordinates and scale of 1.0.

Functions exposed to the user are these:

Code: Select all

	virtual void OnCreate()
	{
		
	}
	
	virtual bool Process(ElementEvent ev)
	{
		return false;
	}
	
	virtual void Tick()
	{
		//
	}
	
	virtual void Draw()
	{
		//
	}
The preferred way of constructing Elements is calling "new ('Element').Init()", for example. That automatically sets certain fields to the initial values, otherwise you might end up with a wild null pointer error.
Unobviously enough, OnCreate is actually called when you add a child element, on the child. For a root element, you have to call OnCreate manually or it won't get a chance to initialize.

You can draw things by receiving an instance of Drawer class:

Code: Select all

Drawer d = GetDrawer();
And then using it's methods to draw things as opposed to directly using Screen.
Alternatively, you can instantiate a drawer by using

Code: Select all

Drawer d = Drawer.Create(self);
or even

Code: Select all

Drawer d = Drawer.CreateMod('Drawer', self);
The latter option is for custom Drawer classes if you want to do some fancy stuff.

Also, the system doesn't care about your input/rendering backend — you can provide OnProcessUi/OnProcessInput/OnTick by an event handler, and OnDraw by DrawPowerup (I blame Graf for consistently removing any ways to draw anything while playing the game and providing no alternative), or you can do all this in a menu.
The system is able to receive mouse input both from UiEvent and InputEvent — the elements will receive an uniform ElementEvent anyway. This means that it transparently supports both enabled and disabled mouse in menus.
ElementEvent is the same as UiEvent, except there is no information about pressed modifier keys (yet?)
Also, you can receive the current mouse coordinates relative to the current element (including all scaling) by using GetMouseXY() — which returns a vector2, see the example.

What it doesn't have yet is setting arbitrary clipping values in a Drawer. That'll be added eventually.
Added, actually.

The system is kinda new and untested, pls report bugs if anyone is actually going to use this.

ZZYZX: That needs to be on its own thread, and a simple user-runnable example please! Sounds awesome.
User avatar
Major Cooke
Posts: 8196
Joined: Sun Jan 28, 2007 3:55 pm
Preferred Pronouns: He/Him
Location: QZDoom Maintenance Team

Re: ZScript "Standard Library" - Brainstorming

Post by Major Cooke »

No it doesn't need it's own thread, this is perfect. He's giving it over here for ease of use in a library.
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

Just an idea at this point, jotting it down so it doesn't get lost...

A replacement for ACS HudMessageOnActor.

Code: Select all

Vector2 ProjectToScreen(Vector3 worldPos)
{
    Vector2 screenPos;
    return screenPos;
}

struct FDeproject
{
    Vector3 worldPos;
    Vector3 worldAng;
}

FDeproject DeprojectToWorld(Vector2 screenPos)
{
    FDeproject w;
    return w;
}
Method naming convention borrowed from Unreal Engine

These would most probably be status bar methods, except there's one problem... status bars are UI and DeprojectToWorld needs to be play because it will affect the world. I'm not sure how to solve this yet.

Also the pinhole projection calculations aren't in yet, I just prototyped these quickly in Notepad++ :V
User avatar
ZZYZX
 
 
Posts: 1384
Joined: Sun Oct 14, 2012 1:43 am
Location: Ukraine

Re: ZScript "Standard Library" - Brainstorming

Post by ZZYZX »

I think this should be native and based on the active renderer, in Screen or StatusBar class. Probably sbar, since active statusbar affects viewport size.
Also, it probably should return a vector3 with -1..1 coordinates where 0 is the center of the screen, and Z coordinate would be the distance from the screen plane.

Might try making a PR later, but better if done by someone who knows the renderer (dp, for example).

We don't want to repeat the story with ACS misdetecting the renderer or proportions.
Last edited by ZZYZX on Wed Mar 29, 2017 1:18 am, edited 1 time in total.
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

ZZYZX wrote:I think this should be native and based on the active renderer
Agreed. The projection is vastly different between software Y-sheering and real 3D in OpenGL, so the engine is a better place to take care of that. There's also the issue of making the projections look correct in VR headsets, which user scripting just cannot resolve. It has to be done at the drawer-level.
User avatar
ZZYZX
 
 
Posts: 1384
Joined: Sun Oct 14, 2012 1:43 am
Location: Ukraine

Re: ZScript "Standard Library" - Brainstorming

Post by ZZYZX »

There's also the polygonal software renderer, now officially in GZDoom :D
Which detects as software, but is actually true 3D.

Also, for "deprojection" the renderer needs to return two values: horizontal and vertical effective FOV in relation to the visible area.
Knowing horizontal and vertical FOV, you can interpolate angles and calculate direction based on coordinates.

https://www.youtube.com/watch?v=ZB3Gj7CGjF8
Here I'm using GetScreenWidth/GetScreenHeight for aspect ratio, and assume vertical FOV to be ~67.5 (+33.75 above center, -33.75 below center), and 67.5*aspect for horizontal FOV (-45 to +45 for 4:3, -60 to +60 for 16:9).

Otherwise deprojection might as well simply return a vector3 pointing in the right direction based on the above data, this wouldn't be as universal (screen size wise), but works in most cases.
Just to clarify, I want the stuff to be returned as angles and -1..1 because this way it works with any size (of the same aspect ratio that is).
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

Crazy idea (and posting because I /do/ plan to use ZGUI:

Use Fill at the low level to create vector-ey widgets that can be exposed to high level methods. >8D Or event fonts?!?! OMG


EDIT: Ahhh Fill is a BaseStatusBar method, not Screen. Well scratch that idea then

Would have loved to at least make buttons and sliders and simple widgets without providing rasterized graphics at any rate
User avatar
ZZYZX
 
 
Posts: 1384
Joined: Sun Oct 14, 2012 1:43 am
Location: Ukraine

Re: ZScript "Standard Library" - Brainstorming

Post by ZZYZX »

This is possible using Dim, I believe.
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

I just remembered, there's Screen.Clear too. Maybe I'll try make some buttons with that.
User avatar
Nash
 
 
Posts: 17465
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia

Re: ZScript "Standard Library" - Brainstorming

Post by Nash »

ZZYZX can you make a runnable example of ZUI please... some people learn faster by runnable examples (I learned most of my EventHandler stuff from your ZSDemo.pk3 you posted).
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
Posts: 49183
Joined: Sat Jul 19, 2003 10:19 am
Location: Germany

Re: ZScript "Standard Library" - Brainstorming

Post by Graf Zahl »

ZZYZX wrote:This is possible using Dim, I believe.

Fill is just a wrapper around Dim with coordinate transformation.
User avatar
ZZYZX
 
 
Posts: 1384
Joined: Sun Oct 14, 2012 1:43 am
Location: Ukraine

Re: ZScript "Standard Library" - Brainstorming

Post by ZZYZX »

I'll make a runnable example as soon as I understand why the hell half of the events goes to MenuEvent instead of OnUIEvent.
Actually, I might make it even without that, but it's really bad for certain complex UI things.

edit: okkkk take this http://www.mediafire.com/file/5bu5vclsl ... xample.pk3
Screenshot: http://imgur.com/a/lwf9A
This is a simple example of implementing your own GUI from scratch.
The scale of everything is specified in OnCreate of MMenuElement.
Note: use 2.5 devbuild.

Also, unless it's possible to prevent translation of UiEvents into MenuEvents, I'll probably have to add another input function to Element that'd take menu events and pretend they are instant UI keydown+keyup of specified key.

Oh, and if it looks too complicated or something...
Well, if you are simply making a statusbar (or RenderOverlay), you need only the first two calls from MMenuElement.OnCreate (set mRect, set mScale) and a Draw method. That's all.
I mainly split it in three classes so that I could have mouse, complex input and fancy hover animation.

Regarding mouse input: mouse WITHOUT m_use_mouse is only possible in fullscreen and is kinda half supported.
However, there's a large problem that I can't get my own mouse cursor to draw if m_use_mouse is enabled (it always draws the default Windows/SDL cursor). How to fix that? @Graf?
User avatar
Graf Zahl
Lead GZDoom+Raze Developer
Lead GZDoom+Raze Developer
Posts: 49183
Joined: Sat Jul 19, 2003 10:19 am
Location: Germany

Re: ZScript "Standard Library" - Brainstorming

Post by Graf Zahl »

M_Responder does indeed perform some preprocessing on events and sends them as MenuEvents to the current menu.
dpJudas
 
 
Posts: 3134
Joined: Sat May 28, 2016 1:01 pm

Re: ZScript "Standard Library" - Brainstorming

Post by dpJudas »

ZZYZX wrote:I think this should be native and based on the active renderer, in Screen or StatusBar class. Probably sbar, since active statusbar affects viewport size.
Also, it probably should return a vector3 with -1..1 coordinates where 0 is the center of the screen, and Z coordinate would be the distance from the screen plane.
Assuming you're talking about eye/view space, the Z coordinate would always be 1. Getting from there to world space would mean multiplying with the inverse of the world to eye normal matrix (just doing the inverse rotate from Viewpoint is probably easier). To get the point the user clicked at requires a follow up ray shooting.

The code required to do the unproject and project looks like this (in pseudo C++):

Code: Select all

Vector3 DeprojectScreenToView(float tanHalfFovy, Vector2 viewportSize, Vector2 screenPos)
{
	float invFocalLenX = tanHalfFovy * viewportSize.X / viewportSize.Y;
	float invFocalLenY = tanHalfFovy;
	Vector2 screenToViewA(2.0f * invFocalLenX, 2.0f * invFocalLenY);
	Vector2 screenToViewB(-invFocalLenX, -invFocalLenY);
	return Vector3(screenToViewA * screenPos + screenToViewB, 1.0f);
}

Vector2 ProjectViewToScreen(float tanHalfFovy, Vector2 viewportSize, Vector3 viewPos)
{
	float focalLenY = 1.0f / tanHalfFovy;
	float focalLenX = focalLenY * viewportSize.Y / viewportSize.X;
	return Vector2(viewPos.X / viewPos.Z * focalLenX + viewportSize.X * 0.5f, viewPos.Y / viewPos.Z * focalLenY + viewportSize.Y * 0.5f);
}
The tricky part is finding tanHalfFovy and viewportSize. Theoretically, the math should be identical for all three renderers. The math used the poly renderer is as follows:

Code: Select all

float GetTanHalfFovY()
{
	float ratio = Viewwindow.WidescreenRatio;
	float fovratio = (Viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio;
	float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(Viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees);
	return tan(fovy * M_PI / 360.0f);
}
I'm not entirely sure if that math covers all use cases, though. The code calculating it in the GL renderer is somewhat scattered and suffering from old times where it shared variables with the software renderer. In the SSAO pass I cheated and grabbed it out of the projection matrix:

Code: Select all

float GetTanHalfFovY()
{
	return 1.0f / gl_RenderState.mProjectionMatrix.get()[5];
}
As for calculating the viewport size, I am not sure what is the best way to deal with that part. Maybe querying the renderer for those things might be better, but I'm not sure if it is needed since I'm assuming the goal here is to get some screen coordinates in a virtual coordinate space. Last, there's also the problem with the VR support. I'm not sure what to do there as there's two eyes, with two independent world to eye matrices.
User avatar
ZZYZX
 
 
Posts: 1384
Joined: Sun Oct 14, 2012 1:43 am
Location: Ukraine

Re: ZScript "Standard Library" - Brainstorming

Post by ZZYZX »

dpJudas wrote:Assuming you're talking about eye/view space, the Z coordinate would always be 1. Getting from there to world space would mean multiplying with the inverse of the world to eye normal matrix (just doing the inverse rotate from Viewpoint is probably easier). To get the point the user clicked at requires a follow up ray shooting.
I'm actually talking about whatever coordinate that often gets stored in the shaders as W and means distance from screen plane to the point (it's just that there is no vector4 IIRC and just easier to store it as Z, as Z is unused otherwise).
That's needed so that user code can take the -1..1 coordinates and draw with corresponding virtual screen size to scale their pictures according to the distance to the point.
dpJudas wrote:As for calculating the viewport size, I am not sure what is the best way to deal with that part. Maybe querying the renderer for those things might be better, but I'm not sure if it is needed since I'm assuming the goal here is to get some screen coordinates in a virtual coordinate space. Last, there's also the problem with the VR support. I'm not sure what to do there as there's two eyes, with two independent world to eye matrices.
Well that's why I said that it should be in the engine. So that the current active renderer can decide what to return, based on current settings as well — any manual code would be completely forward incompatible and cover it only partially.
dpJudas wrote:Last, there's also the problem with the VR support. I'm not sure what to do there as there's two eyes, with two independent world to eye matrices.
What does the regular HUD code do in this case? Does it render twice? It's not supposed to work outside of the drawing hooks, so maybe just use whatever matrix that's active in currently drawn half? (this may as well make zero sense because I don't know how VR works really)

Return to “Script Library”