Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Objects/envobject.cpp')
-rw-r--r--Source/Objects/envobject.cpp1945
1 files changed, 1945 insertions, 0 deletions
diff --git a/Source/Objects/envobject.cpp b/Source/Objects/envobject.cpp
new file mode 100644
index 00000000..83a5f6a5
--- /dev/null
+++ b/Source/Objects/envobject.cpp
@@ -0,0 +1,1945 @@
+//-----------------------------------------------------------------------------
+// Name: envobject.cpp
+// Developer: Wolfire Games LLC
+// Description:
+// License: Read below
+//-----------------------------------------------------------------------------
+//
+// Copyright 2022 Wolfire Games LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-----------------------------------------------------------------------------
+
+#include <Physics/bulletobject.h>
+#include <Physics/bulletworld.h>
+#include <Physics/physics.h>
+
+#include <Utility/assert.h>
+#include <Utility/compiler_macros.h>
+
+#include <Graphics/graphics.h>
+#include <Graphics/particles.h>
+#include <Graphics/camera.h>
+#include <Graphics/shaders.h>
+#include <Graphics/textures.h>
+#include <Graphics/models.h>
+#include <Graphics/particles.h>
+#include <Graphics/sky.h>
+#include <Graphics/pxdebugdraw.h>
+#include <Graphics/detailobjectsurface.h>
+
+#include <Objects/movementobject.h>
+#include <Objects/riggedobject.h>
+#include <Objects/decalobject.h>
+#include <Objects/lightvolume.h>
+#include <Objects/envobject.h>
+#include <Objects/group.h>
+
+#include <Internal/datemodified.h>
+#include <Internal/collisiondetection.h>
+#include <Internal/timer.h>
+#include <Internal/filesystem.h>
+#include <Internal/memwrite.h>
+#include <Internal/profiler.h>
+#include <Internal/common.h>
+#include <Internal/config.h>
+#include <Internal/message.h>
+
+#include <Math/vec2math.h>
+#include <Math/vec3math.h>
+#include <Math/vec4math.h>
+#include <Math/quaternions.h>
+
+#include <Timing/timingevent.h>
+#include <Timing/intel_gl_perf.h>
+
+#include <Asset/Asset/material.h>
+#include <Asset/Asset/image_sampler.h>
+#include <Asset/Asset/averagecolorasset.h>
+
+#include <Main/scenegraph.h>
+#include <Main/engine.h>
+
+#include <Sound/sound.h>
+#include <Editors/map_editor.h>
+#include <Compat/fileio.h>
+#include <Scripting/angelscript/ascontext.h>
+#include <Online/online.h>
+
+#include <SDL.h>
+#include <tinyxml.h>
+
+#include <cmath>
+#include <sstream>
+
+extern Timer game_timer;
+extern char* global_shader_suffix;
+extern bool g_simple_shadows;
+extern bool g_level_shadows;
+extern bool g_simple_water;
+extern bool g_no_decals;
+const int _shadow_update_delay = 50;
+extern bool shadow_cache_dirty;
+extern bool g_draw_collision;
+extern bool g_no_reflection_capture;
+extern bool g_make_invisible_visible;
+extern bool g_attrib_envobj_intancing_support;
+extern bool g_attrib_envobj_intancing_enabled;
+extern bool g_ubo_batch_multiplier_force_1x;
+
+extern bool g_debug_runtime_disable_env_object_draw;
+extern bool g_debug_runtime_disable_env_object_draw_depth_map;
+extern bool g_debug_runtime_disable_env_object_draw_detail_object_instances;
+extern bool g_debug_runtime_disable_env_object_draw_instances;
+extern bool g_debug_runtime_disable_env_object_draw_instances_transparent;
+extern bool g_debug_runtime_disable_env_object_pre_draw_camera;
+
+bool last_ofr_is_valid = false;
+std::string last_ofr_shader_name;
+int last_shader;
+
+struct EnvObjectGLState {
+ GLState gl_state;
+ EnvObjectGLState() {
+ gl_state.depth_test = true;
+ gl_state.cull_face = true;
+ gl_state.depth_write = true;
+ gl_state.blend = false;
+ }
+};
+
+static const EnvObjectGLState env_object_gl_state;
+
+static void UpdateDetailObjectSurfaces(EnvObject::DOSList *detail_object_surfaces,
+ const ObjectFileRef& ofr,
+ const TextureAssetRef& base_color,
+ const TextureAssetRef& base_normal,
+ const mat4 transform,
+ int model_id)
+{
+ //TODO: Make sure we aren't duplicating code from Terrain.cpp
+ for(int i=0, len=detail_object_surfaces->size(); i<len; ++i){
+ delete detail_object_surfaces->at(i);
+ }
+ detail_object_surfaces->clear();
+ detail_object_surfaces->resize(ofr->m_detail_object_layers.size());
+ int counter = 0;
+ for(EnvObject::DOSList::iterator iter = detail_object_surfaces->begin();
+ iter != detail_object_surfaces->end(); ++iter)
+ {
+ DetailObjectSurface*& dos = (*iter);
+ dos = new DetailObjectSurface();
+ DetailObjectLayer& dol = ofr->m_detail_object_layers[counter];
+ dos->AttachTo(Models::Instance()->GetModel(model_id), transform);
+ dos->GetTrisInPatches(transform);
+ dos->LoadDetailModel(dol.obj_path);
+ dos->LoadWeightMap(dol.weight_path);
+ dos->SetDensity(dol.density);
+ dos->SetNormalConform(dol.normal_conform);
+ dos->SetMinEmbed(dol.min_embed);
+ dos->tint_weight = dol.tint_weight;
+ dos->SetMaxEmbed(dol.max_embed);
+ dos->SetMinScale(dol.min_scale);
+ dos->SetMaxScale(dol.max_scale);
+ dos->SetViewDist(dol.view_dist);
+ dos->SetJitterDegrees(dol.jitter_degrees);
+ dos->SetOverbright(dol.overbright);
+ dos->SetCollisionType(dol.collision_type);
+ dos->SetBaseTextures(base_color, base_normal);
+ ++counter;
+ }
+}
+
+EnvObject::EnvObject():
+ bullet_object_(NULL),
+ csg_modified_(false),
+ model_id_(-1),
+ attached_(NULL),
+ placeholder_(false),
+ base_color_tint(1.0f),
+ normal_override_buffer_dirty(true),
+ no_navmesh(false)
+{
+ added_to_physics_scene_ = false;
+ collidable = true;
+}
+
+void EnvObject::UpdateParentHierarchy() {
+ bool found_movement_object = false;
+ Object* obj = this;
+ while(obj->parent){
+ obj = obj->parent;
+ if(obj->GetType() == _movement_object){
+ found_movement_object = true;
+ break;
+ }
+ }
+ if(found_movement_object && !attached_){
+ // object was not attached to a character and now is
+ RemovePhysicsShape();
+ attached_ = (MovementObject*)obj;
+ } else if(!found_movement_object && attached_){
+ // object was attached to a character and now is not
+ CreatePhysicsShape();
+ attached_ = NULL;
+ }
+}
+
+void EnvObject::HandleMaterialEvent( const std::string &the_event, const vec3 &event_pos ) {
+ MaterialRef material = ofr_material;
+ material->HandleEvent(the_event, event_pos);
+}
+
+const MaterialEvent& EnvObject::GetMaterialEvent( const std::string &the_event, const vec3 &event_pos, int *tri ) {
+ MaterialRef material = ofr_material;
+ return material->GetEvent(the_event);
+}
+
+const MaterialEvent& EnvObject::GetMaterialEvent( const std::string &the_event, const vec3 &event_pos, const std::string &mod, int *tri) {
+ MaterialRef material = ofr_material;
+ return material->GetEvent(the_event, mod);
+}
+
+EnvObject::~EnvObject() {
+ RemovePhysicsShape();
+ for(int i=0, len=detail_object_surfaces.size(); i<len; ++i){
+ delete detail_object_surfaces[i];
+ }
+}
+
+void EnvObject::Draw() {
+ if (g_debug_runtime_disable_env_object_draw) {
+ return;
+ }
+
+ EnvObject* instances[1];
+ instances[0] = this;
+ Camera* camera = ActiveCameras::Get();
+ Graphics* graphics = Graphics::Instance();
+ mat4 proj_view_mat = camera->GetProjMatrix() * camera->GetViewMatrix();
+ mat4 prev_proj_view_mat = camera->GetProjMatrix() * camera->prev_view_mat;
+ vec3 cam_pos = camera->GetPos();
+ std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = camera->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ last_ofr_is_valid = false;
+ DrawInstances(instances, 1, proj_view_mat, prev_proj_view_mat, &shadow_matrix, cam_pos, Object::kFullDraw);
+ DrawDetailObjectInstances(instances, 1, Object::kFullDraw);
+}
+
+static void UpdateNormalOverride(EnvObject* obj, Model* model){
+ PROFILER_ZONE(g_profiler_ctx, "UpdateNormalOverride");
+ obj->normal_override.resize(model->faces.size()/3);
+ obj->normal_override_custom.resize(model->faces.size()/3, vec4(0.0f));
+ for(int i=0, len=obj->normal_override.size(); i<len; ++i){
+ vec3 vert[3];
+ for(int vert_index=0; vert_index<3; ++vert_index){
+ int start = model->faces[i*3+vert_index]*3;
+ for(int axis=0; axis<3; ++axis){
+ vert[vert_index][axis] = model->vertices[start+axis];
+ }
+ vert[vert_index] = obj->GetTransform() * vert[vert_index];
+ }
+ vec3 normal = normalize(cross(vert[1] - vert[0], vert[2] - vert[0]));
+ obj->normal_override[i] = mix(normal, obj->normal_override_custom[i].xyz(), obj->normal_override_custom[i][3]);//model->face_normals[i];
+ }
+}
+
+const size_t kAttribIdCountVboInstancing = 11;
+const size_t kAttribIdCountUboInstancing = 7;
+static int attrib_ids[kAttribIdCountVboInstancing];
+
+static void SetupAttribPointers(bool shader_changed, Model* model, VBORingContainer& env_object_model_translation_instance_vbo, VBORingContainer& env_object_model_scale_instance_vbo, VBORingContainer& env_object_model_rotation_quat_instance_vbo, VBORingContainer& env_object_color_tint_instance_vbo, VBORingContainer& env_object_detail_scale_instance_vbo, Shaders* shaders, int the_shader, Graphics* graphics) {
+ bool attrib_envobj_instancing = g_attrib_envobj_intancing_support && g_attrib_envobj_intancing_enabled;
+ int attrib_count = attrib_envobj_instancing ? kAttribIdCountVboInstancing : kAttribIdCountUboInstancing;
+ if(shader_changed){
+ for(int i=0; i<attrib_count; ++i){
+ const char* attrib_str;
+ int num_el;
+ VBOContainer* vbo = NULL;
+ VBORingContainer* vboRing = NULL;
+ bool instanced;
+ switch(i){
+ case 0:
+ attrib_str = "vertex_attrib";
+ num_el = 3;
+ vbo = &model->VBO_vertices;
+ instanced = false;
+ break;
+ case 1:
+ attrib_str = "tangent_attrib";
+ num_el = 3;
+ vbo = &model->VBO_tangents;
+ instanced = false;
+ break;
+ case 2:
+ attrib_str = "bitangent_attrib";
+ num_el = 3;
+ vbo = &model->VBO_bitangents;
+ instanced = false;
+ break;
+ case 3:
+ attrib_str = "normal_attrib";
+ num_el = 3;
+ vbo = &model->VBO_normals;
+ instanced = false;
+ break;
+ case 4:
+ attrib_str = "tex_coord_attrib";
+ num_el = 2;
+ vbo = &model->VBO_tex_coords;
+ instanced = false;
+ break;
+ case 5:
+ attrib_str = "plant_stability_attrib";
+ num_el = 3;
+ vbo = &model->VBO_aux;
+ instanced = false;
+ break;
+ case 6:
+ attrib_str = "model_translation_attrib";
+ num_el = 3;
+ vboRing = &env_object_model_translation_instance_vbo;
+ instanced = true;
+ break;
+ case 7:
+ attrib_str = "model_scale_attrib";
+ num_el = 3;
+ vboRing = &env_object_model_scale_instance_vbo;
+ instanced = true;
+ break;
+ case 8:
+ attrib_str = "model_rotation_quat_attrib";
+ num_el = 4;
+ vboRing = &env_object_model_rotation_quat_instance_vbo;
+ instanced = true;
+ break;
+ case 9:
+ attrib_str = "color_tint_attrib";
+ num_el = 4;
+ vboRing = &env_object_color_tint_instance_vbo;
+ instanced = true;
+ break;
+ case 10:
+ attrib_str = "detail_scale_attrib";
+ num_el = 4;
+ vboRing = &env_object_detail_scale_instance_vbo;
+ instanced = true;
+ break;
+ default:
+ __builtin_unreachable();
+ break;
+ }
+ CHECK_GL_ERROR();
+ attrib_ids[i] = shaders->returnShaderAttrib(attrib_str, the_shader);
+ CHECK_GL_ERROR();
+ if(attrib_ids[i] != -1 && ((vbo && vbo->valid()) || (vboRing && vboRing->valid()))){
+ uintptr_t buffer_offset = 0;
+ if(vbo) { vbo->Bind(); buffer_offset = 0; }
+ if(vboRing) { vboRing->Bind(); buffer_offset = vboRing->offset(); }
+ graphics->EnableVertexAttribArray(attrib_ids[i]);
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(attrib_ids[i], num_el, GL_FLOAT, false, num_el*sizeof(GLfloat), (void*)buffer_offset);
+ CHECK_GL_ERROR();
+ glVertexAttribDivisorARB(attrib_ids[i], instanced ? 1 : 0);
+ CHECK_GL_ERROR();
+ }
+ }
+ } else {
+ for(int i=0; i<attrib_count; ++i){
+ if(attrib_ids[i] != -1){
+ int num_el;
+ VBOContainer* vbo = NULL;
+ VBORingContainer* vboRing = NULL;
+ bool instanced;
+ switch(i){
+ case 0:
+ num_el = 3;
+ vbo = &model->VBO_vertices;
+ instanced = false;
+ break;
+ case 1:
+ num_el = 3;
+ vbo = &model->VBO_tangents;
+ instanced = false;
+ break;
+ case 2:
+ num_el = 3;
+ vbo = &model->VBO_bitangents;
+ instanced = false;
+ break;
+ case 3:
+ num_el = 3;
+ vbo = &model->VBO_normals;
+ instanced = false;
+ break;
+ case 4:
+ num_el = 2;
+ vbo = &model->VBO_tex_coords;
+ instanced = false;
+ break;
+ case 5:
+ num_el = 3;
+ vbo = &model->VBO_aux;
+ instanced = false;
+ break;
+ case 6:
+ num_el = 3;
+ vboRing = &env_object_model_translation_instance_vbo;
+ instanced = true;
+ break;
+ case 7:
+ num_el = 3;
+ vboRing = &env_object_model_scale_instance_vbo;
+ instanced = true;
+ break;
+ case 8:
+ num_el = 4;
+ vboRing = &env_object_model_rotation_quat_instance_vbo;
+ instanced = true;
+ break;
+ case 9:
+ num_el = 4;
+ vboRing = &env_object_color_tint_instance_vbo;
+ instanced = true;
+ break;
+ case 10:
+ num_el = 4;
+ vboRing = &env_object_detail_scale_instance_vbo;
+ instanced = true;
+ break;
+ default:
+ __builtin_unreachable();
+ break;
+ }
+ if((vbo && vbo->valid()) || (vboRing && vboRing->valid())){
+ uintptr_t buffer_offset = 0;
+ if(vbo) { vbo->Bind(); buffer_offset = 0; }
+ if(vboRing) { vboRing->Bind(); buffer_offset = vboRing->offset(); }
+ graphics->EnableVertexAttribArray(attrib_ids[i]);
+ glVertexAttribPointer(attrib_ids[i], num_el, GL_FLOAT, false, num_el*sizeof(GLfloat), (void*)buffer_offset);
+ glVertexAttribDivisorARB(attrib_ids[i], instanced ? 1 : 0);
+ }
+ }
+ }
+ }
+}
+
+void EnvObject::DrawInstances(EnvObject** instance_array, int num_instances, const mat4& proj_view_matrix, const mat4& prev_proj_view_matrix, const std::vector<mat4>* shadow_matrix, const vec3& cam_pos, Object::DrawType type) {
+ if (g_debug_runtime_disable_env_object_draw_instances) {
+ return;
+ }
+
+ if (g_debug_runtime_disable_env_object_draw_instances_transparent && transparent) {
+ return;
+ }
+
+ if (type == Object::DrawType::kDrawDepthOnly && transparent) {
+ return;
+ }
+
+ PROFILER_GPU_ZONE(g_profiler_ctx, "EnvObject::DrawInstances");
+
+ if(g_draw_collision){
+ if(GetCollisionModelID() == -1 || ofr->no_collision){
+ return;
+ }
+ }
+ PROFILER_ENTER(g_profiler_ctx, "Setup");
+
+ // Tessellation is ignored when preloading shaders right now because the use-case isn't obvious.
+ // Is it set once? Is it per-object? Where would it be set?
+ bool use_tesselation = false;// GLEW_ARB_tessellation_shader;
+ Shaders* shaders = Shaders::Instance();
+ Models* models = Models::Instance();
+ Graphics* graphics = Graphics::Instance();
+ Textures* textures = Textures::Instance();
+ Timer* timer = &game_timer;
+ Model* model = &models->GetModel(model_id_);
+ if(g_draw_collision){
+ model = &models->GetModel(GetCollisionModelID());
+ }
+ Camera* cam = ActiveCameras::Get();
+
+ PROFILER_ENTER(g_profiler_ctx, "GL State");
+ GLState gl_state;
+ gl_state.cull_face = !ofr->double_sided;;
+ gl_state.depth_test = true;
+ if(type == Object::kDecal){
+ gl_state.blend = true;
+ gl_state.depth_write = false;
+ } else if(transparent) {
+ gl_state.blend = false;
+ gl_state.depth_write = false;
+ if(g_simple_water){
+ gl_state.depth_write = true;
+ }
+ } else {
+ gl_state.blend = false;
+ gl_state.depth_write = true;
+ }
+ graphics->setGLState(gl_state);
+
+ if(graphics->use_sample_alpha_to_coverage && !transparent){
+ glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // GL State
+
+ static int ubo_batch_size_multiplier = 1;
+ static GLint max_ubo_size = -1;
+ if(max_ubo_size == -1) {
+ glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_ubo_size);
+
+ if(max_ubo_size >= 131072) {
+ ubo_batch_size_multiplier = 8;
+ }
+ else if(max_ubo_size >= 65536) {
+ ubo_batch_size_multiplier = 4;
+ }
+ else if(max_ubo_size >= 32768) {
+ ubo_batch_size_multiplier = 2;
+ }
+ }
+
+ int the_shader = last_shader;
+
+ bool use_textures = true;
+ if(type == Object::kDrawDepthOnly || type == Object::kDrawDepthNoAA || type == Object::kDrawAllShadowCascades){
+ use_textures = false;
+ }
+
+ bool shader_changed = true;
+ if(last_ofr_is_valid && ofr->shader_name == last_ofr_shader_name){
+ shader_changed = false;
+ }
+
+ if(shader_changed){
+ PROFILER_ENTER(g_profiler_ctx, "Shader");
+
+ PROFILER_ENTER(g_profiler_ctx, "Create shader string");
+ const int kShaderStrSize = 1024;
+ char buf[2][kShaderStrSize];
+ char* shader_str[2] = {buf[0], buf[1]};
+ if(g_make_invisible_visible && ofr->shader_name.find("#INVISIBLE") != ofr->shader_name.npos && type == Object::kFullDraw) {
+ size_t index = ofr->shader_name.find("#INVISIBLE");
+ std::string name = ofr->shader_name.substr(0, index);
+ name += ofr->shader_name.substr(index + strlen("#INVISIBLE"));
+
+ strcpy(shader_str[0], name.c_str());
+ placeholder_ = true;
+ } else {
+ strcpy(shader_str[0], ofr->shader_name.c_str());
+ }
+ if(type == Object::kDrawDepthOnly || type == Object::kDrawDepthNoAA || type == Object::kDrawAllShadowCascades || type == Object::kWireframe){
+ } else if(type == Object::kDecal){
+ FormatString(shader_str[1], kShaderStrSize, "%s #DECAL", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+ if (config["decal_normals"].toNumber<bool>()){
+ FormatString(shader_str[1], kShaderStrSize, "%s #DECAL_NORMALS", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+ if (g_draw_collision){
+ FormatString(shader_str[1], kShaderStrSize, "%s #COLLISION", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+ if (placeholder_){
+ FormatString(shader_str[1], kShaderStrSize, "%s #HALFTONE_STIPPLE", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+ if (type == Object::kWireframe){
+ FormatString(shader_str[1], kShaderStrSize, "%s #WIREFRAME", shader_str[0]);
+ std::swap(shader_str[0], shader_str[1]);
+ }
+
+ FormatString(shader_str[1], kShaderStrSize, "%s %s", shader_str[0], global_shader_suffix);
+ PROFILER_LEAVE(g_profiler_ctx); // Create shader string
+ PROFILER_ENTER(g_profiler_ctx, "returnProgram");
+ the_shader = shaders->returnProgram(shader_str[1], use_tesselation?Shaders::kTesselation:Shaders::kNone);
+ last_shader = the_shader;
+ PROFILER_LEAVE(g_profiler_ctx); // returnProgram
+
+ PROFILER_ENTER(g_profiler_ctx, "setProgram");
+ shaders->setProgram(the_shader);
+ PROFILER_LEAVE(g_profiler_ctx); // setProgram
+ PROFILER_ENTER(g_profiler_ctx, "Misc uniforms");
+ shaders->SetUniformMat4("projection_view_mat", proj_view_matrix);
+ shaders->SetUniformMat4("prev_projection_view_mat", prev_proj_view_matrix);
+ shaders->SetUniformFloat("time",timer->GetRenderTime());
+ shaders->SetUniformVec3("cam_pos", cam_pos);
+ if(type == Object::kFullDraw){
+ if(shadow_matrix){
+ std::vector<mat4> temp_shadow_matrix = *shadow_matrix;
+ if(g_simple_shadows || !g_level_shadows){
+ temp_shadow_matrix[3] = cam->biasMatrix * graphics->simple_shadow_mat;
+ }
+ shaders->SetUniformMat4Array("shadow_matrix", temp_shadow_matrix);
+ }
+ shaders->SetUniformVec3("ws_light", scenegraph_->primary_light.pos);
+ shaders->SetUniformVec4("primary_light_color",vec4(scenegraph_->primary_light.color,
+ scenegraph_->primary_light.intensity));
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Misc uniforms
+
+ PROFILER_LEAVE(g_profiler_ctx); // Shader
+ }
+ PROFILER_ENTER(g_profiler_ctx, "Textures");
+ textures->bindTexture(texture_ref_[0], TEX_COLOR);
+ if (use_textures) {
+ textures->bindTexture(normal_texture_ref_[0], TEX_NORMAL);
+ if(!translucency_texture_ref_.empty()){
+ textures->bindTexture(translucency_texture_ref_[0], TEX_TRANSLUCENCY);
+ }
+ if(weight_map_ref_.valid()){
+ textures->bindTexture(weight_map_ref_, 5);
+
+ vec4 temp(0.0f, 0.0f, 0.0f, 0.0f);
+ for (unsigned int i = 0; i < detail_texture_color_indices_.size(); i++) {
+ temp[i] = (float)detail_texture_color_indices_[i];
+ }
+ shaders->SetUniformVec4("detail_color_indices", temp);
+
+ for (unsigned int i = 0; i < detail_texture_normal_indices_.size(); i++) {
+ temp[i] = (float)detail_texture_normal_indices_[i];
+ }
+ shaders->SetUniformVec4("detail_normal_indices", temp);
+
+ for(unsigned i=0; i<detail_texture_color_srgb_.size(); ++i){
+ const int kBufSize = 256;
+ char buf[kBufSize];
+ FormatString(buf, kBufSize, "avg_color%d",i);
+ shaders->SetUniformVec4(buf, detail_texture_color_srgb_[i]);
+ }
+ }
+ if(shader_changed){
+ textures->bindTexture(scenegraph_->sky->GetSpecularCubeMapTexture(), TEX_SPEC_CUBEMAP);
+ if(g_simple_shadows || !g_level_shadows){
+ textures->bindTexture(graphics->static_shadow_depth_ref, TEX_SHADOW);
+ } else {
+ textures->bindTexture(graphics->cascade_shadow_depth_ref, TEX_SHADOW);
+ }
+ if(scenegraph_->light_probe_collection.light_volume_enabled && scenegraph_->light_probe_collection.ambient_3d_tex.valid()){
+ textures->bindTexture(scenegraph_->light_probe_collection.ambient_3d_tex, 16);
+ }
+ if(weight_map_ref_.valid()){
+ textures->bindTexture(textures->getDetailColorArray(), 6);
+ textures->bindTexture(textures->getDetailNormalArray(), 7);
+ }
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Textures
+
+ if((type == Object::kFullDraw || type == Object::kWireframe) && shader_changed){
+ PROFILER_ZONE(g_profiler_ctx, "kFullDraw uniforms");
+ shaders->SetUniformFloat("haze_mult", scenegraph_->haze_mult);
+ shaders->SetUniformInt("reflection_capture_num", scenegraph_->ref_cap_matrix.size());
+ if(!scenegraph_->ref_cap_matrix.empty()){
+ assert(!scenegraph_->ref_cap_matrix_inverse.empty());
+ shaders->SetUniformMat4Array("reflection_capture_matrix", scenegraph_->ref_cap_matrix);
+ shaders->SetUniformMat4Array("reflection_capture_matrix_inverse", scenegraph_->ref_cap_matrix_inverse);
+ }
+
+ if(scenegraph_->light_probe_collection.ShaderNumLightProbes() == 0){
+ shaders->SetUniformInt("light_volume_num", 0);
+ shaders->SetUniformInt("num_tetrahedra", 0);
+ shaders->SetUniformInt("num_light_probes", 0);
+ } else {
+ std::vector<mat4> light_volume_matrix;
+ std::vector<mat4> light_volume_matrix_inverse;
+ for(int i=0, len=scenegraph_->light_volume_objects_.size(); i<len; ++i){
+ Object* obj = scenegraph_->light_volume_objects_[i];
+ const mat4 &mat = obj->GetTransform();
+ light_volume_matrix.push_back(mat);
+ light_volume_matrix_inverse.push_back(invert(mat));
+ }
+ shaders->SetUniformInt("light_volume_num", light_volume_matrix.size());
+ if(!light_volume_matrix.empty()){
+ assert(!light_volume_matrix_inverse.empty());
+ shaders->SetUniformMat4Array("light_volume_matrix", light_volume_matrix);
+ shaders->SetUniformMat4Array("light_volume_matrix_inverse", light_volume_matrix_inverse);
+ }
+
+ shaders->SetUniformInt("num_tetrahedra", scenegraph_->light_probe_collection.ShaderNumTetrahedra());
+ shaders->SetUniformInt("num_light_probes", scenegraph_->light_probe_collection.ShaderNumLightProbes());
+ shaders->SetUniformVec3("grid_bounds_min", scenegraph_->light_probe_collection.grid_lookup.bounds[0]);
+ shaders->SetUniformVec3("grid_bounds_max", scenegraph_->light_probe_collection.grid_lookup.bounds[1]);
+ shaders->SetUniformInt("subdivisions_x", scenegraph_->light_probe_collection.grid_lookup.subdivisions[0]);
+ shaders->SetUniformInt("subdivisions_y", scenegraph_->light_probe_collection.grid_lookup.subdivisions[1]);
+ shaders->SetUniformInt("subdivisions_z", scenegraph_->light_probe_collection.grid_lookup.subdivisions[2]);
+ shaders->SetUniformInt(shaders->GetTexUniform(TEX_AMBIENT_COLOR_BUFFER), TEX_AMBIENT_COLOR_BUFFER);
+ shaders->SetUniformInt(shaders->GetTexUniform(TEX_AMBIENT_GRID_DATA), TEX_AMBIENT_GRID_DATA);
+ if(scenegraph_->light_probe_collection.light_probe_buffer_object_id != -1){
+ glBindBuffer(GL_TEXTURE_BUFFER, scenegraph_->light_probe_collection.light_probe_buffer_object_id);
+ }
+ }
+
+ if( g_no_reflection_capture == false) {
+ textures->bindTexture(scenegraph_->cubemaps, 19);
+ }
+
+ if(use_textures){
+ PROFILER_ZONE(g_profiler_ctx, "Decals and lights");
+ scenegraph_->BindDecals(the_shader);
+ scenegraph_->BindLights(the_shader);
+ }
+ //textures->bindTexture(graphics->screen_color_tex, 17);
+ textures->bindTexture(graphics->post_effects.temp_screen_tex, 17);
+ textures->bindTexture(graphics->screen_depth_tex, 18);
+ }
+
+ PROFILER_ENTER(g_profiler_ctx, "Attribs");
+ if(!model->vbo_loaded){
+ model->createVBO();
+ }
+ model->VBO_faces.Bind();
+
+ int kBatchSize = 256 * (!g_ubo_batch_multiplier_force_1x ? ubo_batch_size_multiplier : 1);
+ const bool ignore_multiplier = true;
+ static VBORingContainer env_object_model_translation_instance_vbo(sizeof(vec3) * kBatchSize * 16, kVBOFloat | kVBOStream, ignore_multiplier);
+ static VBORingContainer env_object_model_scale_instance_vbo(sizeof(vec3) * kBatchSize * 15, kVBOFloat | kVBOStream, ignore_multiplier);
+ static VBORingContainer env_object_model_rotation_quat_instance_vbo(sizeof(vec4) * kBatchSize * 17, kVBOFloat | kVBOStream, ignore_multiplier);
+ static VBORingContainer env_object_color_tint_instance_vbo(sizeof(vec4) * kBatchSize * 14, kVBOFloat | kVBOStream, ignore_multiplier);
+ static VBORingContainer env_object_detail_scale_instance_vbo(sizeof(vec4) * kBatchSize * 18, kVBOFloat | kVBOStream, ignore_multiplier);
+
+ PROFILER_LEAVE(g_profiler_ctx); // Attribs
+ PROFILER_LEAVE(g_profiler_ctx); // Setup
+
+ bool attrib_envobj_instancing = g_attrib_envobj_intancing_support && g_attrib_envobj_intancing_enabled;
+
+ int instance_block_index = shaders->GetUBOBindIndex(the_shader, "InstanceInfo");
+ if (attrib_envobj_instancing || (unsigned)instance_block_index != GL_INVALID_INDEX)
+ {
+ PROFILER_ENTER(g_profiler_ctx, "Setup uniform block");
+ GLint block_size = !attrib_envobj_instancing ? shaders->returnShaderBlockSize(instance_block_index, the_shader) : -1;
+
+ static UniformRingBuffer env_object_instance_buffer;
+ if(!attrib_envobj_instancing && env_object_instance_buffer.gl_id == -1){
+ env_object_instance_buffer.Create(2 * 1024 * 1024);
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Setup uniform block
+
+ if(g_draw_collision){
+ kBatchSize = 1;
+ }
+
+ static GLubyte blockBuffer[131072]; // Big enough for 8x the OpenGL guaranteed minimum size. Max supported by shader flags currently. 16x or higher could be added if new platforms end up having > 128k frequently
+
+ static std::vector<vec3> model_translation_buffer;
+ model_translation_buffer.resize(kBatchSize);
+
+ static std::vector<vec3> model_scale_buffer;
+ static std::vector<vec4> model_rotation_quat_buffer;
+ static std::vector<vec4> color_tint_buffer;
+ static std::vector<vec4> detail_scale_buffer;
+
+ if(attrib_envobj_instancing) {
+ model_scale_buffer.resize(kBatchSize);
+ model_rotation_quat_buffer.resize(kBatchSize);
+ color_tint_buffer.resize(kBatchSize);
+ detail_scale_buffer.resize(kBatchSize);
+ }
+
+ for(int i=0; i<num_instances; i+=kBatchSize){
+ vec3* model_translation_attrib = &model_translation_buffer[0];
+ vec3* model_scale = attrib_envobj_instancing ? &model_scale_buffer[0] : (vec3*)((uintptr_t)blockBuffer + 0*sizeof(float)*4);
+ vec4* model_rotation_quat = attrib_envobj_instancing ? &model_rotation_quat_buffer[0] : (vec4*)((uintptr_t)blockBuffer + 1*sizeof(float)*4);
+ vec4* color_tint = attrib_envobj_instancing ? &color_tint_buffer[0] : (vec4*)((uintptr_t)blockBuffer + 2*sizeof(float)*4);
+ vec4* detail_scale = attrib_envobj_instancing ? &detail_scale_buffer[0] : (vec4*)((uintptr_t)blockBuffer + 3*sizeof(float)*4);
+ if(!attrib_envobj_instancing) {
+ glUniformBlockBinding(shaders->programs[the_shader].gl_program, instance_block_index, 0);
+ }
+ int to_draw = min(kBatchSize, num_instances-i);
+ PROFILER_ENTER(g_profiler_ctx, "Setup batch data");
+ for(int j=0; j<to_draw; ++j){
+ EnvObject* obj = instance_array[i+j];
+ PlantComponent* plant_component = obj->plant_component_.get();
+ {
+ *model_scale = obj->scale_;
+ *model_rotation_quat = *(vec4*)&obj->GetRotation();
+ *model_translation_attrib = obj->translation_;
+ if(plant_component && plant_component->IsActive()){
+ // TODO: This used to calculate a pivot. Is that important anymore, or can it just rotate around its origin?
+ //if(!plant_component->IsPivotCalculated()){
+ // plant_component->SetPivot(*scenegraph_->bullet_world_,
+ // obj->sphere_center_, obj->sphere_radius_);
+ //}
+ quaternion plant_rotation = plant_component->GetQuaternion(obj->sphere_radius_) * obj->GetRotation();
+ *model_rotation_quat = *(vec4*)&plant_rotation;
+ }
+ }
+ {
+ *color_tint = obj->GetDisplayTint();
+ if(plant_component){
+ (*color_tint)[3] = plant_component->GetPlantShake(obj->sphere_radius_);
+ } else {
+ (*color_tint)[3] = 0.0f;
+ }
+ if(attached_ != NULL){
+ (*color_tint)[3] = -1.0f;
+ }
+ }
+ {
+ vec4 scale = obj->GetScale();
+ float scaled_up = max(scale[0],max(scale[1],scale[2]));
+ float texel_scale = scaled_up/model->texel_density * 0.15f;
+ vec4 scale_vec(1.0f);
+ for(int k=0; k<(int)ofr->m_detail_map_scale.size(); ++k){
+ scale_vec[k] = texel_scale * ofr->m_detail_map_scale[k];
+ }
+ *detail_scale = scale_vec;
+ }
+
+ ++model_translation_attrib;
+ if(attrib_envobj_instancing) {
+ ++model_scale;
+ ++model_rotation_quat;
+ ++color_tint;
+ ++detail_scale;
+ } else {
+ model_scale = (vec3*)((uintptr_t)model_scale+64);
+ model_rotation_quat = (vec4*)((uintptr_t)model_rotation_quat+64);
+ color_tint = (vec4*)((uintptr_t)color_tint+64);
+ detail_scale = (vec4*)((uintptr_t)detail_scale+64);
+ }
+
+ if(g_draw_collision){
+ if(obj->normal_override_buffer_dirty){
+ if(!obj->normal_override_buffer.valid()){
+ obj->normal_override_buffer = Textures::Instance()->makeBufferTexture(obj->normal_override.size()*4*3, GL_RGB32F);
+ }
+ UpdateNormalOverride(obj, model);
+ PROFILER_ZONE(g_profiler_ctx, "UpdateNormalOverride buffer texture");
+ Textures::Instance()->updateBufferTexture(obj->normal_override_buffer, obj->normal_override.size()*4*3, &obj->normal_override[0]);
+ obj->normal_override_buffer_dirty = false;
+ }
+ textures->bindTexture(obj->normal_override_buffer, TEX_LIGHT_DECAL_DATA_BUFFER);
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Setup batch data
+ {
+ env_object_model_translation_instance_vbo.Fill(sizeof(vec3) * to_draw, &model_translation_buffer[0]);
+
+ if(attrib_envobj_instancing) {
+ env_object_model_scale_instance_vbo.Fill(sizeof(vec3) * to_draw, &model_scale_buffer[0]);
+ env_object_model_rotation_quat_instance_vbo.Fill(sizeof(vec4) * to_draw, &model_rotation_quat_buffer[0]);
+ env_object_color_tint_instance_vbo.Fill(sizeof(vec4) * to_draw, &color_tint_buffer[0]);
+ env_object_detail_scale_instance_vbo.Fill(sizeof(vec4) * to_draw, &detail_scale_buffer[0]);
+ }
+
+ SetupAttribPointers(shader_changed, model, env_object_model_translation_instance_vbo, env_object_model_scale_instance_vbo, env_object_model_rotation_quat_instance_vbo, env_object_color_tint_instance_vbo, env_object_detail_scale_instance_vbo, shaders, the_shader, graphics);
+ shader_changed = false;
+
+ if(!attrib_envobj_instancing) {
+ env_object_instance_buffer.Fill(block_size / kBatchSize * to_draw, blockBuffer);
+ glBindBufferRange( GL_UNIFORM_BUFFER, 0, env_object_instance_buffer.gl_id, env_object_instance_buffer.offset, env_object_instance_buffer.next_offset - env_object_instance_buffer.offset);
+ }
+ }
+ CHECK_GL_ERROR();
+ {
+ PROFILER_ZONE(g_profiler_ctx, "glDrawElementsInstanced");
+ graphics->DrawElementsInstanced(use_tesselation?GL_PATCHES:GL_TRIANGLES, model->faces.size(), GL_UNSIGNED_INT, 0, to_draw);
+ }
+ CHECK_GL_ERROR();
+ }
+ }
+
+ graphics->ResetVertexAttribArrays();
+ graphics->BindArrayVBO(0);
+ graphics->BindElementVBO(0);
+
+ int attrib_count = attrib_envobj_instancing ? kAttribIdCountVboInstancing : kAttribIdCountUboInstancing;
+ for(int i=0; i<attrib_count; ++i){
+ if(attrib_ids[i] != -1){
+ glVertexAttribDivisorARB(attrib_ids[i], 0);
+ }
+ }
+
+ if(graphics->use_sample_alpha_to_coverage){
+ glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+ }
+
+ last_ofr_is_valid = ofr.valid();
+ if(last_ofr_is_valid) {
+ last_ofr_shader_name = ofr->shader_name;
+ }
+}
+
+void EnvObject::DrawDetailObjectInstances(EnvObject** instance_array, int num_instances, Object::DrawType type) {
+ if (g_debug_runtime_disable_env_object_draw_detail_object_instances) {
+ return;
+ }
+
+ // Note: Should never get called with Object::kDrawDepthOnly. When this comment was written, that was true
+
+ if(!detail_object_surfaces.empty() && type == Object::kFullDraw){
+ for(int i=0; i<num_instances; ++i){
+ EnvObject* obj = instance_array[i];
+ for(DOSList::iterator iter = obj->detail_object_surfaces.begin();
+ iter != obj->detail_object_surfaces.end(); ++iter)
+ {
+ (*iter)->Draw(obj->GetTransform(), DetailObjectSurface::ENVOBJECT, obj->GetDisplayTint(), scenegraph_->sky->GetSpecularCubeMapTexture(), &scenegraph_->light_probe_collection,scenegraph_);
+ }
+ }
+ }
+}
+
+void EnvObject::PreDrawCamera(float curr_game_time) {
+ if (g_debug_runtime_disable_env_object_pre_draw_camera) {
+ return;
+ }
+
+ for(DOSList::iterator iter = detail_object_surfaces.begin();
+ iter != detail_object_surfaces.end(); ++iter)
+ {
+ (*iter)->PreDrawCamera(transform_);
+ }
+}
+
+void EnvObject::UpdateDetailScale() {
+ if(!detail_texture_color_.empty()){
+ vec4 scale = GetScale();
+ float scaled_up = max(scale[0],max(scale[1],scale[2]));
+ Model* model = &Models::Instance()->GetModel(model_id_);
+ float texel_scale = scaled_up/model->texel_density * 0.15f;
+ vec4 scale_vec(1.0f);
+ for(int i=0; i<(int)ofr->m_detail_map_scale.size(); ++i){
+ scale_vec[i] = texel_scale * ofr->m_detail_map_scale[i];
+ }
+ }
+}
+
+void EnvObject::GetObj2World(float *obj2world) {
+ const float *gl_matrix = transform_;
+ memcpy(obj2world,gl_matrix,sizeof(float) * 16);
+}
+
+
+
+void EnvObject::EnvInterpolate(uint16_t pending_updates) {
+ /*delta_time_between_interpolation_frames = (next_time - current_time) / 1000.0;
+
+ interpolation_step += (1.0 / (delta_time_between_interpolation_frames / game_timer.GetFrameTime()));
+ double catchup = (pending_updates > 2) * pending_updates * pending_updates / 100.0;
+ interpolation_step += catchup;
+
+ if (interpolation_step > 1.0 || interpolation_time > delta_time_between_interpolation_frames) {
+ interpolation_step = 0.0;
+ interpolation_time = 0.0;
+ interp = false;
+ }
+ else {
+ vec3 new_scale = lerp(curr_scale, next_scale, interpolation_step);
+ vec3 new_translation = lerp(curr_translation, next_translation, interpolation_step);
+ quaternion new_rot = Slerp(curr_rot, next_rot, interpolation_step);
+
+ SetTranslation(new_translation);
+ SetScale(new_scale);
+ SetRotation(new_rot);
+ }
+
+
+ interpolation_time += game_timer.GetFrameTime();
+ */
+
+ //LOGI << "first_frame" << first_frame << " Interp_step: " << interpolation_step << " delta: " << delta << " Frame time: " << game_timer.GetFrameTime() << " Interp time: " << interpolation_time << " pending transformations:" << std::endl;
+
+}
+
+void EnvObject::DrawDepthMap(const mat4& proj_view_matrix, const vec4* cull_planes, int num_cull_planes, Object::DrawType draw_type) {
+ if (g_debug_runtime_disable_env_object_draw_depth_map) {
+ return;
+ }
+
+ EnvObject* instances[1];
+ instances[0] = this;
+ Camera* camera = ActiveCameras::Get();
+ Graphics* graphics = Graphics::Instance();
+ vec3 cam_pos = camera->GetPos();
+ std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = camera->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ last_ofr_is_valid = false;
+ DrawInstances(instances, 1, proj_view_matrix, proj_view_matrix, &shadow_matrix, cam_pos, draw_type);
+}
+
+void EnvObject::SetEnabled(bool val) {
+ if(val != enabled_){
+ Object::SetEnabled(val);
+ if(!enabled_){
+ RemovePhysicsShape();
+ } else {
+ if(!attached_) {
+ CreatePhysicsShape();
+ }
+ }
+ shadow_cache_dirty = true;
+ }
+}
+
+void EnvObject::SetCollisionEnabled(bool val) {
+ if(val && !bullet_object_) {
+ if(!attached_) {
+ CreatePhysicsShape();
+ }
+ } else if(!val && bullet_object_) {
+ RemovePhysicsShape();
+ }
+}
+
+struct DistanceSorter {
+ float distance;
+ int id;
+};
+
+class DistanceSorterCompare {
+public:
+ bool operator()(const DistanceSorter &a, const DistanceSorter &b) {
+ return a.distance < b.distance;
+ }
+};
+
+void CreatePointCloud(std::vector<vec3> &points, const Model &model, const ImageSamplerRef &image) {
+ float total_triangle_area = 0.0f;;
+ std::vector<float> triangle_area(model.faces.size()/3);
+ {
+ vec3 verts[3];
+ int face_index = 0;
+ int vert_index;
+ for(unsigned i=0; i<triangle_area.size(); ++i){
+ for(unsigned j=0; j<3; ++j){
+ vert_index = model.faces[face_index+j]*3;
+ verts[j] = vec3(model.vertices[vert_index+0],
+ model.vertices[vert_index+1],
+ model.vertices[vert_index+2]);
+ }
+ triangle_area[i] = length(cross(verts[2]-verts[0], verts[1]-verts[0]))*0.5f;
+ total_triangle_area += triangle_area[i];
+ face_index += 3;
+ }
+ }
+
+ float density = 100.0f;
+ if(total_triangle_area * density > 10000.0f){
+ density = 10000.0f / total_triangle_area;
+ }
+ int vert_index;
+ int tex_index;
+ vec3 verts[3];
+ vec2 tex_coords[3];
+ std::vector<vec3> temp_points;
+ for(unsigned face_index=0; face_index<model.faces.size(); face_index+=3){
+ for(unsigned j=0; j<3; ++j){
+ vert_index = model.faces[face_index+j]*3;
+ tex_index = model.faces[face_index+j]*2;
+ verts[j] = vec3(model.vertices[vert_index+0],
+ model.vertices[vert_index+1],
+ model.vertices[vert_index+2]);
+ tex_coords[j] = vec2(model.tex_coords[tex_index+0],
+ model.tex_coords[tex_index+1]);
+ }
+ unsigned num_dots = max((unsigned)5,(unsigned)(triangle_area[face_index/3]*density));
+ for(unsigned j=0; j<num_dots; ++j){
+ vec2 coord(RangedRandomFloat(0.0f,1.0f),
+ RangedRandomFloat(0.0f,1.0f));
+ if(coord[0] + coord[1] > 1.0f){
+ std::swap(coord[0], coord[1]);
+ coord[0] = 1.0f - coord[0];
+ coord[1] = 1.0f - coord[1];
+ }
+ vec3 pos = (verts[0] + (verts[1]-verts[0])*coord[0] + (verts[2]-verts[0])*coord[1]);
+ vec2 tex_coord;
+ tex_coord = (tex_coords[0] + (tex_coords[1]-tex_coords[0])*coord[0] + (tex_coords[2]-tex_coords[0])*coord[1]);
+ if(image->GetInterpolatedColorUV(tex_coord[0], tex_coord[1]).a()>0.0f){
+ temp_points.push_back(pos);
+ }
+ }
+ }
+
+ vec3 center;
+ for(unsigned i=0; i<temp_points.size(); ++i){
+ center += temp_points[i];
+ }
+ center /= (float)temp_points.size();
+
+ std::vector<DistanceSorter> ds(temp_points.size());
+ for(unsigned i=0; i<temp_points.size(); ++i){
+ ds[i].id = i;
+ ds[i].distance = distance_squared(temp_points[i], center);
+ }
+ std::sort(ds.begin(), ds.end(), DistanceSorterCompare());
+ //ds.resize(ds.size()*0.9f);
+ points.resize(ds.size());
+ for(unsigned i=0; i<points.size(); ++i){
+ points[i] = temp_points[ds[i].id];
+ }
+}
+
+BulletWorld* EnvObject::GetBulletWorld() {
+ return ofr->bush_collision ? scenegraph_->plant_bullet_world_ : scenegraph_->bullet_world_;
+}
+
+void ShortName(const std::string &input, std::string &output){
+ int slash = input.rfind('/');
+ int dot = input.rfind('.');
+ output = input.substr(slash+1, dot-slash-1);
+}
+
+typedef std::map<std::string, bool> HullExistsCache;
+static HullExistsCache hull_exists_cache;
+
+bool DoesHullFileExist(const std::string& hull_path){
+ HullExistsCache::const_iterator iter = hull_exists_cache.find(hull_path);
+ bool hull_file_exists = false;
+ if(iter != hull_exists_cache.end()){
+ hull_file_exists = iter->second;
+ } else {
+ hull_file_exists = FileExists(hull_path.c_str(), kDataPaths | kModPaths);
+ hull_exists_cache[hull_path] = hull_file_exists;
+ }
+ return hull_file_exists;
+}
+
+int EnvObject::GetCollisionModelID() {
+ if(ofr->bush_collision){
+ return -1;
+ } else {
+ std::string collision_model_path = ofr->model_name.substr(0, ofr->model_name.size()-4)+"_col.obj";
+ if(DoesHullFileExist(collision_model_path)){
+ int collision_model_id = Models::Instance()->loadModel(collision_model_path.c_str());
+ Model& collision_model = Models::Instance()->GetModel(collision_model_id);
+ if(collision_model.old_center == collision_model.center_coords){
+ Model& model = Models::Instance()->GetModel(model_id_);
+ collision_model.center_coords = model.old_center;
+ collision_model.CenterModel();
+ }
+ return collision_model_id;
+ } else {
+ return model_id_;
+ }
+ }
+}
+
+void EnvObject::CreatePhysicsShape() {
+ BWFlags flags = BW_NO_FLAGS;
+ if(ofr->no_collision){
+ flags |= BW_DECALS_ONLY;
+ }
+ BulletWorld* bw = GetBulletWorld();
+ if(ofr->bush_collision){
+ CreateBushPhysicsShape();
+ } else {
+ int id = GetCollisionModelID();
+ Model* model = &Models::Instance()->GetModel(id);
+ normal_override_buffer_dirty = true;
+ bullet_object_ = bw->CreateStaticMesh(model, id, flags);
+ }
+ if(bullet_object_){
+ bullet_object_->owner_object = this;
+ bullet_object_->SetTransform(GetTranslation(), Mat4FromQuaternion(GetRotation()), GetScale());
+ bullet_object_->UpdateTransform();
+ bullet_object_->UpdateTransform();
+ bw->UpdateSingleAABB(bullet_object_);
+ }
+}
+
+void EnvObject::RemovePhysicsShape() {
+ if(bullet_object_){
+ GetBulletWorld()->RemoveStaticObject(&bullet_object_);
+ }
+}
+
+bool EnvObject::Initialize() {
+ //int test_shader_id = Shaders::Instance()->returnProgram(ofr->shader_name);
+ //transparent = Shaders::Instance()->IsProgramTransparent(test_shader_id);
+ transparent = false;
+ if(ofr->transparent) {
+ transparent = true;
+ }
+ if(ofr->terrain_fixed){
+ SetTranslation(Models::Instance()->GetModel(model_id_).old_center);
+ permission_flags &= ~(Object::CAN_ROTATE |
+ Object::CAN_SCALE | Object::CAN_TRANSLATE);
+ }
+ if(scenegraph_ && !added_to_physics_scene_ && !attached_){
+ if(!ofr->bush_collision) {
+ int id = GetCollisionModelID();
+ Model* model = &Models::Instance()->GetModel(id);
+ if(model->faces.empty()) {
+ DisplayError("Couldn't find model", "The given envobject doesn't have a valid collision mesh. Make sure the model/collision mesh exists. The object will not be spawned", ErrorType::_ok);
+ return false;
+ }
+ }
+
+ CreatePhysicsShape();
+ added_to_physics_scene_ = true;
+ UpdatePhysicsTransform();
+ }
+ UpdateBoundingSphere();
+ m_transform_starting_sphere_center = sphere_center_;
+ m_transform_starting_sphere_radius = sphere_radius_;
+ return true;
+}
+
+void EnvObject::GetShaderNames(std::map<std::string, int>& shaders) {
+ shaders[ofr->shader_name] = SceneGraph::kPreloadTypeAll;
+}
+
+void EnvObject::Update(float timestep) {
+ Online* online = Online::Instance();
+
+ if(plant_component_.get()){
+ if(plant_component_->IsActive()){
+ plant_component_->Update(timestep);
+ } else {
+ if(scenegraph_ && update_list_entry != -1){
+ scenegraph_->UnlinkUpdateObject(this, update_list_entry);
+ update_list_entry = -1;
+ }
+ }
+ }
+
+ if (online->IsClient()) {
+ list<OnlineMessageRef>& frames = incoming_online_env_update;
+ if(frames.size() > 0) {
+ EnvObjectUpdate* eou = static_cast<EnvObjectUpdate*>(frames.begin()->GetData());
+ if(eou != nullptr) {
+ SetTransformationMatrix(eou->transform);
+ }
+ frames.pop_front();
+ }
+ }
+}
+
+void EnvObject::UpdateBoundingSphere() {
+ vec3 radius_vec = box_.dims*0.5f;
+ sphere_radius_ = length(transform_.GetRotatedvec3(radius_vec));
+ sphere_center_ = GetTranslation();
+}
+
+int EnvObject::lineCheck(const vec3 &start, const vec3 &end, vec3 *point, vec3 *normal) {
+ if(selected_){
+ if(!sphere_line_intersection( start, end, sphere_center_, sphere_radius_ ) ){ // Much faster than the OBB check, and most objects probably won't fall under cursor
+ return -1;
+ }
+ return LineCheckEditorCube(start, end, point, normal);
+ }
+ if(bullet_object_){
+ return GetBulletWorld()->CheckRayCollisionObj(start, end, *bullet_object_, point, normal);
+ }
+ if (model_id_ == -1 ||
+ !sphere_line_intersection( start, end, sphere_center_, sphere_radius_ ) )
+ {
+ return -1;
+ }
+ const Model& model = Models::Instance()->GetModel(model_id_);
+ int intersecting = model.lineCheckNoBackface(invert(transform_)*start,
+ invert(transform_)*end,
+ point,
+ normal);
+ if(intersecting!=-1){
+ if(point){
+ *point = transform_ * *point;
+ }
+ if(normal){
+ *normal = GetRotation() * *normal;
+ }
+ }
+ return intersecting;
+}
+
+void EnvObject::Moved(Object::MoveType type) {
+ Object::Moved(type);
+ if(type & Object::kScale) {
+ UpdateDetailScale();
+ }
+
+ UpdateDetailScale();
+ UpdateBoundingSphere();
+
+ LoadModel();
+ if(plant_component_.get()){
+ plant_component_->ClearPivot();
+ }
+ Graphics::Instance()->nav_mesh_out_of_date = true;
+
+ if(ofr.valid()){
+ UpdateDetailObjectSurfaces(&detail_object_surfaces, ofr, texture_ref_[0],
+ normal_texture_ref_[0], transform_, model_id_);
+ if(!ofr->dynamic && !attached_ && !placeholder_){
+ shadow_cache_dirty = true;
+ if(scenegraph_) {
+ auto it = std::find(scenegraph_->visible_static_meshes_.begin(), scenegraph_->visible_static_meshes_.end(), this);
+ // Dirty shadow cache bounds for current object
+ if(it != scenegraph_->visible_static_meshes_.end()){
+ auto& shadow_cache_bounds = *(scenegraph_->visible_static_meshes_shadow_cache_bounds_.begin() + std::distance(scenegraph_->visible_static_meshes_.begin(), it));
+ shadow_cache_bounds.is_calculated = false;
+ }
+ }
+ normal_override_buffer_dirty = true;
+ }
+ }
+}
+
+// returns true iff an update occured
+bool EnvObject::UpdatePhysicsTransform() {
+ if (!bullet_object_) {
+ return false;
+ }
+
+ bullet_object_->SetTransform(GetTranslation(), Mat4FromQuaternion(GetRotation()), GetScale());
+ bullet_object_->UpdateTransform();
+ bullet_object_->UpdateTransform();
+ GetBulletWorld()->UpdateSingleAABB(bullet_object_);
+ return true;
+}
+
+void EnvObject::GetDisplayName(char* buf, int buf_size) {
+ if( GetName().empty() ) {
+ FormatString(buf, buf_size, "%d, Static Mesh from %s", GetID(), obj_file.c_str());
+ } else {
+ FormatString(buf, buf_size, "%s, Static Mesh from %s", GetName().c_str(), obj_file.c_str());
+ }
+}
+
+const Model * EnvObject::GetModel() const {
+ return &Models::Instance()->GetModel(model_id_);
+}
+
+const MaterialDecal& EnvObject::GetMaterialDecal( const std::string &type, const vec3 &pos ) {
+ MaterialRef material = ofr_material;
+ return material->GetDecal(type);
+}
+
+const MaterialParticle& EnvObject::GetMaterialParticle( const std::string &type, const vec3 &pos ) {
+ MaterialRef material = ofr_material;
+ return material->GetParticle(type);
+}
+
+bool EnvObject::Load( const std::string& type_file ) {
+ bool ret = true;
+ Textures* textures = Textures::Instance();
+ textures->setWrap(GL_REPEAT);
+
+ ofr = Engine::Instance()->GetAssetManager()->LoadSync<ObjectFile>(type_file);
+
+ if( ofr.valid() ) {
+ modsource_ = ofr->modsource_;
+
+ ofr_material = Engine::Instance()->GetAssetManager()->LoadSync<Material>(ofr->material_path);
+
+ if(ofr_material.valid()) {
+ PROFILER_ENTER(g_profiler_ctx, "Loading detail/weight texture refs");
+ if(!ofr->weight_map.empty()){
+ weight_map_ref_ = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofr->weight_map);
+ if( weight_map_ref_.valid() == false ) {
+ LOGE << "Failed at loading weight_map " << ofr->weight_map << " for envobject " << type_file << std::endl;
+ ret = false;
+ }
+ }
+
+ TextureRef detail_color_map_ref_ = textures->getDetailColorArray();
+ detail_texture_color_indices_.resize(ofr->m_detail_color_maps.size());
+ for(unsigned i=0; i<ofr->m_detail_color_maps.size(); ++i){
+ detail_texture_color_indices_[i] = textures->loadArraySlice(detail_color_map_ref_, ofr->m_detail_color_maps[i]);
+ }
+
+ detail_texture_normal_indices_.resize(ofr->m_detail_normal_maps.size());
+ TextureRef detail_normal_map_ref_ = textures->getDetailNormalArray();
+ for(unsigned i=0; i<ofr->m_detail_normal_maps.size(); ++i){
+ detail_texture_normal_indices_[i] = textures->loadArraySlice(detail_normal_map_ref_, ofr->m_detail_normal_maps[i]);
+ }
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ PROFILER_ENTER(g_profiler_ctx, "Getting average colors");
+ detail_texture_color_.resize(ofr->m_detail_color_maps.size());
+ detail_texture_color_srgb_.resize(ofr->m_detail_color_maps.size());
+ for(unsigned i=0; i<ofr->m_detail_color_maps.size(); i++){
+ vec4 &dtc = detail_texture_color_[i];
+ vec4 &dtc_srgb = detail_texture_color_srgb_[i];
+ //dtc = AverageColors::Instance()->ReturnRef(ofr->m_detail_color_maps[i])->color();
+
+ AverageColorRef color_ref = Engine::Instance()->GetAssetManager()->LoadSync<AverageColor>(ofr->m_detail_color_maps[i]);
+ if( color_ref.valid() ) {
+ average_color_refs.insert(color_ref);
+ dtc = color_ref->color();
+
+ dtc_srgb[0] = pow(dtc[0], 2.2f);
+ dtc_srgb[1] = pow(dtc[1], 2.2f);
+ dtc_srgb[2] = pow(dtc[2], 2.2f);
+ dtc_srgb[3] = dtc[3];
+ } else {
+ LOGE << "Failed at loading AverageColor " << ofr->m_detail_color_maps[i] << " for EnvObject " << type_file << std::endl;
+ ret = false;
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ PROFILER_ENTER(g_profiler_ctx, "Getting main textures");
+ obj_file = type_file;
+
+ if(ofr->clamp_texture){
+ Textures::Instance()->setWrap(GL_CLAMP_TO_EDGE);
+ } else {
+ Textures::Instance()->setWrap(GL_REPEAT);
+ }
+ texture_ref_.clear();
+
+ TextureAssetRef tex = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofr->color_map.c_str(),PX_SRGB,0x0);
+
+ if( tex.valid() ) {
+ texture_ref_.push_back(tex);
+ } else {
+ LOGE << "Failed at loading color map TextureAsset " << ofr->color_map.c_str() << " for EnvObject " << type_file << std::endl;
+ ret = false;
+ }
+
+ normal_texture_ref_.clear();
+
+ tex = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofr->normal_map.c_str());
+
+ if( tex.valid() ) {
+ normal_texture_ref_.push_back(tex);
+ } else {
+ LOGE << "Failed at loading normal map TextureAsset " << ofr->normal_map.c_str() << " for EnvObject " << type_file << std::endl;
+ ret = false;
+ }
+
+
+ translucency_texture_ref_.clear();
+ if(!ofr->translucency_map.empty()) {
+ tex = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>(ofr->translucency_map.c_str(),PX_SRGB,0x0);
+ if( tex.valid() ) {
+ translucency_texture_ref_.push_back(tex);
+ } else {
+ LOGE << "Failed at loading translucency map TextureAsset " << ofr->translucency_map.c_str() << " for EnvObject " << type_file << std::endl;
+ ret = false;
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx);
+
+
+ if(ofr->shader_name == "cubemap"){
+ ofr->shader_name = "envobject #TANGENT";
+ } else if(ofr->shader_name == "cubemapobj" ||
+ ofr->shader_name == "cubemapobjchar" ||
+ ofr->shader_name == "cubemapobjitem")
+ {
+ ofr->shader_name = "envobject";
+ } else if(ofr->shader_name == "cubemapalpha"){
+ ofr->shader_name = "envobject #TANGENT #ALPHA";
+ } else if(ofr->shader_name == "plant" || ofr->shader_name == "plant_less_movement"){
+ ofr->shader_name = "envobject #TANGENT #ALPHA #PLANT";
+ } else if(ofr->shader_name == "plant_foliage") {
+ ofr->shader_name = "envobject #TANGENT #ALPHA #PLANT #NO_DECALS";
+ } else if(ofr->shader_name == "detailmap4"){
+ ofr->shader_name = "envobject #DETAILMAP4 #TANGENT";
+ } else if(ofr->shader_name == "detailmap4tangent"){
+ ofr->shader_name = "envobject #DETAILMAP4 #TANGENT #BASE_TANGENT";
+ } else if(ofr->shader_name == "MagmaFloor"){
+ ofr->shader_name = "envobject #MAGMA_FLOOR";
+ } else if(ofr->shader_name == "MagmaFlow"){
+ ofr->shader_name = "envobject #MAGMA_FLOW";
+ }
+
+ if(ofr->shader_name.find("#ALPHA") != std::string::npos){
+ transparent = true;
+ } else {
+ transparent = ofr->transparent;
+ }
+
+ LoadModel();
+
+ if(!ofr->wind_map.empty()) {
+ if( Models::Instance()->GetModel(model_id_).LoadAuxFromImage(ofr->wind_map) == false ) {
+ LOGE << "Failed to acceptably load AuxFromImage " << ofr->wind_map << " for envobject " << type_file << std::endl;
+ ret = false;
+ }
+ }
+
+ base_color_tint = ofr->color_tint;
+
+ if(ofr->bush_collision){
+ plant_component_.reset(new PlantComponent());
+ }
+
+ UpdateDetailObjectSurfaces(&detail_object_surfaces, ofr, texture_ref_[0],
+ normal_texture_ref_[0], transform_, model_id_);
+ } else {
+ LOGE << "Failure loading Material " << ofr->material_path << " for envobject " << type_file << std::endl;
+ ret = false;
+ }
+ } else {
+ LOGE << "Failure loading envobject when loading its base ObjectFile " << type_file << std::endl;
+ ret = false;
+ }
+ return ret;
+}
+
+static bool NeedsWindingFlip(const vec3 &scale) {
+ int needs_flip = 0;
+ for(int i=0; i<3; ++i){
+ if (scale[i] < 0) {
+ needs_flip++;
+ }
+ }
+ return (needs_flip%2 == 1);
+}
+
+void EnvObject::LoadModel() {
+ if(!ofr.valid()){
+ return;
+ }
+ bool use_tangent = true;
+ char flags = 0;
+ if(use_tangent){
+ flags = flags | _MDL_USE_TANGENT;
+ }
+ flags = flags | _MDL_CENTER;
+
+ bool physics_recalc = false;
+ if(!csg_modified_){
+ int old_model_id = model_id_;
+ if(NeedsWindingFlip(GetScale())){
+ model_id_ = Models::Instance()->loadFlippedModel(ofr->model_name.c_str(), flags);
+ winding_flip = true;
+ } else {
+ model_id_ = Models::Instance()->loadModel(ofr->model_name.c_str(), flags);
+ winding_flip = false;
+ }
+ if(old_model_id != -1 && old_model_id != model_id_) {
+ physics_recalc = true;
+ }
+ }
+ if(scenegraph_ && physics_recalc){
+ RemovePhysicsShape();
+
+ if(!attached_) {
+ CreatePhysicsShape();
+ }
+ }
+}
+
+void EnvObject::SetCSGModified() {
+ csg_modified_ = true;
+ RemovePhysicsShape();
+
+ if(!attached_) {
+ CreatePhysicsShape();
+ }
+}
+
+void EnvObject::Reload() {
+ if(ofr->color_tint != base_color_tint){
+ base_color_tint = ofr->color_tint;
+ }
+}
+
+std::string EnvObject::GetLabel() {
+ return ofr->label;
+}
+
+const vec3 & EnvObject::GetColorTint() {
+ return color_tint_component_.tint_;
+}
+
+const float& EnvObject::GetOverbright() {
+ return color_tint_component_.overbright_;
+}
+
+void EnvObject::GetDesc(EntityDescription &desc) const {
+ if(!attached_ || (attached_ && parent && parent->IsGroupDerived())){
+ // Save everything if part of a group
+ Object::GetDesc(desc);
+ } else {
+ // Skip transform info if object is attached to character
+ desc.AddString( EDF_NAME, GetName());
+ desc.AddVec3( EDF_SCALE, GetScale());
+ desc.AddEntityType( EDF_ENTITY_TYPE, GetType());
+ desc.AddInt( EDF_ID, GetID());
+ desc.AddScriptParams(EDF_SCRIPT_PARAMS, sp.GetParameterMap());
+ }
+ color_tint_component_.AddToDescription(desc);
+ desc.AddString(EDF_FILE_PATH, obj_file);
+ desc.AddBool(EDF_NO_NAVMESH, no_navmesh);
+}
+
+MaterialRef EnvObject::GetMaterial( const vec3 &pos, int* tri ) {
+ //return (*Materials::Instance()->ReturnRef(ofr->material_path));
+ //TODO: Make sure that the reference is sent up instead, so that the asset isn't dropped.
+ return ofr_material;
+}
+
+vec3 EnvObject::GetBoundingBoxSize() {
+ return box_.dims;
+}
+
+HullCacheMap hull_cache;
+
+void EnvObject::CreateBushPhysicsShape() {
+ BulletWorld* bw = GetBulletWorld();
+ Model &model = Models::Instance()->GetModel(model_id_);
+ std::string cache_path;
+ {
+ std::string texture_short, model_short;
+ ShortName(ofr->color_map, texture_short);
+ ShortName(ofr->model_name, model_short);
+ cache_path = ofr->model_name.substr(0,ofr->model_name.rfind('/')+1)+
+ model_short+"_" + texture_short + ".hull";
+ }
+
+ HullCache *hc = NULL;
+ HullCacheMap::iterator iter = hull_cache.find(cache_path);
+ if(iter != hull_cache.end()){
+ hc = &(iter->second);
+ }
+
+ if(!hc){
+ char abs_path[kPathSize];
+ bool found_cache = (FindFilePath(cache_path.c_str(), abs_path, kPathSize, kDataPaths|kModPaths|kWriteDir|kModWriteDirs, false) != -1);
+ if(found_cache){
+ FILE *file = my_fopen(abs_path,"rb");
+ char version;
+ fread(&version, sizeof(char), 1, file);
+ if(version != _hull_cache_version){
+ found_cache = false;
+ } else {
+ //printf("Loading hull cache file: %s\n", cache_path.c_str());
+ hc = &hull_cache[cache_path];
+ std::vector<vec3> &verts = hc->verts;
+ std::vector<int> &faces = hc->faces;
+ std::vector<vec3> &point_cloud = hc->point_cloud;
+ {
+ char num_verts;
+ fread(&num_verts, sizeof(char), 1, file);
+ verts.resize(num_verts);
+ fread(&(verts[0]), sizeof(vec3), num_verts, file);
+ }{
+ unsigned char num_face_indices;
+ fread(&num_face_indices, sizeof(unsigned char), 1, file);
+ faces.resize(num_face_indices);
+ fread(&(faces[0]), sizeof(int), num_face_indices, file);
+ }{
+ int num_points;
+ fread(&num_points, sizeof(int), 1, file);
+ point_cloud.resize(num_points);
+ fread(&(point_cloud[0]), sizeof(vec3), num_points, file);
+ }
+ bullet_object_ = bw->CreateConvexObject(verts, faces, true);
+ }
+ fclose(file);
+ }
+ if(!found_cache){
+ //ImageSamplerRef image = ImageSamplers::Instance()->ReturnRef(ofr->color_map);
+ ImageSamplerRef image = Engine::Instance()->GetAssetManager()->LoadSync<ImageSampler>(ofr->color_map);
+
+ hc = &hull_cache[cache_path];
+ std::vector<vec3> &points = hc->point_cloud;
+ std::vector<vec3> &verts = hc->verts;
+ std::vector<int> &faces = hc->faces;
+ CreatePointCloud(points, model, image);
+ GetSimplifiedHull(points, verts, faces);
+
+ bullet_object_ = bw->CreateConvexObject(verts, faces, true);
+
+ char write_path[kPathSize];
+ FormatString(write_path, kPathSize, "%s%s", GetWritePath(modsource_).c_str(), cache_path.c_str());
+ FILE *file = my_fopen(write_path, "wb");
+ fwrite(&_hull_cache_version, sizeof(char), 1, file);
+ {
+ char num_verts = verts.size();
+ fwrite(&num_verts, sizeof(char), 1, file);
+ fwrite(&(verts[0]), sizeof(vec3), num_verts, file);
+ }{
+ unsigned char num_face_indices = faces.size();
+ fwrite(&num_face_indices, sizeof(unsigned char), 1, file);
+ fwrite(&(faces[0]), sizeof(int), num_face_indices, file);
+ }{
+ int num_points = points.size();
+ fwrite(&num_points, sizeof(int), 1, file);
+ fwrite(&(points[0]), sizeof(vec3), num_points, file);
+ }
+ fclose(file);
+ }
+ } else {
+ //printf("Instancing loaded hull cache: %s\n", cache_path.c_str());
+ bullet_object_ = bw->CreateConvexObject(hc->verts, hc->faces, true);
+ }
+ if(plant_component_.get()){
+ plant_component_->SetHullCache(hc);
+ }
+}
+
+void EnvObject::ReceiveASVec3Message( int type, const vec3 &vec_a, const vec3 &vec_b ) {
+ if(type == _plant_movement_msg && plant_component_.get()){
+ const vec3 &pos = vec_a;
+ const vec3 &vel = vec_b;
+ plant_component_->HandleCollision(pos - GetTranslation(), vel);
+ if(update_list_entry == -1){
+ update_list_entry = scenegraph_->LinkUpdateObject(this);
+ }
+ }
+}
+
+void PlantComponent::Update(float timestep) {
+ angle += ang_vel * timestep;
+ if(length(angle) > 0.5f){
+ angle = normalize(angle)*0.5f;
+ }
+ ang_vel -= angle * timestep * 20.0f;
+ ang_vel *= 0.99f;
+ plant_shake *= 0.99f;
+}
+
+PlantComponent::PlantComponent():
+ plant_shake(0.0f),
+ was_active(false),
+ needs_set_inactive(false)
+{}
+
+void PlantComponent::HandleCollision( const vec3 &position, const vec3 &velocity ) {
+ vec3 impulse = cross(normalize(position), velocity) * 0.01f;
+ ang_vel = impulse + ang_vel;
+ plant_shake = min(1.0f, plant_shake + length(impulse)*1.0f);
+}
+
+mat4 PlantComponent::GetTransform( float scale ) const {
+ mat4 rotation;
+ float angle_len2 = length_squared(angle);
+ if(angle_len2 > 0.00001f){
+ float angle_len = sqrtf(angle_len2);
+ rotation.SetRotationAxisRad(angle_len / max(1.0f,scale*0.6f), angle/angle_len);
+ }
+ return rotation;
+}
+
+quaternion PlantComponent::GetQuaternion( float scale ) const {
+ quaternion result;
+ float angle_len2 = length_squared(angle);
+ if(angle_len2 > 0.00001f){
+ float angle_len = sqrtf(angle_len2);
+ result = QuaternionFromAxisAngle(angle / angle_len, angle_len / std::max(1.0f, scale * 0.6f));
+ }
+ return result;
+}
+
+float PlantComponent::GetPlantShake( float scale ) const {
+ return plant_shake * scale * 0.03f;
+}
+
+vec3 PlantComponent::GetPivot() const {
+ return pivot;
+}
+
+void PlantComponent::SetPivot( BulletWorld &bw, const vec3 &pos, float radius ) {
+ pivot = pos;
+ const float _interval = 1.25f;
+ float temp_rad = radius / pow(_interval,20.0f);
+ vec3 offset = vec3(0.0f);
+ while(offset == vec3(0.0f) && temp_rad <= radius){
+ temp_rad *= _interval;
+ offset = bw.CheckSphereCollisionSlide(pos, temp_rad) - pos;
+ }
+ if(offset != vec3(0.0f)){
+ pivot = pos - normalize(offset) * temp_rad + offset;
+ }
+}
+
+bool PlantComponent::IsPivotCalculated() const {
+ return pivot != vec3(0.0f);
+}
+
+void PlantComponent::ClearPivot() {
+ pivot = vec3(0.0f);
+}
+
+bool PlantComponent::IsActive() {
+ static const float _angle_threshold = 0.0001f;
+ static const float _plant_shake_threshold = 0.0001f;
+ bool active = plant_shake > _plant_shake_threshold ||
+ length_squared(angle) > _angle_threshold;
+ if(was_active && !active){
+ needs_set_inactive = true;
+ plant_shake = 0.0f;
+ }
+ was_active = active;
+ return active;
+}
+
+bool PlantComponent::NeedsSetInactive() {
+ bool val = needs_set_inactive;
+ needs_set_inactive = false;
+ return val;
+}
+
+HullCache * PlantComponent::GetHullCache() {
+ return hull_cache;
+}
+
+void PlantComponent::SetHullCache( HullCache * hc ) {
+ hull_cache = hc;
+}
+
+void EnvObject::CreateLeaf(vec3 pos, vec3 vel, int iterations) {
+ if(!plant_component_.get()){
+ return;
+ }
+ const mat4 &transform = transform_;
+ HullCache* hc = plant_component_->GetHullCache();
+ float closest_dist;
+ vec3 closest_point;
+ for(int i=0; i<iterations; ++i){
+ vec3 point = transform * hc->point_cloud[rand()%(hc->point_cloud.size())];
+ float dist = distance_squared(point, pos);
+ if(i==0 || dist < closest_dist){
+ closest_point = point;
+ closest_dist = dist;
+ }
+ }
+ scenegraph_->particle_system->MakeParticle(
+ scenegraph_, "Data/Particles/leaf.xml", closest_point, vel, vec3(1.0f));
+}
+
+bool EnvObject::SetFromDesc( const EntityDescription& desc ) {
+ bool ret = Object::SetFromDesc(desc);
+ if( ret ) {
+ for(unsigned i=0; i<desc.fields.size(); ++i){
+ const EntityDescriptionField& field = desc.fields[i];
+ switch(field.type){
+ case EDF_FILE_PATH: {
+ std::string type_file;
+ field.ReadString(&type_file);
+ if(!ofr.valid() || ofr->path_ != type_file){
+ if( Load(type_file) == false ) {
+ LOGE << "Failed to load data for EnvObject" << std::endl;
+ ret = false;
+ }
+ }
+ break;}
+ case EDF_NO_NAVMESH: {
+ field.ReadBool(&no_navmesh);
+ break;}
+ }
+ }
+
+ if( ret ) {
+ color_tint_component_.SetFromDescription(desc);
+ ReceiveObjectMessage(OBJECT_MSG::SET_COLOR, &color_tint_component_.tint_);
+ ReceiveObjectMessage(OBJECT_MSG::SET_OVERBRIGHT, &color_tint_component_.overbright_);
+
+ const Model& model = Models::Instance()->GetModel(model_id_);
+ vec3 min = model.min_coords;
+ vec3 max = model.max_coords;
+ box_.center = model.center_coords;
+ box_.dims = max-min;
+ }
+ }
+ return ret;
+}
+
+void EnvObject::ReceiveObjectMessageVAList( OBJECT_MSG::Type type, va_list args ) {
+ switch(type){
+ case OBJECT_MSG::SET_COLOR: {
+ //vec3 old_color = color_tint_component_.temp_tint();
+ color_tint_component_.ReceiveObjectMessageVAList(type, args);
+ for(int i=0, len=detail_object_surfaces.size(); i<len; ++i){
+ detail_object_surfaces[i]->SetColorTint(GetDisplayTint());
+ }
+ CalculateDisplayTint_();
+ break;}
+ case OBJECT_MSG::SET_OVERBRIGHT: {
+ color_tint_component_.ReceiveObjectMessageVAList(type, args);
+ for(int i=0, len=detail_object_surfaces.size(); i<len; ++i){
+ detail_object_surfaces[i]->SetOverbright(GetOverbright());
+ }
+ CalculateDisplayTint_();
+ break;}
+ default:
+ Object::ReceiveObjectMessageVAList(type, args);
+ break;
+ }
+}
+
+void EnvObject::CalculateDisplayTint_()
+{
+ cached_combined_tint_ = color_tint_component_.tint_ * (1.0f + color_tint_component_.overbright_ * 0.3f) * base_color_tint;
+}
+
+vec3 EnvObject::GetDisplayTint() {
+ return cached_combined_tint_;
+}
+
+static int ASGetCollisionFace(EnvObject* eo, int index) {
+ LOG_ASSERT( index >= 0 );
+ if(eo->GetCollisionModelID() == -1 || eo->ofr->no_collision){
+ return -1;
+ } else {
+ Model* model = &Models::Instance()->GetModel(eo->GetCollisionModelID());
+ if(!model || (int)model->faces.size() <= index){
+ return -1;
+ } else {
+ return model->faces[index];
+ }
+ }
+}
+
+static vec3 ASGetCollisionVertex(EnvObject* eo, int index) {
+ LOG_ASSERT( index >= 0 );
+ if(eo->GetCollisionModelID() == -1 || eo->ofr->no_collision){
+ return vec3(0.0);
+ } else {
+ Model* model = &Models::Instance()->GetModel(eo->GetCollisionModelID());
+ if(!model || (int)model->faces.size() <= index){
+ return vec3(0.0);
+ } else {
+ vec3 vec;
+ memcpy (&vec, &model->vertices[index*3], sizeof(vec3));
+ return vec;
+ }
+ }
+}
+
+static int ASGetLedgeLine(EnvObject* eo, int index) {
+ return eo->ledge_lines[index];
+}
+
+static int ASGetNumLedgeLines(EnvObject* eo) {
+ return eo->ledge_lines.size();
+}
+
+void DefineEnvObjectTypePublic(ASContext* as_context) {
+ as_context->RegisterObjectType("EnvObject", 0, asOBJ_REF | asOBJ_NOCOUNT);
+ as_context->RegisterObjectMethod("EnvObject",
+ "int GetID()",
+ asMETHOD(EnvObject, GetID), asCALL_THISCALL);
+ as_context->RegisterObjectMethod("EnvObject",
+ "void CreateLeaf(vec3 target_position, vec3 velocity, int iterations)",
+ asMETHOD(EnvObject, CreateLeaf), asCALL_THISCALL, "Spawn leaf particles from random point on object's surface; more iterations = closer to target_position");
+ as_context->RegisterObjectMethod("EnvObject",
+ "int GetCollisionFace(int index)",
+ asFUNCTION(ASGetCollisionFace), asCALL_CDECL_OBJFIRST);
+ as_context->RegisterObjectMethod("EnvObject",
+ "vec3 GetCollisionVertex(int index)",
+ asFUNCTION(ASGetCollisionVertex), asCALL_CDECL_OBJFIRST);
+ as_context->RegisterObjectMethod("EnvObject",
+ "int GetNumLedgeLines()",
+ asFUNCTION(ASGetNumLedgeLines), asCALL_CDECL_OBJFIRST);
+ as_context->RegisterObjectMethod("EnvObject",
+ "int GetLedgeLine(int index)",
+ asFUNCTION(ASGetLedgeLine), asCALL_CDECL_OBJFIRST);
+ as_context->DocsCloseBrace();
+}