I have reported the issue on the recast site and have been in discussion with Mikko (recast developer).
Anyway, I have done some further testing and found that the problem only seems to exist with my test world. I'm not sure if my test world is just corrupted and needs to be rebuilt from scratch, or if it just happens to be exposing a bug in a certain way.
I have attached my test world to this email, which you can load with a fresh install of the SDK. I then modified the World tutorial in the Game Basics folder like this below. This will load the test world and then you can perform ray casts in the world by holding down the left mouse button and dragging the mouse. It will ray cast between your start position and the mouse position and draw a line. If the line is white, then the ray cast passed. If it is red, then the ray cast failed.
Try doing a ray cast from one area to another above the rocks in the test world. You will see that it fails as soon as it cross the boundary between one area and the other. I have also noticed, that if you load the test world in the Editor and remove the three rocks, then all the ray casts work properly again. Even after putting the rocks back in, they still work correctly.
So this issue only seems to be happening with this test world exactly the way it is???
Code:
/******************************************************************************/
#include "stdafx.h"
/******************************************************************************
When writing applications with worlds, you need to additionaly include all the Headers generated by the World Editor
these Headers are stored in the "enum/_enums.h" file in your Game Data folder
for more information about Enums and Headers please check World Editor documentation.
/******************************************************************************/
#include "../../../../../data/enum/_enums.h"
/******************************************************************************/
Game::ObjMemx<Game::Static> Statics; // container for static objects
Game::ObjMemx<Game::Item > Items; // container for item objects
Game::ObjMemx<Game::Chr > Chrs; // container for character objects
Vec start,end,hit_pos;
/******************************************************************************/
void InitPre()
{
App.name("World Manager");
App.flag=APP_FULL_TOGGLE;
DataPath("../data");
Paks.add("engine.pak");
D.full(false).sync(true).hpRt(true).hdr(true);
Cam.dist =10;
Cam.yaw =-PI_4;
Cam.pitch=-0.5f;
Cam.at.set(16,0,16);
}
/******************************************************************************/
Bool Init()
{
Physics.create(CSS_NONE, true, "../Installation/PhysX");
Sun.image=Images("gfx/sky/sun.gfx");
Sky.atmospheric();
Game::World.init(); // initialize world, optionally you can change default parameters here
// once the world is initialized, we need to tell the world 'which class handles which type'
// this is done by assigning memory containers to certain Object Types defined in Game Enums (which were used in the World Editor)
Game::World.setObjType(Statics, OBJ_STATIC) // set 'Statics' memory container for 'OBJ_STATIC' objects
.setObjType(Items , OBJ_ITEM ) // set 'Items' memory container for 'OBJ_ITEM' objects
.setObjType(Chrs , OBJ_PLAYER); // set 'Chrs' memory container for 'OBJ_PLAYER' objects
// now when the engine is set up properly we can start a 'new game' with a builded world
Game::World.New("world/test.world"); // create the world by giving path to builded world
// when the world is loaded it doesn't actually load all the terrain and objects into memory
// it loads only information about them
// you need to tell the world which terrain and objects you need to use at the moment
// to do that call:
Game::World.update(Cam.at); // which updates world to use only terrain and objects at given position, here camera position is used
return true;
}
/******************************************************************************/
void Shut()
{
}
/******************************************************************************/
Bool Update()
{
if(Kb.bp(KB_ESC))return false;
CamHandle(0.1f, 100, CAMH_ZOOM|(Ms.b(1)?CAMH_ROT:0));
Game::World.update(Cam.at); // update the world to given position
if(Ms.bp(0))
{
PhysHit physHit;
Vec screenPos, screenDir;
ScreenToPosDir(Ms.pos(),screenPos,screenDir);
if(Physics.ray(screenPos,screenDir*D.viewRange(),&physHit,IndexToFlag(AG_TERRAIN)))
start = end = physHit.plane.pos;
}
else if(Ms.b(0))
{
PhysHit physHit;
Vec screenPos, screenDir;
ScreenToPosDir(Ms.pos(),screenPos,screenDir);
if(Physics.ray(screenPos,screenDir*D.viewRange(),&physHit,IndexToFlag(AG_TERRAIN)))
end = physHit.plane.pos;
Flt hit_frac;
Vec hit_normal;
hit_pos.zero();
Game::World.path().ray(start,end,&hit_frac,&hit_pos,&hit_normal);
}
return true;
}
/******************************************************************************/
void Render()
{
Game::World.draw(); // draw world (this is done outside of 'switch(Renderer())' because world automatically detects active rendering mode)
}
void Draw()
{
Renderer(Render);
Game::World.path().draw(64,0.1f);
if(Ms.b(0))
{
D.clearZ();
if(end == hit_pos)
D.line(WHITE,start,end);
else
D.line(RED,start,end);
}
}
/******************************************************************************/