something like this:
pick closest object. each object
wont even cut code and explain, here you have findobjectbyray function that goes through all objects (but befiore that each object has its own bounding box to speedup collision check due to many models have way too many vertices)
[spoiler]
#ifndef terra_obj_insH
#define terra_obj_insH
#include "glwrapper.h"
#include "DxcMath.h"
#include "sim/mission.h"
#include "drop_menu.h"
#include "Spectator.h"
#include "glhelper.h"
#include "Editor/objins_forms/obj_prop.h"
#include "Editor/objins_forms/namebrief_form.h"
#include "Editor/objins_forms/condition_form.h"
#include "Editor/objins_forms/light_prop.h"
#include "Editor/objins_forms/trigger_form.h"
#include "Editor/objins_forms/aihuman_form.h"
#include "sim/game/game_init.h"
#include "editor_shared_data.h"
#include "const_vars.h"
struct TLineVertex
{
vec3 v;
vec3 c;
};
const int ROT_OSD_PITCH_UP = 0;
const int ROT_OSD_PITCH_DOWN = 1;
const int ROT_OSD_YAW_UP = 2;
const int ROT_OSD_YAW_DOWN = 3;
const int ROT_OSD_ROLL_UP = 4;
const int ROT_OSD_ROLL_DOWN = 5;
struct TGLEditorColStack
{
vec3 v;
int index;
TGLEditorColStack& operator=(const TGLEditorColStack& in)
{
v = in.v;
index = in.index;
}
};
struct TOBJINS_OSD
{
bool isModal()
{
return !((!mission_brief_form.visible) &&
(!condition_form.visible) &&
(!object_property_editor.visible) &&
(!light_property_editor.visible) &&
(!FileMenu.unfolded) &&
(!InsertMenu.unfolded) &&
(!EditMenu.unfolded) &&
(!GridMenu.unfolded) &&
(!AIHUMAN_property_editor.visible) );
}
TOSD rot_osd;
TObjectPropertyEditor object_property_editor;
TLightPropertyEditor light_property_editor;
TAIHumanPropertyEditor AIHUMAN_property_editor;
TDropDownMenu FileMenu; //new, open, save, propereties>Brief, name, level, time, day, weather / switch edit mode: <terrain, texture ed>, Exit
TDropDownMenu InsertMenu;//Buildings, Flora, Misc <- everything opens opendialog and lists proper directory
TDropDownMenu EditMenu;//Translate, Rotate, Edit info
TDropDownMenu GridMenu;
TNameInputPanel SaveDialog1;
TFileExplorer OpenDialog1;
TMission * mission;
bool initialized;
TEntityType insert_type;
TSpecCamera * FPP_CAM;
float grid_size;
void Insert(vec3 aposition, AnsiString fname)
{
ALOG("INSERTING: "+fname);
vec3 position = SnapToGrid(aposition, grid_size);
AObject ahue;
ahue.LoadObject(fname, false, true);
int index = find_object_index(ahue.lname, 32);
if (index < 0) { ALOG("ERROR ERROR object not found ffs"); return; }
AObject * p = OBJECT_LIST.container[ index ];
//temp_arr_object[mission->object_len].object = p;
TEntity * last_o = mission->FindLastObject();
TEntity * new_entity = new TEntity();
new_entity->testfname = fname;
new_entity->object = p;
for (int i=0; i < 32; i++)
{
new_entity->caption[i] = ahue.caption[i];
new_entity->lname[i] = ahue.lname[i];
}
new_entity->namelen = ahue.namelen;
new_entity->hp = ahue.default_hp;
new_entity->type = insert_type;
new_entity->pos = position;
new_entity->old = position;
if (!OBJECT_LIST.container[ index ]->preloaded) //load model if not loaded yet
{
OBJECT_LIST.container[ index ]->LoadObject(fname, true, false);
GetAllObjectsAABB();
}
/*
* Dependant on object type fillin additional data (ex. fog -> set default size for fog box etc)
*/
if (insert_type == etLight)
{
//radius
new_entity->info2[0] = 10.0;
//color
new_entity->info2[1] = 1.0;
new_entity->info2[2] = 0.0;
new_entity->info2[3] = 0.0;
}
if (insert_type == etFog)
{
//color
new_entity->info2[0] = 1.0;
new_entity->info2[1] = 1.0;
new_entity->info2[2] = 1.0;
}
if (insert_type == etWaypoint)
{
//range of contact
new_entity->info2[0] = 20.0;
}
if (insert_type == etFire)
{
//color
new_entity->info2[0] = 1.0;
new_entity->info2[1] = 0.5;
new_entity->info2[2] = 0.0;
}
(*insert_mode) = false;
ClearSelection();
mission->AddObject(new_entity);
//mission->object_len = mission->object_len + 1;
sel_len = 1;
selection = new int[1];
selection[0] = mission->FindLastObject()->PID;
}
int * selection;
int sel_len;
vec3 * sel_pos;
vec3 rotation_center;
vec3 rotation_first_pos;
vec3 rotation_second_pos;
TTriggerPropertyEditor trigger_editor;
void EditObjectPropereties()
{
if ( (sel_len == 0) || (sel_len > 1) ) { PUSH_EVENT("PLEASE SELECT ONE OBJECT ONLY"); return; }
if (mission->ObjectByPID(selection[0])->type == etLight)
{
light_property_editor.apply_to = mission->ObjectByPID(selection[0]);
light_property_editor.Show();
return;
}
if (mission->ObjectByPID(selection[0])->type == etTrigger)
{
trigger_editor.apply_to = mission->ObjectByPID(selection[0]);
trigger_editor.Show();
return;
}
if (mission->ObjectByPID(selection[0])->type == etPerson)
{
AIHUMAN_property_editor.apply_to = mission->ObjectByPID(selection[0]);
AIHUMAN_property_editor.Show();
return;
}
object_property_editor.apply_to = mission->ObjectByPID(selection[0]);
object_property_editor.Show();
}
void ClearSelection()
{
if (selection != NULL)
delete [] selection;
selection = NULL;
sel_len = 0;
}
int IsInTable(int * p, int len, int search_for)
{
for (int i=0; i < len; i++)
if (search_for == p[i]) return i;
return -1;
}
void DeleteSelection()
{
if (sel_len == 0) return;
for (int i=0; i < sel_len; i++)
mission->DeleteByPID(selection[i]);
mission->object_len = mission->object_len - sel_len;
}
void Select(int index)
{
PUSH_EVENT("TRYING TO SELECT: "+IntToStr(index));
bool found = false;
//check if we already selected that model then unselect it
for (int i=0; i < sel_len; i++)
if (index == selection[i]) found = true;
int num = 1;
if (found) num = -1;
int * tmp = new int [ sel_len + num ];
int cnt = -1;
for (int i=0; i < sel_len; i++)
if (index != selection[i])
{
cnt = cnt + 1;
tmp[cnt] = selection[i];
}
if (found == false)
tmp[sel_len] = index;
sel_len = sel_len + num;
if (selection != NULL)
delete [] selection;
selection = NULL;
selection = tmp;
}
//float getBiggestDimensionOutOfSelection()
//{
/*
* first compute AABB (bounding box) of selected models
* To do that find minimal x,y,z and maximal x,y,z
*/
// vec3 min = vec3(99999.9, 99999.9, 99999.9);
// vec3 max = vec3(-99999.9, -99999.9, -99999.9);
// for (int i=0; i < sel_len; i++)
// {
// vec3 local_min = mission->object[ selection[i] ].getMin();
// vec3 local_max = mission->object[ selection[i] ].getMax();
// if (min.x > local_min.x) min.x = local_min.x;
// if (min.y > local_min.y) min.y = local_min.y;
// if (min.z > local_min.z) min.z = local_min.z;
//
// if (max.x < local_max.x) max.x = local_max.x;
// if (max.y < local_max.y) max.y = local_max.y;
// if (max.z < local_max.z) max.z = local_max.z;
// }
//
//
// vec3 dst = absvf(max - min);
// return maxfa(maxfa(dst.x,dst.y),dst.z);
//}
vec3 getSelectionCenter()
{
vec3 rc;
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
rc = rc + p->pos;
}
return rc / float(sel_len);
}
typrNota rotation_mat;
bool update_rotate;
void EnableRotationMode()
{
rotation_mat.reset();
StoreTranslation();
rotation_center = getSelectionCenter();
request_switch_mode = true;
switch_to = rotation_mode;
// PUSH_EVENT("ROTATION MODE ENABLED");
// first_run = true;
}
void EnableSelectMode()
{
if (!select_mode)
{
// PUSH_EVENT("SELECT MODE ENABLED");
request_switch_mode = true;
switch_to = &select_mode;
(*insert_mode) = true;
} else
{
// PUSH_EVENT("SELECT MODE DISABLED");
DisableAllModes();
(*insert_mode) = false;
select_mode = false;
}
}
void StoreTranslation()
{
if (sel_pos != NULL) delete [] sel_pos;
sel_pos = new vec3[ sel_len ];
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
sel_pos[i] = p->pos;
}
}
vec3 translation_pos;
vec3 translation_facenormal;
float translation_facedist;
vec3 translation_center;
void BeginTranslationMode(vec3 rA, vec3 rB)
{
if (sel_len == 0) { PUSH_EVENT("NOTHING SELECTED ABORTING"); return; }
translation_center = vec3(0.0, 0.0, 0.0);
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
translation_center = translation_center + p->pos;
}
translation_center = translation_center / float(sel_len);
StoreTranslation();
vec3 objn = -FPP_CAM->ReturnFrontVector();
TEntity * p = mission->ObjectByPID(selection[0]);
if (!SegmentPlaneIntersection(objn, getplaneD(objn, translation_center), rA, rA+Normalize(vectorAB(rA,rB))*9999.0, translation_pos))
{
PUSH_EVENT("Well that was unexpected...");
return;
}
/*
vec3 center;
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
center = center + p->pos;
}
center = center / float(sel_len);
translation_pos = center;
StoreTranslation();
GetTranslationMatrix(ACTUAL_MODEL * ACTUAL_VIEW);
switch_to = translation_mode;
request_switch_mode = true;
// PUSH_EVENT("ROTATION MODE ENABLED");
first_run = true;
(*insert_mode) = true;
*/
}
void EnableResizeColboxMode()
{
switch_to = &colbox_resize_mode;
request_switch_mode = true;
request_resize_colbox = true;
first_run = true;
// (*insert_mode) = true;
}
void EnableInsertMode()
{
switch_to = insert_mode;
request_switch_mode = true;
// PUSH_EVENT("Insert mode enabled");
}
vec3 BillboardX;
vec3 BillboardY;
void GetTranslationMatrix(mat4 ModelViewMatrix)
{
mat4 mvm = ModelViewMatrix;
mvm.Transpose();
BillboardX.x = mvm.m[0];
BillboardX.y = mvm.m[4];
BillboardX.z = mvm.m[8];
BillboardY.x = mvm.m[1];
BillboardY.y = mvm.m[5];
BillboardY.z = mvm.m[9];
translation_facenormal = Normalize(vectorcross(BillboardX * 1000.0, BillboardY * 1000.0));
translation_facedist = -dot(translation_facenormal, translation_pos);
}
void TranslateSelected(vec3 rA, vec3 rB)
{
vec3 sec_col;
vec3 objn = -FPP_CAM->ReturnFrontVector();
TEntity * p = mission->ObjectByPID(selection[0]);
if (!SegmentPlaneIntersection(objn, getplaneD(objn, translation_center), rA, rA+Normalize(vectorAB(rA,rB))*9999.0, sec_col))
{
PUSH_EVENT("Well that was unexpected...");
return;
}
vec3 t = vectorAB(translation_pos, sec_col);
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
p->pos = sel_pos[i] + t;
p->pos = SnapToGrid(p->pos, grid_size);
}
/*
vec3 t = vectorAB(translation_pos, GetPointOnPlaneFromRay(translation_facenormal,translation_facedist, rA, rB));
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
p->pos = sel_pos[i] + t;
}
*/
// PUSH_EVENT("TRANSLATING BY: "+POINT_TO_TEXT(t));
}
void InsertInfoNull()
{
insert_type = etInfoNull;
EnableInsertMode();
}
void InsertModel()
{
OpenDialog1.basedir = appdir+"objects/";
OpenDialog1.mask = ".tga";
OpenDialog1.ListFiles(appdir+"objects/");
OpenDialog1.visible = true;
insert_type = etModel;
EnableInsertMode();
}
void InsertLight()
{
insert_type = etLight;
EnableInsertMode();
}
void InsertTornado()
{
insert_type = etTornado;
EnableInsertMode();
}
void InsertFog()
{
insert_type = etFog;
EnableInsertMode();
}
void InsertTrigger()
{
insert_type = etTrigger;
EnableInsertMode();
}
void InsertWaypoint()
{
insert_type = etWaypoint;
EnableInsertMode();
}
void InsertFire()
{
insert_type = etFire;
EnableInsertMode();
}
void InsertPlayerSpawn()
{
insert_type = etPlayerSpawn;
EnableInsertMode();
}
void BrowseBuildings()
{
OpenDialog1.basedir = appdir+"objects/buildings/";
OpenDialog1.mask = ".tga";
OpenDialog1.ListFiles(appdir+"objects/buildings/");
OpenDialog1.visible = true;
insert_type = etModel;
EnableInsertMode();
}
void BrowseFlora()
{
OpenDialog1.basedir = appdir+"objects/flora/";
OpenDialog1.mask = ".tga";
OpenDialog1.ListFiles(appdir+"objects/flora/");
OpenDialog1.visible = true;
insert_type = etModel;
EnableInsertMode();
}
void BrowseFauna()
{
OpenDialog1.basedir = appdir+"objects/fauna/";
OpenDialog1.mask = ".tga";
OpenDialog1.ListFiles(appdir+"objects/fauna/");
OpenDialog1.visible = true;
insert_type = etModel;
EnableInsertMode();
}
void CreateNewMission()
{
if (mission->object != NULL)
delete [] mission->object;
mission->Nullify();
}
TOBJINS_OSD()
{
grid_size = 1.0;
request_resize_colbox = false;
resizing_colbox = false;
colbox_resize_mode = false;
initialized = false;
EDITOR_LINES = NULL;
EDITOR_LINES_LEN = 0;
select_mode = false;
select_waypoint = false;
}
~TOBJINS_OSD()
{
if (!initialized) return;
if (EDITOR_LINES != NULL) delete [] EDITOR_LINES;
glDeleteBuffers(1, &EDITOR_LINES_VBUFF);
delete ModelInsertShader;
delete LineShader;
delete ROT_BTN_SHADER;
delete TriggerShader;
}
TLineVertex * EDITOR_LINES;
unsigned int EDITOR_LINES_VBUFF;
int EDITOR_LINES_LEN;
TGLFont * font;
TShaderObject * ModelInsertShader; //draw plain with light
TShaderObject * LineShader;
TShaderObject * TriggerShader;
TShaderObject * EntityLightShader;
bool select_mode;
bool select_waypoint;
bool * translation_mode;
bool * rotation_mode;
bool * insert_mode;
vec3 * ray_trace_result;
void DisableAllModes()
{
(*cam_mode) = false;
(*translation_mode) = false;
select_mode = false;
select_waypoint = false;
(*rotation_mode) = false;
colbox_resize_mode = false;
resizing_colbox = false;
request_resize_colbox = false;
//(*insert_mode) = false;
ALOG("ALL MODES DISABLED");
}
TCollisionBoxPoints old_colbox;
bool request_resize_colbox;
bool colbox_resize_mode;
vec3 CollisionBoxResizeStartPoint;
vec3 CollisionBoxResizeActualPoint;
vec3 trans_direction;
vec3 box_size_base[4];
void BeginColBoxResizeMode(vec3 rA, vec3 rB)
{
// PUSH_EVENT("................COLBOX BEGIN MODE");
vec3 objn = -FPP_CAM->ReturnFrontVector();
TEntity * p = mission->ObjectByPID(selection[0]);
if (!SegmentPlaneIntersection(objn, getplaneD(objn, p->pos), rA, rA+Normalize(vectorAB(rA,rB))*9999.0, CollisionBoxResizeStartPoint))
{
PUSH_EVENT("Well that was unexpected...");
return;
}
mat4 invrot;
invrot = p->rotation_mat;
invrot.Inverse();
vec3 hit_vec = Normalize(vectorAB(p->pos, CollisionBoxResizeStartPoint));
hit_vec = invrot * hit_vec;
//ALOG("HIT VECTOR: "+POINT_TO_TEXT(hit_vec));
bool xalgined = false;
bool yalgined = false;
bool zalgined = false;
trans_direction = vec3(0.0, 0.0, 1.0);
if ( ( absnf(hit_vec.x) >= absnf(hit_vec.y) ) && ( absnf(hit_vec.x) >= absnf(hit_vec.z) ) ) //mostly x algined
{
xalgined = true;
trans_direction = vec3(float(PLUSORMINUS2(hit_vec.x)), 0.0, 0.0);
}
if ( ( absnf(hit_vec.y) >= absnf(hit_vec.x) ) && ( absnf(hit_vec.y) >= absnf(hit_vec.z) ) ) //mostly y algined
{
yalgined = true;
trans_direction = vec3(0.0, float(PLUSORMINUS2(hit_vec.y)), 0.0);
}
if ( ( absnf(hit_vec.z) >= absnf(hit_vec.x) ) && ( absnf(hit_vec.z) >= absnf(hit_vec.y) ) ) //mostly z algined
{
zalgined = true;
trans_direction = vec3(0.0, 0.0, float(PLUSORMINUS2(hit_vec.z)));
}
//PUSH_EVENT("TDir: "+POINT_TO_TEXT(trans_direction));
int side_to_use = 0;
if (zalgined)
{
if (hit_vec.z >= 0.0) side_to_use = FRONT;
else side_to_use = BACK;
}
if (xalgined)
{
if (hit_vec.x >= 0.0) side_to_use = LEFT;
else side_to_use = RIGHT;
}
if (yalgined)
{
if (hit_vec.y >= 0.0) side_to_use = TOP;
else side_to_use = BOTTOM;
}
//determine side
//PUSH_EVENT("HIT SIDE: "+SideToStr(side_to_use));
p->colbox.IntersectionIndexToVertArray(side_to_use);
for (int i=0; i < 4; i++)
box_size_base[i] = (*p->colbox.move_verts[i]);
//recreateBoundingBoxGrid();
resizing_colbox = true;
(*insert_mode) = false;
}
bool resizing_colbox;
void ProcessColBoxResizeMode(vec3 rA, vec3 rB)
{
TEntity * p = mission->ObjectByPID(selection[0]);
if (p == NULL) return;
vec3 objn = -FPP_CAM->ReturnFrontVector();
if (!SegmentPlaneIntersection(objn, getplaneD(objn, p->pos), rA, rB, CollisionBoxResizeActualPoint))
{
PUSH_EVENT("Well that was unexpected...");
return;
}
float size = VectorLength(vectorAB(CollisionBoxResizeStartPoint, CollisionBoxResizeActualPoint));
if ( (size < 0.001) || IsNan(size) )
{
PUSH_EVENT("Translation less than 1 cm");
return;
}
//determine the sign to fix sizing
float the_SIGN = dot(
Normalize(vectorAB(p->pos, CollisionBoxResizeStartPoint)),
Normalize(vectorAB(CollisionBoxResizeStartPoint, CollisionBoxResizeActualPoint)));
the_SIGN = float(PLUSORMINUS2(the_SIGN));
for (int i=0; i < 4; i++)
{
(*p->colbox.move_verts[i]) = box_size_base[i] + trans_direction*size*the_SIGN;
}
recreateBoundingBoxGrid();
}
void EndColBoxResizeMode()
{
if (colbox_resize_mode)
{
request_resize_colbox = true;
(*insert_mode) = false;
}
}
void GetAllObjectsAABB()
{
TEntity * p = mission->object; //mission->ObjectByPID(selection[i]);
while (p != NULL)
{
if (p->isModel())
p->object->model->CalculateMinMax();
if (p->isShip())
p->object->ship->model->CalculateMinMax();
if (p->isSizeable() && (!p->colbox.colboxset))
{
ALOG("Sizeable and colbox not set");
p->colbox.SetBoxSize(p->object->model->MIN, p->object->model->MAX);
}
p = p->next;
}
}
void recreateBoundingBoxGrid()
{
//remember to call GetAllObjectsAABB before
TEntity * p = mission->object; //mission->ObjectByPID(selection[i]);
while (p != NULL)
{
if (!p->colbox.init)
{
vec3 min;
vec3 max;
if (p->isModel())
{
min = p->object->model->MIN;
max = p->object->model->MAX;
}
if (p->isShip())
{
min = p->object->ship->model->MIN;
max = p->object->ship->model->MAX;
}
// min = mission->object[i].rotation_mat * min;
// max = mission->object[i].rotation_mat * max;
min = min + p->pos;
max = max + p->pos;
p->colbox.center = p->pos;
p->colbox.fA = min;
p->colbox.fB = vec3(max.x, min.y, min.z);
p->colbox.fC = vec3(max.x, max.y, min.z);
p->colbox.fD = vec3(min.x, max.y, min.z);
p->colbox.bA = vec3(min.x, min.y, max.z);
p->colbox.bB = vec3(max.x, min.y, max.z);
p->colbox.bC = vec3(max.x, max.y, max.z);
p->colbox.bD = vec3(min.x, max.y, max.z);
p->colbox.FasterFaster();
if (p->isSizeable())
p->colbox.TextureMe();
p->colbox.initializebox();
} else
{
p->colbox.fA = p->rotation_mat * p->colbox.bfA;
p->colbox.fB = p->rotation_mat * p->colbox.bfB;
p->colbox.fC = p->rotation_mat * p->colbox.bfC;
p->colbox.fD = p->rotation_mat * p->colbox.bfD;
p->colbox.bA = p->rotation_mat * p->colbox.bbA;
p->colbox.bB = p->rotation_mat * p->colbox.bbB;
p->colbox.bC = p->rotation_mat * p->colbox.bbC;
p->colbox.bD = p->rotation_mat * p->colbox.bbD;
p->colbox.FasterFaster();
for (int n=0; n < 8; n++)
(*p->colbox.fast[n]) = p->pos + (*p->colbox.fast[n]);
if (p->isSizeable())
p->colbox.TextureMe();
p->colbox.initializebox();
}
p = p->next;
}
}
TEntity * FindObjectByRay(vec3 rA, vec3 rB) //performs raycast through all objects in scene then when found multiple intersections selects the closest one
{
TGLEditorColStack col_stack[333]; //o'rly? D:
int col_stack_cnt = -1;
//remember to call recreateBoundingBoxGrid before
TEntity * p = mission->object; //mission->ObjectByPID(selection[i]);
while (p != NULL)
{
TGLEditorColStack res;
vec3 ahue;
if (p->colbox.RayIntersectsBox(rA, rB, ahue))
{
col_stack_cnt = col_stack_cnt + 1;
if (col_stack_cnt >= 333) { PUSH_EVENT("TOO MUCH HIT INTERSECTIONS 333 aborting."); return NULL; }
col_stack[col_stack_cnt].index = p->PID;
col_stack[col_stack_cnt].v = ahue;
}
p = p->next;
}
float mindst = 999999999.90;
int dsti = -1;
TEntity * result = NULL;
for (int i=0; i < col_stack_cnt+1; i++)
{
float dst = n3ddistance(rA, col_stack[i].v);
if (dst < mindst)
{
mindst = dst;
dsti = col_stack[i].index;
result = mission->ObjectByPID(dsti);
}
return result; //returns NULL if nothing found
}
return NULL;
}
//***************************************************************************************************************************************
TEntity* FindWaypointByRay(vec3 rA, vec3 rB) //performs raycast through all waypoints
{
TGLEditorColStack col_stack[333]; //o'rly? D:
int col_stack_cnt = -1;
//remember to call recreateBoundingBoxGrid before
TEntity * p = mission->object; //mission->ObjectByPID(selection[i]);
while (p != NULL)
{
TGLEditorColStack res;
vec3 ahue;
if ( (p->colbox.RayIntersectsBox(rA, rB, ahue)) && (p->type == etWaypoint) )
{
col_stack_cnt = col_stack_cnt + 1;
if (col_stack_cnt >= 333) { PUSH_EVENT("TOO MUCH HIT INTERSECTIONS 333 aborting."); return NULL; }
col_stack[col_stack_cnt].index = p->PID;
col_stack[col_stack_cnt].v = ahue;
}
p = p->next;
}
float mindst = 999999999.90;
int dsti = -1;
TEntity * result = NULL;
for (int i=0; i < col_stack_cnt+1; i++)
{
float dst = n3ddistance(rA, col_stack[i].v);
if (dst < mindst)
{
mindst = dst;
dsti = col_stack[i].index;
result = mission->ObjectByPID(dsti);
}
return result; //returns NULL if nothing found
}
return NULL;
}
//***************************************************************************************************************************************
void GoAway()
{
std::exit(0);
}
bool first_run;
bool * position_set;
TShaderObject * ROT_BTN_SHADER;
int * edit_mode;
void SwitchToHeightmapEditor()
{
(*edit_mode) = TERRAED_OSD;
}
void SwitchToTerraTextureEditor()
{
(*edit_mode) = TERRATEX_OSD;
}
void OpenNameBriefForm()
{
mission_brief_form.apply_to = mission;
mission_brief_form.visible = true;
}
void OpenConditionForm()
{
condition_form.apply_to = mission;
condition_form.visible = true;
}
void SetGridSize0001() { grid_size = 0.001; }
void SetGridSize001() { grid_size = 0.01; }
void SetGridSize01() { grid_size = 0.1; }
void SetGridSize1() { grid_size = 1.0; }
void SetGridSize2() { grid_size = 2.0; }
void SetGridSize4() { grid_size = 4.0; }
void SetGridSize5() { grid_size = 5.0; }
void SetGridSize8() { grid_size = 8.0; }
void SetGridSize10() { grid_size = 10.0; }
void SetGridSize20() { grid_size = 20.0; }
TMissionBriefForm mission_brief_form;
TMissionConditionForm condition_form;
bool * cam_mode;
void Init(
TGLFont * afont, bool * terra_ed_mode_bool,
vec3 * hit_result, bool * depth_recreated,
bool * translate_mode, bool * rotate_mode,
TSpecCamera * CAM, int * edit_modea,
bool * cameramode, TMission * amiss)
{
mission = amiss;
cam_mode = cameramode;
trigger_editor.Init();
mission_brief_form.Init();
condition_form.Init();
light_property_editor.Init();
edit_mode = edit_modea;
object_property_editor.Init();
AIHUMAN_property_editor.Init();
btn_down = false;
first_run = true;
FPP_CAM = CAM;
ALOG("DOING INIT OBJ INS");
// CreateNewMission();
translation_mode = translate_mode;
insert_mode = terra_ed_mode_bool;
ray_trace_result = hit_result;
position_set = depth_recreated;
rotation_mode = rotate_mode;
request_switch_mode = false;
update_rotate = false;
ALOG("POINTERS ACCEPTED");
font = afont;
GridMenu.MakeMain(4);
GridMenu.Items[0].caption = "Meters";
GridMenu.MakeSub(0, 7);
GridMenu.Items[0].Items[0].caption = " 1";
GridMenu.Items[0].Items[0].MenuOnClick = [this]() { this->SetGridSize1(); };
GridMenu.Items[0].Items[1].caption = " 2";
GridMenu.Items[0].Items[1].MenuOnClick = [this]() { this->SetGridSize2(); };
GridMenu.Items[0].Items[2].caption = " 4";
GridMenu.Items[0].Items[2].MenuOnClick = [this]() { this->SetGridSize4(); };
GridMenu.Items[0].Items[3].caption = " 5";
GridMenu.Items[0].Items[3].MenuOnClick = [this]() { this->SetGridSize5(); };
GridMenu.Items[0].Items[4].caption = " 8";
GridMenu.Items[0].Items[4].MenuOnClick = [this]() { this->SetGridSize8(); };
GridMenu.Items[0].Items[5].caption = " 10";
GridMenu.Items[0].Items[5].MenuOnClick = [this]() { this->SetGridSize10(); };
GridMenu.Items[0].Items[6].caption = " 20";
GridMenu.Items[0].Items[6].MenuOnClick = [this]() { this->SetGridSize20(); };
GridMenu.Items[1].caption = "Centimeters";
GridMenu.MakeSub(1, 3);
GridMenu.Items[1].Items[0].caption = " 0.001";
GridMenu.Items[1].Items[0].MenuOnClick = [this]() { this->SetGridSize0001(); };
GridMenu.Items[1].Items[1].caption = " 0.01";
GridMenu.Items[1].Items[1].MenuOnClick = [this]() { this->SetGridSize001(); };
GridMenu.Items[1].Items[2].caption = " 0.1";
GridMenu.Items[1].Items[2].MenuOnClick = [this]() { this->SetGridSize01(); };
GridMenu.Items[2].caption = "-----------";
GridMenu.Items[3].caption = "Disable";
//no grid, 0.001, 0.01, 0.1, 0.5, 1.0, 2.0,
FileMenu.MakeMain(7);
FileMenu.Items[0].caption = "New mission";
FileMenu.Items[0].MenuOnClick = [this]() { this->CreateNewMission(); };
FileMenu.Items[1].caption = "Open mission";
FileMenu.Items[2].caption = "Save mission";
FileMenu.Items[3].caption = "------------";
FileMenu.Items[4].caption = "Propereties";
FileMenu.MakeSub(4, 2);
FileMenu.Items[4].Items[0].caption = " Brief and name";
FileMenu.Items[4].Items[0].MenuOnClick = [this]() { this->OpenNameBriefForm(); };
FileMenu.Items[4].Items[1].caption = " Time and weather";
FileMenu.Items[4].Items[1].MenuOnClick = [this]() { this->OpenConditionForm(); };
FileMenu.MakeSub(5, 2);
FileMenu.Items[5].caption = "Switch mode";//expandable
FileMenu.Items[5].Items[0].caption = " Terrain Editor";
FileMenu.Items[5].Items[0].MenuOnClick = [this]() { this->SwitchToHeightmapEditor(); };
FileMenu.Items[5].Items[1].caption = " Terra Tex Editor";
FileMenu.Items[5].Items[1].MenuOnClick = [this]() { this->SwitchToTerraTextureEditor(); };
FileMenu.Items[6].caption = "Exit";
FileMenu.Items[6].MenuOnClick = [this]() { this->GoAway(); };
InsertMenu.MakeMain(5);
InsertMenu.Items[0].caption = "Misc";//expandable
InsertMenu.MakeSub(0,9);
InsertMenu.Items[0].Items[0].caption = " Info NULL";
InsertMenu.Items[0].Items[0].MenuOnClick = [this]() { this->InsertInfoNull(); };
InsertMenu.Items[0].Items[1].caption = " Model";
InsertMenu.Items[0].Items[1].MenuOnClick = [this]() { this->InsertModel(); };
InsertMenu.Items[0].Items[2].caption = " Light";
InsertMenu.Items[0].Items[2].MenuOnClick = [this]() { this->InsertLight(); };
InsertMenu.Items[0].Items[3].caption = " Tornado";
InsertMenu.Items[0].Items[3].MenuOnClick = [this]() { this->InsertTornado(); };
InsertMenu.Items[0].Items[4].caption = " Fog";
InsertMenu.Items[0].Items[4].MenuOnClick = [this]() { this->InsertFog(); };
InsertMenu.Items[0].Items[5].caption = " Trigger";
InsertMenu.Items[0].Items[5].MenuOnClick = [this]() { this->InsertTrigger(); };
InsertMenu.Items[0].Items[6].caption = " Waypoint";
InsertMenu.Items[0].Items[6].MenuOnClick = [this]() { this->InsertWaypoint(); };
InsertMenu.Items[0].Items[7].caption = " Fire";
InsertMenu.Items[0].Items[7].MenuOnClick = [this]() { this->InsertFire(); };
InsertMenu.Items[0].Items[8].caption = " Player Spwan";
InsertMenu.Items[0].Items[8].MenuOnClick = [this]() { this->InsertPlayerSpawn(); };
InsertMenu.Items[1].caption = "AI";//expandable: Human, Cannon, Ship
InsertMenu.MakeSub(1,3);
InsertMenu.Items[1].Items[0].caption = "Human";
InsertMenu.Items[1].Items[1].caption = "Cannon";
InsertMenu.Items[1].Items[2].caption = "Ship";
InsertMenu.Items[2].caption = "Buildings";//Forts, houses, other buildings
InsertMenu.Items[2].MenuOnClick = [this]() { this->BrowseBuildings(); };
InsertMenu.Items[3].caption = "Flora";
InsertMenu.Items[3].MenuOnClick = [this]() { this->BrowseFlora(); };
InsertMenu.Items[4].caption = "Fauna";
InsertMenu.Items[4].MenuOnClick = [this]() { this->BrowseFauna(); };
EditMenu.MakeMain(7);
EditMenu.Items[0].caption = "Translate";
EditMenu.Items[0].MenuOnClick = [this]() { this->EnableTranslationMode(); };
EditMenu.Items[1].caption = "Rotate";
EditMenu.Items[1].MenuOnClick = [this]() { this->EnableRotationMode(); };
EditMenu.Items[2].caption = "Edit info";
EditMenu.Items[2].MenuOnClick = [this]() { this->EditObjectPropereties(); };
EditMenu.Items[3].caption = "Target";
EditMenu.MakeSub(3,3);
EditMenu.Items[3].Items[0].caption = " deselect";
EditMenu.Items[3].Items[1].caption = " --------";
EditMenu.Items[3].Items[2].caption = " select";
EditMenu.MakeSub(4,3);
EditMenu.Items[4].caption = "Selection";
EditMenu.Items[4].Items[0].caption = " Select";
EditMenu.Items[4].Items[0].MenuOnClick = [this]() { this->EnableSelectMode(); };
EditMenu.Items[4].Items[1].caption = " ------";
EditMenu.Items[4].Items[2].caption = " Delete";
EditMenu.Items[4].Items[2].MenuOnClick = [this]() { this->DeleteSelection(); };
EditMenu.MakeSub(5,3);
EditMenu.Items[5].caption = "Waypoint";
EditMenu.Items[5].Items[0].caption = " Attach";
EditMenu.Items[5].Items[1].caption = " ------";
EditMenu.Items[5].Items[2].caption = " Deattach";
EditMenu.Items[6].caption = "Size colbox";
EditMenu.Items[6].MenuOnClick = [this]() { this->EnableResizeColboxMode(); };
FileMenu.glyph.LoadTGA(appdir+"textures/editor/terratexed_file_menu.tga");
InsertMenu.glyph.LoadTGA(appdir+"textures/editor/insert_menu.tga");
EditMenu.glyph.LoadTGA(appdir+"textures/editor/edit_menu.tga");
ALOG("LOADIN GRID MENU TGA");
GridMenu.glyph.LoadTGA(appdir+"textures/editor/grid_menu.tga");
float sw = float(SCREEN_WIDTH);
float sh = float(SCREEN_HEIGHT);
FileMenu.Init(sw*0.1, sh*0.9, sw*0.1, sh*0.1, font);
InsertMenu.Init(sw*0.2, sh*0.9, sw*0.1, sh*0.1, font);
EditMenu.Init(sw*0.4, sh*0.9, sw*0.1, sh*0.1, font);
GridMenu.Init(sw*0.3, sh*0.9, sw*0.1, sh*0.1, font);
OpenDialog1.Init();
rot_osd.Make( 2 * 3 );
float btn_w = sh*0.1;
PlaceButtonOGL(sw*0.2, (sh / 2.0) - btn_w/2.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_PITCH_UP, sw, sh);
PlaceButtonOGL(sw*0.2, (sh / 2.0) + btn_w/2.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_PITCH_DOWN, sw, sh);
PlaceButtonOGL(sw*0.2, 0.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_ROLL_UP, sw, sh);
PlaceButtonOGL(sw*0.2+btn_w, 0.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_ROLL_DOWN, sw, sh);
PlaceButtonOGL(sw*0.4, 0.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_YAW_UP, sw, sh);
PlaceButtonOGL(sw*0.4+btn_w, 0.0, btn_w, btn_w, rot_osd.VBUFF, rot_osd.Buttons, ROT_OSD_YAW_DOWN, sw, sh);
rot_osd.SendToGPU();
AStringList*s = new AStringList();
s->LoadFromFile(appdir+"textures/osd/rot.txt");
for (int i=0; i < 6; i++)
rot_osd.Buttons[i].tex.LoadTGA24IntensityColorToAlpha(appdir+s->Strings[i]);
delete s;
ModelInsertShader = new TShaderObject();
ModelInsertShader->LoadShaderProgram(appdir+"shaders/editor/editor_model_vp.shader", appdir+"shaders/editor/editor_model_fp.shader");
LineShader = new TShaderObject();
LineShader->LoadShaderProgram(appdir+"shaders/editor/editor_line_vp.shader", appdir+"shaders/editor/editor_line_fp.shader");
TriggerShader = new TShaderObject();
TriggerShader->LoadShaderProgram(appdir+"shaders/editor/trigger_vp.shader", appdir+"shaders/editor/trigger_fp.shader");
EntityLightShader = new TShaderObject();
EntityLightShader->LoadShaderProgram(appdir+"shaders/editor/entitylight_vp.shader", appdir+"shaders/editor/entitylight_fp.shader");
ROT_BTN_SHADER = new TShaderObject();
ROT_BTN_SHADER->LoadShaderProgram(appdir+"shaders/HUD/osd/main_vp.shader", appdir+"shaders/HUD/osd/main_fp.shader");
initialized = true;
ALOG("DONE OBJ INS INIT");
}
void UpdateLineBuffer()
{
int line_len = 0;
TEntity * p = mission->object;
while (p != NULL)
{
if ( p->atarget != NULL ) line_len = line_len + 1;
if ( p->waypoint != NULL) line_len = line_len + 1;
p = p->next;
}
line_len = line_len * 2;
bool update = false;
if (EDITOR_LINES_LEN != line_len)
{
EDITOR_LINES_LEN = line_len;
if (EDITOR_LINES != NULL)
delete [] EDITOR_LINES;
EDITOR_LINES = NULL;
EDITOR_LINES = new TLineVertex[ EDITOR_LINES_LEN ];
glDeleteBuffers(1, &EDITOR_LINES_VBUFF);
glGenBuffers(1, &EDITOR_LINES_VBUFF);
update = true;
}
int cnt = -1;
p = mission->object;
while (p != NULL)
{
if (p->atarget != NULL )
{
cnt = cnt + 1;
EDITOR_LINES[cnt*2 + 0].v = p->pos;
EDITOR_LINES[cnt*2 + 1].v = p->atarget->pos;
EDITOR_LINES[cnt*2 + 0].c = vec3(0.0, 1.0, 0.0);
EDITOR_LINES[cnt*2 + 1].c = vec3(0.0, 1.0, 0.0);
}
if (p->waypoint != NULL)
{
cnt = cnt + 1;
EDITOR_LINES[cnt*2 + 0].v = p->pos;
EDITOR_LINES[cnt*2 + 1].v = p->waypoint->pos;
EDITOR_LINES[cnt*2 + 0].c = vec3(1.0, 0.5, 0.0);
EDITOR_LINES[cnt*2 + 1].c = vec3(1.0, 0.5, 0.0);
}
p = p->next;
}
glBindBuffer(GL_ARRAY_BUFFER, EDITOR_LINES_VBUFF);
if (update)
glBufferData(GL_ARRAY_BUFFER, sizeof(TLineVertex) * EDITOR_LINES_LEN, EDITOR_LINES, GL_STATIC_DRAW);
else
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(TLineVertex) * EDITOR_LINES_LEN, EDITOR_LINES);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
bool request_switch_mode;
bool * switch_to;
void SwitchMode(bool * p)
{
DisableAllModes();
(*p) = true;
}
void YawSelectionBy(float angle)
{
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
p->rotation.yaw(cos(angle*imopi), sin(angle*imopi));
p->rotation.DoRotation();
p->rotation_mat.LoadGLMatrix(p->rotation.AIR_MATRIX);
}
mat4 ahue;
TEntity * p = new TEntity();
p->rotation.yaw(cos(angle*imopi), sin(angle*imopi));
p->rotation.DoRotation();
p->rotation_mat.LoadGLMatrix(p->rotation.AIR_MATRIX);
vec3 zp = vec3(0.0, 0.0, 1.0);
vec3 zrot = p->rotation_mat* zp;
mat4 invrot;
invrot = p->rotation_mat;
//invrot.Transpose();
invrot.Inverse();
vec3 irot = invrot * zrot;
mat4 res;
res = p->rotation_mat * invrot;
vec3 test = res * zp;
ALOG("BASE VEC: "+POINT_TO_TEXT(zp));
ALOG("ROTATED VEC: "+POINT_TO_TEXT(zrot));
ALOG("Inverted rotation VEC: "+POINT_TO_TEXT(irot));
ALOG("test VEC: "+POINT_TO_TEXT(test));
}
void PitchSelectionBy(float angle)
{
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
p->rotation.pitch(cos(angle*imopi), sin(angle*imopi));
p->rotation.DoRotation();
p->rotation_mat.LoadGLMatrix(p->rotation.AIR_MATRIX);
}
}
void RollSelectionBy(float angle)
{
for (int i=0; i < sel_len; i++)
{
TEntity * p = mission->ObjectByPID(selection[i]);
p->rotation.roll(cos(angle*imopi), sin(angle*imopi));
p->rotation.DoRotation();
p->rotation_mat.LoadGLMatrix(p->rotation.AIR_MATRIX);
}
}
float CursorX;
float CursorY;
float RotStartX;
float RotStartY;
bool btn_down;
void InsertNonModel(vec3 epos)
{
AnsiString fname;
if (insert_type == etInfoNull) // non sizeable just a box
fname = appdir + "objects/info_null.objspec";
if (insert_type == etLight) // non sizeable just a box
fname = appdir + "objects/light.objspec";
if (insert_type == etWaypoint) // non sizeable just a box
fname = appdir + "objects/waypoint.objspec";
if (insert_type == etPlayerSpawn) // non sizeable just a box
fname = appdir + "objects/player_spawn.objspec";
if (insert_type == etTrigger) // sizeable box
fname = appdir + "objects/trigger.objspec";
if (insert_type == etTornado) // sizeable box
fname = appdir + "objects/tornado.objspec";
if (insert_type == etFog) // sizeable box
fname = appdir + "objects/fog.objspec";
if (insert_type == etFire) // sizeable box
fname = appdir + "objects/fire.objspec";
Insert(epos, fname);
recreateBoundingBoxGrid();
}
void EnableTranslationMode()
{
switch_to = translation_mode;
request_switch_mode = true;
}
void Draw()
{
if (!initialized) return;
glActiveTexture(GL_TEXTURE0);
if (request_switch_mode)
{
request_switch_mode = false;
SwitchMode(switch_to);
}
//if ((!OpenDialog1.file_is_awaiting) && (!OpenDialog1.visible) ) (*insert_mode) = false;
OpenDialog1.Draw();
if (!(*cam_mode))
if ( (*rotation_mode) && (update_rotate) )
{
update_rotate = false;
recreateBoundingBoxGrid();
}
if (!(*cam_mode))
if (btn_down)
if ( (*translation_mode) )
{
vec3 rdir = GetDirectionFromScreen(ACTUAL_MODEL * ACTUAL_VIEW, FPP_CAM, CursorX, CursorY, SCREEN_WIDTH, SCREEN_HEIGHT, 90.0, 0.1, 1000.0);
if (!first_run)
{
TranslateSelected(FPP_CAM->pos, FPP_CAM->pos + rdir*1000.0);
recreateBoundingBoxGrid();
} else
BeginTranslationMode(FPP_CAM->pos, FPP_CAM->pos + rdir*1000.0);
first_run = false;
}
if (!(*cam_mode))
if ( (*position_set) && (select_mode) )
{
(*position_set) = false;
TEntity * found_index = FindObjectByRay(FPP_CAM->pos, (*ray_trace_result));
if (found_index == NULL) PUSH_EVENT("Hit nothing");
else
Select(found_index->PID);
}
if ( (OpenDialog1.file_is_awaiting) && ((*position_set)) )
{
(*position_set) = false;
OpenDialog1.file_is_awaiting = false;
// * Convert filename coz we are viewing only tga files (otga exension - for model previev during search) and objspec file of that file is in files/objects/ dir
#ifndef WINDOWS_CMP
AnsiString fname = appdir+"objects/"+StringReplace(ExtractFileName(OpenDialog1.FileName),".tga",".objspec");
#endif
#ifdef WINDOWS_CMP
AnsiString fname = appdir+"objects/"+StringReplace(ExtractFileName(OpenDialog1.FileName),".tga",".objspec", TReplaceFlags() << rfReplaceAll);
#endif
if (!FileExists(fname)) ALOG("WRONG FILENAME");
Insert((*ray_trace_result), fname);
recreateBoundingBoxGrid();
if ((*insert_mode)) {ALOG("SOMETHING IS REALLY WRONG. "); (*insert_mode) = false; }
}
if (!(*cam_mode))
if (btn_down)
if (colbox_resize_mode)
if (request_resize_colbox)
{
vec3 rdir = GetDirectionFromScreen(ACTUAL_MODEL * ACTUAL_VIEW, FPP_CAM, CursorX, CursorY, SCREEN_WIDTH, SCREEN_HEIGHT, 90.0, 0.1, 1000.0);
BeginColBoxResizeMode(FPP_CAM->pos, FPP_CAM->pos + rdir*1000.0);
request_resize_colbox = false;
}
if (!(*cam_mode))
if (btn_down)
if (colbox_resize_mode)
if ( resizing_colbox && !request_resize_colbox )
{
if (!first_run)
{
vec3 rdir = GetDirectionFromScreen(ACTUAL_MODEL * ACTUAL_VIEW, FPP_CAM, CursorX, CursorY, SCREEN_WIDTH, SCREEN_HEIGHT, 90.0, 0.1, 1000.0);
ProcessColBoxResizeMode(FPP_CAM->pos, FPP_CAM->pos + rdir*1000.0);
}
first_run = false;
}
//insert something that is not from opendialog but still choosen
if (!(*cam_mode))
if ( ( ((*insert_mode)) && ((*position_set)) ) && (!(*translation_mode) ) && (!(*rotation_mode) && (!select_mode) && (!resizing_colbox) ))
{
(*insert_mode) = false;
(*position_set) = false;
ALOG("INSERTING SOMETHING");
InsertNonModel((*ray_trace_result));
}
glActiveTexture(GL_TEXTURE0);
DrawObjects(true);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
if (!(*cam_mode))
if ((*rotation_mode))
rot_osd.DrawButtons(ROT_BTN_SHADER);
FileMenu.Draw();
InsertMenu.Draw();
EditMenu.Draw();
GridMenu.Draw();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
object_property_editor.Draw();
mission_brief_form.Draw();
condition_form.Draw();
light_property_editor.Draw();
trigger_editor.Draw();
}
/*
* draws all objects in mission
* with_lines bool says if we want to draw connections between different objects
*/
void DrawObjects(bool with_lines)
{
if (mission->object == NULL)
glBindBuffer(GL_ARRAY_BUFFER, 0);
//first draw all objects with standard lighting model (no shadows)
glActiveTexture(GL_TEXTURE0);
//draw standard Entities
TEntity * p = mission->object;
ModelInsertShader->Enable();
while (p != NULL)
{
if ( p->object != NULL )
{
mat4 wrld;
wrld.TranslateP(p->pos);
wrld = p->rotation_mat * wrld;
mat4 MVP = (wrld * ACTUAL_VIEW) * ACTUAL_PROJECTION;
ModelInsertShader->SendMVPtoShader(MVP);
if (p->isModel()) //draw model
p->object->model->DrawSimpleModel(ModelInsertShader);
if (p->isShip())
p->object->ship->model->DrawSimpleModel(ModelInsertShader);
//additionally draw blended cube that covers the area of the 'trigger'
}
p = p->next;
}
ModelInsertShader->Disable();
glBindBuffer(GL_ARRAY_BUFFER, 0);
p = mission->object;
//draw misc Entities
ACTUAL_MODEL.LoadIdentity();
while (p != NULL)
{
if ( p->object != NULL )
{
if (p->isSizeable())
DrawSizeable(p);
/*
* Draw light sphere (blended with radius and color of light
*/
if (p->type == etLight)
{
DrawLight(p);
// info2[0] <- light radius
}
/*
* Draw sphere of radius of contact
*/
if (p->type == etWaypoint)
{
}
}
p = p->next;
}
glDepthMask(GL_FALSE);
glDisable(GL_DEPTH_TEST);
//now draw selection boxes
ACTUAL_MODEL.LoadIdentity();
COLOR_PICK_SHADER->Enable();
COLOR_PICK_SHADER->Send3F(glGetUniformLocation(COLOR_PICK_SHADER->gProgram, "COLOR"), vec3(1.0, 1.0, 0.0));
COLOR_PICK_SHADER->SendMVPtoShader((ACTUAL_MODEL * ACTUAL_VIEW) * ACTUAL_PROJECTION);
for (int i=0; i < sel_len; i++)
{
p = mission->ObjectByPID(selection[i]);
glBindBuffer(GL_ARRAY_BUFFER, p->colbox.vbuff);
glVertexAttribPointer(COLOR_PICK_SHADER->vertex_pos, 3, GL_FLOAT, GL_FALSE, sizeof(TBoundingBoxVertex), (void*)(offsetof(struct TBoundingBoxVertex, v)));
glEnableVertexAttribArray(COLOR_PICK_SHADER->vertex_pos);
for (int n=0; n < 6; n++) glDrawArrays(GL_LINE_LOOP, n*4, 4);
glDisableVertexAttribArray(COLOR_PICK_SHADER->vertex_pos);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
COLOR_PICK_SHADER->Disable();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDepthMask(GL_TRUE);
glEnable(GL_DEPTH_TEST);
//draw lines that are connecting objects
//draw lights along with blended sphere with lights color and radius
}
void DrawLight(TEntity * object)
{
if (object == NULL) return;
if (object->object == NULL) return;
if (object->info2[0] <= 0.01) return;
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
mat4 wrld;
mat4 scale;
scale.Scale(object->info2[0],object->info2[0],object->info2[0]);
wrld.TranslateP(object->pos);
mat4 MVP = ((scale * wrld) * ACTUAL_VIEW) * ACTUAL_PROJECTION;
EntityLightShader->Enable();
EntityLightShader->SendMVPtoShader(MVP);
EntityLightShader->Send3F(glGetUniformLocation(EntityLightShader->gProgram, "light_color"),
vec3(object->info2[1],object->info2[2],object->info2[3]));
low_sphere->DrawSimpleModel(EntityLightShader);
EntityLightShader->Disable();
glDisable(GL_BLEND);
}
void DrawSizeable(TEntity * object)
{
if (object == NULL) return;
if (object->object == NULL) return;
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (object->type == etTrigger) Trigger_texture.Bind();
if (object->type == etFog) Fog_texture.Bind();
if (object->type == etFire) Fire_texture.Bind();
// mat4 wrld;
// wrld.TranslateP(object->pos);
// wrld = object->rotation_mat * wrld;
mat4 MVP = (ACTUAL_MODEL * ACTUAL_VIEW) * ACTUAL_PROJECTION;
TriggerShader->Enable();
TriggerShader->SendMVPtoShader(MVP);
TriggerShader->Send1I(glGetUniformLocation(TriggerShader->gProgram, "trigger_tex"), 0);
glBindBuffer(GL_ARRAY_BUFFER, object->colbox.vbuff);
glVertexAttribPointer(TriggerShader->vertex_pos, 3, GL_FLOAT, GL_FALSE, sizeof(TBoundingBoxVertex), (void*)(offsetof(struct TBoundingBoxVertex, v)));
glVertexAttribPointer(TriggerShader->texture_coord, 2, GL_FLOAT, GL_FALSE, sizeof(TBoundingBoxVertex), (void*)(offsetof(struct TBoundingBoxVertex, t)));
glEnableVertexAttribArray(TriggerShader->vertex_pos);
glEnableVertexAttribArray(TriggerShader->texture_coord);
for (int n=0; n < 6; n++)
glDrawArrays(GL_TRIANGLE_FAN, n*4, 4);
glDisableVertexAttribArray(TriggerShader->vertex_pos);
glDisableVertexAttribArray(TriggerShader->texture_coord);
glBindBuffer(GL_ARRAY_BUFFER, 0);
TriggerShader->Disable();
glDisable(GL_BLEND);
}
void OnMouseMove(float x, float y)
{
if (object_property_editor.visible)
object_property_editor.OnMouseMove(x,y);
if (condition_form.visible) condition_form.OnMouseMove(x, y);
if (object_property_editor.visible) object_property_editor.OnMouseMove(x,y);
if (light_property_editor.visible) light_property_editor.OnMouseMove(x,y);
if (trigger_editor.visible) trigger_editor.OnMouseMove(x,y);
}
void OnMouseUp(float x, float y)
{
if (trigger_editor.visible) trigger_editor.OnMouseUp(x,y);
if (object_property_editor.visible) object_property_editor.OnMouseUp(x,y);
if (condition_form.visible) condition_form.OnMouseUp(x, y);
if (object_property_editor.visible) object_property_editor.OnMouseUp(x,y);
if (light_property_editor.visible) light_property_editor.OnMouseUp(x,y);
btn_down = false;
first_run = true;
EndColBoxResizeMode();
}
bool OnClick(float x, float y)
{
if (trigger_editor.visible) { trigger_editor.OnClick(x, y); return true; }
if (mission_brief_form.visible) { mission_brief_form.OnClick(x, y); return true; }
if (condition_form.visible) { condition_form.OnClick(x, y); return true; }
if (object_property_editor.visible) { object_property_editor.OnClick(x,y); return true; }
if (light_property_editor.visible) { light_property_editor.OnClick(x,y); return true; }
if (OpenDialog1.visible) if(OpenDialog1.OnClick(x, y)) return true;
if (FileMenu.OnClick(x, y)) { return true; }
if (InsertMenu.OnClick(x, y)) { return true; }
if (EditMenu.OnClick(x, y)) { return true; }
if (GridMenu.OnClick(x, y)) { return true; }
btn_down = true;
if ((*rotation_mode))
{
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_PITCH_UP) )
{
PitchSelectionBy(5.0);
update_rotate = true;
return true;
}
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_PITCH_DOWN) )
{
PitchSelectionBy(-5.0);
update_rotate = true;
return true;
}
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_ROLL_UP) )
{
RollSelectionBy(5.0);
update_rotate = true;
return true;
}
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_ROLL_DOWN) )
{
RollSelectionBy(-5.0);
update_rotate = true;
return true;
}
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_YAW_UP) )
{
YawSelectionBy(90.0);
update_rotate = true;
return true;
}
if ( BtnClicked(x, y, rot_osd.Buttons, ROT_OSD_YAW_DOWN) )
{
YawSelectionBy(-90.0);
update_rotate = true;
return true;
}
}
if (!(*cam_mode))
if (colbox_resize_mode) {return true;}
return false; //osd is always hit no matter what
}
};
#endif
#ifndef collisionH
#define collisionH
#include "DxcMath.h"
#include "modelloading/TachoGLModelSRC.h"
#include "glwrapper.h" //for fucking bounding box drawing
#include "txtfile/stringlist.h"
inline AnsiString SideToStr(int side)
{
if (side == 0) return "FRONT";
if (side == 1) return "BACK";
if (side == 2) return "LEFT";
if (side == 3) return "RIGHT";
if (side == 4) return "TOP";
if (side == 5) return "BOTTOM";
}
struct TBoundingBoxVertex
{
vec3 v;
vec2 t;
TBoundingBoxVertex&operator=(const TBoundingBoxVertex & in)
{
v = in.v;
t = in.t;
}
};
extern bool RayTPolygonIntersection(TPolygon<float> face, vec3 rA, vec3 rB, vec3 & res);
struct TCollisionBoxPoints
{
//-- the base
vec3 bbA; //bottom left
vec3 bbB; //bottom right
vec3 bbC; //top right
vec3 bbD; //top left
vec3 bfA; //bottom left
vec3 bfB; //bottom right
vec3 bfC; //top right
vec3 bfD; //top left
//-- the base
vec3 bA; //bottom left
vec3 bB; //bottom right
vec3 bC; //top right
vec3 bD; //top left
vec3 fA; //bottom left
vec3 fB; //bottom right
vec3 fC; //top right
vec3 fD; //top left
vec3 center;
vec3P fast[8];
bool colboxset;
unsigned int vbuff;
TBoundingBoxVertex boxbuff[24];
bool init;
int last_hit;
void SetBoxSize(vec3 amin, vec3 amax)
{
if (colboxset) {ALOG("COLBOX ALREADY SET ABORTING"); return; }
colboxset = true;
center = (amin + amax) / 2.0;
vec3 min = amin + center;
vec3 max = amax + center;
fA = min;
fB = vec3(max.x, min.y, min.z);
fC = vec3(max.x, max.y, min.z);
fD = vec3(min.x, max.y, min.z);
bA = vec3(min.x, min.y, max.z);
bB = vec3(max.x, min.y, max.z);
bC = vec3(max.x, max.y, max.z);
bD = vec3(min.x, max.y, max.z);
FasterFaster();
initializebox();
}
void initializebox() //use only after creation of the bounding box
{
if (!init)
{
bbA = bA;
bbB = bB;
bbC = bC;
bbD = bD;
bfA = fA;
bfB = fB;
bfC = fC;
bfD = fD;
bbA = bbA - center;
bbB = bbB - center;
bbC = bbC - center;
bbD = bbD - center;
bfA = bfA - center;
bfB = bfB - center;
bfC = bfC - center;
bfD = bfD - center;
for (int i=0; i < 6; i++)
{
boxbuff[i*4+0].t = vec2(0.0, 0.0);
boxbuff[i*4+1].t = vec2(1.0, 0.0);
boxbuff[i*4+2].t = vec2(1.0, 1.0);
boxbuff[i*4+3].t = vec2(0.0, 1.0);
}
}
boxbuff[0].v = fA;
boxbuff[1].v = fB;
boxbuff[2].v = fC;
boxbuff[3].v = fD;
boxbuff[4].v = bA;
boxbuff[5].v = bB;
boxbuff[6].v = bC;
boxbuff[7].v = bD;
boxbuff[8].v = bA;
boxbuff[9].v = fA;
boxbuff[10].v = fD;
boxbuff[11].v = bD;
boxbuff[12].v =bB;
boxbuff[13].v =fB;
boxbuff[14].v =fC;
boxbuff[15].v =bC;
boxbuff[16].v =bD;
boxbuff[17].v =bC;
boxbuff[18].v =fC;
boxbuff[19].v =fD;
boxbuff[20].v =bA;
boxbuff[21].v =bB;
boxbuff[22].v =fB;
boxbuff[23].v =fA;
if (!init)
{
glGenBuffers(1, &vbuff);
glBindBuffer(GL_ARRAY_BUFFER, vbuff);
glBufferData(GL_ARRAY_BUFFER, sizeof(TBoundingBoxVertex) * 24, boxbuff, GL_STATIC_DRAW);
} else
{
glBindBuffer(GL_ARRAY_BUFFER, vbuff);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(TBoundingBoxVertex) * 24, boxbuff);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
init = true;
}
TCollisionBoxPoints()
{
colboxset = false;
init = false;
}
TCollisionBoxPoints& operator=(const TCollisionBoxPoints& in)
{
ALOG("HEY IM COPYING MYSELF HELP ME");
bA = in.bA; //bottom left
bB = in.bB; //bottom right
bC = in.bC; //top right
bD = in.bD; //top left
fA = in.fA; //bottom left
fB = in.fB; //bottom right
fC = in.fC; //top right
fD = in.fD; //top left
for (int i=0; i < 8; i++)
fast[i] = in.fast[i];
colboxset = in.colboxset;
for (int i=0; i < 24; i++)
boxbuff[i] = in.boxbuff[i];
bbA = in.bA; //bottom left
bbB = in.bB; //bottom right
bbC = in.bC; //top right
bbD = in.bD; //top left
bfA = in.bfA; //bottom left
bfB = in.bfB; //bottom right
bfC = in.bfC; //top right
bfD = in.bfD; //top left
vbuff = in.vbuff;
init = in.init;
if (init)
{
glGenBuffers(1, &vbuff);
glBindBuffer(GL_ARRAY_BUFFER, vbuff);
glBufferData(GL_ARRAY_BUFFER, sizeof(TBoundingBoxVertex) * 24, boxbuff, GL_STATIC_DRAW);
}
return *this;
}
/*
* Depending on size of side calculate coords
*/
void TextureMe()
{
for (int i=0; i < 6; i++)
{
float size = n3ddistance(boxbuff[i*4+0].v, boxbuff[i*4+2].v);
// ALOG("size of a box: "+FloatToStr(size));
if (size <= 1.010)
{
boxbuff[i*4+0].t = vec2(0.0, 0.0);
boxbuff[i*4+1].t = vec2(1.0, 0.0);
boxbuff[i*4+2].t = vec2(1.0, 1.0);
boxbuff[i*4+3].t = vec2(0.0, 1.0);
} else
{
boxbuff[i*4+0].t = vec2(0.0, 0.0);
boxbuff[i*4+1].t = vec2(size, 0.0);
boxbuff[i*4+2].t = vec2(size, size);
boxbuff[i*4+3].t = vec2(0.0, size);
}
}
}
void FasterFaster()
{
fast[0] = &fA;
fast[1] = &fB;
fast[2] = &fC;
fast[3] = &fD;
fast[4] = &bA;
fast[5] = &bB;
fast[6] = &bC;
fast[7] = &bD;
}
vec3 * move_verts[4];
void IntersectionIndexToVertArray(int ahue)
{
if (ahue == BACK)
{
move_verts[0] = &bfA;
move_verts[1] = &bfB;
move_verts[2] = &bfC;
move_verts[3] = &bfD;
return;
}
if (ahue == FRONT)
{
move_verts[0] = &bbA;
move_verts[1] = &bbB;
move_verts[2] = &bbC;
move_verts[3] = &bbD;
return;
}
if (ahue == LEFT)
{
move_verts[0] = &bbB;
move_verts[1] = &bfB;
move_verts[2] = &bfC;
move_verts[3] = &bbC;
return;
}
if (ahue == RIGHT)
{
move_verts[0] = &bbA;
move_verts[1] = &bfA;
move_verts[2] = &bfD;
move_verts[3] = &bbD;
return;
}
if (ahue == TOP)
{
move_verts[0] = &bbD;
move_verts[1] = &bbC;
move_verts[2] = &bfC;
move_verts[3] = &bfD;
return;
}
if (ahue == BOTTOM)
{
move_verts[0] = &bbA;
move_verts[1] = &bbB;
move_verts[2] = &bfB;
move_verts[3] = &bfA;
return;
}
ALOG("EXCEUSE ME BUT WHAT THE FUCK "+IntToStr(ahue));
}
bool RayIntersectsBox(vec3 rA, vec3 rB, vec3 & result)
{
//combine points with sides
//front
TPolygon<float> poly;
poly.AddVertex(fA);
poly.AddVertex(fB);
poly.AddVertex(fC);
poly.AddVertex(fD);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = FRONT; return true; }
//back
poly.Count = 0;
poly.AddVertex(bA);
poly.AddVertex(bB);
poly.AddVertex(bC);
poly.AddVertex(bD);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = BACK; return true; }
//right
poly.Count = 0;
poly.AddVertex(bA);
poly.AddVertex(fA);
poly.AddVertex(fD);
poly.AddVertex(bD);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = RIGHT; return true; }
//left
poly.Count = 0;
poly.AddVertex(bB);
poly.AddVertex(fB);
poly.AddVertex(fC);
poly.AddVertex(bC);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = LEFT; return true; }
//top
poly.Count = 0;
poly.AddVertex(bD);
poly.AddVertex(bC);
poly.AddVertex(fC);
poly.AddVertex(fD);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = TOP; return true; }
//bottom
poly.Count = 0;
poly.AddVertex(bA);
poly.AddVertex(bB);
poly.AddVertex(fB);
poly.AddVertex(fA);
if (RayTPolygonIntersection(poly, rA, rB, result)) { last_hit = BOTTOM; return true; }
return false;
}
};
struct TCollisionResponsePacket
{
vec3 point_of_collision;
bool isCollision;
TCollisionResponsePacket()
{
isCollision = false;
}
};
struct TCollisionBoxResponse
{
vec3 col_pos;
bool collision;
};
struct TCollisionBox
{
vec3 pos;
TPolygon<float> SIDES [6];
TCollisionBoxResponse response[6];
TachoGLModel<float> * highpoly_model;
bool use_highp;
TCollisionBox()
{
highpoly_model = NULL;
use_highp = false;
}
~TCollisionBox()
{
if (highpoly_model != NULL) delete highpoly_model;
}
// TCollisionBox operator=(const TCollisionBox & in)
// {
//pos = in.pos;
//for (int i=0; i < 6; i++)
//SIDES[i].CopyVerticesFrom(in.SIDES[i]);
// }
void MakeCollisionBoxOutofModel(TachoGLModel<float> * model);
/*
* So box is rotated with rotMat and translated with objpos
*/
TCollisionResponsePacket BoxSegmentIntersection(vec3 A, vec3 B, Matrix44<float> rotMat, vec3 objpos)
{
TCollisionResponsePacket result;
TPolygon<float> T;
mat4 Trotmat;
Trotmat.TranslateP(objpos);
mat4 outputmat = rotMat * Trotmat;
vec3 res;
for (int i=0; i < 6; i++) //through all sides
{
T.CopyVerticesFrom(SIDES[i]);
for (int n=0; n < T.Count; n++) T.V[n] = outputmat * T.V[n];
response[i].collision = RayTPolygonIntersection(T, A, B, response[i].col_pos);
if (response[i].collision) result.isCollision = true;
}
if (!result.isCollision) return result;
/*
* If we're here theres a collision
*/
/*
* Find closest point to the A point
*/
vec3 cp;
float dst = 999999999.9;
int index = 0;
for (int i=0; i < 6; i++)
if (response[i].collision)
{
float Adst = n3ddistance(A, response[i].col_pos);
if (Adst < dst)
{
index = i;
dst = Adst;
}
}
result.point_of_collision = response[index].col_pos;
return result;
}
};
typedef TCollisionBox * TCollisionBoxPointer;
#endif
[/spoiler]
you could modify the code to check through each obect face
collision.cpp
[spoiler]
#include "collision.h"
void TCollisionBox::MakeCollisionBoxOutofModel(TachoGLModel<float> * model)
{
model->CalculateMinMax();
vec3 dim = model->MAX - model->MIN;
for (int i=0; i < 6; i++) SIDES[i].Count = 4;
SIDES[FRONT].V[0] = model->MIN;
SIDES[FRONT].V[1] = model->MIN + vec3(dim.x, 0.0, 0.0);
SIDES[FRONT].V[2] = model->MIN + vec3(dim.x, dim.y, 0.0);
SIDES[FRONT].V[3] = model->MIN + vec3(0.0, dim.y, 0.0);
SIDES[BACK].V[0] = model->MIN + vec3(0.0, 0.0, dim.z);
SIDES[BACK].V[1] = model->MIN + vec3(dim.x, 0.0, dim.z);
SIDES[BACK].V[2] = model->MIN + vec3(dim.x, dim.y, dim.z);
SIDES[BACK].V[3] = model->MIN + vec3(0.0, dim.y, dim.z);
SIDES[LEFT].V[0] = model->MIN;
SIDES[LEFT].V[1] = model->MIN + vec3(0.0, 0.0, dim.z);
SIDES[LEFT].V[2] = model->MIN + vec3(0.0, dim.y, dim.z);
SIDES[LEFT].V[3] = model->MIN + vec3(0.0, dim.y, 0.0);
SIDES[RIGHT].V[0] = model->MIN + vec3(dim.x, 0.0, 0.0);
SIDES[RIGHT].V[1] = model->MIN + vec3(dim.x, 0.0, dim.z);
SIDES[RIGHT].V[2] = model->MIN + vec3(dim.x, dim.y, dim.z);
SIDES[RIGHT].V[3] = model->MIN + vec3(dim.x, dim.y, 0.0);
SIDES[TOP].V[0] = model->MIN + vec3(0.0, dim.y, 0.0);
SIDES[TOP].V[1] = model->MIN + vec3(dim.x, dim.y, 0.0);
SIDES[TOP].V[1] = model->MIN + vec3(dim.x, dim.y, dim.z);
SIDES[TOP].V[1] = model->MIN + vec3(0.0, dim.y, dim.z);
SIDES[BOTTOM].V[0] = model->MIN;
SIDES[BOTTOM].V[1] = model->MIN + vec3(dim.x, 0.0, 0.0);
SIDES[BOTTOM].V[2] = model->MIN + vec3(dim.x, 0.0, dim.z);
SIDES[BOTTOM].V[3] = model->MIN + vec3(0.0, 0.0, dim.z);
}
bool RayTPolygonIntersection(TPolygon<float> face, vec3 rA, vec3 rB, vec3 & res)
{
vec3 A = vectorAB(face.V[0],face.V[1]);
vec3 B = vectorAB(face.V[0],face.V[ face.Count - 1 ]);
vec3 n = Normalize(A * B);
vec3 PointOnPlane;
long double originDistance = -1.0 * ((n.x * face.V[0].x) +
(n.y * face.V[0].y) +
(n.z * face.V[0].z));
long double distance1 = ((n.x * rA.x) +
(n.y * rA.y) +
(n.z * rA.z)) + originDistance;
long double distance2 = ((n.x * rB.x) +
(n.y * rB.y) +
(n.z * rB.z)) + originDistance;
if(distance1 * distance2 >= 0.0)
return false;
vec3 vLineDir = Normalize(rB - rA);
long double Numerator = -1.0 * (n.x * rA.x +
n.y * rA.y +
n.z * rA.z + originDistance);
long double Denominator = ( (n.x * vLineDir.x) + (n.y * vLineDir.y) + (n.z * vLineDir.z) );
if( absnf(Denominator) <= 0.0001) {
PointOnPlane = rA;
} else {
long double dist = Numerator / Denominator;
PointOnPlane = (rA + (vLineDir * double(dist)));
}
res = PointOnPlane;
const long double MATCH_FACTOR = 0.9999;
long double Angle = 0.0;
vec3 vA, vB;
for (int i = 0; i < face.Count; i++)
{
vA = vectorAB(PointOnPlane, face.V[i]);
vB = vectorAB(PointOnPlane, face.V[(i + 1) % face.Count]);
Angle += (long double)AngleBetweenVectors(vA, vB);
}
if(Angle >= (MATCH_FACTOR * (2.0 * pi)) )
return true;
return false;
}
[/spoiler]
you could modify the code to check through each obect face