Hi guys,
is it an expected behaviour the RayCast recognises an entity that is inside another entity? For example if you have a box with a smaller sphere in it and you make a RayCast from the outside of the box in direction of the sphere, the box is not recognised by RayCast. If you put the box between the RayCast origin and the sphere, the box is picked. It's like RayCast is working with the backside of entities. Should it work this way or is that a bug?
Regards
MAC
RayCast does not work if entity is inside another one?
Re: RayCast does not work if entity is inside another one?
I thought I described quite well what the problem is, but I have some code to show the problem in practice.
I modified my dijkstra code example a bit and made one of the walls bigger. Run the code and move the vertical wall left and right by using the cursor keys left/right. As you can see the waypoints that are inside the wall are picked thought the RayCast should pick the wall instead until you move the wall so that the waypoints are completely behind it, only then the wall is picked and not the waypoints on the left. Picked objects are visualized by a Line3D. I know the code is a bit overloaded for an example but it shows the problem.
I modified my dijkstra code example a bit and made one of the walls bigger. Run the code and move the vertical wall left and right by using the cursor keys left/right. As you can see the waypoints that are inside the wall are picked thought the RayCast should pick the wall instead until you move the wall so that the waypoints are completely behind it, only then the wall is picked and not the waypoints on the left. Picked objects are visualized by a Line3D. I know the code is a bit overloaded for an example but it shows the problem.
Code: Select all
InitEngine3D()
InitSprite()
InitKeyboard()
Structure Str_PathConnection
*Waypoint
Weight.d
EndStructure
Structure Str_PathWaypoint
Entity.l
EndStructure
Structure Str_Path
*Waypoint
*Predecessor
Distance.d
Visited.c
List Connection.Str_PathConnection()
Nr.l
EndStructure
Global NewList Waypoint.Str_PathWaypoint()
Global NewList Path.Str_Path()
#WallPick = 1 << 1
#WaypointPick = 1 << 2
OpenWindow(0,100,100,800,600,"Test")
OpenWindowedScreen(WindowID(0),0,0,800,600)
; Camera
CreateCamera(1,0,0,100,100)
MoveCamera(1,0,100,100,#PB_Relative)
RotateCamera(1,-50,0,0)
; Textures
CreateTexture(0,256,256)
StartDrawing(TextureOutput(0))
Box(0,0,255,255,RGB(200,0,0))
StopDrawing()
CreateTexture(1,256,256)
StartDrawing(TextureOutput(1))
Box(0,0,255,255,RGB(0,0,200))
StopDrawing()
CreateTexture(2,256,256)
StartDrawing(TextureOutput(2))
Box(0,0,255,255,RGB(0,200,200))
StopDrawing()
CreateTexture(3,256,256)
StartDrawing(TextureOutput(3))
Box(0,0,255,255,RGB(0,200,0))
StopDrawing()
; Materials
CreateMaterial(0,TextureID(0))
CreateMaterial(1,TextureID(1))
CreateMaterial(2,TextureID(2))
CreateMaterial(3,TextureID(3))
; Floor
CreatePlane(2,100,100,10,10,1,1)
CreateEntity(3,MeshID(2),MaterialID(0))
; Walls
CreateCube(4,10)
CreateEntity(5,MeshID(4),MaterialID(1),0,0,0,#WallPick)
ScaleEntity(5,5,2,0.3)
CreateEntity(8,MeshID(4),MaterialID(1),-10,0,0,#WallPick)
ScaleEntity(8,3,2,8);ScaleEntity(8,0.3,2,8)
; Light
CreateLight(6,RGB(255,255,255),0,100,0)
AmbientColor(RGB(25, 25, 25))
; Waypoint, start and aim mesh
Global WaypointMesh=CreateCylinder(#PB_Any,0.2,15)
Hero=CreateEntity(#PB_Any,MeshID(WaypointMesh),MaterialID(2),10,0,10,#WaypointPick)
ScaleEntity(Hero,3,1,3)
Aim=CreateEntity(#PB_Any,MeshID(WaypointMesh),MaterialID(3),0,0,-20,#WaypointPick)
ScaleEntity(Aim,3,1,3)
; This procedure creates a waypoint graph (Path()) with all predifined waypoints in it and the start and aim mesh
; and inits the dijkstra algorithm
; StartMesh = The mesh to walk the path
; AimMesh = A mesh that represents the aim position
; YOffset = An optional offset on the y-axis (use this to make sure the line of sight does not interfere with the ground)
Procedure Pathfinding_Init(StartMesh,AimMesh,YOffset.d=0)
Protected *TempWaypoint.Str_Path
Protected LineCount=0
ClearList(Path())
; Destroy all former 3d lines
For t=100 To 150
If IsMesh(t)
FreeMesh(t)
EndIf
Next
; Add Start and Aim to the waypoint graph
AddElement(Path())
Path()\Waypoint=StartMesh
Path()\Predecessor=-1
Path()\Distance=0
Path()\Nr=0
AddElement(Path())
Path()\Waypoint=AimMesh
Path()\Predecessor=-1
Path()\Distance=Infinity()
Path()\Nr=1
; Add all predifined waypoints to the graph
Nr=2
ForEach Waypoint()
AddElement(Path())
Path()\Waypoint=Waypoint()\Entity
Path()\Predecessor=-1
Path()\Distance=Infinity()
Path()\Nr=Nr
Nr+1
Next
; Render the world to have an up-to-date world state for using RayCast()
RenderWorld()
; Check all connections between waypoints
ForEach Path()
; ClearList(Path()\Connection())
*TempWaypoint=@Path()
PushListPosition(Path())
ForEach Path()
If @Path()<>*TempWaypoint
; Check line of sight with a ray cast
Res=RayCast(EntityX(*TempWaypoint\Waypoint),EntityY(*TempWaypoint\Waypoint)+YOffset,EntityZ(*TempWaypoint\Waypoint),EntityX(Path()\Waypoint)-EntityX(*TempWaypoint\Waypoint),0,EntityZ(Path()\Waypoint)-EntityZ(*TempWaypoint\Waypoint),#WallPick|#WaypointPick)
; Claculate the distance between the waypoint to have a weighted connection
DistWaypoint.d=Sqr(Pow(EntityX(*TempWaypoint\Waypoint)-EntityX(Path()\Waypoint),2.0) + Pow(EntityY(*TempWaypoint\Waypoint)-EntityY(Path()\Waypoint),2.0) + Pow(EntityZ(*TempWaypoint\Waypoint)-EntityZ(Path()\Waypoint),2.0))
; Debug "Cast ("+StrD(EntityX(*TempWaypoint\Waypoint))+", "+StrD(EntityY(*TempWaypoint\Waypoint))+", "+StrD(EntityZ(*TempWaypoint\Waypoint))+") - ("+StrD(EntityX(Path()\Waypoint))+", "+StrD(EntityY(Path()\Waypoint))+", "+StrD(EntityZ(Path()\Waypoint))+")"
If Res=Path()\Waypoint
; Add the found connection to the connection list of the currently tested waypoint
AddElement(*TempWaypoint\Connection())
*TempWaypoint\Connection()\Waypoint=@Path()\Waypoint
*TempWaypoint\Connection()\Weight=DistWaypoint
; Also add the connection to the list of connections of the waypoint that was found if it is not already in the list
Found=0
ForEach Path()\Connection()
If Path()\Connection()\Waypoint=@*TempWaypoint\Waypoint
Found=1
Break
EndIf
Next
If Found=0
AddElement(Path()\Connection())
Path()\Connection()\Waypoint=@*TempWaypoint\Waypoint
Path()\Connection()\Weight=DistWaypoint
EndIf
; Show the connection as a 3d line
CreateLine3D(100+LineCount,EntityX(*TempWaypoint\Waypoint),EntityY(*TempWaypoint\Waypoint)+YOffset,EntityZ(*TempWaypoint\Waypoint),RGB(255,255,255),EntityX(Path()\Waypoint),EntityY(Path()\Waypoint)+YOffset,EntityZ(Path()\Waypoint),RGB(255,255,255))
LineCount+1
EndIf
EndIf
Next
PopListPosition(Path())
Next
EndProcedure
; This is the dijkstra algorithm
; The shortest path can be found after using this procedure by starting with the aim mesh in the Path() list and check all predecessors until the start mesh is found
; StartMesh = The mesh to walk the path
; AimMesh = A mesh that represents the aim position
Procedure Pathfinding_FindPath(StartMesh,AimMesh)
; Repeat until all graph entries are visited
Repeat
Searching=0
Dist.d=Infinity()
; Find the waypoint with the lowest distance
ForEach Path()
If Path()\Visited=0
If Path()\Distance<Dist
Searching=1
*CurrentWaypoint.Str_Path=@Path()
Dist=Path()\Distance
EndIf
EndIf
Next
If Searching=1
*CurrentWaypoint\Visited=1
ForEach *CurrentWaypoint\Connection()
*NextWaypoint.Str_Path=*CurrentWaypoint\Connection()\Waypoint
If *NextWaypoint\Visited=0
If *CurrentWaypoint\Distance+*CurrentWaypoint\Connection()\Weight<*NextWaypoint\Distance
*NextWaypoint\Distance=*CurrentWaypoint\Distance+*CurrentWaypoint\Connection()\Weight
*NextWaypoint\Predecessor=*CurrentWaypoint\Waypoint
EndIf
EndIf
Next
EndIf
Until Searching=0
EndProcedure
; Parses the Path() list from end to start and prints the steps in the debug window
Procedure Pathfinding_ShowPath(StartMesh,AimMesh)
ForEach Path()
If Path()\Waypoint=AimMesh
*Temp.Str_Path=@Path()
; Check if path is found or not
If Path()\Predecessor=-1
Debug "There is no path!"
Else
; Show path from aim to start (found by the predecessors)
Repeat
Debug "No: "+Str(*temp\Nr)+" Pos: "+StrD(EntityX(*Temp\Waypoint))+", "+StrD(EntityY(*Temp\Waypoint))+", "+StrD(EntityZ(*Temp\Waypoint))+" - Predecessor: "+Str(*Temp\Predecessor)
If *Temp\Predecessor=-1
; The start is reached -> break loop
Break
EndIf
ForEach Path()
If Path()\Waypoint=*Temp\Predecessor
*Temp=@Path()
Break
EndIf
Next
ForEver
Break
EndIf
EndIf
Next
Debug "-----------------------------------"
EndProcedure
; Add a waypoint
Procedure Pathfinding_AddWaypoint(x.d,y.d,z.d)
AddElement(Waypoint())
Waypoint()\Entity=CreateEntity(#PB_Any,MeshID(WaypointMesh),MaterialID(1),x,y,z,#WaypointPick)
ScaleEntity(Waypoint()\Entity,3,1,3)
EndProcedure
; Add some waypoints
Pathfinding_AddWaypoint(30,0,0)
Pathfinding_AddWaypoint(-40,0,5)
Pathfinding_AddWaypoint(-30,0,-30)
; Render world has to be called once, otherwise RayCast does not work
RenderWorld()
; Make a first pathfinding
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
; Create overlay sprite for text output
Overlay=CreateSprite(#PB_Any,800,600)
StartDrawing(SpriteOutput(Overlay))
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(0,0,"Use cursor keys to move the walls.")
DrawText(0,15,"Use space key to calculate shortest path.")
DrawText(0,30,"Use D key to show distance table.")
Txt.s="Light blue is the start mesh (No. 0)"
DrawText(ScreenWidth()-TextWidth(Txt),0,Txt)
Txt.s="Green is the aim mesh (No. 1)"
DrawText(ScreenWidth()-TextWidth(Txt),15,Txt)
ForEach Path()
DrawText(CameraProjectionX(1,EntityX(Path()\Waypoint),EntityY(Path()\Waypoint),EntityZ(Path()\Waypoint)),CameraProjectionY(1,EntityX(Path()\Waypoint),EntityY(Path()\Waypoint),EntityZ(Path()\Waypoint)),"("+StrD(EntityX(Path()\Waypoint))+", "+StrD(EntityY(Path()\Waypoint))+", "+StrD(EntityZ(Path()\Waypoint))+") - No: "+Str(Path()\Nr))
Next
StopDrawing()
; Main loop
Repeat
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Left)
MoveEntity(8,-1,0,0,#PB_Relative)
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
EndIf
If KeyboardPushed(#PB_Key_Right)
MoveEntity(8,1,0,0,#PB_Relative)
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
EndIf
If KeyboardPushed(#PB_Key_Down)
MoveEntity(5,0,0,1,#PB_Relative)
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
EndIf
If KeyboardPushed(#PB_Key_Up)
MoveEntity(5,0,0,-1,#PB_Relative)
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
EndIf
If KeyboardReleased(#PB_Key_Space)
Pathfinding_ShowPath(Hero,Aim)
EndIf
If KeyboardReleased(#PB_Key_D)
Pathfinding_Init(Hero,Aim,4)
Pathfinding_FindPath(Hero,Aim)
ForEach Path()
Debug "No: "+Str(Path()\Nr)+" - Distance to start mesh: "+StrD(Path()\Distance)
Next
Debug "-----------------------------------"
EndIf
RenderWorld()
DisplayTransparentSprite(Overlay,0,0)
FlipBuffers()
Until WindowEvent()=#PB_Event_CloseWindow
Re: RayCast does not work if entity is inside another one?
Hi guys,
I found out what the Problem is: RayCast does not recognise entities from inside of them. What I mean is that if you cast a ray from inside an entity to the outside it won't work, and as I use a bi-directional waypoint checking in my example the connection shown is from the waypoint inside the wall to the waypoint outside of the wall and not the other way. I found this out with another example I wrote:
So another question rises in me: Is there a way to cast an entity from the inside? For example to detect the walls of a room?
EDIT: I tried with different material culling modes, but that has no effect.
I found out what the Problem is: RayCast does not recognise entities from inside of them. What I mean is that if you cast a ray from inside an entity to the outside it won't work, and as I use a bi-directional waypoint checking in my example the connection shown is from the waypoint inside the wall to the waypoint outside of the wall and not the other way. I found this out with another example I wrote:
Code: Select all
InitEngine3D()
InitSprite()
OpenWindow(0,100,100,800,600,"Test")
OpenWindowedScreen(WindowID(0),0,0,800,600)
; Camera
CreateCamera(1,0,0,100,100)
MoveCamera(1,0,100,100,#PB_Relative)
RotateCamera(1,-50,0,0)
; Textures
CreateTexture(0,256,256)
StartDrawing(TextureOutput(0))
Box(0,0,255,255,RGB(255,255,255))
StopDrawing()
CreateMaterial(0,TextureID(0))
CreateMaterial(1,TextureID(0))
SetMaterialColor(0,#PB_Material_DiffuseColor,RGBA(255,0,0,100))
SetMaterialColor(1,#PB_Material_DiffuseColor,RGBA(255,0,0,100))
; Light
CreateLight(6,RGB(255,255,255),0,50,0)
AmbientColor(RGB(25, 25, 25))
Cube=CreateCube(#PB_Any,10)
Sphere=CreateCylinder(#PB_Any,2,20)
Plane=CreatePlane(#PB_Any,100,100,1,1,1,1)
CubeEnt=CreateEntity(#PB_Any,MeshID(Cube),MaterialID(0))
SphereEnt=CreateEntity(#PB_Any,MeshID(Sphere),MaterialID(1))
PlaneEnt=CreateEntity(#PB_Any,MeshID(Plane),#PB_Material_None)
MoveEntity(PlaneEnt,0,0,-20)
RotateEntity(PlaneEnt,90,0,0,#PB_Absolute)
Dir=1
Speed.f=0.2
x.f=0
Repeat
If Dir=1
x+Speed
Else
x-Speed
EndIf
If x>40 Or x<-10
Dir=1-Dir
EndIf
MoveEntity(CubeEnt,x,0,0,#PB_Absolute)
Res=RayCast(30,0,0,-1,0,0,-1)
If Res>0
If Material<>0
SetMaterialColor(Material,#PB_Material_SelfIlluminationColor,RGB(0,0,0))
FreeMaterial(Material)
EndIf
Material=FetchEntityMaterial(Res,#PB_Any)
SetMaterialColor(Material,#PB_Material_SelfIlluminationColor,RGB(0,255,0))
EndIf
CreateLine3D(0,30,0,0,RGB(255,255,255),PickX(),PickY(),PickZ(),RGB(255,255,255))
RenderWorld()
FlipBuffers()
Until WindowEvent()=#PB_Event_CloseWindow
EDIT: I tried with different material culling modes, but that has no effect.
