Lua, OpenSteer port
4 posters
Lua, OpenSteer port
Hi, I've ported some of the OpenSteer library to Lua and wanted to share in the hopes that someone will find this useful and perhaps complete some of the other OpenSteer functions. I'm stuck on how to cast a ray from within Lua, for example, so someone who knows Lua or SWIG better can perhaps take this forward.
This sample code will allow you to add a physics object to Tutorial10 that is steered around a loop, for example. There are three parts:
(1) A Lua script to add to the object per the directions in Tutorial10
(2) A Python script to extract a Lua path table from a circular mesh in Blender
(3) A SIO2 routine to pass velocity to the Lua function and handle the steering vector
This may eventually wind up being easier to do inside of an engine, but I like the idea of having individual objects contain their own AI.
Thanks,
--yarri
PS: Opensteer library docs, here...
http://opensteer.sourceforge.net/doc.html
Part (1), the Lua script....
Part (2), the Python script...
Part (3), the SIO2 control function inside sio2WindowLeaveLandscape3D()...
This sample code will allow you to add a physics object to Tutorial10 that is steered around a loop, for example. There are three parts:
(1) A Lua script to add to the object per the directions in Tutorial10
(2) A Python script to extract a Lua path table from a circular mesh in Blender
(3) A SIO2 routine to pass velocity to the Lua function and handle the steering vector
This may eventually wind up being easier to do inside of an engine, but I like the idea of having individual objects contain their own AI.
Thanks,
--yarri
PS: Opensteer library docs, here...
http://opensteer.sourceforge.net/doc.html
Part (1), the Lua script....
- Code:
Bot = {};
Bot.obj = SIO2.sio2ResourceGetObject( SIO2.sio2._SIO2resource, "object/bot" );
-- Local registers
Bot.vec = SIO2.sio2Vec3Init();
Bot.vec1 = SIO2.sio2Vec3Init();
Bot.vec2 = SIO2.sio2Vec3Init();
Bot.vec3 = SIO2.sio2Vec3Init();
Bot.vec4 = SIO2.sio2Vec3Init();
Bot.vec5 = SIO2.sio2Vec3Init();
Bot.vec6 = SIO2.sio2Vec3Init();
Bot.vec7 = SIO2.sio2Vec3Init();
Bot.vecZero = SIO2.sio2Vec3Init();
-- Reference vector
Bot.vecZero.x = 0.0;
Bot.vecZero.y = 0.0;
Bot.vecZero.z = 0.0;
-- Bot Steering Defaults
Bot.targetSpeed = 10.0;
Bot.breakingForce = 0.5;
Bot.speed = 10.0;
Bot.predictionTime = 0.5;
Bot.radius = 6.0;
Bot.direction = -1;
Bot.pathindex = 1;
Bot.PathX = {};
Bot.PathY = {};
Bot.PathZ = {};
table.insert(Bot.PathX,-12.678687); table.insert(Bot.PathY,-0.704995); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-12.405396); table.insert(Bot.PathY,1.300526); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-11.724273); table.insert(Bot.PathY,3.213867); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-10.650522); table.insert(Bot.PathY,4.958889); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-9.213523); table.insert(Bot.PathY,6.472259); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-7.456849); table.insert(Bot.PathY,7.703459); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-5.438245); table.insert(Bot.PathY,8.614784); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-3.229649); table.insert(Bot.PathY,9.181347); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-0.917173); table.insert(Bot.PathY,9.391068); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,1.400265); table.insert(Bot.PathY,9.244518); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,3.626143); table.insert(Bot.PathY,8.753069); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,5.681536); table.insert(Bot.PathY,7.937060); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,7.506485); table.insert(Bot.PathY,6.825620); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,9.060017); table.insert(Bot.PathY,5.456678); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,10.320121); table.insert(Bot.PathY,3.876957); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,11.283770); table.insert(Bot.PathY,2.141973); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,11.966908); table.insert(Bot.PathY,0.316041); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,12.403264); table.insert(Bot.PathY,-1.528662); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,12.631278); table.insert(Bot.PathY,-3.322323); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,12.681028); table.insert(Bot.PathY,-5.007728); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,12.573030); table.insert(Bot.PathY,-6.541186); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,12.318259); table.insert(Bot.PathY,-7.892548); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,11.918127); table.insert(Bot.PathY,-9.045170); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,11.364498); table.insert(Bot.PathY,-9.995960); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,10.639685); table.insert(Bot.PathY,-10.755341); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,9.717466); table.insert(Bot.PathY,-11.346323); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,8.574410); table.insert(Bot.PathY,-11.794176); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,7.201181); table.insert(Bot.PathY,-12.116089); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,5.603547); table.insert(Bot.PathY,-12.320230); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,3.802409); table.insert(Bot.PathY,-12.405756); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,1.833784); table.insert(Bot.PathY,-12.362793); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-0.251194); table.insert(Bot.PathY,-12.172461); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-2.386273); table.insert(Bot.PathY,-11.806856); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-4.491139); table.insert(Bot.PathY,-11.230139); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-6.483099); table.insert(Bot.PathY,-10.410519); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-8.288757); table.insert(Bot.PathY,-9.332218); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-9.845076); table.insert(Bot.PathY,-7.996544); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-11.099375); table.insert(Bot.PathY,-6.421920); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-12.009325); table.insert(Bot.PathY,-4.643858); table.insert(Bot.PathZ,0.000000);
table.insert(Bot.PathX,-12.542964); table.insert(Bot.PathY,-2.714977); table.insert(Bot.PathZ,0.000000);
-- The function that we are going to use inside our rendering loop.
function Bot.render()
-- Call steering functions
Bot.vec1.x = Bot.obj._SIO2transform.dir.x;
Bot.vec1.y = Bot.obj._SIO2transform.dir.y;
Bot.vec1.z = Bot.obj._SIO2transform.dir.z;
-- print("getCurrentVelocity x: " .. Bot.vec1.x .. " y: " .. Bot.vec1.y .. " z: " .. Bot.vec1.z );
Bot.vec.x = Bot.obj._SIO2transform.loc.x;
Bot.vec.y = Bot.obj._SIO2transform.loc.y;
Bot.vec.z = Bot.obj._SIO2transform.loc.z;
-- print("getCurrentLocation x: " .. Bot.vec.x .. " y: " .. Bot.vec.y .. " z: " .. Bot.vec.z );
Bot.vec = Bot.steerToFollowPathLinear ( Bot.direction, Bot.predictionTime );
Bot.obj.userdata = Bot.vec;
end
function Bot.steerToFollowPathLinear ( direction, predictionTime )
-- Update steering to stay on path with linear interpolation
-- print("steerToFollowPathLinear dir:" .. direction .. " t:" .. predictionTime .. " mag:" .. SIO2.sio2Magnitude( Bot.vec1 ) );
-- Find waypoint nearest to current object location
index = Bot.mapPointAndDirectionToTangent( Bot.vec );
nextindex = index + direction;
if (nextindex > table.maxn(Bot.PathX)) then
-- wrap around path for cyclic curves
nextindex = 1;
end
if (nextindex == 0) then
nextindex = table.maxn(Bot.PathX);
end
Bot.vec4.x = Bot.PathX[ index ];
Bot.vec4.y = Bot.PathY[ index ];
Bot.vec4.z = Bot.PathZ[ index ];
if ( SIO2.sio2Distance(Bot.vec, Bot.vec4) > Bot.radius ) then
-- print("Current location outside path, head straight towards nearest waypoint: " .. index );
return Bot.steerForSeek (Bot.vec4);
end
-- print("mapPointAndDirectionToTangentNow " .. index .. " " .. nextindex );
-- our goal will be offset from our path distance by this amount
-- const float pathDistanceOffset = direction * predictionTime * speed();
pathDistanceOffset = direction * predictionTime * SIO2.sio2Magnitude( Bot.vec1 );
Bot.vec2 = Bot.predictFuturePosition( pathDistanceOffset );
-- print("predictFuturePosition x: " .. Bot.vec2.x .. " y: " .. Bot.vec2.y .. " z: " .. Bot.vec2.z );
indexFuture = Bot.mapPointAndDirectionToTangent( Bot.vec2 );
if ( direction > 0) then
if (indexFuture == table.maxn(Bot.PathX)) then
indexFuture = 1;
end
if (indexFuture < nextindex) then
indexFuture = nextindex;
end
if (indexFuture > nextindex + 4) then
indexFuture = nextindex;
end
end
if ( direction < 0) then
if (indexFuture == 1) then
indexFuture = table.maxn(Bot.PathX);
end
if (indexFuture > nextindex) then
indexFuture = nextindex;
end
if (indexFuture < nextindex - 4) then
indexFuture = nextindex;
end
end
-- print("mapPointAndDirectionToTangentFuture " .. indexFuture );
Bot.vec4.x = Bot.PathX[ indexFuture ];
Bot.vec4.y = Bot.PathY[ indexFuture ];
Bot.vec4.z = Bot.PathZ[ indexFuture ];
steering = Bot.steerForSeek ( Bot.vec4 );
-- print("steerForSeek.Path x: " .. Bot.vec4.x .. " y: " .. Bot.vec4.y .. " z: " .. Bot.vec4.z );
--steering = Bot.vec4;
return steering;
end
function Bot.predictFuturePosition( predictionTime )
-- Assume linear motion so return position() + (velocity() * predictionTime);
-- Ideally get velocity from Bullet
Bot.vec2.x = Bot.obj._SIO2transform.loc.x + (Bot.vec1.x * predictionTime);
Bot.vec2.y = Bot.obj._SIO2transform.loc.y + (Bot.vec1.y * predictionTime);
Bot.vec2.z = Bot.obj._SIO2transform.loc.z + (Bot.vec1.z * predictionTime);
return Bot.vec2;
end
function Bot.mapPointAndDirectionToTangent ( Pos )
-- Search path table, return index of point closest to bot object
index = 1;
Bot.vec5.x = Bot.PathX[ index ];
Bot.vec5.y = Bot.PathY[ index ];
Bot.vec5.z = Bot.PathZ[ index ];
for x=2,table.maxn(Bot.PathX) do
-- Iterate over points in the table
Bot.vec6.x = Bot.PathX[ x ];
Bot.vec6.y = Bot.PathY[ x ];
Bot.vec6.z = Bot.PathZ[ x ];
d1 = SIO2.sio2Distance( Pos, Bot.vec6 );
d2 = SIO2.sio2Distance( Pos, Bot.vec5 );
-- print("Check distance x: " .. x .. " one: " .. d1 .. " < two: " .. d2 );
if d1 < d2 then
-- print("Update distance x: " .. x .. " one: " .. d1 .. " < two: " .. d2 );
index = x;
Bot.vec5.x = Bot.PathX[ index ];
Bot.vec5.y = Bot.PathY[ index ];
Bot.vec5.z = Bot.PathZ[ index ];
end
end
return index;
end
function Bot.steerForSeek ( target )
-- Vec3 desiredVelocity = target - position();
-- return desiredVelocity - velocity();
SIO2.sio2Vec3Diff( target, Bot.vec, Bot.vec3 );
--SIO2.sio2Vec3Diff( Bot.vec3, Bot.vec1, Bot.vec4 );
SIO2.sio2Normalize( Bot.vec3, Bot.vec3 );
desiredVelocity = Bot.targetSpeed - SIO2.sio2Magnitude( Bot.vec1 );
if ( desiredVelocity < -2.0 ) then
-- print("steerForSeek: apply brakes");
desiredVelocity = Bot.breakingForce;
end
Bot.vec7.x = Bot.vec3.x * desiredVelocity;
Bot.vec7.y = Bot.vec3.y * desiredVelocity;
Bot.vec7.z = Bot.vec3.z * desiredVelocity;
return Bot.vec7;
end
-- Another function here for "best practices",
-- in order to deallocate the memory that we
-- have assigned in the script initialization.
function Bot.release()
-- Dealloc the memory previously allocated... Even
-- from LUA you can create memory leaks... so be
-- extra carefull here...
SIO2.sio2Vec3Free( Bot.vec );
Bot.vec = 0;
SIO2.sio2Vec3Free( Bot.vec1 );
Bot.vec1 = 0;
SIO2.sio2Vec3Free( Bot.vec2 );
Bot.vec2 = 0;
SIO2.sio2Vec3Free( Bot.vec3 );
Bot.vec3 = 0;
SIO2.sio2Vec3Free( Bot.vec4 );
Bot.vec4 = 0;
SIO2.sio2Vec3Free( Bot.vecZero );
Bot.vecZero = 0;
end
Part (2), the Python script...
- Code:
#!BPY
import Blender
import bpy
def write_obj(filepath):
out = file(filepath, 'w')
sce = bpy.data.scenes.active
ob = sce.objects.active
mesh = ob.getData(mesh=1)
i = 0
for vert in mesh.verts:
i = i + 1
out.write( 'table.insert(Bot.PathX,%f); table.insert(Bot.PathY,%f); table.insert(Bot.PathZ,%f);\n' % (vert.co.x, vert.co.y, vert.co.z) )
out.close()
Blender.Window.FileSelector(write_obj, "Export")
Part (3), the SIO2 control function inside sio2WindowLeaveLandscape3D()...
- Code:
SIO2object *bot = sio2ResourceGetObject(sio2->_SIO2resource, "object/bot");
bot->_SIO2transform->dir = (vec3 *)&bot->_btRigidBody->getLinearVelocity();
sio2ExecLUA( "Bot.render();" );
if (bot->userdata) {
vec3 *botvec;
botvec = (vec3 *)bot->userdata;
bot->_btRigidBody->applyCentralImpulse( btVector3(botvec->x,botvec->y,bot->_btRigidBody->getLinearVelocity()[2]) );
}
sio2ResourceRender( sio2->_SIO2resource, sio2->_SIO2window, sio2->_SIO2camera,
SIO2_RENDER_SOLID_OBJECT | SIO2_RENDER_CLIPPED_OBJECT);
yarri- Posts : 81
Join date : 2009-04-10
Re: Lua, OpenSteer port
Thanks for posting this Yarri. Great work.
This is very interesting and useful indeed.
Regarding Ray casting from LUA, couldn't you just call a C function as part of the LUA logic?
By creating a specific LUA module (name.module) using swig (from inside the lua directory), I guess anything would be possible from within LUA.
SIO2 was kind enough to place the SIO2.module def file (sio2_to_lua) as well as the swig conversion script inside src/lua/swig
Cheers
This is very interesting and useful indeed.
Regarding Ray casting from LUA, couldn't you just call a C function as part of the LUA logic?
By creating a specific LUA module (name.module) using swig (from inside the lua directory), I guess anything would be possible from within LUA.
SIO2 was kind enough to place the SIO2.module def file (sio2_to_lua) as well as the swig conversion script inside src/lua/swig
Cheers
Francescu- Posts : 136
Join date : 2009-03-18
Re: Lua, OpenSteer port
So I'm excited about this but I'm having trouble testing this out. I'm wondering if there is more code to it than just what is posted. Given a working tutorial10 blend file I added another object and named it "bot", added this lua script and connected it, then added the bit of C code in my 'sio2WindowEnterLandscape3D' section. The Bot.Render() function is being called (i have a debug print stmt there) but the line after it is dying with EXC_BAD_ACCESS. After inspecting the bot->_SIO2transform->dir after the call to getLinearVelocity, it seems to contain an empty vec3. I'm using the code for template 10, but I'm thinking I might need to add some physics set up code from 06?
geekschmoe- Posts : 14
Join date : 2009-06-22
Re: Lua, OpenSteer port
With physics now turned on, the script runs but the object does not move. With debugging I can see the location coords dont move but the velocity starts out with values and then hits a plateau of:
getCurrentVelocity x: -2.2171075344086 y: 9.751124382019 z: 0
Am I correct in assuming that the "Bot steering defaults" at the top of the lua script should be enough to move this thing around? Or is there some extra C code needed for pushing it around (like setting some velocity maybe)?
getCurrentVelocity x: -2.2171075344086 y: 9.751124382019 z: 0
- Code:
getCurrentVelocity x: 0 y: 0 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -1.9876462221146 y: 8.741925239563 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.1933591365814 y: 9.6466770172119 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2146496772766 y: 9.7403154373169 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2168531417847 y: 9.7500066757202 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2170810699463 y: 9.7510089874268 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2171046733856 y: 9.7511119842529 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.21710729599 y: 9.7511234283447 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2171075344086 y: 9.751124382019 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
getCurrentVelocity x: -2.2171075344086 y: 9.751124382019 z: 0
getCurrentLocation x: -4.2130002975464 y: 3.2260003089905 z: 2.7310001850128
Am I correct in assuming that the "Bot steering defaults" at the top of the lua script should be enough to move this thing around? Or is there some extra C code needed for pushing it around (like setting some velocity maybe)?
geekschmoe- Posts : 14
Join date : 2009-06-22
Re: Lua, OpenSteer port
Hi, you might want to first sanity check your scene before adding steering: as with Tutorial06, set your object's initial location above the ground and make sure it's a dynamic body affected by gravity, etc. Does your bot drop?
Second, yes, you'll need to use the output of the steering algorithm to apply an impulse. Did you add the applyCentralImpulse() code?
--yarri
Second, yes, you'll need to use the output of the steering algorithm to apply an impulse. Did you add the applyCentralImpulse() code?
--yarri
yarri- Posts : 81
Join date : 2009-04-10
Re: Lua, OpenSteer port
Why you guys want to use opensteer? The last update on the project was in 2004... is there any other similar lib out there that is a little bit less out of date?
Re: Lua, OpenSteer port
@Yarri - Thanks. I'm going to reset and start with the tutorial 06 code and blender files. I think I'll have less trouble copying the tutorial 10 behavior into tutorial 06, because I'm pretty sure I'm missing some of the physics boilerplate stuff.
@ sio2interactive - I don't know the answer to your question because I'm just a beginner, but I'm curious what you're using for similar functionality? This "autonomous steering" seems like it would be an integral part of a lot of games (for background characters and enemies and such), but maybe I'm wrong.
@ sio2interactive - I don't know the answer to your question because I'm just a beginner, but I'm curious what you're using for similar functionality? This "autonomous steering" seems like it would be an integral part of a lot of games (for background characters and enemies and such), but maybe I'm wrong.
geekschmoe- Posts : 14
Join date : 2009-06-22
got it working
I finally got this working and just wanted to update this thread in hopes it might help another beginner who is trying this out. Thanks to Yarri for helping out!
My object ("bot") kept busting through the floor every time and I had to replace the following code:
with this code:
My object ("bot") kept busting through the floor every time and I had to replace the following code:
- Code:
bot->_btRigidBody->applyCentralImpulse( btVector3(botvec->x,botvec->y,bot->_btRigidBody->getLinearVelocity()[2]) );
with this code:
- Code:
bot->_btRigidBody->setLinearVelocity( btVector3(botvec->x,botvec->y,bot->_btRigidBody->getLinearVelocity()[2]) );
geekschmoe- Posts : 14
Join date : 2009-06-22
Permissions in this forum:
You cannot reply to topics in this forum