Building Walls

Blitz3D Forums/Blitz3D Programming/Building Walls

_PJ_(Posted 2016) [#1]
I am trying to add the ability to construct walls in straight lines along a terrain.

The walls are to be placed when holding down the mouse button and dragging along on the terrain to determine the length of the wall.

The walls are only placed parallel to the global coordinate axes.

The wall unit dimensions are multiples of units, so the positioning can be rounded to the nearest units.

Essentially, think of the SIM city 'road' positioning, or, if anyone knows of it, wall/fence construction in the game "Warrior Kings" - I'm sure there's plenty of other examples...


I'm controlling whether or not a wall is in progress with some global variables. The actual camerapick coordinates on the terrain etc. are working since I use the saame routines for terrain manipulation.

The real problems I have is actually in determining the lengths and scaling of the wall.

I have a function to distinguish and simplify the direction of the walls into whether horizontal or vertical:
;RETURNS TRUE IF X and Y COORDINATES ARE APPROXIMATELY ALIGNED TO oY AXIS
Function nb_MATH_GetIsCoordinateAlignment(f_oX#,f_oY#,f_X#,f_Y#)
	If (f_oX=f_X) Return True
	If (f_oY=f_Y) Return False
	Local f_Orientation#=Abs(((ATan2(f_X-f_oX,f_y-f_oY) )) )
	Local nb_Binary=( (f_Orientation>135) Or (f_Orientation<45) )
	Return nb_Binary
End function

The real problem is actually with the scaling, and whether Blitz seems to want to scale absolutely or relatively and to maintain a consistent origin for the walls.

I've tried a number of different approaches, but just can't seem to get it to work.

I'd really appreciate if anyone has any suggestions or recommendations on how this ought to be approached.

Currently the main function (still not working, with a few attempted 'fixes' or original tries, still present but commented:

[code]
Function v_CON_PlaceWall()
	If (nb_TRN_MousePickTerrain())
		Local X=PickedX()
		Local Z=PickedZ()
		Local Y=TerrainY(thtr_TRN_TERRAIN,X,0,Z)
		
		DebugLog("CON (Place Wall): Picked "+Str(X)+" , "+Str(Y))
		
		X=Floor(X)
		Y=Floor(Y)
		Z=Floor(Z)
		
		If (Not(gh3d_CON_PROPOSAL))
			
			DebugLog("CON (Place Wall): No current proposal.")
			
			gh3d_CON_PROPOSAL=CreateCube();CopyEntity(th3d_CON_WALLMESH_TEMPLATE)
			PositionEntity gh3d_CON_PROPOSAL,X,Y,Z,True
			gn_CON_WALL_INIT_X=X
			gn_CON_WALL_INIT_Y=Y
			gn_CON_WALL_INIT_Z=Z
			
			DebugLog("CON (Place Wall): Proposal created at "+Str(gn_CON_WALL_INIT_X)+" , "+Str(gn_CON_WALL_INIT_Z)+" Altitude: "+Str(gn_CON_WALL_INIT_Y))
			
			Return
		Else
		;	FreeEntity gh3d_CON_PROPOSAL
		;	gh3d_CON_PROPOSAL=CopyEntity(th3d_CON_WALLMESH_TEMPLATE)
		;	PositionEntity gh3d_CON_PROPOSAL,gn_CON_WALL_INIT_X,gn_CON_WALL_INIT_Y,gn_CON_WALL_INIT_Z,True
		End If
		
		Local Orient=nb_MATH_GetIsCoordinateAlignment(gn_CON_WALL_INIT_X,gn_CON_WALL_INIT_Z,X,Z)
		
		;Z=(Z*(1-Orient))-gn_CON_WALL_INIT_X
		;X=(X*(Orient))-gn_CON_WALL_INIT_Z
		
		If (Orient)
			DebugLog("CON (Place Wall): Aligned with proposal on Z axis")
			;ScaleEntity gh3d_CON_PROPOSAL,cf_CON_WALLMESH_UNITWIDTH,cf_CON_WALLMESH_UNITHEIGHT,Z,True
			ScaleMesh gh3d_CON_PROPOSAL,cf_CON_WALLMESH_UNITWIDTH,cf_CON_WALLMESH_UNITHEIGHT,Z
			DebugLog("CON (Place Wall): Scaled to: "+Str(MeshWidth(gh3d_CON_PROPOSAL)+" , "+Str(MeshHeight(gh3d_CON_PROPOSAL))+" , "+Str(MeshDepth(gh3d_CON_PROPOSAL))))
		Else
			DebugLog("CON (Place Wall): Aligned with proposal on X axis")
			;ScaleEntity gh3d_CON_PROPOSAL,cf_CON_WALLMESH_UNITWIDTH,cf_CON_WALLMESH_UNITHEIGHT,Z,True
			ScaleMesh gh3d_CON_PROPOSAL,X,cf_CON_WALLMESH_UNITHEIGHT,cf_CON_WALLMESH_UNITDEPTH
			DebugLog("CON (Place Wall): Scaled to: "+Str(MeshWidth(gh3d_CON_PROPOSAL)+" , "+Str(MeshHeight(gh3d_CON_PROPOSAL))+" , "+Str(MeshDepth(gh3d_CON_PROPOSAL))))
		End If
		
	End If
End Function



Floyd(Posted 2016) [#2]
You might be mixing up ScaleEntity and ScaleMesh.

A cube has vertices in eight locations. The default vertices are (1,1,1), (1,1,-1) etc.

ScaleEntity does not change this. It temporarily multiplies coordinates by some scale factor before doing rendering and collision detection. There is no change to the underlying mesh. If you scale an entity by a factor of two, and then by a factor of three the end result is scaling by three.

ScaleMesh actually changes coordinates. Thus if you ScaleMesh by a factor of two and then by a factor of three you have actually multiplied everything by six.


_PJ_(Posted 2016) [#3]
Yeah I read that, and have tried with both commands.

This executable code based on the above can highlight the problem better:


___



edit:
Okay I've got a slightly better idea of the root of the problem now. I think it seems to be that the mesh is scaled around a central point, rather being extended from that point.

I think I'll need to use FitMesh() or something? Or the "Laser" beam code in the archives which projected a mesh from point A to point B
Pretty sure I'm on the right lines...




Graphics3D 800,600,32,2
SetBuffer BackBuffer()

Global Cam=CreateCamera()
MoveEntity Cam,0,1,0
TurnEntity Cam,0,-45,0
AmbientLight 128,128,128
Global T=CreateTerrain(128)
EntityPickMode T,2

Global gh3d_CON_PROPOSAL
Global gn_CON_WALL_INIT_X
Global gn_CON_WALL_INIT_y
Global gn_CON_WALL_INIT_z

While (Not(KeyDown(1)))
	If MouseDown(1)
		v_CON_PlaceWall
	Else
		FreeEntity gh3d_CON_PROPOSAL
		gh3d_CON_PROPOSAL=0
	End If
	RenderWorld
	Flip
Wend



;RETURNS TRUE IF X and Y COORDINATES ARE APPROXIMATELY ALIGNED TO oY AXIS
Function nb_MATH_GetIsCoordinateAlignment(f_oX#,f_oY#,f_X#,f_Y#)
	If (f_oX=f_X) Return True
		If (f_oY=f_Y) Return False
			Local f_Orientation#=Abs(((ATan2(f_X-f_oX,f_Y-f_oY) )) )
			Local nb_Binary=( (f_Orientation>135) Or (f_Orientation<45) )
			Return nb_Binary
End Function

Function nb_TRN_MousepickTerrain()
	Return (CameraPick(Cam,MouseX(),MouseY())=T)
End Function

Function v_CON_PlaceWall()
	If (nb_TRN_MousepickTerrain())
		Local X=PickedX()
		Local Z=PickedZ()
		Local Y=TerrainY(T,X,0,Z)
		
		DebugLog("CON (Place Wall): Picked "+Str(X)+" , "+Str(Y))
		
		X=Floor(X)
		Y=Floor(Y)
		Z=Floor(Z)
		
		If (Not(gh3d_CON_PROPOSAL))
			
			DebugLog("CON (Place Wall): No current proposal.")
			
			gh3d_CON_PROPOSAL=CreateCube();CopyEntity(th3d_CON_WALLMESH_TEMPLATE)
			EntityColor gh3d_CON_PROPOSAL,255,0,0
			EntityAlpha gh3d_CON_PROPOSAL,0.5
			
			PositionEntity gh3d_CON_PROPOSAL,X,Y,Z,True
			gn_CON_WALL_INIT_X=X
			gn_CON_WALL_INIT_y=Y
			gn_CON_WALL_INIT_z=Z
			
			DebugLog("CON (Place Wall): Proposal created at "+Str(gn_CON_WALL_INIT_X)+" , "+Str(gn_CON_WALL_INIT_z)+" Altitude: "+Str(gn_CON_WALL_INIT_y))
			
			Return
		Else
		;	FreeEntity gh3d_CON_PROPOSAL
		;	gh3d_CON_PROPOSAL=CopyEntity(th3d_CON_WALLMESH_TEMPLATE)
		;	PositionEntity gh3d_CON_PROPOSAL,gn_CON_WALL_INIT_X,gn_CON_WALL_INIT_Y,gn_CON_WALL_INIT_Z,True
		End If
		
		Local Orient=nb_MATH_GetIsCoordinateAlignment(gn_CON_WALL_INIT_X,gn_CON_WALL_INIT_z,X,Z)
		
		;Z=(Z*(1-Orient))-gn_CON_WALL_INIT_X
		;X=(X*(Orient))-gn_CON_WALL_INIT_z
		
		If (Orient)
			DebugLog("CON (Place Wall): Aligned with proposal on Z axis")
			ScaleEntity gh3d_CON_PROPOSAL,1,1,Z,True
			;ScaleMesh gh3d_CON_PROPOSAL,1,1,Z
			DebugLog("CON (Place Wall): Scaled to: "+Str(MeshWidth(gh3d_CON_PROPOSAL)+" , "+Str(MeshHeight(gh3d_CON_PROPOSAL))+" , "+Str(MeshDepth(gh3d_CON_PROPOSAL))))
		Else
			DebugLog("CON (Place Wall): Aligned with proposal on X axis")
			ScaleEntity gh3d_CON_PROPOSAL,X,1,1,True
			;ScaleMesh gh3d_CON_PROPOSAL,X,1,1
			DebugLog("CON (Place Wall): Scaled to: "+Str(MeshWidth(gh3d_CON_PROPOSAL)+" , "+Str(MeshHeight(gh3d_CON_PROPOSAL))+" , "+Str(MeshDepth(gh3d_CON_PROPOSAL))))
		End If
		
	End If
End Function



_PJ_(Posted 2016) [#4]
I went for a different approach which at least place a wall and extends it in the correct direction.

However, I am still unable to see how I can get it to actually START at the 'marker' and END with the relevant cursor coord:

unction v_CON_ProposeWall(n_X,n_Y,n_Z)
	If (gh3d_CON_CURRENT_PROPOSAL_MESH)
		FreeEntity gh3d_CON_CURRENT_PROPOSAL_MESH
		gh3d_CON_CURRENT_PROPOSAL_MESH=0
	End If
	
	gh3d_CON_CURRENT_PROPOSAL_MESH=CopyEntity(th3d_CON_WALLMESH_TEMPLATE)
	ShowEntity gh3d_CON_CURRENT_PROPOSAL_MESH
	
	gm_CON_CURRENT_PROPOSAL_ORIENTATION=nb_MATH_GetIsCoordinateAlignment(gn_CON_CURRENT_PROPOSAL_X,gn_CON_CURRENT_PROPOSAL_Z,n_X,n_Z)
	
	Local ExtendX
	Local ExtendZ
	
	Select (gm_CON_CURRENT_PROPOSAL_ORIENTATION)
		Case cm_CON_ORIENTATION_HORIZONTAL:
			ExtendZ=(n_X-gn_CON_CURRENT_PROPOSAL_X)
			gn_CON_CURRENT_PROPOSAL_LENGTH=Abs(ExtendZ)
			ExtendZ=0-(ExtendZ*0.5)
			
			ExtendX=0
			
			TurnEntity gh3d_CON_CURRENT_PROPOSAL_MESH,0,90,0
			
		Case cm_CON_ORIENTATION_VERTICAL:
			ExtendX=(n_Z-gn_CON_CURRENT_PROPOSAL_Z)
			gn_CON_CURRENT_PROPOSAL_LENGTH=(Abs(ExtendX))
			ExtendX=0-(ExtendX*0.5)
			
			ExtendZ=0
			
	End Select
	
	PositionEntity gh3d_CON_CURRENT_PROPOSAL_MESH,gn_CON_CURRENT_PROPOSAL_X+ExtendX,gn_CON_CURRENT_PROPOSAL_Y,gn_CON_CURRENT_PROPOSAL_Z+ExtendZ,True
	ScaleEntity gh3d_CON_CURRENT_PROPOSAL_MESH,gn_CON_CURRENT_PROPOSAL_LENGTH,cf_CON_WALLMESH_UNITHEIGHT,1,True
	
End Function



Floyd(Posted 2016) [#5]
Conceptually I would look at the placement this way. You have a standard cube which you want to scale and position. It originally extends from -1 to +1 on each axis. Later calculations are a little simpler if you do this

ScaleMesh StandardCube, 0.5, 0.5, 0.5

Now it has length 1 on each axis and the center is still at (0,0,0).

Suppose you want a section of wall to extend in the X direction from 5 to 9. This has length 4 and center 7. So you start with a copy of StandardCube, scale X by 4 so it has length 4 in the X direction, then place it at X = 7, i.e. place the center at the center of the wall segment.

Similarly, if X is to go from A to B then the length is B-A and the center is 0.5*(A+B). I doubt it matters if length is negative ( A bigger than B ), but if it does you can use Abs(B-A) instead.

Then scale X by (B-A) and position at X=0.5*(A+B).


_PJ_(Posted 2016) [#6]
Thanks Floyd. That makes it all so much clearer.
I think I confused myself positioning the object at the cursor offset instead of at the origin as well as forgetting the initial, local (X,Z) dimensions of the primitive are centred around 0.0 rather than extending FROM 0,0.

I am actually using a flat quad, so that's why there's a RotateMesh (since the template has no actual Z dimension to scale) which also made things a little (possibly unnecessarily) more complicated.

Anyway, I've gotten it working beautifully now:

Function v_CON_ProposeWall(n_X,n_Y,n_Z)
	If (gh3d_CON_CURRENT_PROPOSAL_MESH)
		FreeEntity gh3d_CON_CURRENT_PROPOSAL_MESH
		gh3d_CON_CURRENT_PROPOSAL_MESH=0
	End If
	
	gh3d_CON_CURRENT_PROPOSAL_MESH=h3d_CON_GetWallTemplateCopy()
	
	gm_CON_CURRENT_PROPOSAL_ORIENTATION=nb_MATH_GetIsCoordinateAlignment(gn_CON_CURRENT_PROPOSAL_X,gn_CON_CURRENT_PROPOSAL_Z,n_X,n_Z)
	
	Select (gm_CON_CURRENT_PROPOSAL_ORIENTATION)
		Case cm_CON_ORIENTATION_HORIZONTAL:
			gn_CON_CURRENT_PROPOSAL_LENGTH=n_Z - gn_CON_CURRENT_PROPOSAL_Z
			ScaleMesh gh3d_CON_CURRENT_PROPOSAL_MESH,gn_CON_CURRENT_PROPOSAL_LENGTH,1,1
			
			RotateMesh gh3d_CON_CURRENT_PROPOSAL_MESH,0,90,0
			
		Case cm_CON_ORIENTATION_VERTICAL:
			gn_CON_CURRENT_PROPOSAL_LENGTH=n_X-gn_CON_CURRENT_PROPOSAL_X
			
			ScaleMesh gh3d_CON_CURRENT_PROPOSAL_MESH,gn_CON_CURRENT_PROPOSAL_LENGTH,1,1
			
	End Select
	
	PositionEntity gh3d_CON_CURRENT_PROPOSAL_MESH,gn_CON_CURRENT_PROPOSAL_X,gn_CON_CURRENT_PROPOSAL_Y,gn_CON_CURRENT_PROPOSAL_Z,True
	
End Function