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/Graphics/particles.cpp')
-rw-r--r--Source/Graphics/particles.cpp1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/Source/Graphics/particles.cpp b/Source/Graphics/particles.cpp
new file mode 100644
index 00000000..dd4f166c
--- /dev/null
+++ b/Source/Graphics/particles.cpp
@@ -0,0 +1,1342 @@
+//-----------------------------------------------------------------------------
+// Name: particles.cpp
+// Developer: Wolfire Games LLC
+// Author: David Rosen
+// Description: This class handles particle animation and rendering
+// 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 "particles.h"
+
+#include <Graphics/graphics.h>
+#include <Graphics/camera.h>
+#include <Graphics/textures.h>
+#include <Graphics/camera.h>
+#include <Graphics/pxdebugdraw.h>
+#include <Graphics/shaders.h>
+
+#include <Internal/common.h>
+#include <Internal/timer.h>
+#include <Internal/profiler.h>
+
+#include <Objects/movementobject.h>
+#include <Objects/decalobject.h>
+#include <Objects/lightvolume.h>
+
+#include <Physics/physics.h>
+#include <Physics/bulletworld.h>
+
+#include <Math/enginemath.h>
+#include <Math/vec3math.h>
+#include <Math/vec4math.h>
+
+#include <Timing/timingevent.h>
+#include <Timing/intel_gl_perf.h>
+
+#include <Online/online_datastructures.h>
+#include <Main/scenegraph.h>
+#include <XML/xml_helper.h>
+#include <Sound/sound.h>
+#include <Asset/Asset/material.h>
+#include <Graphics/sky.h>
+#include <Editors/actors_editor.h>
+#include <Main/engine.h>
+#include <Logging/logdata.h>
+#include <Utility/assert.h>
+
+#include <tinyxml.h>
+
+#include <cassert>
+
+extern std::string script_dir_path;
+extern Timer game_timer;
+extern bool g_no_reflection_capture;
+extern bool g_debug_runtime_disable_gpu_particle_field_draw;
+extern bool g_debug_runtime_disable_particle_draw;
+extern bool g_debug_runtime_disable_particle_system_draw;
+
+//-----------------------------------------------------------------------------
+//Functions
+//-----------------------------------------------------------------------------
+
+bool CParticleCompare(Particle *p1, Particle *p2) {
+ if(p1->particle_type == p2->particle_type){
+ return p1->cam_dist > p2->cam_dist;
+ } else {
+ return p1->particle_type->asset_id < p2->particle_type->asset_id;
+ }
+}
+
+extern bool g_simple_shadows;
+extern bool g_level_shadows;
+extern bool g_no_decals;
+extern bool g_particle_field_simple;
+extern char* global_shader_suffix;
+
+
+TextureAssetRef smoke_texture;
+
+void DrawGPUParticleField(SceneGraph *scenegraph, const char* type) {
+ if (g_debug_runtime_disable_gpu_particle_field_draw) {
+ return;
+ }
+
+ static bool initialized = false;
+
+ if( initialized == false ) {
+ smoke_texture = Engine::Instance()->GetAssetManager()->LoadSync<TextureAsset>("Data/Textures/smoke.tga");
+ initialized = true;
+ }
+
+ Graphics* graphics = Graphics::Instance();
+ Shaders* shaders = Shaders::Instance();
+ Camera* cam = ActiveCameras::Get();
+ Textures* textures = Textures::Instance();
+ mat4 proj_view_matrix = cam->GetProjMatrix() * cam->GetViewMatrix();
+ CHECK_GL_ERROR();
+
+ GLState gl_state;
+ gl_state.blend = true;
+ gl_state.depth_test = true;
+ gl_state.depth_write = false;
+ gl_state.cull_face = false;
+ graphics->setGLState(gl_state);
+ graphics->setAdditiveBlend(false);
+ glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+
+ int shader_id;
+ {
+ // Get shader id with flags
+ char shader_name[ParticleType::kMaxNameLen];
+ if(g_particle_field_simple) {
+ FormatString(shader_name, ParticleType::kMaxNameLen, "envobject #GPU_PARTICLE_FIELD #GPU_PARTICLE_FIELD_SIMPLE %s %s", type, global_shader_suffix);
+ } else {
+ FormatString(shader_name, ParticleType::kMaxNameLen, "envobject #GPU_PARTICLE_FIELD %s %s", type, global_shader_suffix);
+ }
+ shader_id = shaders->returnProgram(shader_name);
+ }
+ shaders->setProgram(shader_id);
+ shaders->SetUniformMat4("mvp",proj_view_matrix);
+ shaders->SetUniformMat4("projection_matrix",cam->GetProjMatrix());
+ shaders->SetUniformMat4("view_matrix",cam->GetViewMatrix());
+ shaders->SetUniformFloat("time",game_timer.GetRenderTime());
+ if(Engine::Instance()->paused){
+ shaders->SetUniformFloat("time_scale",0.05f);
+ } else {
+ shaders->SetUniformFloat("time_scale",game_timer.time_scale);
+ }
+
+ shaders->SetUniformFloat("haze_mult", scenegraph->haze_mult);
+
+ textures->bindTexture(smoke_texture->GetTextureRef());
+
+ { // Bind shadow matrices to shader
+ std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = cam->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ 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);
+ }
+
+ // Bind shadow texture
+ 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);
+ }
+ CHECK_GL_ERROR();
+ shaders->SetUniformVec3("ws_light",scenegraph->primary_light.pos);
+ shaders->SetUniformVec4("primary_light_color",vec4(scenegraph->primary_light.color, scenegraph->primary_light.intensity));
+ shaders->SetUniformVec3("cam_pos",cam->GetPos());
+ shaders->SetUniformVec3("cam_dir",cam->GetFacing());
+ scenegraph->BindLights(shader_id);
+ vec2 viewport_dims;
+ for(int i=0; i<2; ++i){
+ viewport_dims[i] = (float)graphics->viewport_dim[i+2];
+ }
+ shaders->SetUniformVec2("viewport_dims",viewport_dims);
+ CHECK_GL_ERROR();
+
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 2);
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 3);
+
+ graphics->SetBlendFunc(
+ shaders->GetProgramBlendSrc(shader_id),
+ shaders->GetProgramBlendDst(shader_id));
+
+ textures->bindTexture(graphics->screen_depth_tex, 5);
+ scenegraph->BindLights(shader_id);
+
+ 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);
+ }
+
+ 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);
+ }
+ if( g_no_reflection_capture == false) {
+ textures->bindTexture(scenegraph->cubemaps, 19);
+ }
+ 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(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);
+ }
+
+ CHECK_GL_ERROR();
+ PROFILER_LEAVE(g_profiler_ctx); // Prepare instanced particle draw
+
+ static VBOContainer data_vbo;
+ if(!data_vbo.valid()){
+ static const GLfloat data[] = {
+ 1, 1, 1, 1, 0,
+ 0, 1, -1, 1, 0,
+ 0, 0, -1, -1, 0,
+ 1, 0, 1, -1, 0
+ };
+ static GLfloat data_vec[20*1000];
+ int val=0;
+ for(int i=0; i<1000; ++i){
+ for(int j=0; j<20; ++j){
+ data_vec[val++] = data[j];
+ }
+ }
+ data_vbo.Fill(kVBOStatic | kVBOFloat,sizeof(data_vec), (void*)data_vec);
+ }
+ static VBOContainer index_vbo;
+ if(!index_vbo.valid()){
+ static const GLuint index[] = {0, 1, 2, 0, 3, 2};
+ static GLuint index_vec[6*1000];
+ int val = 0;
+ int val2 = 0;
+ for(int i=0; i<1000; ++i){
+ for(int j=0; j<6; ++j){
+ index_vec[val2++]=index[j]+val;
+ }
+ val += 4;
+ }
+ index_vbo.Fill(kVBOStatic | kVBOElement, sizeof(index_vec), (void*)index_vec);
+ }
+ CHECK_GL_ERROR();
+ int vert_attrib_id = shaders->returnShaderAttrib("vertex_attrib", shader_id);
+ int tex_coord_attrib_id = shaders->returnShaderAttrib("tex_coord_attrib", shader_id);
+ data_vbo.Bind();
+ CHECK_GL_ERROR();
+ if(vert_attrib_id != -1){
+ graphics->EnableVertexAttribArray(vert_attrib_id);
+ glVertexAttribPointer(vert_attrib_id, 3, GL_FLOAT, false, 5*sizeof(GLfloat), (const void*)(data_vbo.offset()+2*sizeof(GLfloat)));
+ }
+ CHECK_GL_ERROR();
+ if(tex_coord_attrib_id != -1){
+ graphics->EnableVertexAttribArray(tex_coord_attrib_id);
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 5*sizeof(GLfloat), (const void*)data_vbo.offset());
+ }
+ CHECK_GL_ERROR();
+ index_vbo.Bind();
+ CHECK_GL_ERROR();
+ {
+ PROFILER_ZONE(g_profiler_ctx, "glDrawElementsInstanced");
+ graphics->DrawElementsInstanced(GL_TRIANGLES, 6000, GL_UNSIGNED_INT, 0, 1000);
+ }
+ graphics->ResetVertexAttribArrays();
+}
+
+//Draw a particle
+void Particle::Draw(SceneGraph *scenegraph, DrawType draw_type, const mat4& proj_view_matrix) {
+ if (g_debug_runtime_disable_particle_draw) {
+ return;
+ }
+
+ PROFILER_GPU_ZONE(g_profiler_ctx, "Particle::Draw");
+ Graphics* graphics = Graphics::Instance();
+ Shaders* shaders = Shaders::Instance();
+ Camera* cam = ActiveCameras::Get();
+ Textures* textures = Textures::Instance();
+ CHECK_GL_ERROR();
+
+ int shader_id;
+ {
+ // Get shader id with flags
+ char shader_name[ParticleType::kMaxNameLen];
+
+ FormatString(shader_name, ParticleType::kMaxNameLen, "%s %s", particle_type->shader_name, global_shader_suffix);
+
+ shader_id = shaders->returnProgram(shader_name);
+ //LOGI << shader_name << std::endl;
+ }
+
+ shaders->setProgram(shader_id);
+ shaders->SetUniformMat4("mvp",proj_view_matrix);
+ shaders->SetUniformVec4("color_tint",interp_color);
+ shaders->SetUniformFloat("time",game_timer.GetRenderTime());
+
+ shaders->SetUniformFloat("haze_mult", scenegraph->haze_mult);
+
+ if(particle_type->color_map.valid()){
+ textures->bindTexture(particle_type->color_map->GetTextureRef());
+ } else if (ae_reader.valid()){
+ textures->bindTexture(ae_reader.GetTextureAssetRef());
+ }
+
+ if(draw_type == COLOR){
+ { // Bind shadow matrices to shader
+ std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = cam->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ 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);
+ }
+
+ // Bind shadow texture
+ 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);
+ }
+ CHECK_GL_ERROR();
+ shaders->SetUniformVec3("ws_light",scenegraph->primary_light.pos);
+ shaders->SetUniformVec4("primary_light_color",vec4(scenegraph->primary_light.color, scenegraph->primary_light.intensity));
+ shaders->SetUniformVec3("cam_pos",cam->GetPos());
+ scenegraph->BindLights(shader_id);
+ vec2 viewport_dims;
+ for(int i=0; i<2; ++i){
+ viewport_dims[i] = (float)graphics->viewport_dim[i+2];
+ }
+ shaders->SetUniformVec2("viewport_dims",viewport_dims);
+ CHECK_GL_ERROR();
+
+ if(particle_type->normal_map.valid()){
+ textures->bindTexture(particle_type->normal_map->GetTextureRef(), 1);
+ }
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 2);
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 3);
+
+ graphics->SetBlendFunc(
+ shaders->GetProgramBlendSrc(shader_id),
+ shaders->GetProgramBlendDst(shader_id));
+
+ textures->bindTexture(graphics->screen_depth_tex, 5);
+ shaders->SetUniformFloat("size",interp_size);
+ scenegraph->BindLights(shader_id);
+
+ 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);
+ }
+
+ std::vector<mat4> light_volume_matrix;
+ std::vector<mat4> light_volume_matrix_inverse;
+ for(int i=0, len=scenegraph->objects_.size(); i<len; ++i){
+ Object* obj = scenegraph->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);
+ }
+
+ if( g_no_reflection_capture == false) {
+ textures->bindTexture(scenegraph->cubemaps, 19);
+ }
+ 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(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);
+ }
+ }
+
+ CHECK_GL_ERROR();
+
+ if(connected.empty()){
+ vec3 up,right;
+ float temp_size;
+ vec3 temp_pos;
+ vec3 particle_facing;
+ if(draw_type == COLOR){
+ vec3 look;
+ look = cam->GetPos() - interp_position;
+ // Don't draw if cam is not facing towards particle
+ if(dot(look,cam->GetFacing())>0.0f){
+ return;
+ }
+ float cam_distance = length(look);
+ if(cam_distance > 0.0f){
+ look /= cam_distance;
+ }
+
+ bool velocity_axis = particle_type->velocity_axis;
+ if(velocity_axis || particle_type->speed_stretch){
+ particle_facing = look;
+ } else {
+ particle_facing = cam->GetFacing();
+ }
+
+ if(velocity_axis){
+ if(particle_type->speed_stretch){
+ up = velocity / sqrtf(length(velocity)) * 2.0f * particle_type->speed_mult;
+ } else {
+ up = normalize(velocity);
+ }
+ } else {
+ up = AngleAxisRotation(cam->GetUpVector(), particle_facing, interp_rotation);
+ }
+
+ right = normalize(cross(up, particle_facing));
+ if(particle_type->min_squash){
+ if(particle_type->speed_stretch){
+ float velocity_length = length(up);
+ if(fabs(dot(normalize(particle_facing),normalize(up)))>1.0f-0.2f/velocity_length/velocity_length){
+ float v = 1.0f-0.2f/velocity_length/velocity_length;
+ if( v < -1.0f ) {
+ v = -1.0f;
+ } else if( v > 1.0f ) {
+ v = 1.0f;
+ }
+
+ up = AngleAxisRotationRadian(particle_facing*velocity_length,right,acosf(v));
+ }
+ }
+ if(!particle_type->speed_stretch && fabs(dot(particle_facing,up))>0.8f){
+ up = AngleAxisRotationRadian(particle_facing,right,acosf(0.8f));
+ }
+ }
+ if(velocity_axis){
+ particle_facing = normalize(cross(up, right));
+ }
+
+ temp_pos = interp_position;
+ temp_size = interp_size;
+
+ float offset = interp_size * 0.5f;
+ temp_pos += look * offset;
+ temp_size *= (cam_distance-offset)/cam_distance;
+ } else {
+ temp_size = interp_size;
+ temp_pos = interp_position;
+ particle_facing = normalize(scenegraph->primary_light.pos);
+ right = cross(particle_facing, vec3(0,-1,0));
+ up = cross(right, particle_facing);
+ }
+
+ vec3 vertices[4];
+ vertices[0]=temp_pos+(up+right)*temp_size;
+ vertices[1]=temp_pos+(up-right)*temp_size;
+ vertices[2]=temp_pos-(up+right)*temp_size;
+ vertices[3]=temp_pos-(up-right)*temp_size;
+
+ GLfloat data[] = {
+ 1, 1, vertices[0][0], vertices[0][1], vertices[0][2], right[0], right[1], right[2], particle_facing[0], particle_facing[1], particle_facing[2],
+ 0, 1, vertices[1][0], vertices[1][1], vertices[1][2], right[0], right[1], right[2], particle_facing[0], particle_facing[1], particle_facing[2],
+ 0, 0, vertices[2][0], vertices[2][1], vertices[2][2], right[0], right[1], right[2], particle_facing[0], particle_facing[1], particle_facing[2],
+ 1, 0, vertices[3][0], vertices[3][1], vertices[3][2], right[0], right[1], right[2], particle_facing[0], particle_facing[1], particle_facing[2],
+ };
+
+ CHECK_GL_ERROR();
+ static VBORingContainer data_vbo(sizeof(data), kVBODynamic | kVBOFloat);
+ data_vbo.Fill(sizeof(data), data);
+ static VBOContainer index_vbo;
+ if(!index_vbo.valid()){
+ static const GLuint index[] = {0, 1, 2, 0, 3, 2};
+ index_vbo.Fill(kVBOStatic | kVBOElement, sizeof(index), (void*)index);
+ }
+ CHECK_GL_ERROR();
+ int vert_attrib_id = shaders->returnShaderAttrib("vertex_attrib", shader_id);
+ int normal_attrib_id = shaders->returnShaderAttrib("normal_attrib", shader_id);
+ int tangent_attrib_id = shaders->returnShaderAttrib("tangent_attrib", shader_id);
+ int tex_coord_attrib_id = shaders->returnShaderAttrib("tex_coord_attrib", shader_id);
+ data_vbo.Bind();
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(vert_attrib_id);
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(tex_coord_attrib_id);
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(vert_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(data_vbo.offset()+2*sizeof(GLfloat)));
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(data_vbo.offset()));
+ CHECK_GL_ERROR();
+ if(normal_attrib_id != -1){
+ graphics->EnableVertexAttribArray(normal_attrib_id);
+ glVertexAttribPointer(normal_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(data_vbo.offset()+8*sizeof(GLfloat)));
+ }
+ if(tangent_attrib_id != -1){
+ graphics->EnableVertexAttribArray(tangent_attrib_id);
+ glVertexAttribPointer(tangent_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(data_vbo.offset()+5*sizeof(GLfloat)));
+ }
+ CHECK_GL_ERROR();
+ index_vbo.Bind();
+ CHECK_GL_ERROR();
+ graphics->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (const void*)index_vbo.offset());
+ CHECK_GL_ERROR();
+ graphics->ResetVertexAttribArrays();
+ CHECK_GL_ERROR();
+ }
+ for(ParticleList::iterator iter = connected.begin(); iter != connected.end(); ++iter) {
+ if(this < (*iter)) {
+ float thickness = interp_size;
+ vec3 a = interp_position;
+ vec3 b = (*iter)->interp_position;
+
+ vec3 up,right,cam_to_particle;
+ vec3 vertices[4];
+
+ if(draw_type == COLOR){
+ cam_to_particle = cam->GetPos() - (a+b)*0.5f;
+ float cam_distance = length(cam_to_particle);
+ if(cam_distance > 0.0f){
+ cam_to_particle /= cam_distance; // Normalize cam_to_particle
+ }
+
+ if(dot(cam_to_particle,cam->GetFacing())>0.0f){
+ return; // Return if particle is behind cam
+ }
+ } else {
+ cam_to_particle = scenegraph->primary_light.pos;
+ }
+
+ vec3 particle_facing = cam_to_particle;
+ up = b-a;
+ right = normalize(cross(normalize(up), particle_facing));
+ vec3 true_facing = normalize(cross(up, right));
+
+ vec3 temp_pos = (a+b)*0.5f;
+
+ vertices[0]=temp_pos+up+right*thickness;
+ vertices[1]=temp_pos+up-right*thickness;
+ vertices[2]=temp_pos-up-right*thickness;
+ vertices[3]=temp_pos-up+right*thickness;
+
+ GLfloat data[] = {
+ 1, 1, vertices[0][0], vertices[0][1], vertices[0][2], right[0], right[1], right[2], true_facing[0], true_facing[1], true_facing[2],
+ 0, 1, vertices[1][0], vertices[1][1], vertices[1][2], right[0], right[1], right[2], true_facing[0], true_facing[1], true_facing[2],
+ 0, 0, vertices[2][0], vertices[2][1], vertices[2][2], right[0], right[1], right[2], true_facing[0], true_facing[1], true_facing[2],
+ 1, 0, vertices[3][0], vertices[3][1], vertices[3][2], right[0], right[1], right[2], true_facing[0], true_facing[1], true_facing[2],
+ };
+
+ CHECK_GL_ERROR();
+ static VBOContainer data_vbo;
+ data_vbo.Fill(kVBODynamic | kVBOFloat, sizeof(data), data);
+ static VBOContainer index_vbo;
+ if(!index_vbo.valid()){
+ static const GLuint index[] = {0, 1, 2, 0, 3, 2};
+ index_vbo.Fill(kVBOStatic | kVBOElement, sizeof(index), (void*)index);
+ }
+ CHECK_GL_ERROR();
+ int vert_attrib_id = shaders->returnShaderAttrib("vertex_attrib", shader_id);
+ int normal_attrib_id = shaders->returnShaderAttrib("normal_attrib", shader_id);
+ int tangent_attrib_id = shaders->returnShaderAttrib("tangent_attrib", shader_id);
+ int tex_coord_attrib_id = shaders->returnShaderAttrib("tex_coord_attrib", shader_id);
+ data_vbo.Bind();
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(vert_attrib_id);
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(tex_coord_attrib_id);
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(vert_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(2*sizeof(GLfloat)));
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 11*sizeof(GLfloat), 0);
+ CHECK_GL_ERROR();
+ if(normal_attrib_id != -1){
+ graphics->EnableVertexAttribArray(normal_attrib_id);
+ glVertexAttribPointer(normal_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(8*sizeof(GLfloat)));
+ }
+ if(tangent_attrib_id != -1){
+ graphics->EnableVertexAttribArray(tangent_attrib_id);
+ glVertexAttribPointer(tangent_attrib_id, 3, GL_FLOAT, false, 11*sizeof(GLfloat), (const void*)(5*sizeof(GLfloat)));
+ }
+ CHECK_GL_ERROR();
+ index_vbo.Bind();
+ CHECK_GL_ERROR();
+ graphics->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+ CHECK_GL_ERROR();
+ graphics->ResetVertexAttribArrays();
+ CHECK_GL_ERROR();
+ }
+ }
+ CHECK_GL_ERROR();
+}
+
+//Update particle
+void Particle::Update(SceneGraph *scenegraph, float timestep, float curr_game_time) {
+ old_position = position;
+ old_color = color;
+ old_size = size;
+ old_rotation = rotation;
+
+ if(ae_reader.valid()){
+ ae_reader.Update(timestep);
+ if(ae_reader.Done()){
+ size = 0.0f;
+ color[3] = 0.0f;
+ }
+ }
+
+ position+=velocity*timestep;
+ alive_time+=timestep;
+
+ velocity *= particle_type->inertia;
+ Physics *pi = Physics::Instance();
+ velocity += pi->gravity * particle_type->gravity * timestep;
+ velocity += pi->GetWind(position, curr_game_time, 1.0f) * particle_type->wind * timestep;
+ if(particle_type->quadratic_expansion){
+ size=initial_size * sqrtf(alive_time * particle_type->qe_speed);
+ } else {
+ size-= particle_type->size_decay_rate * timestep;
+ }
+ if(particle_type->quadratic_dispersion){
+ color[3]=square(initial_size/(size+initial_size))*particle_type->qd_mult;
+ color[3]=min(1.0f,color[3])*min((alive_time+0.05f)*10,1.0f)*initial_opacity-0.1f;
+ } else {
+ color[3] = initial_opacity - particle_type->opacity_decay_rate * alive_time;
+ if(alive_time < particle_type->opacity_ramp_time) {
+ color[3] *= alive_time/particle_type->opacity_ramp_time;
+ }
+ }
+ rotation+=timestep*rotate_speed*0.3f/(size/initial_size);
+
+ if(!connected.empty()){
+ has_last_connected = true;
+ last_connected_pos = (*connected.begin())->position;
+ }
+
+ if(particle_type->collision){
+ SimpleRayResultCallbackInfo cb;
+ vec3 point, normal;
+ bool hit = (scenegraph->bullet_world_->CheckRayCollision(
+ old_position,
+ position,
+ &point,
+ &normal) != NULL);
+ vec3 new_pos = hit?point:position;
+ int char_id = -1;
+ if(particle_type->character_collide){
+ char_id = scenegraph->CheckRayCollisionCharacters(
+ old_position,
+ new_pos,
+ &point,
+ &normal,
+ NULL);
+ }
+ if(hit && char_id == -1){
+ //SDL.position = point;
+ //SDL.velocity = reflect(SDL.velocity, normal);
+ if(particle_type->collision_destroy){
+ color[3] = 0.0f;
+ }
+ if (!particle_type->collision_decal.empty() && !collided){
+ // Create decal at collision point
+ EntityDescriptionList desc_list;
+ std::string file_type;
+ Path source;
+ ActorsEditor_LoadEntitiesFromFile(particle_type->collision_decal, desc_list, &file_type, &source);
+ Object* obj = CreateObjectFromDesc(desc_list[0]);
+ if (obj) {
+ obj->permission_flags = obj->permission_flags & ~Object::CAN_SELECT;
+ // align decal up (y+) with surface normal
+ vec3 up = vec3(0.0f, 1.0f, 0.0f);
+ vec3 rot_axis = cross(up, normal);
+ float angle2_cos = dot(up, normal);
+
+ //Sometimes rounding errors in combination to similar vectors cause the dot product to be more than 1.0f
+ //Which results in a NaN when used in the sqrtf later.
+ if( angle2_cos > 1.0f ) {
+ angle2_cos = 1.0f;
+ }
+
+ // constructing quaternion requires half-angle
+ // use trig identities to calculate sin and cos without
+ // actually calculating the angle
+ float angle_cos = sqrtf((1.0f + angle2_cos) / 2.0f);
+ float angle_sin = sqrtf(1.0f - (angle_cos * angle_cos));
+
+ quaternion rot;
+ rot.entries[0] = rot_axis.x() * angle_sin;
+ rot.entries[1] = rot_axis.y() * angle_sin;
+ rot.entries[2] = rot_axis.z() * angle_sin;
+ rot.entries[3] = angle_cos;
+ obj->SetRotation(rot);
+
+ if (has_last_connected){
+ vec3 dir = normalize(velocity);
+ vec3 direction = normal * -1.0f;
+ dir = last_connected_pos - position;
+ float length_dir = length(dir);
+ if (length_dir < 0.01f){
+ // if dir is zero, just construct any vector that is non-collinear with direction
+ // so that we can construct orthogonal u and v properly later
+ if (direction[0] != 0.0f || direction[1] != 0.0f){
+ dir = vec3(-direction[1], direction[0], direction[2]);
+ } else {
+ dir = vec3(direction[0], -direction[2], direction[1]);
+ }
+ }
+ float decal_size = size * particle_type->collision_decal_size_mult;
+ length_dir = min(length_dir, decal_size * 3);
+ // Decal placement position and direction
+ obj->SetTranslation((position + last_connected_pos) * 0.5f);
+ obj->SetScale(vec3(decal_size, length_dir + decal_size, length_dir + decal_size));
+ {
+ vec3 axes[3];
+ axes[1] = normal;
+ axes[2] = normalize(dir);
+ axes[0] = normalize(cross(axes[1], axes[2]));
+ axes[2] = normalize(cross(axes[1], axes[0]));
+ mat4 temp;
+ temp.SetColumn(0, -axes[0]);
+ temp.SetColumn(1, axes[1]);
+ temp.SetColumn(2, axes[2]);
+ obj->SetRotation(QuaternionFromMat4(temp));
+ }
+ } else {
+ obj->SetTranslation(point);
+ obj->SetRotation(obj->GetRotation()*quaternion(vec4(0.0f, 1.0f, 0.0f, RangedRandomFloat(0.0f, PI_f * 2.0f))));
+ obj->SetScale(vec3(size * particle_type->collision_decal_size_mult));
+ }
+ obj->exclude_from_undo = true;
+ obj->exclude_from_save = true;
+
+ DecalObject *dec = static_cast<DecalObject*>(obj);
+
+ if (dec->decal_file_ref->special_type == 2) {
+ // Only blood decals should use user specified color tint
+ const vec3 blood_color = Graphics::Instance()->config_.blood_color();
+ dec->color_tint_component_.tint_ = blood_color;
+ }
+
+ if( scenegraph->AddDynamicDecal(dec) ) {
+ } else {
+ delete dec;
+ }
+ } else {
+ LOGE << "Failed at loading object for particle system." << std::endl;
+ }
+ //DebugDraw::Instance()->AddLine(decal_info.point-decal_info.u * decal_info.
+ /*
+ std::vector<Decal*> decals =
+ scenegraph->decals->makeDecalOnScene(scenegraph, decal_info, NULL, curr_game_time);
+ for(unsigned i=0; i<decals.size(); ++i){
+ scenegraph->decals->AddToDecalList(decals[i]);
+ }
+ */
+ }
+ if(!particle_type->collision_event.empty() && !collided){
+ const MaterialEvent* me = scenegraph->GetMaterialEvent(particle_type->collision_event, point);
+ if(me && !me->soundgroup.empty()){
+ //SoundGroupRef sgr = SoundGroups::Instance()->ReturnRef(me->soundgroup);
+ SoundGroupRef sgr = Engine::Instance()->GetAssetManager()->LoadSync<SoundGroup>(me->soundgroup);
+ SoundGroupPlayInfo sgpi(*sgr, point);
+ sgpi.gain = min(1.0f,length_squared(velocity)*0.2f*size);
+ sgpi.priority = _sound_priority_low;
+ unsigned long handle = Engine::Instance()->GetSound()->CreateHandle(__FUNCTION__);
+ Engine::Instance()->GetSound()->PlayGroup(handle,sgpi);
+ }
+ }
+ collided = true;
+ } else if (char_id != -1) {
+ if(particle_type->collision_destroy){
+ color[3] = 0.0f;
+ }
+ if(!particle_type->collision_event.empty() && !collided){
+ Object* o = scenegraph->GetObjectFromID(char_id);
+ const MaterialEvent& me = o->GetMaterialEvent(particle_type->collision_event, point);
+ if(!me.soundgroup.empty()){
+ //SoundGroupRef sgr = SoundGroups::Instance()->ReturnRef(me.soundgroup);
+ SoundGroupRef sgr = Engine::Instance()->GetAssetManager()->LoadSync<SoundGroup>(me.soundgroup);
+ SoundGroupPlayInfo sgpi(*sgr, point);
+ sgpi.gain = min(1.0f,length_squared(velocity)*0.2f*size);
+ sgpi.priority = _sound_priority_low;
+ unsigned long handle = Engine::Instance()->GetSound()->CreateHandle(__FUNCTION__);
+ Engine::Instance()->GetSound()->PlayGroup(handle,sgpi);
+ }
+ }
+ if(particle_type->character_add_blood && !collided){
+ MovementObject* mo = (MovementObject*)scenegraph->GetObjectFromID(char_id);
+ mo->rigged_object()->AddBloodAtPoint(point);
+ }
+ collided = true;
+ }
+ }
+
+ if(alive_time == timestep){
+ old_position = position;
+ old_color = color;
+ old_size = size;
+ old_rotation = rotation;
+ }
+}
+
+UniformRingBuffer particle_uniform_buffer;
+
+//Draw all particles
+void ParticleSystem::Draw(SceneGraph *scenegraph) {
+ if (g_debug_runtime_disable_particle_system_draw) {
+ return;
+ }
+
+ Graphics *graphics = Graphics::Instance();
+ CHECK_GL_ERROR();
+ if(!particles.empty() && !graphics->drawing_shadow){
+ Camera* cam = ActiveCameras::Get();
+ GLubyte* blockBuffer = (GLubyte*)alloca(16384);
+ GLState gl_state;
+ gl_state.blend = true;
+ gl_state.depth_test = true;
+ gl_state.depth_write = false;
+ gl_state.cull_face = false;
+ graphics->setGLState(gl_state);
+ glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE );
+
+ vec2 viewport_dims;
+ for(int i=0; i<2; ++i){
+ viewport_dims[i] = (float)graphics->viewport_dim[i+2];
+ }
+
+ static std::vector<mat4> shadow_matrix;
+ shadow_matrix.resize(4);
+ for(int i=0; i<4; ++i){
+ shadow_matrix[i] = cam->biasMatrix * graphics->cascade_shadow_mat[i];
+ }
+ static std::vector<mat4> temp_shadow_matrix;
+ temp_shadow_matrix = shadow_matrix;
+ if(g_simple_shadows || !g_level_shadows){
+ temp_shadow_matrix[3] = cam->biasMatrix * graphics->simple_shadow_mat;
+ }
+
+ PROFILER_ENTER(g_profiler_ctx, "Interpolate particles");
+ float interp = game_timer.GetInterpWeight();
+ for (unsigned i=0, len=particles.size(); i<len; ++i) {
+ Particle& p = *particles[i];
+ p.interp_position = mix(p.old_position, p.position, interp);
+ p.interp_rotation = mix(p.old_rotation, p.rotation, interp);
+ p.interp_size = mix(p.old_size, p.size, interp);
+ p.interp_color = mix(p.old_color, p.color, interp);
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Interpolate particles
+
+ vec3 cam_pos = ActiveCameras::Get()->GetPos();
+ for (unsigned i=0;i<particles.size();i++){
+ particles[i]->cam_dist=distance_squared(particles[i]->interp_position, cam_pos);
+ }
+ PROFILER_ENTER(g_profiler_ctx, "Sort particles");
+ std::sort(particles.begin(), particles.end(), CParticleCompare);
+ PROFILER_LEAVE(g_profiler_ctx); // Sort particles
+ mat4 proj_view_matrix = cam->GetProjMatrix() * cam->GetViewMatrix();
+ ParticleTypeRef curr_type = particles[0]->particle_type;
+ bool unconnected = particles[0]->connected.empty();
+ unsigned start = 0;
+ PROFILER_ENTER(g_profiler_ctx, "Draw Loop");
+ for (unsigned i=0, len=particles.size(); i<=len; i++){
+ if(i== particles.size() || particles[i]->particle_type != curr_type || particles[i]->connected.empty() != unconnected){
+ ParticleTypeRef particle_type = particles[i-1]->particle_type;
+ if(!particle_type->color_map.valid() || !particles[i-1]->connected.empty()){
+ for(unsigned index=start; index<i; ++index){
+ particles[index]->Draw(scenegraph, Particle::COLOR, proj_view_matrix);
+ }
+ } else {
+ PROFILER_ENTER(g_profiler_ctx, "Prepare instanced particle draw");
+ Graphics* graphics = Graphics::Instance();
+ Shaders* shaders = Shaders::Instance();
+ Camera* cam = ActiveCameras::Get();
+ Textures* textures = Textures::Instance();
+ CHECK_GL_ERROR();
+
+ PROFILER_ENTER(g_profiler_ctx, "Set up shader");
+ PROFILER_ENTER(g_profiler_ctx, "Bind shader");
+ int shader_id;
+ {
+ // Get shader id with flags
+ char shader_name[ParticleType::kMaxNameLen];
+
+ FormatString(shader_name, ParticleType::kMaxNameLen, "%s #INSTANCED %s", particle_type->shader_name, global_shader_suffix);
+ shader_id = shaders->returnProgram(shader_name);
+ //LOGI << shader_name << std::endl;
+ }
+
+ shaders->setProgram(shader_id);
+ PROFILER_LEAVE(g_profiler_ctx);
+ PROFILER_ENTER(g_profiler_ctx, "Uniforms");
+ shaders->SetUniformMat4("mvp",proj_view_matrix);
+ shaders->SetUniformFloat("time",game_timer.GetRenderTime());
+ shaders->SetUniformFloat("haze_mult", scenegraph->haze_mult);
+ { // Bind shadow matrices to shader
+ shaders->SetUniformMat4Array("shadow_matrix", temp_shadow_matrix);
+ }
+ CHECK_GL_ERROR();
+ shaders->SetUniformVec3("ws_light",scenegraph->primary_light.pos);
+ shaders->SetUniformVec4("primary_light_color",vec4(scenegraph->primary_light.color, scenegraph->primary_light.intensity));
+ shaders->SetUniformVec3("cam_pos",cam->GetPos());
+ shaders->SetUniformVec2("viewport_dims",viewport_dims);
+ PROFILER_LEAVE(g_profiler_ctx);
+ PROFILER_ENTER(g_profiler_ctx, "BindLights");
+ scenegraph->BindLights(shader_id);
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ 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);
+ }
+
+ PROFILER_ENTER(g_profiler_ctx, "reflection capture");
+ 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);
+ }
+ PROFILER_LEAVE(g_profiler_ctx);
+
+ graphics->SetBlendFunc(
+ shaders->GetProgramBlendSrc(shader_id),
+ shaders->GetProgramBlendDst(shader_id));
+
+ CHECK_GL_ERROR();
+ PROFILER_LEAVE(g_profiler_ctx); // "Set up shader"
+
+ PROFILER_ENTER(g_profiler_ctx, "Textures");
+ if(particle_type->color_map.valid()){
+ textures->bindTexture(particle_type->color_map);
+ }
+ 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(particle_type->normal_map.valid()){
+ textures->bindTexture(particle_type->normal_map, 1);
+ }
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 2);
+ textures->bindTexture(scenegraph->sky->GetSpecularCubeMapTexture(), 3);
+ textures->bindTexture(graphics->screen_depth_tex, 5);
+
+ if( g_no_reflection_capture == false) {
+ textures->bindTexture(scenegraph->cubemaps, 19);
+ }
+ 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(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);
+ }
+ PROFILER_LEAVE(g_profiler_ctx); //"Textures"
+ CHECK_GL_ERROR();
+ PROFILER_LEAVE(g_profiler_ctx); // Prepare instanced particle draw
+
+ int instance_block_index = shaders->GetUBOBindIndex(shader_id, "InstanceInfo");
+ if ((unsigned)instance_block_index != GL_INVALID_INDEX)
+ {
+ const GLchar *names[] = {
+ "instance_color[0]",
+ "instance_transform[0]",
+ // These long names were necessary on a Mac OS 10.7 ATI card
+ "InstanceInfo.instance_color[0]",
+ "InstanceInfo.instance_transform[0]"
+ };
+
+ GLuint indices[2];
+ for(int i=0; i<2; ++i){
+ indices[i] = shaders->returnShaderVariableIndex(names[i], shader_id);
+ if(indices[i] == GL_INVALID_INDEX){
+ indices[i] = shaders->returnShaderVariableIndex(names[i+2], shader_id);
+ }
+ }
+ GLint offset[2];
+ for(int i=0; i<2; ++i){
+ if(indices[i] != GL_INVALID_INDEX){
+ offset[i] = shaders->returnShaderVariableOffset(indices[i], shader_id);
+ }
+ }
+ GLint block_size = shaders->returnShaderBlockSize(instance_block_index, shader_id);
+
+ //TODO: Allocate this memory on a stack_allocator, it's safer, this solution is less predictable and might blow the stack in certain cases, but at least it has a slightly higher chance of running than a statically defined array size.
+ LOG_ASSERT_GT(block_size, 0 );
+ LOG_ASSERT_LT(block_size, 32 * 1024 ); //Assert that the block size doesn't exceed a hefty 32kib
+
+ PROFILER_ENTER(g_profiler_ctx, "Setup uniform block");
+ if(particle_uniform_buffer.gl_id == -1){
+ particle_uniform_buffer.Create(128*1024);
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Setup uniform block
+
+ vec4* instance_color = (vec4*)((uintptr_t)blockBuffer + offset[0]);
+ mat4* instance_transform = (mat4*)((uintptr_t)blockBuffer + offset[1]);
+
+ const unsigned kBatchSize = 100;
+
+ for(unsigned index=start; index<i; index+=kBatchSize){
+ glUniformBlockBinding(shaders->programs[shader_id].gl_program, instance_block_index, 0);
+ unsigned to_draw = min(kBatchSize, ((int)i-index));
+ PROFILER_ENTER(g_profiler_ctx, "Setup batch data");
+
+ for(unsigned j=0; j<to_draw; ++j){
+ Particle* particle = particles[index+j];
+ mat4 transform;
+ vec3 up,right;
+ float temp_size;
+ vec3 temp_pos;
+ vec3 particle_facing;
+
+ vec3 look;
+ look = cam->GetPos() - particle->interp_position;
+
+ float cam_distance = length(look);
+ if(cam_distance > 0.0f){
+ look /= cam_distance;
+ }
+
+ bool velocity_axis = particle_type->velocity_axis;
+ if(velocity_axis || particle_type->speed_stretch){
+ particle_facing = look;
+ } else {
+ particle_facing = cam->GetFacing();
+ }
+
+ if(velocity_axis){
+ if(particle_type->speed_stretch){
+ up = particle->velocity / sqrtf(length(particle->velocity)) * 2.0f * particle_type->speed_mult;
+ } else {
+ up = normalize(particle->velocity);
+ }
+ } else {
+ up = AngleAxisRotation(cam->GetUpVector(), particle_facing, particle->interp_rotation);
+ }
+
+ right = normalize(cross(up, particle_facing));
+ if(particle_type->min_squash){
+ if(particle_type->speed_stretch){
+ float velocity_length = length(up);
+ if(fabs(dot(normalize(particle_facing),normalize(up)))>1.0f-0.2f/velocity_length/velocity_length){
+ float v = 1.0f-0.2f/velocity_length/velocity_length;
+
+ if( v < -1.0f ) {
+ v = -1.0f;
+ } else if( v > 1.0f ) {
+ v = 1.0f;
+ }
+
+ up = AngleAxisRotationRadian(particle_facing*velocity_length,right,acosf(v));
+ }
+ }
+ if(!particle_type->speed_stretch && fabs(dot(particle_facing,up))>0.8f){
+ up = AngleAxisRotationRadian(particle_facing,right,acosf(0.8f));
+ }
+ }
+ if(velocity_axis){
+ particle_facing = normalize(cross(up, right));
+ }
+
+ temp_pos = particle->interp_position;
+ temp_size = particle->interp_size;
+
+ float offset = particle->interp_size * 0.5f;
+ temp_pos += look * offset;
+ temp_size *= (cam_distance-offset)/cam_distance;
+
+ mat4 translation;
+ translation.SetTranslation(temp_pos);
+ mat4 rotation;
+ rotation.SetColumn(0, right);
+ rotation.SetColumn(1, normalize(up));
+ rotation.SetColumn(2, particle_facing);
+ mat4 scale;
+ scale[0] = temp_size;
+ scale[5] = temp_size*length(up);
+ scale[10] = temp_size;
+ transform = translation * rotation * scale;
+
+ if(indices[0] != GL_INVALID_INDEX){
+ instance_color[j] = particle->interp_color;
+ }
+ if(indices[1] != GL_INVALID_INDEX){
+ instance_transform[j] = transform;
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx); // Setup batch data
+ {
+ particle_uniform_buffer.Fill(16384-1, blockBuffer);
+ glBindBuffer( GL_UNIFORM_BUFFER, particle_uniform_buffer.gl_id );
+ glBindBufferRange( GL_UNIFORM_BUFFER, 0, particle_uniform_buffer.gl_id, particle_uniform_buffer.offset, particle_uniform_buffer.next_offset - particle_uniform_buffer.offset);
+ }
+ CHECK_GL_ERROR();
+ {
+ static VBOContainer data_vbo;
+ if(!data_vbo.valid()){
+ static const GLfloat data[] = {
+ 1, 1, 1, 1, 0,
+ 0, 1, -1, 1, 0,
+ 0, 0, -1, -1, 0,
+ 1, 0, 1, -1, 0
+ };
+ data_vbo.Fill(kVBOStatic | kVBOFloat,sizeof(data), (void*)data);
+ }
+ static VBOContainer index_vbo;
+ if(!index_vbo.valid()){
+ static const GLuint index[] = {0, 1, 2, 0, 3, 2};
+ index_vbo.Fill(kVBOStatic | kVBOElement, sizeof(index), (void*)index);
+ }
+ CHECK_GL_ERROR();
+ int vert_attrib_id = shaders->returnShaderAttrib("vertex_attrib", shader_id);
+ int tex_coord_attrib_id = shaders->returnShaderAttrib("tex_coord_attrib", shader_id);
+ data_vbo.Bind();
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(vert_attrib_id);
+ CHECK_GL_ERROR();
+ graphics->EnableVertexAttribArray(tex_coord_attrib_id);
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(vert_attrib_id, 3, GL_FLOAT, false, 5*sizeof(GLfloat), (const void*)(data_vbo.offset()+2*sizeof(GLfloat)));
+ CHECK_GL_ERROR();
+ glVertexAttribPointer(tex_coord_attrib_id, 2, GL_FLOAT, false, 5*sizeof(GLfloat), (const void*)data_vbo.offset());
+ CHECK_GL_ERROR();
+ index_vbo.Bind();
+ CHECK_GL_ERROR();
+ {
+ PROFILER_ZONE(g_profiler_ctx, "glDrawElementsInstanced");
+ graphics->DrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, to_draw);
+ }
+ graphics->ResetVertexAttribArrays();
+ CHECK_GL_ERROR();
+ }
+ CHECK_GL_ERROR();
+ }
+ }
+ }
+ if(i != particles.size()){
+ curr_type = particles[i]->particle_type;
+ unconnected = particles[i]->connected.empty();
+ start = i;
+ }
+ }
+ }
+ PROFILER_LEAVE(g_profiler_ctx); //"Draw Loop"
+ }
+ CHECK_GL_ERROR();
+}
+
+//Update particles
+void ParticleSystem::Update(SceneGraph *scenegraph, float timestep, float curr_game_time) {
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Particle script update");
+ script_context_.CallScriptFunction(as_funcs.update);
+ }
+ {
+ PROFILER_ZONE(g_profiler_ctx, "Looping through all particles");
+ for(int i=(int)particles.size()-1;i>=0;i--){
+ particles[i]->Update(scenegraph, timestep, curr_game_time);
+ if(particles[i]->color[3]<=0||particles[i]->size<=0)deleteParticle(i);
+ }
+ }
+}
+
+ParticleSystem::ParticleSystem(const ASData& as_data):
+ script_context_("particle_system", as_data), last_id_created(0)
+{
+ Path script_path = FindFilePath(script_dir_path+"particle_system.as", kModPaths | kDataPaths);
+ as_funcs.update = script_context_.RegisterExpectedFunction("void Update()", false);
+ script_context_.LoadScript(script_path);
+}
+
+//Delete particles
+void ParticleSystem::deleteParticle(unsigned int which)
+{
+ if(which<particles.size()){
+ Particle* deleted_particle = particles[which];
+ for(std::list<Particle*>::iterator iter = deleted_particle->connected_from.begin();
+ iter != deleted_particle->connected_from.end();
+ ++iter)
+ {
+ (*iter)->connected.remove(deleted_particle);
+ }
+ for(std::list<Particle*>::iterator iter = deleted_particle->connected.begin();
+ iter != deleted_particle->connected.end();
+ ++iter)
+ {
+ (*iter)->connected_from.remove(deleted_particle);
+ }
+ particle_map.erase(particle_map.find(particles[which]->id));
+ std::swap(particles[which],particles.back());
+ delete particles.back();
+ particles.pop_back();
+ }
+}
+
+unsigned ParticleSystem::CreateID() {
+ ++last_id_created;
+ return last_id_created;
+}
+
+unsigned ParticleSystem::MakeParticle( SceneGraph *scenegraph, const std::string &path, const vec3 &pos, const vec3 &vel, const vec3 &tint) {
+ //ParticleTypeRef particle_type_ref = particle_types->ReturnRef(path);
+ ParticleTypeRef particle_type_ref = Engine::Instance()->GetAssetManager()->LoadSync<ParticleType>(path);
+ Particle *particle = new Particle();
+ if(particle_type_ref->ae_ref.valid()){
+ particle->ae_reader.AttachTo(particle_type_ref->ae_ref);
+ }
+ particle->size = RangedRandomFloat(particle_type_ref->size_range[0], particle_type_ref->size_range[1]) * 0.5f;
+ for(int i=0; i<4; ++i){
+ particle->color[i] = RangedRandomFloat(particle_type_ref->color_range[0][i], particle_type_ref->color_range[1][i]);
+ }
+ particle->position = pos;
+ particle->velocity = vel;
+ for(int i=0; i<3; ++i){
+ particle->color[i] *= tint[i];
+ }
+ particle->particle_type = particle_type_ref;
+ particle->collided = false;
+ particle->alive_time=0.0f;
+ particle->initial_size = particle->size;
+ particle->initial_opacity = particle->color[3];
+ if(particle_type_ref->opacity_ramp_time > 0.0f){
+ particle->color[3] = 0.0f;
+ }
+ if(!particle_type_ref->no_rotation){
+ particle->rotate_speed = RangedRandomFloat(particle_type_ref->rotation_range[0], particle_type_ref->rotation_range[1]);
+ particle->rotation=(float)abs(rand()%720)-360;
+ } else {
+ particle->rotate_speed = 0.0f;
+ particle->rotation = 0.0f;
+ }
+ particle->old_position = particle->position;
+ particle->old_color = particle->color;
+ particle->old_size = particle->size;
+ particle->old_rotation = particle->rotation;
+ particle->has_last_connected = false;
+ int id = CreateID();
+ particle->id = id;
+
+ particles.push_back(particle);
+ particle_map[particle->id] = particle;
+ // Only allow one theora-reading particle at any given time
+ if(particle->ae_reader.valid()){
+ for(int i=(int)particles.size()-2; i>=0; --i){
+ if(particles[i]->ae_reader.valid()){
+ deleteParticle(i);
+ }
+ }
+ }
+ return id;
+}
+
+//Dispose of particle system
+void ParticleSystem::Dispose() {
+ for(ParticleVector::iterator it = particles.begin(); it != particles.end(); ++it){
+ delete (*it);
+ }
+ particles.clear();
+}
+
+void ParticleSystem::ConnectParticles( unsigned a, unsigned b ) {
+ ParticleMap::iterator a_iter = particle_map.find(a);
+ ParticleMap::iterator b_iter = particle_map.find(b);
+ if(a_iter == particle_map.end() || b_iter == particle_map.end()){
+ return;
+ }
+ a_iter->second->connected.push_back(b_iter->second);
+ b_iter->second->connected_from.push_back(a_iter->second);
+}
+
+void ParticleSystem::TintParticle( unsigned id, const vec3& color ) {
+ ParticleMap::iterator iter = particle_map.find(id);
+ if(iter != particle_map.end()){
+ Particle& particle = *(iter->second);
+ particle.color[0] *= color[0];
+ particle.color[1] *= color[1];
+ particle.color[2] *= color[2];
+ particle.old_color = particle.color;
+ }
+}
+
+ParticleSystem::~ParticleSystem() {
+ Dispose();
+}
+