Fast replacement for Line command?

Blitz3D Forums/Blitz3D Programming/Fast replacement for Line command?

I'm looking for a fast replacement for Blitz's own Line command which is pretty slow, it has to be said.

There's quite a few in the code archives, but there seems to be a problem: they use WritePixelFast to plot the pixels so if the line goes off-screen, nasty things happen. Looks like blitz will blindly try to write a pixel outside of the locked image buffer, if you tell it to. :/

I don't suppose anyone knows of a fast replacement for Line that can handle lines drawn fully/partially off-screen?

I think I read somewhere that you could also use Line when the buffer is Locked ?

Hmm, I doubt it. It says locked buffers should only be used by Read/WritePixelFast, in the docs.

I think I'm done for, one this one, TBH.

[edit] Actually, that does work and is quite a bit faster. I wonder how 'safe' it is, though?

No idea, but it doesn't complain when drawing outside the screen. On my pc, the speed ratio is 2:3 (locked/not-locked)

I'm confident that Devils Child has the answer.

b32: takes 20ms to just draw 300 lines normally on my PC, and only 2ms when locking the buffer! I wish someone (Mark/skid) could confirm whether this method is safe, or not. I might stick to using this anyway, as it's only for a small, non-serious project. Thanks for the tip!

_33: Devil's Child has a line algo in the archives - it too uses WritePixelFast. :)

well, I didn't have much inspiration to write a good answer for you big10p, but now that I think of it, and it could bloody work; why not try to do a pokeint ?

You calculate the displacement by doing "(x + y * width) shl 2", and you pokeint the color + alpha value by using the handle of the imagebuffer. This should work, but I'm not 100% sure.

I'll have to try it myself sometime soon.

EDIT: Just checked, haven't been able to do a pokeint to a texture yet. If anyone is successful on doing this, I'll gladly pick up the recipe.

NOTE: If you're doing this on a texture, it shouldn't work in the case that the texture is in the GFX card memory.

This:
Graphics 800,600,32,2
a = CreateImage (64, 64)
buffer = ImageBuffer (a)
For x = 0 To 63
   For y = 0 To 63
      disp = (x + y * 64) * 4
      value = Rand ($00000000, $00FFFFFF) And $FF000000
      PokeInt (buffer, disp, value)
   Next
Next
WaitKey()
...doesn't work :(

And this:
;RtlMoveMemory1%(Destination*, Source%, Length%) : "RtlMoveMemory" 
Graphics 800,600,32,2
a = CreateImage (64, 64)
For x = 0 To 63
   For y = 0 To 63
      disp = (x + y * 64) * 4
      value = Rand ($00000000, $00FFFFFF) And $FF000000
      RtlMoveMemory1(a + disp, value, 4)
   Next
Next
WaitKey()
...Still, doesn't work!

And...
;RtlMoveMemory3%(Destination*, Source*, Length%) : "RtlMoveMemory"
temp% = CreateBank(100)
Graphics 800,600,32,1
image = CreateImage (64, 64)
RtlMoveMemory3(temp, image, 100)
buffer = PeekInt (temp, 72) ; (+72) buffer graphics data pointer
Print "image is located here :" + buffer
WaitKey()
For x = 0 To 63
   For y = 0 To 63
      addr = buffer + (x + y * 64) * 4
      value = Rand ($00000000, $00FFFFFF) And $FF000000
      RtlMoveMemory1(addr, value, 4)
   Next
Next
WaitKey()
Doesn't work, but I got the image bank address.

It is a good idea, however to do it is sort of complicated.
As I understood, the handle that CreateBank provides, points to a structure with a 4-byte number to indicate that it is a bank, followed by a pointer to the banks memory.
So it would be basically a pointer to another pointer.
An image has the similair structure, accessed by ImageBuffer. Before using it, you need to use LockBuffer/UnLockBuffer on it, and instead of an offset of 4-bytes, it has an offset of 72 bytes. I'm not sure if I understood it correctly, but this is basically what I could make of it.
'Tom' did this, a while ago. The thread is here: http://www.blitzbasic.com/Community/posts.php?topic=30480

Definately, good! Works flawlessly on ImageBuffers!

Thoe, I'm doubtful as to if it could work on textures stored with flag 256 on.

This is something I might test tonight.

hmmmm.....
Function PointBankToTexturebuffer(b,texture)
;  Change 'Bank' to point to the TextureBuffer
   api_RtlMoveMemory (b+4,TextureBuffer(image)+IMAGEBUFFER_OFFSET,4)
   size=TextureWidth (texture) * TextureHeight (texture) * 4
   Local temp=CreateBank(4)
   PokeInt(temp,0,size)
   api_RtlMoveMemory2(b+8,temp,4)
   FreeBank temp
End Function
... It's off topic, but I would like to think this works!

You can first clip the line to screen coordinates and then call the line function with the clipped line. That way you will know it will fall completely within the screen buffer. This site shows one method for clipping lines.
http://www.nondot.org/sabre/graphpro/line6.html

EDIT: I translated the code from that site into Blitz Basic:
Type TRect
	Field x1
	Field y1
	Field x2
	Field y2
End Type

Const CodeBottom = 1
Const CodeTop    = 2;             { BitFields for output codes }
Const CodeLeft   = 4
Const CodeRight  = 8;

Graphics 800,600,32,1
SetBuffer BackBuffer()
Local View.TRect = New TRect
View\x1 = 0
view\x2 = 799
view\y1 = 0
view\y2 = 599 ;set the viewport

While Not KeyHit(1)
	Cls
	Color 255,255,255
	SeedRnd(1);use the same randome sequences each loop
	For t = 1 To 20 ;draw 20 lines
	    x1 = Rand(0,799)
		x2 = Rand(0,799)
		y1 = Rand(0,599)
		y2 = Rand(0,599)
		
		LineC(x1,y1,x2,y2,view)
	Next
	Color 0,255,0
	Rect view\x1,view\y1,(view\x2-view\x1)+1,(view\y2-view\y1)+1,False
	Color 255,0,0
	Text 10,10,"WASD to move top/left side of viewport"
	Text 10,30,"Arrow keys to move bottom/right side of viewport"
	
	Flip
	If KeyDown(30) And View\x1 > 0 Then View\x1 = View\x1 - 1
	If KeyDown(32) And View\x1 < View\x2-1 Then view\x1 = view\x1 + 1
	If KeyDown(17) And view\y1 > 0 Then view\y1 = view\y1 - 1
	If KeyDown(31) And view\y1 < view\y2-1 Then view\y1 = view\y1 + 1
	If KeyDown(203) And view\x2 > view\x1+1 Then view\x2 = view\x2 - 1
	If KeyDown(205) And view\x2 < 799 Then view\x2 = view\x2 + 1
	If KeyDown(200) And view\y2 > view\y1+1 Then view\y2 = view\y2 - 1
	If KeyDown(208) And view\y2 < 599 Then view\y2 = view\y2 + 1
Wend

Function LineC(X1, Y1, X2, Y2, Clip.TRect);
  Local OutCode0, OutCode1, OutCodeOut, X, Y

  OutCode0 = CompOutCode(X1, Y1, Clip);            { Compute the original codes   }
  OutCode1 = CompOutCode(X2, Y2, CLip);

  While (OutCode0 <> 0) Or (OutCode1 <> 0); { While Not Trivially Accepted }
    If (OutCode0 And OutCode1);      { Trivial Reject }
      Return
    Else
    ;        { Failed both tests, so calculate the Line segment To clip }
      If OutCode0 > 0
        OutCodeOut = OutCode0;    { Clip the First point }
      Else
        OutCodeOut = OutCode1;   { Clip the last point  }
      EndIf
      If (OutCodeOut And CodeBottom) = CodeBottom
      ;BEGIN               { Clip the Line To the bottom of the Viewport     }
        Y = Clip\Y2;
        X = X1+(X2-X1)*(Y-Y1)/(Y2 - Y1);
      ElseIf (OutCodeOut And CodeTop) = CodeTop
      ;BEGIN               { Clip the Line To the top of the Viewport        }
        Y = Clip\Y1;
        X = X1+(X2-X1)*(Y-Y1)/ (Y2 - Y1);
      ElseIf (OutCodeOut And CodeRight) = CodeRight
      ;BEGIN               { Clip the Line To the Right edge of the Viewport }
        X = Clip\X2;
        Y = Y1+(Y2-Y1)*(X-X1)/(X2-X1);
      ElseIf (OutCodeOut And CodeLeft) = CodeLeft
      ;BEGIN               { Clip the Line To the Left edge of the Viewport  }
        X = Clip\X1;
        Y = Y1+(Y2-Y1)*(X-X1)/(X2-X1);
      EndIf

      If OutCodeOut = OutCode0;      { Modify the First coordinate   }
      ;BEGIN
        X1 = X
        Y1 = Y;                   { Update temporary variables    }  
        OutCode0 = CompOutCode(X1, Y1, clip);    { Recalculate the OutCode       }
      Else                                  ;{ Modify the second coordinate  }
      ;BEGIN
        X2 = X; 
        Y2 = Y;                   { Update temporary variables    }  
        OutCode1 = CompOutCode(X2, Y2, clip);    { Recalculate the OutCode       }
      EndIf
    EndIf
  Wend;

  Line(X1, Y1, X2, Y2);              { Draw the new line!            }
End Function

Function CompOutCode(X, Y, Clip.TRect)  ;{ Nested Function }
Local Code
;BEGIN
  Code = 0;
  If      Y > Clip\Y2
    Code = CodeBottom
  ElseIf Y < Clip\Y1
    Code = CodeTop;
  EndIf
  If      X > Clip\X2
    Code = Code+CodeRight
  ElseIf X < Clip\X1 
    Code = Code+CodeLeft;
  EndIf
  Return Code;
End Function


Big10p,

Have you considered using 2d in 3d methods using single surface quads?

That PokeInt method on image buffers sounds like witchcraft, to me. :) Besides, for lines I'd need to access the back buffer which is, of course, in VRAM.

TomTom: That's interesting but I fear the additional clipping code would slow things down too much. I also noticed the lines 'wobble' when moving the clip viewport.

Stevie: This is just for a simple vector thing I'm mucking about with - it's not a major project, or anything. Writing a whole single surface system for this would be overkill, I think. :) For future reference though, has anyone done a decent 2D-in-3D vector graphics lib?

I'm pretty sure Indiepath did something a while back which I remember helping with. I've done some vector stuff myself.

I'll see if I can dig something out when I get home. I'm going out on a limb here but it'd be much faster than drawing clipped lines and you can make use of all the other 3d commands ( deltayaw , tform etc.. ) to get rid of all that angle cr .. [stuff] you've been discussing in other threads.

Stevie

Ah, yes - I seem to remember that Indiepath lib, now.

The reason I didn't go down the 3D route is that I actually want to do all the maths stuff myself, as a kind of learning exercise, mostly. I seem to have developed a morbid interest in trig. :) I just wanted to see if I could write a simple vector graphics game.

However, I may be interested in doing a vector game in 3D in the future as I love the graphics style.

Thanks.

P.S. Further tests show that locking the buffer for Line gives a MASSIVE speed boost. I can draw ~1000 lines before the FPS drop, and that's including all the vertex transformations I'm doing in-code, too. Should be easily fast enough for my basic needs. :)

Hi big10p, I knocked this up for you. I hope it's what your after :)

;*** VectorGFX - R. Ferriby

Graphics3D 800,600,32,0
SetBuffer BackBuffer()
AntiAlias 1

Global camera%=CreateCamera()
PositionEntity camera,0,0,-40

vectorcube%=CreateVectorMesh()

AddVector(vectorcube,-1,-1,-1, -1,1,-1)      ; 0,1
VectorTo(vectorcube,1,1,-1)		     ; 1,2
VectorTo(vectorcube,1,-1,-1)		     ; 2,3
CloseVector(vectorcube,0,3)		     ; 3,0

AddVector(vectorcube,-1,-1, 1, -1,1, 1)	     ; 4,5
VectorTo(vectorcube,1,1, 1)		     ; 5,6
VectorTo(vectorcube,1,-1, 1)		     ; 6,7
CloseVector(vectorcube,7,4)		     ; 7,4

CloseVector(vectorcube,0,4)		     ; 0,4
CloseVector(vectorcube,1,5)		     ; 1,5
CloseVector(vectorcube,2,6)		     ; 2,6
CloseVector(vectorcube,3,7)		     ; 3,7

ScaleEntity vectorcube,10,10,10

WireFrame 1
While Not KeyHit(1)
	TurnEntity vectorcube,1,2,3
	RenderWorld
	Flip
Wend
End


Function CreateVectorMesh%()
	Local mesh%=CreateMesh()
	Local surf%=CreateSurface(mesh)
	EntityFX mesh,17
	Return mesh
End Function

Function AddVector(mesh%,x1#,y1#,z1#,x2#,y2#,z2#)
	Local surf%=GetSurface(mesh,1)
	Local v1%,v2%

	v1=AddVertex(surf,x1,y1,z1)
	v2=AddVertex(surf,x2,y2,z2)
	AddTriangle(surf,v1,v2,v1)
End Function

Function VectorTo(mesh,x2#,y2#,z2#)
	Local surf%=GetSurface(mesh,1)
	Local v1,v2%
	v2=AddVertex(surf,x2,y2,z2)
	v1=v2-1
	AddTriangle(surf,v1,v2,v1)
End Function

Function CloseVector(mesh,v1%,v2%)
	Local surf%=GetSurface(mesh,1)
	AddTriangle(surf,v1,v2,v1)
End Function


Cheers,

Roy

Thanks Roy but I was more thinking along the lines of rendering each line as a textured quad. This allows the lines to be alpha/additive blended to give them a nice glow effect.

I don't need this yet, though. I still haven't finished mucking about with vectors in pure 2D. :)

Ahhh - I see! I'll have a dabble when I get some free time. I might come up with something. :)

Cheers,

Roy

No need to go to any trouble on my behalf, but I would be interested to see what you come up with, if you do. :)

Re: Locked buffer with Line.

Interestingly, Line is the only drawing command that seems to work on locked buffers. Oval, Plot, Rect don't work - nothing is drawn. Seems like Line is some kind of special case, so maybe it is safe to use on locked buffers, then...

OK big10p,
I think I've got something more in line with what your looking for! It's definately on the right track anyway.
You can add textures too if you really want them but personally I think it looks fine the way it is :)
The idea is that you pre-cache your glowing vector geometry inside standard blitz meshes then clone as many as you like with CopyEnitity as and when you need them.
Should be fast enough!
Sorry I could'nt give you something more interesting to look at though!
Mess around with it and see what you think.

;*** GlowVectors - R. Ferriby

Graphics3D 800,600,32,0
SetBuffer BackBuffer()
AntiAlias 1
AppTitle "GlowVectors demo - R Ferriby"

;create ortho camera
Global camera%=CreateCamera()
CameraProjMode camera,2
CameraZoom camera,1.0/GraphicsWidth()
PositionEntity camera,0,100,0
AlignToVector camera,0,-1,0,3

Global template_mesh%=CreateTemplateMesh()
Global template_surf%=GetSurface(template_mesh,1)

;*** vector geometry
box%=CreateVectorMesh()
AddVector2D(box,-100, 100, 100, 100,2,255,160,0)
AddVector2D(box, 100, 100, 100,-100,2,255,160,0)
AddVector2D(box, 100,-100,-100,-100,2,255,160,0)
AddVector2D(box,-100,-100,-100, 100,2,255,160,0)

;*** add glow geometry
AddVector2D(box,-100, 100, 100, 100,20,255,160,0,0.25)
AddVector2D(box, 100, 100, 100,-100,20,255,160,0,0.25)
AddVector2D(box, 100,-100,-100,-100,20,255,160,0,0.25)
AddVector2D(box,-100,-100,-100, 100,20,255,160,0,0.25)



While Not KeyHit(1)
	TurnEntity box,0,1,0
	RenderWorld
	Text 10,10,TrisRendered()
	Flip 1
Wend
End


Function CreateVectorMesh%()
	Local mesh%=CreateMesh()
	Local surf%=CreateSurface(mesh)
	EntityFX mesh,32+2+1+16
	EntityBlend mesh,3
	Return mesh
End Function

Function AddVector2D(mesh%,x1#,z1#,x2#,z2#,linethickness#=2,red#=255,green#=255,blue#=255,alpha#=1)
	Local surf%,i%,c%,v1%,v2%,vi%,tv2%,tv3%
	Local piv%=CreatePivot()
	Local nx#,nz#,l#,x#,y#,z#
	nx=x2-x1 : nz=z2-z1
	l=Sqr(nx*nx+nz*nz)
	If l=0 Then l=1
	nx=nx/l : nz=nz/l
	If Abs(nx)<0.00001 Then nx=0
	If Abs(nz)<0.00001 Then nz=0
	AlignToVector piv,nx,0,nz,3
	ScaleEntity piv,1.0/linethickness,1.0/linethickness,1.0/linethickness
	
	surf=GetSurface(mesh,1)
	c=CountVertices(template_surf)
	v1=AddVertex(surf,x1,0,z1,0,0)
	VertexColor(surf,v1,red,green,blue,alpha)
	v2=AddVertex(surf,x2,0,z2,0,0)
	VertexColor(surf,v2,red,green,blue,alpha)
	For i=0 To c-1
		x=VertexX(template_surf,i)
		z=VertexZ(template_surf,i)
		TFormVector x,0,z,0,piv
		x=TFormedX() : z=-TFormedZ()
		vi=AddVertex(surf,x1+x,0,z1+z,0,0)
		VertexColor surf,vi,red,green,blue,0
		vi=AddVertex(surf,x2-x,0,z2-z,0,0)
		VertexColor surf,vi,red,green,blue,0
	Next
	;*** make triangles
	tv2=v1+2 : tv3=v2+2
	For i=1 To c-1
		AddTriangle(surf,v1,tv2,tv2+2)
		AddTriangle(surf,v2,tv3,tv3+2)
		tv2=tv2+2 : tv3=tv3+2
	Next
	AddTriangle(surf,v1+2,v1,v2)
	AddTriangle(surf,v1+2,v2,v2+c+c)
	AddTriangle(surf,v1,v1+c+c,v2+2)
	AddTriangle(surf,v1,v2+2,v2)
	FreeEntity piv
End Function

Function CreateTemplateMesh%(numsegments%=16)
	Local mesh%,surf%,i%,v%
	Local angle#,ang_inc#
	mesh=CreateMesh()
	surf=CreateSurface(mesh)
	ang_inc=360.0/numsegments
	angle=-90
	For i=0 To numsegments/2
		v=AddVertex(surf,Sin(angle),0,Cos(angle),0,1)
		VertexColor(surf,v,255,255,255,0)
		angle=angle+ang_inc
	Next
	HideEntity mesh
	Return mesh
End Function


Cheers,

Roy

Thanks Roy, I'll take a look. ;)