webgl-3d-animation
Version:
Interactive 3D animation using WebGL showing a 2D predator prey ecology on a grid which is real-time mapped onto the surface of a 3D torus. node.js server side gives access to WAV format files which are rendered using Web Audio API
1,434 lines (890 loc) • 71.2 kB
JavaScript
// http://stackoverflow.com/questions/881515/javascript-namespace-declaration#answer-3588712
// var fns = {};
// fns._construct = function () { // this engulfs the entirety of below file
// var fns = function () { // this engulfs the entirety of below file
var fns = function() {
var fish_colors = [];
fish_colors.R = 0.0;
fish_colors.G = 1.0;
fish_colors.B = 0.0;
fish_colors.A = 1.0;
var shark_colors = [];
shark_colors.R = 1.0;
shark_colors.G = 0.0;
shark_colors.B = 0.0;
shark_colors.A = 1.0;
var blank_colors = [];
blank_colors.R = 0.0;
blank_colors.G = 0.0;
blank_colors.B = 0.0;
blank_colors.A = 1.0;
// ---
// CONSTANTS
// var index_species = 0; // grid_board[source_x][source_y][index_species] = species_fish;
// var index_animal = 1; // grid_board[source_x][source_y][index_animal] = 4;
// var index_age = 2; // grid_board[source_x][source_y][index_age] = 17;
// var index_hunger = 3; // grid_board[source_x][source_y][index_hunger] = 14;
// var MAX_BOARD_ATTRIBUTES = 4; // IMPORTANT - if new index entries added above increment this
var index_species = 0; // grid_board[source_x][source_y][index_species] = species_fish;
var index_animal = 1; // grid_board[source_x][source_y][index_animal] = 4;
var index_age = 2; // grid_board[source_x][source_y][index_age] = 17;
var index_hunger = 3; // grid_board[source_x][source_y][index_hunger] = 14;
var MAX_BOARD_ATTRIBUTES = 4; // IMPORTANT - if new index entries added above increment this
// CONSTANTS
var species_fish = 0;
var species_shark = 1;
var species_no_animal_here = 2;
// var species_fish = 0;
// var species_shark = 1;
// var species_no_animal_here = 2;
var unused = -1; // place holder of no real value
// var hunger_eaten = -1; // only used for fish after eaten by shark
var hunger_starved = -2; // only used for shark after starvation
// ---
var desired_point_size = 1.0;
// ---
var conversion_grid_to_world_x;
var conversion_grid_to_world_y;
var conversion_world_to_grid_x;
var conversion_world_to_grid_y;
// ---------------------------------
// var object_handle[animals_fish] = {};
// var object_handle[animals_sharks] = {};
// var object_handle[animals_doughnut] = {};
var object_handle = {}; // holds all the various flavors of graphic object types
var animals_fish = "animals_fish";
var animals_sharks = "animals_sharks";
var animals_doughnut = "animals_doughnut";
object_handle[animals_fish] = {};
object_handle[animals_sharks] = {};
object_handle[animals_doughnut] = {};
var all_object_labels = [];
all_object_labels.push(animals_fish);
all_object_labels.push(animals_sharks);
all_object_labels.push(animals_doughnut);
// ---------------------------------
var playboard = {}; // grab bag of board items
// ---
// --- CONSTANTS
var stop_early = false;
var count_chronos = 0;
var time_for_birthin_fish;
var time_for_birthin_shark;
var time_for_shark_starvation;
var invisible = 88888.0;
// var invisible = 0.0;
var do_output;
var do_single_step;
var allow_sharks_to_eat_fish;
// ------- now setup doughnut ----------- //
var doughnut_box = []; // stens TODO - maybe not needed used for static other doughnut visual
var curr_doughnut_box = 0;
// var doughnut_box_vertices = [];
var doughnut_box_vertices;
var curr_doughnut_vertex = 0;
var doughnut_box_colors;
var curr_doughnut_color = 0;
var doughnut_indices;
var curr_doughnut_index = 0;
// ---
var random_sequence;
var MAX_SIZE_RAND_SEQUENCE = 233; // arbitrary ... just pick a nice prime
var curr_random = 0; // increment to walk thru sequence .. loop back to 0
var dead_fish_bucket = []; // maintains index of dead fish
var dead_shark_bucket = [];
var curr_dead_fish = 0;
var curr_dead_shark = 0;
var SIZE_BOARD_X;
var SIZE_BOARD_Y; // do a square board for now could be rectangular
var MAX_NUM_ANIMALS_PER_SPECIES;
var world_min_x;
var world_min_y;
var world_max_x;
var world_max_y;
var shaderProgram;
var gl;
// ----------- below will get populated by Common Utils namespace function
/*
var R;
var G;
var B;
var A;
var X;
var Y;
var Z;
var min;
var max;
var median;
var SIZE_DIM_3D;
var SIZE_DIM_COLORS;
*/
var R = 0;
var G = 1;
var B = 2;
var A = 3;
var X = 0;
var Y = 1;
var Z = 2;
var min = 3;
var max = 4;
var median = 5;
var SIZE_DIM_3D = 3;
var SIZE_DIM_COLORS = 4;
var FLT_MAX = 99999.99;
var FLT_MIN_nnnn = -99999.99;
// ---
var neighbor_min_value = 0; // sequence consists of random series of 0, 1, 2 or 3
var neighbor_max_value = 3; // which indicate N, S, E or West IE. neighbor directions
// pre calculated random sequence 0<->3
random_sequence = Common_Utils.generate_random_sequence(MAX_SIZE_RAND_SEQUENCE, neighbor_min_value, neighbor_max_value);
// console.debug('playboard.grid_board ', "%o", playboard.grid_board);
// show_values();
// ---
var mvMatrix = mat4.create();
var mvMatrixStack = [];
var pMatrix = mat4.create();
// console.log("initial def mvMatrix = " + mat4.str(mvMatrix));
// ------------------------------------------
function mvPushMatrix() {
var copy = mat4.create();
mat4.set(mvMatrix, copy);
mvMatrixStack.push(copy);
}
function mvPopMatrix() {
if (mvMatrixStack.length === 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
}
// ----------- above will get populated by Common Utils namespace function
function init_board(size_x, size_y) {
var local_grid_board = []; // flat surface animals walk about across X & Y
for (var index_x = 0; index_x < size_x; index_x++) {
local_grid_board[index_x] = [];
for (var index_y = 0; index_y < size_y; index_y++) {
local_grid_board[index_x][index_y] = [];
for (var curr_attrib = 0; curr_attrib < MAX_BOARD_ATTRIBUTES; curr_attrib++) {
if (curr_attrib === index_species) {
local_grid_board[index_x][index_y][curr_attrib] = species_no_animal_here;
// console.log('seeing species attribute');
} else {
local_grid_board[index_x][index_y][curr_attrib] = 0;
}
}
}
}
return local_grid_board;
} // init_board
// ---
function init_this_buffer(board, given_animal, given_max_animal, given_species, is_animal_fish) {
// console.log('MAX_NUM_ANIMALS_PER_SPECIES ', MAX_NUM_ANIMALS_PER_SPECIES);
// console.log('f N s init_this_buffer ..... SIZE_DIM_3D ', SIZE_DIM_3D);
var this_species_vertices = new Float32Array(MAX_NUM_ANIMALS_PER_SPECIES * SIZE_DIM_3D);
given_animal.vertices = this_species_vertices;
// var this_species_colors = new Float32Array(MAX_NUM_FISH * MAX_NUM_SHARKS * SIZE_DIM_COLORS);
var this_species_colors = new Float32Array(MAX_NUM_ANIMALS_PER_SPECIES * SIZE_DIM_COLORS);
given_animal.colors = this_species_colors;
var color_index = 0;
var curr_vertex = 0;
var curr_individual = 0;
for (; curr_vertex < given_max_animal;) {
var x_index = null;
var y_index = null;
var curr_x = null;
var curr_y = null;
var num_trials = 0;
var max_attemps_find_empty_spot = 200;// could maintain a list of available spots to avoid this
do {
x_index = Common_Utils.get_random_in_range_inclusive_int(board.board_min_x, board.board_max_x - 1);
y_index = Common_Utils.get_random_in_range_inclusive_int(board.board_min_y, board.board_max_y - 1);
// var curr_x = x_index * conversion_grid_to_world_x + board.world_min_x;
// var curr_y = y_index * conversion_grid_to_world_y + board.world_min_y;
curr_x = x_index * conversion_grid_to_world_x;
curr_y = y_index * conversion_grid_to_world_y;
// console.log('curr_vertex ', curr_vertex, ' x_index ', x_index, ' curr_x ' , curr_x);
// console.log('curr_vertex ', curr_vertex, ' y_index ', y_index, ' curr_y ' , curr_y);
// console.log('species_no_animal_here ', species_no_animal_here, ' from array ', board.grid_board[x_index][y_index][index_species]);
num_trials++;
} while (species_no_animal_here != board.grid_board[x_index][y_index][index_species] &&
num_trials < max_attemps_find_empty_spot);
if (num_trials >= max_attemps_find_empty_spot) {
var error_msg_grid_too_full = 'ERROR - grid board is TOO full cannot find empty spot for baby animal';
console.log(error_msg_grid_too_full);
alert(error_msg_grid_too_full);
}
board.grid_board[x_index][y_index][index_species] = given_species;
board.grid_board[x_index][y_index][index_animal] = curr_individual;
board.grid_board[x_index][y_index][index_age] = 0;
// --- vertex 1 of triangle 1
given_animal.vertices[curr_vertex * SIZE_DIM_3D + X] = curr_x;
given_animal.vertices[curr_vertex * SIZE_DIM_3D + Y] = curr_y;
given_animal.vertices[curr_vertex * SIZE_DIM_3D + Z] = 0.0;
// console.log(' X ', given_animal.vertices[curr_vertex * SIZE_DIM_3D + X],
// ' Y ', given_animal.vertices[curr_vertex * SIZE_DIM_3D + Y]);
// cube_indices[curr_indices_index++] = curr_vertex;
given_animal.colors[color_index + R] = is_animal_fish ? fish_colors.R : shark_colors.R;
given_animal.colors[color_index + G] = is_animal_fish ? fish_colors.G : shark_colors.G;
given_animal.colors[color_index + B] = is_animal_fish ? fish_colors.B : shark_colors.B;
given_animal.colors[color_index + A] = is_animal_fish ? fish_colors.A : shark_colors.A;
// console.log(cube_vertices[curr_index + X], cube_vertices[curr_index + Y], cube_vertices[curr_index + Z], curr_vertex);
curr_vertex++;
color_index += SIZE_DIM_COLORS;
curr_individual++;
}
given_animal.count = curr_vertex;
// ---
given_animal.vertex_position_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, given_animal.vertex_position_buffer);
gl.bufferData(gl.ARRAY_BUFFER, given_animal.vertices, gl.STATIC_DRAW);
given_animal.vertex_position_buffer.itemSize = 3;
given_animal.vertex_position_buffer.numItems = curr_vertex;
// ---
given_animal.vertex_color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, given_animal.vertex_color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, given_animal.colors, gl.STATIC_DRAW);
given_animal.vertex_color_buffer.itemSize = 4;
given_animal.vertex_color_buffer.numItems = curr_vertex;
} // init_this_buffer
// ---
function init_buffers(given_playboard, given_size_board_x, given_size_board_y, given_max_fish, given_max_shark) {
// console.log("given_max_fish ", given_max_fish);
// console.log("given_max_shark ", given_max_shark);
// var NUM_TRIANGLES = 5;
// var SIZE_DIM_3D = 3;
// var NUM_VERTEX_PER_TRI = 3;
// var NUM_TRIANGLES_PER_ANIMAL = 2; // animals are rectangular ... so 2 triangles per animal
// ---
var board_min_x = 0;
var board_max_x = given_size_board_x;
var board_min_y = 0;
var board_max_y = given_size_board_y;
// console.log('board_min_x ', board_min_x);
// console.log('board_max_x ', board_max_x);
// console.log('board_min_y ', board_min_y);
// console.log('board_max_y ', board_max_y);
// helper to convert integer grid board X or Y into float scaled to display world size typically -1 to 1
conversion_grid_to_world_x = (world_max_x - world_min_x) / (board_max_x - board_min_x);
conversion_grid_to_world_y = (world_max_y - world_min_y) / (board_max_y - board_min_y);
// reverse of above - helper to convert from float world coords to int grid board X & Y
conversion_world_to_grid_x = (board_max_x - board_min_x) / (world_max_x - world_min_x);
conversion_world_to_grid_y = (board_max_y - board_min_y) / (world_max_y - world_min_y);
// --
var x_incr = (world_max_x - world_min_x) / (board_max_x - board_min_x);
var y_incr = (world_max_y - world_min_y) / (board_max_y - board_min_y);
// console.log('world_min_x ', world_min_x);
// console.log('world_max_x ', world_max_x);
// console.log('world_min_y ', world_min_y);
// console.log('world_max_y ', world_max_y);
// console.log('board_min_x ', board_min_x);
// console.log('board_max_x ', board_max_x);
// console.log('board_min_y ', board_min_y);
// console.log('board_max_y ', board_max_y);
// console.log('x_incr ', x_incr, world_max_x, world_min_x, board_max_x, board_min_x);
// console.log('y_incr ', y_incr, world_max_y, world_min_y, board_max_y, board_min_y);
// console.log('conversion_grid_to_world_x ', conversion_grid_to_world_x);
// console.log('conversion_grid_to_world_y ', conversion_grid_to_world_y);
// console.log('conversion_world_to_grid_x ', conversion_world_to_grid_x);
// console.log('conversion_world_to_grid_y ', conversion_world_to_grid_y);
// (Math.random() * (max_end - min_end) + min_end);
// for (curr_index = 0;
// curr_index < NUM_TRIANGLES * SIZE_DIM_3D * NUM_VERTEX_PER_TRI;
// curr_index += SIZE_DIM_3D) {
given_playboard.board_min_x = board_min_x;
given_playboard.board_max_x = board_max_x;
given_playboard.board_min_y = board_min_y;
given_playboard.board_max_y = board_max_y;
given_playboard.world_min_x = world_min_x;
given_playboard.world_min_y = world_min_y;
object_handle[animals_fish].want_translate = false;
var is_animal_fish = true;
init_this_buffer(given_playboard, object_handle[animals_fish], given_max_fish, species_fish, is_animal_fish);
object_handle[animals_sharks].want_translate = false;
is_animal_fish = false;
init_this_buffer(given_playboard, object_handle[animals_sharks], given_max_shark, species_shark, is_animal_fish);
} // init_buffers
// ---
function setMatrixUniforms(given_point_size, gl) {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
gl.uniform1f(shaderProgram.point_size, given_point_size);
gl.uniform2f(shaderProgram.screen_resolution, gl.viewportWidth, gl.viewportHeight); // vec2 for shader
// console.log('fish N shark width ', gl.viewportWidth, ' height ',
// gl.viewportHeight);
}
// function inner_indexed_draw(given_animal) {
// mvPushMatrix();
// // Common_Utils.mvPushMatrix(mvMatrix, mvMatrixStack);
// // mat4.translate(mvMatrix, [0.7, -0.2, 3.0]);
// // mat4.translate(mvMatrix, [1.7, 0.8, 4.0]); // OK for board 4 by 4
// // mat4.translate(mvMatrix, [min_max[X][median], min_max[Y][median], min_max[Z][median]]); // OK for board 4 by 4
// mat4.translate(mvMatrix, [ given_animal.min_max[X][median],
// given_animal.min_max[Y][median],
// given_animal.min_max[Z][median]]); // OK for board 4 by 4
// mat4.rotate(mvMatrix, Common_Utils.degToRad(curr_degree_rotation_torus), [ 0.2, 0.2, -0.2]);
// // mat4.translate(mvMatrix, [-min_max[X][median], -min_max[Y][median], -min_max[Z][median]]); // OK for board 4 by 4
// mat4.translate(mvMatrix, [ -given_animal.min_max[X][median],
// -given_animal.min_max[Y][median],
// -given_animal.min_max[Z][median]]); // OK for board 4 by 4
// // mat4.translate(mvMatrix, [ 0, 0, 3.0]); // OK for board 4 by 4
// gl.bindBuffer(gl.ARRAY_BUFFER, given_animal.vertex_position_buffer);
// gl.bufferData(gl.ARRAY_BUFFER, given_animal.vertices, gl.STATIC_DRAW);
// gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, given_animal.vertex_position_buffer.itemSize, gl.FLOAT, false, 0, 0);
// // -------------------------------------
// gl.bindBuffer(gl.ARRAY_BUFFER, given_animal.vertex_color_buffer);
// gl.bufferData(gl.ARRAY_BUFFER, given_animal.colors, gl.STATIC_DRAW);
// gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, given_animal.vertex_color_buffer.itemSize, gl.FLOAT, false, 0, 0);
// // --------------------------------------
// gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, given_animal.indices, gl.STATIC_DRAW);
// setMatrixUniforms(desired_point_size, gl);
// // setMatrixUniforms(10.0, gl);
// gl.drawElements(gl.TRIANGLES, given_animal.vertex_indices_buffer.numItems, gl.UNSIGNED_SHORT, 0);
// mvPopMatrix();
// // Common_Utils.mvPopMatrix(mvMatrix, mvMatrixStack);
// } // inner_indexed_draw
function get_neighbor_x(given_source_x, chosen_neighbor_index) {
var answer_x;
switch (chosen_neighbor_index) {
case 0: { // look left
answer_x = given_source_x - 1;
if (answer_x < playboard.board_min_x) {
answer_x = playboard.board_max_x - 1; // wrap back to far right
}
break;
}
case 1: { // look right
answer_x = given_source_x + 1;
if (answer_x == playboard.board_max_x) {
answer_x = playboard.board_min_x; // wrap back to far left
}
break;
}
case 2: { // look up
answer_x = given_source_x;
break;
}
case 3: { // look down
answer_x = given_source_x;
break;
}
break;
}
return answer_x;
} // get_neighbor_x
function get_neighbor_y(given_source_y, chosen_neighbor_index) {
var answer_y;
switch (chosen_neighbor_index) {
case 0: { // look left
answer_y = given_source_y;
break;
}
case 1: { // look right
answer_y = given_source_y;
break;
}
case 2: { // look up
answer_y = given_source_y - 1;
if (answer_y < playboard.board_min_y) {
answer_y = playboard.board_max_y - 1; // wrap back to far bottom
}
break;
}
case 3: { // look down
answer_y = given_source_y + 1;
if (answer_y == playboard.board_max_y) {
answer_y = playboard.board_min_y; // wrap back to far top
}
break;
}
break;
}
return answer_y;
} // get_neighbor_y
// ---
function show_grid() {
console.log('show_grid ........ grid_board ');
for (var index_x = 0; index_x < SIZE_BOARD_X; index_x++) {
for (var index_y = 0; index_y < SIZE_BOARD_Y; index_y++) {
var this_line = " attrib values ";
for (var curr_attrib = 0; curr_attrib < MAX_BOARD_ATTRIBUTES; curr_attrib++) {
this_line += " " + playboard.grid_board[index_x][index_y][curr_attrib];
}
console.log("x&y ", index_x, index_y, this_line);
}
}
}
function show_animals(given_animal, given_label) {
console.log('show_animals ', given_label, ' count ', given_animal.count);
// console.debug(" given_animal.vertices ", "%o", given_animal.vertices);
// console.debug(" given_animal.vertices ", "%o", given_animal.vertices);
// console.log(' %c given_animal.vertices ', 'background: #222; color: #bada55');
var label_invisible = " <-- invisible";
var msg_invisible;
for (var curr_animal = 0; curr_animal < given_animal.count; curr_animal++) {
msg_invisible = "";
if (invisible == given_animal.vertices[curr_animal*SIZE_DIM_3D + X]) {
msg_invisible = label_invisible;
}
console.log(given_label, ' vertices ', curr_animal,
// ' X ', given_animal.vertices[curr_animal*SIZE_DIM_3D + X],
// ' Y ', given_animal.vertices[curr_animal*SIZE_DIM_3D + Y],
' index X ', given_animal.vertices[curr_animal*SIZE_DIM_3D + X]*conversion_world_to_grid_x,
' index Y ', given_animal.vertices[curr_animal*SIZE_DIM_3D + Y]*conversion_world_to_grid_y,
msg_invisible
);
}
}
// ------------
function update_board() {
if (do_single_step && stop_early) {
return;
}
var found_new_location = false;
var neighbor_index;
var chosen_neighbor_index;
var source_x, source_y;
var target_x, target_y;
if (do_output) {
show_animals(object_handle[animals_fish], 'fish');
show_animals(object_handle[animals_sharks], 'shark');
show_grid();
console.log('------------ top of chronos ------------------ ', count_chronos);
}
// ------------------ do fish movement ------------------
// ------------------ do fish movement ------------------
// ------------------ do fish movement ------------------
var curr_animal_index = 0; // curr_fish or curr_shark ... keep separate as size 3
var color_index = 0; // color for curr_animal_index ... keep separate as size 4
// console.log('object_handle[animals_fish].count ', object_handle[animals_fish].count);
var current_fish_count = object_handle[animals_fish].count; // just to avoid visiting new borns
if (do_output) {
console.log('prior to for loop current_fish_count ', current_fish_count);
}
for (; curr_animal_index < current_fish_count; curr_animal_index++) {
var local_fish = curr_animal_index * SIZE_DIM_3D;
if (invisible == object_handle[animals_fish].vertices[local_fish + X]) {
// if (do_output) {
// console.log("doing a fish skip");
// }
continue; // skip over dead fish
}
source_x = Math.round(object_handle[animals_fish].vertices[local_fish + X] * conversion_world_to_grid_x);
source_y = Math.round(object_handle[animals_fish].vertices[local_fish + Y] * conversion_world_to_grid_y);
if (do_output) {
console.log('TOP for loop fish curr_animal_index ', curr_animal_index,
' source_x ', source_x, ' source_y ',source_y);
}
if (species_fish != playboard.grid_board[source_x][source_y][index_species]) {
console.log('ERROR - looking at grid board for fish ', species_fish,
' but saw : grid_board index_species ',
playboard.grid_board[source_x][source_y][index_species],
' at source_x ', source_x, ' source_y ', source_y
);
}
// if (hunger_eaten == playboard.grid_board[source_x][source_y][index_hunger]) {
// if (do_output) {
// console.log('ok this fish was eaten by shark so skip over it');
// }
// continue;
// }
playboard.grid_board[source_x][source_y][index_age]++; // advance age by one chronos
// console.log('curr_animal_index ', curr_animal_index, ' X&Y ', source_x, source_y,
// ' age ', playboard.grid_board[source_x][source_y][index_age]);
found_new_location = false;
neighbor_index = 0;
do {
chosen_neighbor_index = random_sequence[curr_random];
target_x = get_neighbor_x(source_x, chosen_neighbor_index);
target_y = get_neighbor_y(source_y, chosen_neighbor_index);
// console.log('curr_animal_index ', curr_animal_index, ' X&Y ', source_x, source_y,
// ' age ', playboard.grid_board[source_x][source_y][index_age],
// ' target ', target_x, target_y);
neighbor_index++;
// look for a blank spot
if (species_no_animal_here == playboard.grid_board[target_x][target_y][index_species]) {
if (do_output) {
console.log('moving fish to empty spot target ', target_x, target_y);
}
// OK found empty location so move fish here
playboard.grid_board[target_x][target_y][index_species] = species_fish;
playboard.grid_board[target_x][target_y][index_animal] =
playboard.grid_board[source_x][source_y][index_animal];
playboard.grid_board[target_x][target_y][index_age] =
playboard.grid_board[source_x][source_y][index_age];
object_handle[animals_fish].vertices[local_fish + X] = target_x * conversion_grid_to_world_x;
object_handle[animals_fish].vertices[local_fish + Y] = target_y * conversion_grid_to_world_y;
object_handle[animals_fish].vertices[local_fish + Z] = 0.0;
// ---
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + R] = 0.0; // fish red
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + G] = 1.0; // fish green
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + B] = 0.0; // fish blue
// ---
if (playboard.grid_board[source_x][source_y][index_animal] == curr_animal_index) {
// OK cool
} else {
console.log('ERROR - NO fish curr_animal_index is NOT == playboard.grid_board[source_x][source_y][index_animal]');
}
// ----------- now vacate previous spot -----------
if (time_for_birthin_fish < playboard.grid_board[source_x][source_y][index_age]) {
// fish gives birth
if (do_output) {
console.log('fish gives birth');
}
playboard.grid_board[target_x][target_y][index_age] = 0; // parent moves to new location
// child stays back in source location
playboard.grid_board[source_x][source_y][index_species] = species_fish;
playboard.grid_board[source_x][source_y][index_age] = 0;
var baby_fish_index;
if (do_output) {
console.log('prior object_handle[animals_fish].count ', object_handle[animals_fish].count);
}
if (curr_dead_fish > 0) { // do we have any dead fish laying about - primed for resurrection
curr_dead_fish--; // decrement dead fish count
baby_fish_index = dead_fish_bucket[curr_dead_fish];
} else { // create new baby fish out of thin air
baby_fish_index = object_handle[animals_fish].count++;
object_handle[animals_fish].vertex_position_buffer.numItems++;
object_handle[animals_fish].vertex_color_buffer.numItems++;
}
if (do_output) {
console.log('post object_handle[animals_fish].count ', object_handle[animals_fish].count,
' baby_fish_index ', baby_fish_index);
}
playboard.grid_board[source_x][source_y][index_animal] = baby_fish_index;
object_handle[animals_fish].vertices[baby_fish_index*SIZE_DIM_3D + X] =
source_x * conversion_grid_to_world_x;
object_handle[animals_fish].vertices[baby_fish_index*SIZE_DIM_3D + Y] =
source_y * conversion_grid_to_world_y;
object_handle[animals_fish].vertices[baby_fish_index*SIZE_DIM_3D + Z] = 0.0;
if (do_output) {
console.log('baby vertices X ', baby_fish_index*SIZE_DIM_3D + X,
' Y ', baby_fish_index*SIZE_DIM_3D + Y,
' source_x ', source_x, ' source_y ',source_y
);
}
object_handle[animals_fish].colors[baby_fish_index*SIZE_DIM_COLORS + R] = fish_colors.R;
object_handle[animals_fish].colors[baby_fish_index*SIZE_DIM_COLORS + G] = fish_colors.G;
object_handle[animals_fish].colors[baby_fish_index*SIZE_DIM_COLORS + B] = fish_colors.B;
object_handle[animals_fish].colors[baby_fish_index*SIZE_DIM_COLORS + A] = fish_colors.A;
// ---
} else { // NOT YET if (time_for_birthin_fish
if (do_output) {
console.log('no birth yet so vacate space ');
}
playboard.grid_board[source_x][source_y][index_species] = species_no_animal_here;
playboard.grid_board[source_x][source_y][index_animal] = unused;
playboard.grid_board[source_x][source_y][index_age] = unused;
playboard.grid_board[source_x][source_y][index_hunger] = unused;
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + R] = 0.0; // fish red
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + G] = 0.0; // fish green
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + B] = 1.0; // fish blue
}
found_new_location = true;
}
curr_random++;
if (curr_random == MAX_SIZE_RAND_SEQUENCE) {
curr_random = 0;
}
} while (false === found_new_location && neighbor_index < 4);
// color_index += SIZE_DIM_COLORS;
} // for (; curr_animal_index
// do_animation = false; // stens TODO - remove this in a bit
// ------------------ now do shark movement ------------------
// ------------------ now do shark movement ------------------
// ------------------ now do shark movement ------------------
var baby_shark_index;
if (do_output) {
show_animals(object_handle[animals_fish], 'fish');
show_animals(object_handle[animals_sharks], 'shark');
show_grid();
console.log('\n<><><> end of fish ... start of shark <><><>', count_chronos, '\n');
}
var current_shark_count = object_handle[animals_sharks].count; // just to avoid visiting new borns
if (do_output) {
console.log('prior to for loop current_shark_count ', current_shark_count);
}
curr_animal_index = 0;
for (; curr_animal_index < current_shark_count; curr_animal_index++) {
var local_shark = curr_animal_index * SIZE_DIM_3D;
if (invisible == object_handle[animals_sharks].vertices[local_shark + X]) {
// console.log("seeing a dead shark skip");
continue; // skip over dead shark
}
source_x = Math.round(object_handle[animals_sharks].vertices[local_shark + X] * conversion_world_to_grid_x);
source_y = Math.round(object_handle[animals_sharks].vertices[local_shark + Y] * conversion_world_to_grid_y);
if (do_output) {
console.log('TOP for loop shark curr_animal_index ', curr_animal_index,
' source_x ', source_x, ' source_y ',source_y);
}
if (species_shark != playboard.grid_board[source_x][source_y][index_species]) {
console.log('ERROR - looking at grid board for shark ', species_shark,
' but saw : grid_board index_species ',
playboard.grid_board[source_x][source_y][index_species],
' at source_x ', source_x, ' source_y ', source_y
);
}
// if (hunger_starved == playboard.grid_board[source_x][source_y][index_hunger]) {
// if (do_output) {
// console.log('ok this shark was found starved so skip over it');
// }
// continue;
// }
playboard.grid_board[source_x][source_y][index_age]++; // advance age by one chronos
playboard.grid_board[source_x][source_y][index_hunger]++; // advance age by one chronos
// --------- initially have shark look for neighbor fish -------------
found_new_location = false;
neighbor_index = 0;
do { // hunt for fish only .... ignore any blank spots
chosen_neighbor_index = random_sequence[curr_random];
target_x = get_neighbor_x(source_x, chosen_neighbor_index);
target_y = get_neighbor_y(source_y, chosen_neighbor_index);
neighbor_index++;
// std::cout << "target_x " << target_x << " target_y " << target_y << std::endl;
// fish for a fish
if (species_fish == playboard.grid_board[target_x][target_y][index_species] &&
// hunger_eaten != playboard.grid_board[target_x][target_y][index_hunger]
allow_sharks_to_eat_fish) {
// OK found empty location to move fish onto - move there now
var target_fish_index = playboard.grid_board[target_x][target_y][index_animal];
// clean up dead fish
dead_fish_bucket[curr_dead_fish++] = target_fish_index;
object_handle[animals_fish].vertices[target_fish_index*SIZE_DIM_3D + X] = invisible; // stens TODO confirm OK
object_handle[animals_fish].vertices[target_fish_index*SIZE_DIM_3D + Y] = invisible; // stens TODO confirm OK
object_handle[animals_fish].vertices[target_fish_index*SIZE_DIM_3D + Z] = invisible; // stens TODO confirm OK
// stens TODO - probably NOT necessary as its location is off visible grid
// object_handle[animals_fish].colors[target_fish_index*SIZE_DIM_COLORS + R] = blank_colors.R;
// object_handle[animals_fish].colors[target_fish_index*SIZE_DIM_COLORS + G] = blank_colors.G;
// object_handle[animals_fish].colors[target_fish_index*SIZE_DIM_COLORS + B] = blank_colors.B;
// object_handle[animals_fish].colors[target_fish_index*SIZE_DIM_COLORS + A] = blank_colors.A;
if (do_output) {
console.log('shark ', curr_animal_index, ' ATE fish ',target_fish_index,
' source_x ', source_x, ' source_y ',source_y,
' target_x ', target_x, ' target_y ',target_y
);
}
// count reduction spot
// ---------
playboard.grid_board[target_x][target_y][index_species] = species_shark;
playboard.grid_board[target_x][target_y][index_animal] =
playboard.grid_board[source_x][source_y][index_animal];
playboard.grid_board[target_x][target_y][index_age] =
playboard.grid_board[source_x][source_y][index_age];
// stens TODO - assure you handle burial of freshly eaten fish
object_handle[animals_sharks].vertices[local_shark+X] = target_x * conversion_grid_to_world_x;
object_handle[animals_sharks].vertices[local_shark+Y] = target_y * conversion_grid_to_world_y;
object_handle[animals_sharks].vertices[local_shark+Z] = 0.0;
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + R] = shark_colors.R; // shark red
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + G] = shark_colors.G; // shark green
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + B] = shark_colors.B; // shark blue
if (playboard.grid_board[source_x][source_y][index_animal] == curr_animal_index) {
// std::cout << "YES fish curr_animal_index == grid_board[source_x][source_y][animal_index]"
// << std::endl;
} else {
console.log("ERROR - while eating fish NO shark curr_animal_index is NOT == grid_board[source_x][source_y][animal_index]");
// exit(8);
}
// ------- can this shark give birth yet ------
if (time_for_birthin_shark < playboard.grid_board[source_x][source_y][index_age]) {
// baby shark goes into source location
playboard.grid_board[source_x][source_y][index_species] = species_shark;
playboard.grid_board[source_x][source_y][index_age] = 0;
playboard.grid_board[source_x][source_y][index_hunger] = 0;
// var baby_shark_index;
if (0 < curr_dead_shark) {
curr_dead_shark--;
baby_shark_index = dead_shark_bucket[curr_dead_shark];
} else {
baby_shark_index = object_handle[animals_sharks].count++;
object_handle[animals_sharks].vertex_position_buffer.numItems++;
object_handle[animals_sharks].vertex_color_buffer.numItems++;
}
playboard.grid_board[source_x][source_y][index_animal] = baby_shark_index;
// local_shark
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +X] =
source_x * conversion_grid_to_world_x;
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +Y] =
source_y * conversion_grid_to_world_y;
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +Z] = 0.0;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + R] = shark_colors.R;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + G] = shark_colors.G;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + B] = shark_colors.B;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + A] = shark_colors.A;
// ---
} else {
// ------- now vacate previous spot -----------
playboard.grid_board[source_x][source_y][index_species] = species_no_animal_here;
playboard.grid_board[source_x][source_y][index_animal] = unused;
playboard.grid_board[source_x][source_y][index_age] = unused;
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + R] = 0.0; // shark red
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + G] = 0.0; // shark green
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + B] = 1.0; // shark blue
}
// -----------
found_new_location = true;
}
curr_random++;
if (curr_random == MAX_SIZE_RAND_SEQUENCE) {
curr_random = 0;
}
} while (false === found_new_location && neighbor_index < 4);
if (found_new_location) {
continue; // jump to next animal if above found fish for hungry shark
}
// --- since could not find any neighbor fish to eat ... just look for an empty spot
// -------- has shark starved to death ?
if (time_for_shark_starvation < playboard.grid_board[source_x][source_y][index_hunger]) {
if (do_output) {
console.log("shark just starved to death " << curr_animal_index);
}
dead_shark_bucket[curr_dead_shark++] = curr_animal_index;
playboard.grid_board[source_x][source_y][index_species] = species_no_animal_here;
playboard.grid_board[source_x][source_y][index_animal] = unused;
playboard.grid_board[source_x][source_y][index_age] = unused;
object_handle[animals_sharks].vertices[local_shark + X] = invisible;
object_handle[animals_sharks].vertices[local_shark + Y] = invisible;
object_handle[animals_sharks].vertices[local_shark + Z] = invisible;
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + R] = 0.0; // shark red
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + G] = 0.0; // shark green
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + B] = 1.0; // shark blue
continue; // jump to next shark since this one just starved to death
}
// -------------
neighbor_index = 0;
// stens TODO - add efficiency logic to keep track of all neighbors and skip over
// neighbor if previously visited in above steps
do { // previously hunted for neighbor fish - no luck ... now just look for empty spot
chosen_neighbor_index = random_sequence[curr_random];
target_x = get_neighbor_x(source_x, chosen_neighbor_index);
target_y = get_neighbor_y(source_y, chosen_neighbor_index);
neighbor_index++;
if (species_no_animal_here == playboard.grid_board[target_x][target_y][index_species]) {
// look for blank
// OK found empty location to move shark onto - move there now
playboard.grid_board[target_x][target_y][index_species] = species_shark;
playboard.grid_board[target_x][target_y][index_animal] =
playboard.grid_board[source_x][source_y][index_animal];
playboard.grid_board[target_x][target_y][index_age] =
playboard.grid_board[source_x][source_y][index_age];
object_handle[animals_sharks].vertices[local_shark + X] = target_x * conversion_grid_to_world_x;
object_handle[animals_sharks].vertices[local_shark + Y] = target_y * conversion_grid_to_world_y;
object_handle[animals_sharks].vertices[local_shark + Z] = 0.0;
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + R] = 1.0; // shark red
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + G] = 0.0; // shark green
doughnut_box_colors[(target_x * SIZE_BOARD_X + target_y) * 3 + B] = 0.0; // shark blue
if (playboard.grid_board[source_x][source_y][index_animal] == curr_animal_index) {
// std::cout << "YES shark curr_animal_index == grid_board[source_x][source_y][animal_index]"
// << std::endl;
} else {
console.log("ERROR - moving to blank NO shark curr_animal_index is NOT == grid_board[source_x][source_y][animal_index]");
// exit(8);
}
// ------- can this shark give birth yet ------
if (time_for_birthin_shark < playboard.grid_board[source_x][source_y][index_age]) {
// baby shark goes into source location
playboard.grid_board[source_x][source_y][index_species] = species_shark;
playboard.grid_board[source_x][source_y][index_age] = 0;
playboard.grid_board[source_x][source_y][index_hunger] = 0;
// var baby_shark_index;
if (0 < curr_dead_shark) {
curr_dead_shark--;
baby_shark_index = dead_shark_bucket[curr_dead_shark];
} else {
baby_shark_index = object_handle[animals_sharks].count++;
object_handle[animals_sharks].vertex_position_buffer.numItems++;
object_handle[animals_sharks].vertex_color_buffer.numItems++;
}
playboard.grid_board[source_x][source_y][index_animal] = baby_shark_index;
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +X] =
source_x * conversion_grid_to_world_x;
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +Y] =
source_y * conversion_grid_to_world_y;
object_handle[animals_sharks].vertices[baby_shark_index*SIZE_DIM_3D +Z] = 0.0;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + R] = shark_colors.R;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + G] = shark_colors.G;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + B] = shark_colors.B;
object_handle[animals_sharks].colors[baby_shark_index*SIZE_DIM_COLORS + A] = shark_colors.A;
// ---
} else {
// ------- now vacate previous spot -----------
playboard.grid_board[source_x][source_y][index_species] = species_no_animal_here;
playboard.grid_board[source_x][source_y][index_animal] = unused;
playboard.grid_board[source_x][source_y][index_age] = unused;
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + R] = 0.0; // shark red
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + G] = 0.0; // shark green
doughnut_box_colors[(source_x * SIZE_BOARD_X + source_y) * 3 + B] = 1.0; // shark blue
}
// -----------
found_new_location = true;
}
curr_random++;
if (curr_random == MAX_SIZE_RAND_SEQUENCE) {
curr_random = 0;
}
} while (false === found_new_location && neighbor_index < 4);
}
// ------------- end of shark movement ----------------
if (do_output) {
show_animals(object_handle[animals_fish], 'fish');
show_animals(object_handle[animals_sharks], 'shark');
show_grid();
console.log('count fish ', object_handle[animals_fish].count);
console.log('count shark ', object_handle[animals_sharks].count);
console.log('------------ bottom of chronos ------------------ ', count_chronos++);
}
if (do_single_step) {
stop_early = true; // stens TODO - remove this in a bit
}
} // update_board
// ---
function init_torus_buffers() {
object_handle[animals_doughnut].vertices = doughnut_box_vertices;
object_handle[animals_doughnut].colors = doughnut_box_colors;
object_handle[animals_doughnut].indices = doughnut_indices;
// ---
object_handle[animals_doughnut].vertex_position_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, object_handle[animals_doughnut].vertex_position_buffer);
gl.bufferData(gl.ARRAY_BUFFER, object_handle[animals_doughnut].vertices, gl.STATIC_DRAW);
object_handle[animals_doughnut].vertex_position_buffer.itemSize = 3;
object_handle[animals_doughnut].vertex_position_buffer.numItems = curr_doughnut_vertex / 3;
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, object_handle[animals_doughnut].vertex_position_buffer.itemSize, gl.FLOAT, false, 0, 0);
// ---
object_handle[animals_doughnut].vertex_color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, object_handle[animals_doughnut].vertex_color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, object_handle[animals_doughnut].colors, gl.STATIC_DRAW);
object_handle[animals_doughnut].vertex_color_buffer.itemSize = 3;
object_handle[animals_doughnut].vertex_color_buffer.numItems = curr_doughnut_color / 3;
gl.vertexAttribPointer(shaderProgram.vertexColorAttribute, object_handle[animals_doughnut].vertex_color_buffer.itemSize, gl.FLOAT, false, 0, 0);
// ---
object_handle[animals_doughnut].vertex_indices_buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, object_handle[animals_doughnut].vertex_indices_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, object_handle[animals_doughnut].indices, gl.STATIC_DRAW);
object_handle[animals_doughnut].vertex_indices_buffer.itemSize = 1;
object_handle[animals_doughnut].vertex_indices_buffer.numItems = curr_doughnut_index;
} // init_torus_buffers
// ---------------------------------------------
function init_doughnut() {
doughnut_box_vertices = new Float32Array(MAX_NUM_ANIMALS_PER_SPECIES * SIZE_DIM_3D);
doughnut_box_colors = new Float32Array(MAX_NUM_ANIMALS_PER_SPECIES * SIZE_DIM_3D);
doughnut_indices = new Uint16Array(MAX_NUM_ANIMALS_PER_SPECIES * 6);
var toggle_red_N_green = true;
// var radius_of_letter_C = 0.16; // thickness of doughnut
var radius_of_letter_C = 0.2; // thickness of doughnut
// var radius_of_letter_C = 0.24; // thickness of doughnut
// var radius_of_letter_C = 1.6; // thickness of doughnut
// var radius_of_letter_C = 0.36; // thickness of doughnut
var x_center = -0.3;
// float y_center = 0.5;
// float z_center = 0.5;
var working_main_radius; // size of entire doughnut ... influenced by x_center above
// var two_pi = 2.0 * M_PI;
var two_pi = 2.0 * Math.PI;
var incr_of_circle = 2.0 * Math.PI / playboard.board_max_x;
// float outer_curr_x, outer_curr_y, outer_curr_z;
var outer_curr_x, outer_curr_z;
var inner_curr_x, inner_curr_y, inner_curr_z;
var doughnut_offset_x = 0.0;
var doughnut_offset_y = 0.5;
var doughnut_offset_z = 0.5;
// std::cout << "incr_neck_choaker " << incr_of_circle << std::endl;
// ------- synthesize a doughnut --------- https://en.wikipedia.org/wiki/Unit_circle
// --- first create a circle - lay a doughnut on a table cut through its diameter
//