[Zscript] DrawTexture question

Archive of the old editing forum
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
Locked
GiveOneMoreChance
Posts: 19
Joined: Thu Aug 10, 2017 8:09 pm

[Zscript] DrawTexture question

Post 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;
	}
}
User avatar
AFADoomer
Posts: 1326
Joined: Tue Jul 15, 2003 4:18 pm
Contact:

Re: [Zscript] DrawTexture question

Post 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;
    }
}
User avatar
Nash
 
 
Posts: 17439
Joined: Mon Oct 27, 2003 12:07 am
Location: Kuala Lumpur, Malaysia
Contact:

Re: [Zscript] DrawTexture question

Post 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.
GiveOneMoreChance
Posts: 19
Joined: Thu Aug 10, 2017 8:09 pm

Re: [Zscript] DrawTexture question

Post 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.
GiveOneMoreChance
Posts: 19
Joined: Thu Aug 10, 2017 8:09 pm

Re: [Zscript] DrawTexture question

Post 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.
Locked

Return to “Editing (Archive)”