Lua, OpenSteer port

View previous topic View next topic Go down

Lua, OpenSteer port

Post  yarri on Sun May 31, 2009 10:39 am

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....

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

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  Francescu on Wed Jun 24, 2009 10:58 am

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

Francescu

Posts : 136
Join date : 2009-03-18

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  geekschmoe on Mon Jun 29, 2009 9:43 pm

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

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  yarri on Mon Jun 29, 2009 10:04 pm

Yes, you need to add physics. Smile

--yarri

yarri

Posts : 81
Join date : 2009-04-10

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  geekschmoe on Mon Jun 29, 2009 10:39 pm

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

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

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  yarri on Tue Jun 30, 2009 12:36 am

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

yarri

Posts : 81
Join date : 2009-04-10

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  sio2interactive on Tue Jun 30, 2009 1:11 am

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?

_________________
SIO2 Interactive
Free Open Source 3D Game Engine for iPhone and iPod Touch
http://sio2interactive.com
avatar
sio2interactive

Posts : 1526
Join date : 2008-08-26
Age : 38
Location : Shanghai

View user profile http://sio2interactive.com

Back to top Go down

Re: Lua, OpenSteer port

Post  geekschmoe on Tue Jun 30, 2009 12:51 pm

@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.

geekschmoe

Posts : 14
Join date : 2009-06-22

View user profile

Back to top Go down

got it working

Post  geekschmoe on Thu Jul 02, 2009 12:50 am

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:

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

View user profile

Back to top Go down

Re: Lua, OpenSteer port

Post  Sponsored content


Sponsored content


Back to top Go down

View previous topic View next topic Back to top

- Similar topics

 
Permissions in this forum:
You cannot reply to topics in this forum