Page 1 of 1

[Zscript] DrawTexture question

Posted: Wed Sep 20, 2017 9:47 pm
by GiveOneMoreChance
Hello! I try to make when you look at specific actors (can more five actors on screen) -> draw images on screen.
I try use RenderOverlay but i doesn't know how to access to actors positions.
It's possible convert object scopes from play to ui ?
Current code:

Code: Select all

Class TestDrawerClass : Actor
{
	TextureId tex;
	Default
	{
		Health 	0x7FFFFFFF;
		Mass 	0x7FFFFFFF;
		Renderstyle "Add";
		Scale 1.25;
		radius 32;
		height 32;
	}

	override void BeginPlay() 
	{
		tex = TexMan.CheckForTexture("ALERT", TexMan.Type_Any);
		super.BeginPlay();
	}	
	
	void DrawOnScreen()
	{
               //Taken from HudMessageOnActor, broken
		double x,y,vang,angle,dist;
	
		Vector2 selfPos = (self.pos.x,self.pos.y);
		Vector2 playerPos = (players[0].mo.pos.x,players[0].mo.pos.y);
		
		x = selfPos.x - playerPos.x;
		y = selfPos.y - playerPos.y;
		
		vang = atan2(x,y);
		angle = (vang - players[0].mo.angle + 1.0) % 1.0;
		
		if(((vang+0.125)%0.5) > 0.25) dist = y * sin(vang);
		else dist = x * cos(vang);
		
		
		if ((angle < 0.23 || angle < 0.85) && dist < 2048)
		{
			if (players[0].mo.pitch >= -0.5 && players[0].mo.pitch <= 0.5)
			{
				Screen.DrawTexture(tex,false,x,y); //Error
			}
		}
		
	}
	
	override void Tick()
	{
		Super.Tick();
		
		if(!master)
			{ Destroy(); } 
			
		if (master)
			{
			DrawOnScreen();
			if(radius != master.radius || height != master.height)
				{ A_SetSize(master.radius+2, master.height/8);  } 
				
			A_Warp(AAPTR_MASTER,1,0,master.height - self.height,0,WARPF_INTERPOLATE|WARPF_COPYINTERPOLATION|WARPF_NOCHECKPOSITION);
			}
	}
	
	States
	{
		Spawn:
			TNT1 A 1;
			loop;
		Death:
			TNT1 A 1;
			stop;
	}
}

Re: [Zscript] DrawTexture question

Posted: Wed Sep 20, 2017 11:14 pm
by AFADoomer
Not sure if this will help - This code doesn't quite match what you are doing (it's written using a status bar to draw on screen instead of RenderOverlay), and I think this is more complicated than what you are wanting, but...

I created a custom player class that uses a ThinkerIterator to find all of the actors in range that meet certain criteria, then pushes those actors to an array. Then the status bar reads that array's contents from the player and does some sketchy HudMessageOnActor-derived math to calculate screen coordinates from the actor's world coordinates, and handles drawing a frame around the actor on screen.

Download here.

ZScript here... It's a bit messy, and needs some optimization. But it works.

Code: Select all

version "2.5"

struct Coords
{
    double x;
    double y;
    double left;
    double right;
    double top;
    double bottom;
}

class TestPlayer : DoomPlayer
{
    Array<Actor> ScannableThings;

    override void Tick()
    {
        int Range = 1024;
        int index;

        ScannableThings.Clear();

        ThinkerIterator Finder = ThinkerIterator.Create("Actor");
        Actor mo;

        while ( mo = Actor(Finder.Next()) )
        {
            target = mo;

            if (
                mo == self ||
                mo.health <= 0 || 
                mo.bDormant || 
                mo.bInvisible ||
                (mo.bNoBlockMap && !mo.bMissile) ||
                Distance3d(mo) > Range ||
                !IsVisible(mo, false)
            ) { continue; }

            if (ScannableThings.Find(mo) == ScannableThings.Size())
            {
                ScannableThings.Push(mo);
            }
        }

        Super.Tick();
    }
}

class TestStatusBar : DoomStatusBar
{
    double ScreenHeight, ScreenWidth, FOVfactor;
    Vector2 hudscale;
    int hudstate;

    HUDFont mSmallFont;

    override void Init()
    {
        Super.Init();

        SetSize(32, 320, 200);

        Font fnt = "SMALLFONT";
        mSmallFont = HUDFont.Create(fnt, 1);
    }

    override void NewGame ()
    {
        Super.NewGame();
    }

    override void Tick()
    {
        Super.Tick();
    }

    override void Draw (int state, double TicFrac)
    {
        Super.Draw (state, TicFrac);

        hudstate = state;

        BeginHUD(1, False); // Urgh...  Force overlays to always draw with fullscreen coords and under other bars.  
            DrawOverlays();
        BeginHUD(1, True);

        if (state == HUD_StatusBar)
        {
            BeginStatusBar(False);
            DrawMainBar(TicFrac);
        }
        else if (state == HUD_Fullscreen)
        {
            BeginHUD(1, False);
            DrawFullScreenStuff();
        }
    }

    void DrawOverlays()
    {
        bool draw;
        Coords ScreenCoords;

        hudscale = GetHUDScale();

        FOVFactor = CPlayer.FOV / 90;

        ScreenHeight = double(Screen.GetHeight()) / hudscale.y;
        ScreenWidth = ScreenHeight * 1.333; // Use standard ratio for handling offsets

        for (int i = 0; i < TestPlayer(CPlayer.mo).ScannableThings.Size(); i++)
        {
            actor mo = TestPlayer(CPlayer.mo).ScannableThings[i];

            [draw, ScreenCoords.x, ScreenCoords.y, ScreenCoords.left, ScreenCoords.right, ScreenCoords.top, ScreenCoords.bottom] = GetTargetScreenCoords(CPlayer.mo, mo, 0);

            if (draw)
            {
                DrawFrame(CPlayer.mo, mo, ScreenCoords);
            }
        }
    }

    void DrawFrame(Actor origin, Actor mo, Coords ScreenCoords)
    {
        if (!origin || !mo) { return; }
        if (mo.health <= 0) { return; }
        if (origin.Distance3d(mo) - origin.radius - mo.radius < 32) { return; }

        double drawscale = ((ScreenHeight / 2) / origin.Distance3d(mo)) / FOVFactor;

        String BaseImage = "Scan";

        readonly<Actor> defmobj = GetDefaultByType(mo.GetClass());
        mo.bMissile |= defmobj.bMissile;

        if ((mo.bIsMonster && !mo.bFriendly) || mo.bShootable || mo.bMissile) { BaseImage = "ScanA"; }
        else if (mo is "Inventory") { BaseImage = "ScanB"; }

        double alpha = drawscale * drawscale * drawscale * 2;

        DrawString(mSmallFont, "+", (ScreenCoords.x, ScreenCoords.y), DI_ITEM_CENTER, Font.CR_PURPLE, 1.0);

        DrawImage(BaseImage .. "_TL", (ScreenCoords.left, ScreenCoords.top), DI_ITEM_RIGHT_BOTTOM, alpha, (-1, -1));
        DrawImage(BaseImage .. "_TR", (ScreenCoords.right, ScreenCoords.top), DI_ITEM_LEFT_BOTTOM, alpha, (-1, -1));
        DrawImage(BaseImage .. "_BL", (ScreenCoords.left, ScreenCoords.bottom), DI_ITEM_RIGHT_TOP, alpha, (-1, -1));
        DrawImage(BaseImage .. "_BR", (ScreenCoords.right, ScreenCoords.bottom), DI_ITEM_LEFT_TOP, alpha, (-1, -1));
    }

    bool, double, double, double, double, double, double GetTargetScreenCoords(Actor origin, Actor target, double zOffset = 0)
    {
        if (!origin || !target) { return false, 0, 0, 0, 0, 0, 0; }

        double x, y, left, right, top, bottom;

        double angle = origin.AngleTo(target) - origin.angle;
        double dist = origin.Distance2d(target);

        if (abs(origin.pitch) <= 50)
        {
            double radius = target.radius;
            double height = target.height > 32 ? target.height : 32;
            if (target.bMissile) { height = 0; }

            if (zOffset == 0) { zOffset = height / 2; }

            double viewpitch = VectorAngle(dist, target.pos.z + zOffset - (origin.pos.z + origin.player.viewheight));
            viewpitch = viewpitch + origin.pitch; 
            viewpitch *= 1.5 / FOVFactor;    

            if ((ScreenWidth / 2) * sin(angle) != 0 && cos(angle) != 0 && (ScreenWidth / 2) * sin(viewpitch) != 0 && cos(viewpitch) != 0)
            {
                double xscale = ((ScreenWidth / 2) / origin.Distance3d(target)) / FOVFactor;
                double yscale = ((ScreenHeight / 2) / origin.Distance3d(target)) / FOVFactor;

                x = (Screen.GetWidth() / hudscale.x) / 2 - ((ScreenWidth / 2) * sin(angle) / cos(angle)) / FOVFactor;
                y = ScreenHeight / 2 - ((ScreenHeight / 2) * sin(viewpitch) / cos(viewpitch));

                if (screenblocks <= 10) { y -= RelTop; }

                left = x - radius * xscale;
                right = x + radius * xscale;
                top = y - (height / 2 + 8) * yscale;
                bottom = y + (height / 2 + 8) * yscale;

                x = clamp(x, 0, Screen.GetWidth());
                y = clamp(y, 0, Screen.GetHeight());
                left = clamp(left, 0, Screen.GetWidth());
                right = clamp(right, 0, Screen.GetWidth());
                top = clamp(top, 0, Screen.GetHeight());
                bottom = clamp(bottom, 0, Screen.GetHeight());

                return true, x, y, left, right, top, bottom;
            }
        }
        return false, x, y, left, right, top, bottom;
    }
}

Re: [Zscript] DrawTexture question

Posted: Thu Sep 21, 2017 1:16 am
by Nash
Please note that using this technique will result in completely broken visuals in VR headsets; what's needed is some native project to screen/deproject to world stuff the engine needs to natively support. There have been talks about this stuff in the ZScript library brainstorming thread but no work has been done yet.

Re: [Zscript] DrawTexture question

Posted: Thu Sep 21, 2017 1:37 am
by GiveOneMoreChance
AFADoomer wrote:Not sure if this will help - This code doesn't quite match what you are doing (it's written using a status bar to draw on screen instead of RenderOverlay), and I think this is more complicated than what you are wanting, but...

I created a custom player class that uses a ThinkerIterator to find all of the actors in range that meet certain criteria, then pushes those actors to an array. Then the status bar reads that array's contents from the player and does some sketchy HudMessageOnActor-derived math to calculate screen coordinates from the actor's world coordinates, and handles drawing a frame around the actor on screen.

Download here.

ZScript here... It's a bit messy, and needs some optimization. But it works.

Code: Select all

version "2.5"

struct Coords
{
    double x;
    double y;
    double left;
    double right;
    double top;
    double bottom;
}

class TestPlayer : DoomPlayer
{
    Array<Actor> ScannableThings;

    override void Tick()
    {
        int Range = 1024;
        int index;

        ScannableThings.Clear();

        ThinkerIterator Finder = ThinkerIterator.Create("Actor");
        Actor mo;

        while ( mo = Actor(Finder.Next()) )
        {
            target = mo;

            if (
                mo == self ||
                mo.health <= 0 || 
                mo.bDormant || 
                mo.bInvisible ||
                (mo.bNoBlockMap && !mo.bMissile) ||
                Distance3d(mo) > Range ||
                !IsVisible(mo, false)
            ) { continue; }

            if (ScannableThings.Find(mo) == ScannableThings.Size())
            {
                ScannableThings.Push(mo);
            }
        }

        Super.Tick();
    }
}

class TestStatusBar : DoomStatusBar
{
    double ScreenHeight, ScreenWidth, FOVfactor;
    Vector2 hudscale;
    int hudstate;

    HUDFont mSmallFont;

    override void Init()
    {
        Super.Init();

        SetSize(32, 320, 200);

        Font fnt = "SMALLFONT";
        mSmallFont = HUDFont.Create(fnt, 1);
    }

    override void NewGame ()
    {
        Super.NewGame();
    }

    override void Tick()
    {
        Super.Tick();
    }

    override void Draw (int state, double TicFrac)
    {
        Super.Draw (state, TicFrac);

        hudstate = state;

        BeginHUD(1, False); // Urgh...  Force overlays to always draw with fullscreen coords and under other bars.  
            DrawOverlays();
        BeginHUD(1, True);

        if (state == HUD_StatusBar)
        {
            BeginStatusBar(False);
            DrawMainBar(TicFrac);
        }
        else if (state == HUD_Fullscreen)
        {
            BeginHUD(1, False);
            DrawFullScreenStuff();
        }
    }

    void DrawOverlays()
    {
        bool draw;
        Coords ScreenCoords;

        hudscale = GetHUDScale();

        FOVFactor = CPlayer.FOV / 90;

        ScreenHeight = double(Screen.GetHeight()) / hudscale.y;
        ScreenWidth = ScreenHeight * 1.333; // Use standard ratio for handling offsets

        for (int i = 0; i < TestPlayer(CPlayer.mo).ScannableThings.Size(); i++)
        {
            actor mo = TestPlayer(CPlayer.mo).ScannableThings[i];

            [draw, ScreenCoords.x, ScreenCoords.y, ScreenCoords.left, ScreenCoords.right, ScreenCoords.top, ScreenCoords.bottom] = GetTargetScreenCoords(CPlayer.mo, mo, 0);

            if (draw)
            {
                DrawFrame(CPlayer.mo, mo, ScreenCoords);
            }
        }
    }

    void DrawFrame(Actor origin, Actor mo, Coords ScreenCoords)
    {
        if (!origin || !mo) { return; }
        if (mo.health <= 0) { return; }
        if (origin.Distance3d(mo) - origin.radius - mo.radius < 32) { return; }

        double drawscale = ((ScreenHeight / 2) / origin.Distance3d(mo)) / FOVFactor;

        String BaseImage = "Scan";

        readonly<Actor> defmobj = GetDefaultByType(mo.GetClass());
        mo.bMissile |= defmobj.bMissile;

        if ((mo.bIsMonster && !mo.bFriendly) || mo.bShootable || mo.bMissile) { BaseImage = "ScanA"; }
        else if (mo is "Inventory") { BaseImage = "ScanB"; }

        double alpha = drawscale * drawscale * drawscale * 2;

        DrawString(mSmallFont, "+", (ScreenCoords.x, ScreenCoords.y), DI_ITEM_CENTER, Font.CR_PURPLE, 1.0);

        DrawImage(BaseImage .. "_TL", (ScreenCoords.left, ScreenCoords.top), DI_ITEM_RIGHT_BOTTOM, alpha, (-1, -1));
        DrawImage(BaseImage .. "_TR", (ScreenCoords.right, ScreenCoords.top), DI_ITEM_LEFT_BOTTOM, alpha, (-1, -1));
        DrawImage(BaseImage .. "_BL", (ScreenCoords.left, ScreenCoords.bottom), DI_ITEM_RIGHT_TOP, alpha, (-1, -1));
        DrawImage(BaseImage .. "_BR", (ScreenCoords.right, ScreenCoords.bottom), DI_ITEM_LEFT_TOP, alpha, (-1, -1));
    }

    bool, double, double, double, double, double, double GetTargetScreenCoords(Actor origin, Actor target, double zOffset = 0)
    {
        if (!origin || !target) { return false, 0, 0, 0, 0, 0, 0; }

        double x, y, left, right, top, bottom;

        double angle = origin.AngleTo(target) - origin.angle;
        double dist = origin.Distance2d(target);

        if (abs(origin.pitch) <= 50)
        {
            double radius = target.radius;
            double height = target.height > 32 ? target.height : 32;
            if (target.bMissile) { height = 0; }

            if (zOffset == 0) { zOffset = height / 2; }

            double viewpitch = VectorAngle(dist, target.pos.z + zOffset - (origin.pos.z + origin.player.viewheight));
            viewpitch = viewpitch + origin.pitch; 
            viewpitch *= 1.5 / FOVFactor;    

            if ((ScreenWidth / 2) * sin(angle) != 0 && cos(angle) != 0 && (ScreenWidth / 2) * sin(viewpitch) != 0 && cos(viewpitch) != 0)
            {
                double xscale = ((ScreenWidth / 2) / origin.Distance3d(target)) / FOVFactor;
                double yscale = ((ScreenHeight / 2) / origin.Distance3d(target)) / FOVFactor;

                x = (Screen.GetWidth() / hudscale.x) / 2 - ((ScreenWidth / 2) * sin(angle) / cos(angle)) / FOVFactor;
                y = ScreenHeight / 2 - ((ScreenHeight / 2) * sin(viewpitch) / cos(viewpitch));

                if (screenblocks <= 10) { y -= RelTop; }

                left = x - radius * xscale;
                right = x + radius * xscale;
                top = y - (height / 2 + 8) * yscale;
                bottom = y + (height / 2 + 8) * yscale;

                x = clamp(x, 0, Screen.GetWidth());
                y = clamp(y, 0, Screen.GetHeight());
                left = clamp(left, 0, Screen.GetWidth());
                right = clamp(right, 0, Screen.GetWidth());
                top = clamp(top, 0, Screen.GetHeight());
                bottom = clamp(bottom, 0, Screen.GetHeight());

                return true, x, y, left, right, top, bottom;
            }
        }
        return false, x, y, left, right, top, bottom;
    }
}
Wow! With some your code edits, it's look like Unreal Style Light Coronas hack.

Re: [Zscript] DrawTexture question

Posted: Thu Sep 21, 2017 1:40 am
by GiveOneMoreChance
Nash wrote:Please note that using this technique will result in completely broken visuals in VR headsets; what's needed is some native project to screen/deproject to world stuff the engine needs to natively support. There have been talks about this stuff in the ZScript library brainstorming thread but no work has been done yet.
Yeah screen/deproject to world stuff would be very useful stuff, thanks for tip.