WorldToScreenCoord? - draw debug lines with 2D API

Hello,

DebugDraw class uses a plane to draw a line: it’s creepy :slight_smile: !

I would draw a line with 2D API. But I don’t understand the “World To Screen Coords” logic Node :

	override function get(from:Int):Dynamic {
	var v1:Vec4 = inputs[0].get();
	if (v1 == null) return null;

	var cam = iron.Scene.active.camera;
	v.setFrom(v1);
	v.applyproj(cam.V);
	v.applyproj(cam.P);

	return v;
}

I found a topic about “Screen to World” and so I try this :

public function WorldToScreen(loc:Vec4):Vec2 {
	var v = new Vec4();
	var cam = iron.Scene.active.camera;
	v.setFrom(loc);
	v.applyproj(cam.V);
	v.applyproj(cam.P);

	var w = kha.System.windowWidth();
	var h = kha.System.windowHeight();

	return new Vec2((v.x*w+0.5)/2, (v.y*h-0.5)/-2);
}

It’s much better, but it’s bad. How to use “World To Screen Coords” correctly to draw a overlay.

thx

I found it :slight_smile:

Camera plan To Screen :
x’ = xw/2 + w/2 = w/2 * (x+1)
y’= -y
h/2 + h/2 = h/2*(-y+1)

implementation

public function WorldToScreen(loc:Vec4):Vec2 {
	var v = new Vec4();
	var cam = iron.Scene.active.camera;
	if (cam != null) {
		v.setFrom(loc);
		v.applyproj(cam.V);
		v.applyproj(cam.P);
	}

	var w = kha.System.windowWidth();
	var h = kha.System.windowHeight();
	return new Vec2(v.x*0.5 * w + w*0.5, -v.y*0.5 * h + h * 0.5);
}

If you need other but similar functions like plane intersection there is a class for them in Iron, used in a few examples/template like here

For information, I implemented a trait to draw line, see below

and the code :

package arm;

#if arm_debug
import iron.math.Vec2;
import kha.Color;
import kha.System;
import iron.math.Vec4;

class LineDebug {
	public var a:Vec4 = null;
	public var b:Vec4 = null;
	public var color:kha.Color = 0xffff0000;
	public var strength : Float = 1;

	public function new() {}
}

interface IDebug {
	 public function addLine(a:Vec4, b:Vec4, color:Color, strength:Float):Void; 
}

class VisualDebugTrait extends iron.Trait implements IDebug  {
	private var lines:Array<LineDebug> = new Array<LineDebug>();
	public static var instance : IDebug;

	public function new() {
		super();
		this.notifyOnRender2D(this.onRender);
		VisualDebugTrait.instance = this;
	}

	public function addLine(a:Vec4, b:Vec4, color:Color, strength:Float) {
		var line = new LineDebug();
		line.a = a;
		line.b = b;
		line.color = color;
		line.strength = strength;
		this.lines.push(line);
	}

	/**
	 * World To Screen Coords
	 * thread http://forums.armory3d.org/t/worldtoscreencoord-draw-debug-lines-with-2d-api/3467
	 * @param loc
	 * @return Vec2
	 */
	public function WorldToScreen(loc:Vec4):Vec2 {
		var v = new Vec4();
		var cam = iron.Scene.active.camera;
		if (cam != null) {
			v.setFrom(loc);
			v.applyproj(cam.V);
			v.applyproj(cam.P);
		}

		var w = System.windowWidth();
		var h = System.windowHeight();
		return new Vec2((v.x + 1) * 0.5 * w, (-v.y + 1) * 0.5 * h);
	}

	public function onRender(g:kha.graphics2.Graphics) {
		while (this.lines.length > 0) {
			var line = this.lines.pop();
			var aScreen = WorldToScreen(line.a);
			var bScreen = WorldToScreen(line.b);
			g.color = line.color;
			g.drawLine(aScreen.x, aScreen.y, bScreen.x, bScreen.y,line.strength);
		}
	}
}
#end
2 Likes

That’s really cool! Maybe you can contribute it to the official repo so debugging is improved?

Yes I would like contribute…

I found a way to add this in the SDK. I will do it.

So, did you find a way to implement this on the SDK? :thinking:
Can the debugDraw.hx be expanded to include this functionality, to replace the current implementation of line drawing?

It’s not baked, I have an issue : lines are projected from the front :slight_smile: and back :frowning: of camera… I have to compute an intersection betwen the segment and the camera plan to fix it correctly.

And I’m not sure to release on the SDK.

My VDebug works like a logger. You write your “log” anywhere. It’s very different from the current debug system.

I could just change the drawline method… why not :slightly_smiling_face:

EDIT : the code is released as a library here : https://github.com/marcgardent/armory3D-VDebug

3 Likes

Hi @Marc , so did you find a solution to that problem yet?

On a different note: I’m currently evaluating different ways to draw lines in Armory to add support for grease pencil objects back to the engine, and I found that your solution works and is simple enough :+1:, although still there are a few things that prevent me from just going with it: first is that one you mentioned about back projection, other is no anti-aliasing and last one I found is inconsistent line size depending on distance.

so, my question is: do you know if it’s possible to fix this drawing solution to make it work for such a feature? or should another solution be used for the task?

Maybe some of them could be solved using something like drawInnerLine ?https://github.com/Kode/Kha/blob/71514b514980a6d201c2f41cfe9ede283a66e2b1/Sources/kha/graphics2/GraphicsExtension.hx#L140
which draws two fill triangles instead of a line… I will try to make it work with that projection thingy, to try to get some results…

This looks like multiplying the viewport matrix, is the viewport matrix stored somewhere in the armory code?

So I am trying to track a 3D object with a 2D element and I can’t get it to work correctly. I am trying to work with Marcs WorldToScree function, something like this here:

var my3dObjectPos = object.transform.loc;
var toScreen = WorldToScreen(my3dObjectPos);

var element2d = canvas.getElement("my2Dthing");
element2D.x = toScreen.x;
element2d.y = toScreen.y;

function WorldToScreen(loc:Vec4):Vec2 {
  var v = new Vec4();
  var cam = iron.Scene.active.camera;
  if (cam != null) {
    v.setFrom(loc);
    v.applyproj(cam.V);
    v.applyproj(cam.P);
  }

  var w = App.w();  // var w = System.windowWidth();
  var h = App.h();  // var h = System.windowHeight();
  return new Vec2((v.x + 1) * 0.5 * w, (-v.y + 1) * 0.5 * h);
}

I also tried this with nodes, but that didn’t work at all. Any ideas? I can’t imagine this to be that difficult. I guess I just think wrong here?

Does element2d never show up on the screen? or does it show up but it’s incorrectly placed?

This is a test with the code you’ve posted:

track

3 Likes

Oh. Great. So why the heck doesn’t it work on my side? :smile_cat:
The element is completely off the screen.
I get some crazy numbers back as well:

trace object3D.tranform.loc : (1.0420458316802979, 1.0025041103363037, -1.1368683772161603e-13, 1)
trace toScreen Vec2: toScreen.x: 24442484270678310, toScreen.y: -23514983893435628

EDIT: Ok, I got it as well, I just moved some stuff around, now it works. Not sure why it didn’t in the first place.

2 Likes