so how does it work ?

a top level empty (empty3) spawns lower level empties (empty2) all north oriented on a 3x3 lat-lon mesh around the camera , but not on all meshpoints. On which meshpoints to spawn empty2 is controlled by pseudorandom number computed by function mulberry32. Each meshpoint is assigned a different pseudorandom number and empty2 is spawned if the number exceeds a threshold.

empty2 are only spawned within a certain range about the camera..and a list of the spawned objects is stored in an array this.spawnedObject=[].

An id is assigned to each empty2 object spawned computed from lat lon (100000*Math.round(100000*this.meshLat[i+3][k+3])+Math.round(100000*this.meshLon[i+3][k+3]))

To do the cleanup of empty2 which get out of camera range a scan is done on a 6x6 grid of meshpoints around the camera and any empty2 that exists in the list of created objects beyond the camera range is called - via my plugin and passing the apropriate id - that it should

do the cleanup of its stuff (plants, deers, etc.) and then delete itself. Its assumed that empty2 does what it needs to do and so the list of spawned empty2 objects is updated.

Note the cleanup still does not work correctly ..it still leaves many empty2 behind..I don't know why.

function mulberry32(a) { // generate pseudorandom number from seed a

// return function() {

var t = a += 0x6D2B79F5;

t = Math.imul(t ^ t >>> 15, t | 1);

t ^= t + Math.imul(t ^ t >>> 7, t | 61);

return ((t ^ t >>> 14) >>> 0) / 4294967296;

// }

}

...other functions used in the code (not reported here)

function init_chassis(param)

{

$igc = this.$query_interface("ot::js::igc.get");

$world = this.$query_interface("ot::js::world.get");

screen_size = $world.screen_size();

$plugin = this.$query_interface('xt::js::fly77_plugin.get');

}

function init_vehicle()

{

this.geomob = this.get_geomob(0);

this.cnv=this.$query_interface("ot::js::canvas.create",true);

$world = this.$query_interface("ot::js::world.get");

fnt=this.cnv.load_font("ui/courier_new_18_bold.fnt");

this.ecef = this.geomob.get_pos();

this.norm = gravity_normal(this.ecef);

this.rot = this.geomob.get_rot();

this.north = localnorth(this.ecef);

this.west = localwest(this.ecef);

// retrieve camera heading ..needed to get north oriented rotation for spawning empty2 north oriented

this.camera_rot = this.rot;

var unitquat = {x:0,y:1,z:0,w:0}; // y is model forward durection

this.cam_direc = Quatmult(this.camera_rot,Quatmult(unitquat,QuatConjug(this.camera_rot)));

this.cam_heading = Math.acos( this.cam_direc.x*this.north.x + this.cam_direc.y*this.north.y + this.cam_direc.z*this.north.z)*180.0/PI;

if ( this.cam_direc.x * this.west.x + this.cam_direc.y * this.west.y + this.cam_direc.z * this.west.z > 0.00001){

this.cam_heading = 360.0 - this.cam_heading;

}

// get north rotation quaternion for spawning empty2 north oriented

var Q = toQuatRot( this.cam_heading/180*PI, {x:0,y:0,z:1}); // rotation about model z axis by angle -heading

this.rotNorth = Quatmult(this.rot,Q);

this.ecef_cam = $igc.pos();

this.camLat = ecef2lat(this.ecef_cam);

this.camLon = ecef2lon(this.ecef_cam);

this.camLat5 = Math.round(this.camLat*E5)/E5;

this.camLon5 = Math.round(this.camLon*E5)/E5;

// setup spawnpoints mesh

this.meshLat = [];

this.meshLon = [];

this.meshEcef = [];

this.meshEcefDist = [];

for (var i=-3; i<3; i++){

this.meshLat[i+3] = [];

this.meshLon[i+3] = [];

this.meshEcef[i+3] = [];

this.meshEcefDist[i+3] = [];

for (var k = -3; k<3; k++){

this.meshLat[i+3][k+3] = 0;

this.meshLon[i+3][k+3] = 0;

this.meshEcef[i+3][k+3] = 0;

this.meshEcefDist[i+3][k+3] = 0;

}

}

for (var i=-3; i<3; i++){

for (var k = -3; k<3; k++){

this.meshLat[i+3][k+3]= this.camLat5 + i/E5;

this.meshLon[i+3][k+3]= this.camLon5 + k/E5;

this.meshEcef[i+3][k+3]= latlon2ecef(this.meshLat[i+3][k+3],this.meshLon[i+3][k+3]);

this.elev = $world.elevation_above_terrain_layers(this.meshEcef[i+3][k+3],1000).x;

this.meshEcef[i+3][k+3] = { x: this.meshEcef[i+3][k+3].x -this.elev*this.norm.x, y: this.meshEcef[i+3][k+3].y -this.elev*this.norm.y, z: this.meshEcef[i+3][k+3].z -this.elev*this.norm.z};

this.meshEcefDist[i+3][k+3]= Math.sqrt( (this.meshEcef[i+3][k+3].x-this.ecef_cam.x)*(this.meshEcef[i+3][k+3].x-this.ecef_cam.x) +

(this.meshEcef[i+3][k+3].y-this.ecef_cam.y)*(this.meshEcef[i+3][k+3].y-this.ecef_cam.y) +

(this.meshEcef[i+3][k+3].z-this.ecef_cam.z)*(this.meshEcef[i+3][k+3].z-this.ecef_cam.z) );

}

}

// setup secon (wider) spawnpoints mesh (for cleanup procedure)

this.meshLat2 = [];

this.meshLon2 = [];

this.meshEcef2 = [];

this.meshEcefDist2 = [];

for (var i=-6; i<6; i++){

this.meshLat2[i+6] = [];

this.meshLon2[i+6] = [];

this.meshEcef2[i+6] = [];

this.meshEcefDist2[i+6] = [];

for (var k = -6; k<6; k++){

this.meshLat2[i+6][k+6] = 0;

this.meshLon2[i+6][k+6] = 0;

this.meshEcef2[i+6][k+6] = 0;

this.meshEcefDist2[i+6][k+6] = 0;

}

}

for (var i=-6; i<6; i++){

for (var k = -6; k<6; k++){

this.meshLat2[i+6][k+6]= this.camLat5 + i/E5;

this.meshLon2[i+6][k+6]= this.camLon5 + k/E5;

this.meshEcef2[i+6][k+6]= latlon2ecef(this.meshLat2[i+6][k+6],this.meshLon2[i+6][k+6]);

this.elev = $world.elevation_above_terrain_layers(this.meshEcef2[i+6][k+6],10000).x;

this.meshEcef2[i+6][k+6] = { x: this.meshEcef2[i+6][k+6].x -this.elev*this.norm.x, y: this.meshEcef2[i+6][k+6].y -this.elev*this.norm.y, z: this.meshEcef2[i+6][k+6].z -this.elev*this.norm.z};

this.meshEcefDist2[i+6][k+6]= Math.sqrt( (this.meshEcef2[i+6][k+6].x-this.ecef_cam.x)*(this.meshEcef2[i+6][k+6].x-this.ecef_cam.x) +

(this.meshEcef2[i+6][k+6].y-this.ecef_cam.y)*(this.meshEcef2[i+6][k+6].y-this.ecef_cam.y) +

(this.meshEcef2[i+6][k+6].z-this.ecef_cam.z)*(this.meshEcef2[i+6][k+6].z-this.ecef_cam.z) );

}

}

// spawned object variables

this.spawnedMax=0;

this.spawnedObject = [];

this.spawnedObjectId = [];

this.spawnedObjectCreated = [];

}

function update_frame(dt)

{

for (var m=0; m< this.spawnedMax; m++){

scaledtext( this.cnv, this.spawnedObjectId[m],1, {x: 1900 , y:300+m*20}, {x:255, y:0, z:0, w:255});

}

// update mesh of "fixed" spawnpoints around camera and figure out if objects need to be spawned for those which are within a range around the camera

this.ecef_cam = $igc.pos();

this.camLat = ecef2lat(this.ecef_cam);

this.camLon = ecef2lon(this.ecef_cam);

this.camLat5 = Math.round(this.camLat*E5)/E5;

this.camLon5 = Math.round(this.camLon*E5)/E5;

for (var i=-3; i<3; i++){

for (var k = -3; k<3; k++){

this.meshLat[i+3][k+3]= Math.round(this.camLat5*E5 + i)/E5 ;

this.meshLon[i+3][k+3]= Math.round(this.camLon5*E5 + k)/E5;

this.meshEcef[i+3][k+3]= latlon2ecef(this.meshLat[i+3][k+3],this.meshLon[i+3][k+3]);

this.elev = $world.elevation_above_terrain_layers(this.meshEcef[i+3][k+3],1000).x;

this.meshEcef[i+3][k+3] = { x: this.meshEcef[i+3][k+3].x -this.elev*this.norm.x, y: this.meshEcef[i+3][k+3].y -this.elev*this.norm.y, z: this.meshEcef[i+3][k+3].z -this.elev*this.norm.z};

this.meshEcefDist[i+3][k+3]= Math.sqrt( (this.meshEcef[i+3][k+3].x-this.ecef_cam.x)*(this.meshEcef[i+3][k+3].x-this.ecef_cam.x) +

(this.meshEcef[i+3][k+3].y-this.ecef_cam.y)*(this.meshEcef[i+3][k+3].y-this.ecef_cam.y) +

(this.meshEcef[i+3][k+3].z-this.ecef_cam.z)*(this.meshEcef[i+3][k+3].z-this.ecef_cam.z) );

this.id = (100000*Math.round(100000*this.meshLat[i+3][k+3])+Math.round(100000*this.meshLon[i+3][k+3])) ; // % 4294967295 ;

// to avoid creating an object twice ..if its id has already been associated with a created object mark it as already created

this.ObjectFoundAtMeshPoint=false;

this.foundIndex=0;

for (var j=0; j<this.spawnedMax;j++){

if(this.id==this.spawnedObjectId[j] ) {this.ObjectFoundAtMeshPoint = true; this.foundIndex=j; break;};

}

if (this.meshEcefDist[i+3][k+3] < factor*radius/E5 && !this.ObjectFoundAtMeshPoint && mulberry32((i+3)+10*(k+3)) <0.15 ) { // if meshpoint is within range and was not previously created at that meshpoint ..create it

this.spawn_pos = { x:this.meshEcef[i+3][k+3].x + (1)*this.norm.x , y:this.meshEcef[i+3][k+3].y + (1)*this.norm.y, z:this.meshEcef[i+3][k+3].z + (0)*this.norm.z } ;

this.spawnedObject[this.spawnedMax]= $world.create_instance(name, this.spawn_pos , this.rotNorth , false);

this.spawnedObjectId[this.spawnedMax]= (100000*Math.round(100000*this.meshLat[i+3][k+3])+Math.round(100000*this.meshLon[i+3][k+3])) ; // % 4294967295 ;

this.spawnedObject[this.spawnedMax].set_editor_id((100000*Math.round(100000*this.meshLat[i+3][k+3])+Math.round(100000*this.meshLon[i+3][k+3])) );

this.spawnedMax++;

}

}

}

// scan the meshoints around the camera and do a "remove-yourself" call on any objects who are out of range

for (var i=-6; i<6; i++){

for (var k = -6; k<6; k++){

this.meshLat2[i+6][k+6]= Math.round(this.camLat5*E5 + i)/E5 ;

this.meshLon2[i+6][k+6]= Math.round(this.camLon5*E5 + k)/E5;

this.meshEcef2[i+6][k+6]= latlon2ecef(this.meshLat2[i+6][k+6],this.meshLon2[i+6][k+6]);

this.elev = $world.elevation_above_terrain_layers(this.meshEcef2[i+6][k+6],1000).x;

this.meshEcef2[i+6][k+6] = { x: this.meshEcef2[i+6][k+6].x -this.elev*this.norm.x, y: this.meshEcef2[i+6][k+6].y -this.elev*this.norm.y, z: this.meshEcef2[i+6][k+6].z -this.elev*this.norm.z};

this.meshEcefDist2[i+6][k+6]= Math.sqrt( (this.meshEcef2[i+6][k+6].x-this.ecef_cam.x)*(this.meshEcef2[i+6][k+6].x-this.ecef_cam.x) +

(this.meshEcef2[i+6][k+6].y-this.ecef_cam.y)*(this.meshEcef2[i+6][k+6].y-this.ecef_cam.y) +

(this.meshEcef2[i+6][k+6].z-this.ecef_cam.z)*(this.meshEcef2[i+6][k+6].z-this.ecef_cam.z) );

this.id = (100000*Math.round(100000*this.meshLat2[i+6][k+6])+Math.round(100000*this.meshLon2[i+6][k+6])) ; // % 4294967295 ;

// signal for removal only those object if its found on the list of spawned objects

this.ObjectFoundAtMeshPoint=false;

this.foundIndex=0;

for (var j=0; j<this.spawnedMax;j++){

if(this.id==this.spawnedObjectId[j] ) {this.ObjectFoundAtMeshPoint = true; this.foundIndex=j; break;};

}

if (this.meshEcefDist2[i+6][k+6] >= factor*radius/E5 && this.ObjectFoundAtMeshPoint ){

$plugin.my_set_id( 0, (100000*Math.round(100000*this.meshLat2[i+6][k+6])+Math.round(100000*this.meshLon2[i+6][k+6])) ) ; //do a "remove yourself" call

if (this.spawnedMax>=1){

for (var q=this.foundIndex; q<this.spawnedMax-1; q++){

this.spawnedObject[q]= this.spawnedObject[q+1];

this.spawnedObjectId[q]= this.spawnedObjectId[q+1];

}

}

this.spawnedObjectId[this.spawnedMax]=null;

this.spawnedMax--;

}

}

}

}